File size: 8,275 Bytes
9847531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# chatbot_agent.py

import os
import json
import re
from openai import OpenAI
import traceback
import logging

# Get logger
logger = logging.getLogger(__name__)

client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def format_message(role, content):
    """Format message for chatbot history."""
    return {"role": role, "content": content}

def initialize_graph_prompt(graph_data):
    """Initialize the conversation with detailed node, edge, linker, and text information."""
    summary_info = (
        f"Symbols (represented as nodes): {graph_data['summary']['symbol_count']}, "
        f"Texts: {graph_data['summary']['text_count']}, "
        f"Lines: {graph_data['summary']['line_count']}, "
        f"Linkers: {graph_data['summary']['linker_count']}, "
        f"Edges: {graph_data['summary']['edge_count']}."
    )

    # Prepare detailed node (symbol) data
    node_details = "Nodes (symbols) in the graph include the following details:\n"
    for symbol in graph_data["detailed_results"]["symbols"]:
        node_details += (
            f"Node ID: {symbol['symbol_id']}, Class ID: {symbol['class_id']}, "
            f"Category: {symbol['category']}, Type: {symbol['type']}, "
            f"Label: {symbol['label']}, Confidence: {symbol['confidence']}\n"
        )

    # Prepare edge data
    edge_details = "Edges in the graph showing connections between nodes are as follows:\n"
    for edge in graph_data["detailed_results"].get("edges", []):
        edge_details += (
            f"Edge ID: {edge['edge_id']}, From Node: {edge['symbol_1_id']}, "
            f"To Node: {edge['symbol_2_id']}, Type: {edge.get('type', 'unknown')}\n"
        )


    # Prepare linker data
    linker_details = "Linkers in the diagram are as follows:\n"
    for linker in graph_data["detailed_results"].get("linkers", []):
        linker_details += (
            f"Symbol ID: {linker['symbol_id']}, Associated Text IDs: {linker.get('text_ids', [])}, "
            f"Associated Edge IDs: {linker.get('edge_ids', [])}, Position: {linker.get('bbox', 'unknown')}\n"
        )


    # Prepare text (tag) data
    text_details = "Text elements with associated tags in the diagram are as follows:\n"
    for text in graph_data["detailed_results"].get("texts", []):
        text_details += (
            f"Text ID: {text['text_id']}, Content: {text['content']}, "
            f"Confidence: {text['confidence']}, Position: {text['bbox']}\n"
        )


    initial_prompt = (
        "You have access to a knowledge graph generated from a P&ID diagram. "
        f"The summary information includes:\n{summary_info}\n\n"
        "The detailed information about each node (symbol) in the graph is as follows:\n"
        f"{node_details}\n"
        "The edges connecting these nodes are as follows:\n"
        f"{edge_details}\n"
        "The linkers in the diagram are as follows:\n"
        f"{linker_details}\n"
        "The text elements and their tags in the diagram are as follows:\n"
        f"{text_details}\n"
        "Answer questions about specific nodes, edges, types, labels, categories, linkers, or text tags using this information."
    )

    return initial_prompt

def get_assistant_response(user_message, json_path):
    """Generate response based on P&ID data and OpenAI."""
    try:
        # Load the aggregated data
        with open(json_path, 'r') as f:
            data = json.load(f)
            
        # Process the user's question
        question = user_message.lower()
        
        # Use rule-based responses for specific questions
        if "valve" in question or "valves" in question:
            valve_count = sum(1 for symbol in data.get('symbols', []) 
                            if 'class' in symbol and 'valve' in symbol['class'].lower())
            return f"I found {valve_count} valves in this P&ID."
            
        elif "pump" in question or "pumps" in question:
            pump_count = sum(1 for symbol in data.get('symbols', [])
                           if 'class' in symbol and 'pump' in symbol['class'].lower())
            return f"I found {pump_count} pumps in this P&ID."
            
        elif "equipment" in question or "components" in question:
            equipment_types = {}
            for symbol in data.get('symbols', []):
                if 'class' in symbol:
                    eq_type = symbol['class']
                    equipment_types[eq_type] = equipment_types.get(eq_type, 0) + 1
            
            response = "Here's a summary of the equipment I found:\n"
            for eq_type, count in equipment_types.items():
                response += f"- {eq_type}: {count}\n"
            return response
            
        # For other questions, use OpenAI
        else:
            # Prepare the conversation context
            graph_data = {
                "summary": {
                    "symbol_count": len(data.get('symbols', [])),
                    "text_count": len(data.get('texts', [])),
                    "line_count": len(data.get('lines', [])),
                    "edge_count": len(data.get('edges', [])),
                },
                "detailed_results": data
            }
            
            initial_prompt = initialize_graph_prompt(graph_data)
            conversation = [
                {"role": "system", "content": initial_prompt},
                {"role": "user", "content": user_message}
            ]

            response = client.chat.completions.create(
                model="gpt-4-turbo",
                messages=conversation
            )
            return response.choices[0].message.content
            
    except Exception as e:
        logger.error(f"Error in get_assistant_response: {str(e)}")
        logger.error(traceback.format_exc())
        return "I apologize, but I encountered an error analyzing the P&ID data. Please try asking a different question."

# Testing and Usage block
if __name__ == "__main__":
    # Load the knowledge graph data from JSON file
    json_file_path = "results/0_aggregated_detections.json"
    try:
        with open(json_file_path, 'r') as file:
            graph_data = json.load(file)
    except FileNotFoundError:
        print(f"Error: File not found at {json_file_path}")
        graph_data = None
    except json.JSONDecodeError:
        print("Error: Failed to decode JSON. Please check the file format.")
        graph_data = None

    # Initialize conversation history with assistant's welcome message
    history = [format_message("assistant", "Hello! I am ready to answer your questions about the P&ID knowledge graph. The graph includes nodes (symbols), edges, linkers, and text tags, and I have detailed information available about each. Please ask any questions related to these elements and their connections.")]

    # Print the assistant's welcome message
    print("Assistant:", history[0]["content"])

    # Individual Testing Options
    if graph_data:
        # Option 1: Test the graph prompt initialization
        print("\n--- Test: Graph Prompt Initialization ---")
        initial_prompt = initialize_graph_prompt(graph_data)
        print(initial_prompt)

        # Option 2: Simulate a conversation with a test question
        print("\n--- Test: Simulate Conversation ---")
        test_question = "Can you tell me about the connections between the nodes?"
        history.append(format_message("user", test_question))
        
        print(f"\nUser: {test_question}")
        for response in get_assistant_response(test_question, json_file_path):
            print("Assistant:", response)
            history.append(format_message("assistant", response))

        # Option 3: Manually input questions for interactive testing
        while True:
            user_question = input("\nYou: ")
            if user_question.lower() in ["exit", "quit"]:
                print("Exiting chat. Goodbye!")
                break

            history.append(format_message("user", user_question))
            for response in get_assistant_response(user_question, json_file_path):
                print("Assistant:", response)
                history.append(format_message("assistant", response))
    else:
        print("Unable to load graph data. Please check the file path and format.")