SysModeler commited on
Commit
2c2a6f3
·
verified ·
1 Parent(s): c154f1a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -132
app.py CHANGED
@@ -3,38 +3,32 @@ import gradio as gr
3
  import warnings
4
  import json
5
  from dotenv import load_dotenv
6
- from typing import Dict, Any, List, Optional
7
  import time
8
  from functools import lru_cache
9
  import logging
10
-
11
- from langchain.agents import Tool, AgentExecutor
12
- from langchain.tools.retriever import create_retriever_tool
13
- from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
14
  from langchain_community.vectorstores import FAISS
15
  from langchain_community.embeddings import AzureOpenAIEmbeddings
16
- from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
17
  from openai import AzureOpenAI
18
 
19
-
20
  # Patch Gradio bug
21
  import gradio_client.utils
22
  gradio_client.utils.json_schema_to_python_type = lambda schema, defs=None: "string"
23
 
24
-
25
  # Load environment variables
26
  load_dotenv()
27
  AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
28
  AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
29
  AZURE_OPENAI_LLM_DEPLOYMENT = os.getenv("AZURE_OPENAI_LLM_DEPLOYMENT")
30
  AZURE_OPENAI_EMBEDDING_DEPLOYMENT = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT")
31
-
32
  if not all([AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_LLM_DEPLOYMENT, AZURE_OPENAI_EMBEDDING_DEPLOYMENT]):
33
  raise ValueError("Missing one or more Azure OpenAI environment variables.")
34
-
35
  warnings.filterwarnings("ignore")
36
-
37
- # Embeddings for retriever
38
  embeddings = AzureOpenAIEmbeddings(
39
  azure_deployment=AZURE_OPENAI_EMBEDDING_DEPLOYMENT,
40
  azure_endpoint=AZURE_OPENAI_ENDPOINT,
@@ -42,57 +36,41 @@ embeddings = AzureOpenAIEmbeddings(
42
  openai_api_version="2025-01-01-preview",
43
  chunk_size=1000
44
  )
45
-
46
- # Get the directory where this script is located
47
  SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
48
-
49
- # Build the absolute path to the faiss_index_sysml directory relative to this script
50
  FAISS_INDEX_PATH = os.path.join(SCRIPT_DIR, "faiss_index_sysml")
51
- # Load FAISS vectorstore
52
  vectorstore = FAISS.load_local(FAISS_INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
53
-
54
- # Initialize Azure OpenAI client directly
55
  client = AzureOpenAI(
56
  api_key=AZURE_OPENAI_API_KEY,
57
  api_version="2025-01-01-preview",
58
  azure_endpoint=AZURE_OPENAI_ENDPOINT
59
  )
60
-
61
-
62
  logger = logging.getLogger(__name__)
 
63
  # SysML retriever function
64
  @lru_cache(maxsize=100)
65
  def sysml_retriever(query: str) -> str:
66
- start_time = time.time()
67
  try:
68
  results = vectorstore.similarity_search(query, k=100)
69
  contexts = [doc.page_content for doc in results]
70
- response = "\n\n".join(contexts)
71
-
72
- # Log performance metrics
73
- duration = time.time() - start_time
74
- print(f"Retrieval completed in {duration:.2f}s for query: {query[:50]}...")
75
-
76
- return response
77
  except Exception as e:
78
  logger.error(f"Retrieval error: {str(e)}")
79
  return "Unable to retrieve information at this time."
80
-
81
-
82
- # sysml_retriever = create_retriever_tool(
83
- # retriever=vectorstore.as_retriever(),
84
- # name="SysMLRetriever",
85
- # description="Use this to answer questions about SysML diagrams and modeling."
86
- # )
87
 
88
  # Dummy functions
89
  def dummy_weather_lookup(location: str = "London") -> str:
90
  return f"The weather in {location} is sunny and 25°C."
91
-
92
  def dummy_time_lookup(timezone: str = "UTC") -> str:
93
  return f"The current time in {timezone} is 3:00 PM."
94
-
95
- # Tools definition for OpenAI function calling
96
  tools_definition = [
97
  {
98
  "type": "function",
@@ -102,10 +80,7 @@ tools_definition = [
102
  "parameters": {
103
  "type": "object",
104
  "properties": {
105
- "query": {
106
- "type": "string",
107
- "description": "The search query to find information about SysML"
108
- }
109
  },
110
  "required": ["query"]
111
  }
@@ -119,14 +94,11 @@ tools_definition = [
119
  "parameters": {
120
  "type": "object",
121
  "properties": {
122
- "location": {
123
- "type": "string",
124
- "description": "The location to look up the weather for"
125
- }
126
  },
127
  "required": ["location"]
128
  }
129
- },
130
  },
131
  {
132
  "type": "function",
@@ -136,24 +108,21 @@ tools_definition = [
136
  "parameters": {
137
  "type": "object",
138
  "properties": {
139
- "timezone": {
140
- "type": "string",
141
- "description": "The timezone to look up the current time for"
142
- }
143
  },
144
  "required": ["timezone"]
145
  }
146
  }
147
  }
148
  ]
149
-
150
  # Tool execution mapping
151
  tool_mapping = {
152
  "SysMLRetriever": sysml_retriever,
153
  "WeatherLookup": dummy_weather_lookup,
154
  "TimeLookup": dummy_time_lookup
155
  }
156
-
157
  # Convert chat history
158
  def convert_history_to_messages(history):
159
  messages = []
@@ -162,113 +131,72 @@ def convert_history_to_messages(history):
162
  messages.append({"role": "assistant", "content": bot})
163
  return messages
164
 
165
-
166
- history = []
167
-
168
-
169
- # Main chatbot function with direct function calling
170
  def sysml_chatbot(message, history):
171
- # Convert history to messages format
172
  chat_messages = convert_history_to_messages(history)
173
-
174
- # Add system message at beginning
175
  full_messages = [
176
- {"role": "system", "content": "You are a helpful SysML modeling assistant and also a capable smart Assistant "}
177
- ]
178
- full_messages.extend(chat_messages)
179
-
180
- # Add current user message
181
- full_messages.append({"role": "user", "content": message})
182
-
183
  try:
184
- # First call to get either a direct answer or a function call
185
  response = client.chat.completions.create(
186
  model=AZURE_OPENAI_LLM_DEPLOYMENT,
187
  messages=full_messages,
188
  tools=tools_definition,
189
  tool_choice={"type": "function", "function": {"name": "SysMLRetriever"}}
190
  )
191
-
192
  assistant_message = response.choices[0].message
193
-
194
- # Check if the model wants to call a function
195
  if assistant_message.tool_calls:
196
- # Get the function call details
197
  tool_call = assistant_message.tool_calls[0]
198
  function_name = tool_call.function.name
199
  function_args = json.loads(tool_call.function.arguments)
200
- print("Attempting function calling...")
201
- # Execute the function
202
  if function_name in tool_mapping:
203
  function_response = tool_mapping[function_name](**function_args)
204
-
205
- # Append the assistant's request and the function response to messages
206
- full_messages.append({"role": "assistant", "content": None, "tool_calls": [
207
- {"id": tool_call.id, "type": "function", "function": {"name": function_name, "arguments": tool_call.function.arguments}}
208
- ]})
209
-
 
 
 
 
 
 
210
  full_messages.append({
211
  "role": "tool",
212
  "tool_call_id": tool_call.id,
213
  "content": function_response
214
  })
215
-
216
- # Second call to get the final answer based on the function result
217
  second_response = client.chat.completions.create(
218
  model=AZURE_OPENAI_LLM_DEPLOYMENT,
219
  messages=full_messages
220
  )
221
-
222
  answer = second_response.choices[0].message.content
223
- print("Getting final response after function execution...")
224
- #print(f"Function '{function_name}' executed successfully. Response: {answer}")
225
  else:
226
- answer = f"I tried to use a function '{function_name}' that's not available. Let me try again with general knowledge: SysML is a modeling language for systems engineering that helps visualize and analyze complex systems."
227
  else:
228
- # Model provided a direct answer
229
  answer = assistant_message.content
230
-
231
  history.append((message, answer))
232
- return answer, history
233
-
234
  except Exception as e:
235
  print(f"Error in function calling: {str(e)}")
236
-
237
- # Fallback to a direct response without function calling
238
- try:
239
- simple_messages = [
240
- {"role": "system", "content": "You are a helpful SysML modeling assistant."}
241
- ]
242
- simple_messages.extend(chat_messages)
243
- simple_messages.append({"role": "user", "content": message})
244
-
245
- fallback_response = client.chat.completions.create(
246
- model=AZURE_OPENAI_LLM_DEPLOYMENT,
247
- messages=simple_messages
248
- )
249
-
250
- answer = fallback_response.choices[0].message.content
251
- except Exception as fallback_error:
252
- print(f"Error in fallback: {str(fallback_error)}")
253
- answer = "I'm having trouble accessing my tools right now. SysML is a modeling language used in systems engineering to visualize and analyze complex systems through various diagram types."
254
-
255
- history.append((message, answer))
256
- return answer, history
257
 
258
- # Gradio UI
259
  with gr.Blocks(css="""
260
- #submit-btn {
261
- height: 100%;
262
- background-color: #48CAE4;
263
- color: white;
264
- font-size: 1.5em;
265
- }
266
  """) as demo:
267
-
268
  gr.Markdown("## SysModeler Chatbot")
269
 
270
  chatbot = gr.Chatbot(height=600)
271
-
272
  with gr.Row():
273
  with gr.Column(scale=5):
274
  msg = gr.Textbox(
@@ -277,19 +205,14 @@ with gr.Blocks(css="""
277
  show_label=False
278
  )
279
  with gr.Column(scale=1, min_width=50):
280
- submit_btn = gr.Button("➤")
281
 
282
  clear = gr.Button("Clear")
 
283
 
284
- state = gr.State(history)
285
-
286
- submit_btn.click(fn=sysml_chatbot, inputs=[msg, state], outputs=["", chatbot])
287
- msg.submit(fn=sysml_chatbot, inputs=[msg, state], outputs=["", chatbot]) # still supports enter key
288
-
289
- # submit_btn.click(fn=sysml_chatbot, inputs=[msg, state], outputs=[gr.Textbox.update(value=""), chatbot])
290
- # msg.submit(fn=sysml_chatbot, inputs=[msg, state], outputs=[gr.Textbox.update(value=""), chatbot])
291
-
292
  clear.click(fn=lambda: ([], ""), inputs=None, outputs=[chatbot, msg])
293
 
294
  if __name__ == "__main__":
295
- demo.launch()
 
3
  import warnings
4
  import json
5
  from dotenv import load_dotenv
6
+ from typing import List
7
  import time
8
  from functools import lru_cache
9
  import logging
10
+
 
 
 
11
  from langchain_community.vectorstores import FAISS
12
  from langchain_community.embeddings import AzureOpenAIEmbeddings
 
13
  from openai import AzureOpenAI
14
 
 
15
  # Patch Gradio bug
16
  import gradio_client.utils
17
  gradio_client.utils.json_schema_to_python_type = lambda schema, defs=None: "string"
18
 
 
19
  # Load environment variables
20
  load_dotenv()
21
  AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
22
  AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
23
  AZURE_OPENAI_LLM_DEPLOYMENT = os.getenv("AZURE_OPENAI_LLM_DEPLOYMENT")
24
  AZURE_OPENAI_EMBEDDING_DEPLOYMENT = os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT")
25
+
26
  if not all([AZURE_OPENAI_API_KEY, AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_LLM_DEPLOYMENT, AZURE_OPENAI_EMBEDDING_DEPLOYMENT]):
27
  raise ValueError("Missing one or more Azure OpenAI environment variables.")
28
+
29
  warnings.filterwarnings("ignore")
30
+
31
+ # Embeddings
32
  embeddings = AzureOpenAIEmbeddings(
33
  azure_deployment=AZURE_OPENAI_EMBEDDING_DEPLOYMENT,
34
  azure_endpoint=AZURE_OPENAI_ENDPOINT,
 
36
  openai_api_version="2025-01-01-preview",
37
  chunk_size=1000
38
  )
39
+
40
+ # Vectorstore
41
  SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
 
 
42
  FAISS_INDEX_PATH = os.path.join(SCRIPT_DIR, "faiss_index_sysml")
 
43
  vectorstore = FAISS.load_local(FAISS_INDEX_PATH, embeddings, allow_dangerous_deserialization=True)
44
+
45
+ # OpenAI client
46
  client = AzureOpenAI(
47
  api_key=AZURE_OPENAI_API_KEY,
48
  api_version="2025-01-01-preview",
49
  azure_endpoint=AZURE_OPENAI_ENDPOINT
50
  )
51
+
52
+ # Logger
53
  logger = logging.getLogger(__name__)
54
+
55
  # SysML retriever function
56
  @lru_cache(maxsize=100)
57
  def sysml_retriever(query: str) -> str:
 
58
  try:
59
  results = vectorstore.similarity_search(query, k=100)
60
  contexts = [doc.page_content for doc in results]
61
+ return "\n\n".join(contexts)
 
 
 
 
 
 
62
  except Exception as e:
63
  logger.error(f"Retrieval error: {str(e)}")
64
  return "Unable to retrieve information at this time."
 
 
 
 
 
 
 
65
 
66
  # Dummy functions
67
  def dummy_weather_lookup(location: str = "London") -> str:
68
  return f"The weather in {location} is sunny and 25°C."
69
+
70
  def dummy_time_lookup(timezone: str = "UTC") -> str:
71
  return f"The current time in {timezone} is 3:00 PM."
72
+
73
+ # Tools for function calling
74
  tools_definition = [
75
  {
76
  "type": "function",
 
80
  "parameters": {
81
  "type": "object",
82
  "properties": {
83
+ "query": {"type": "string", "description": "The search query to find information about SysML"}
 
 
 
84
  },
85
  "required": ["query"]
86
  }
 
94
  "parameters": {
95
  "type": "object",
96
  "properties": {
97
+ "location": {"type": "string", "description": "The location to look up the weather for"}
 
 
 
98
  },
99
  "required": ["location"]
100
  }
101
+ }
102
  },
103
  {
104
  "type": "function",
 
108
  "parameters": {
109
  "type": "object",
110
  "properties": {
111
+ "timezone": {"type": "string", "description": "The timezone to look up the current time for"}
 
 
 
112
  },
113
  "required": ["timezone"]
114
  }
115
  }
116
  }
117
  ]
118
+
119
  # Tool execution mapping
120
  tool_mapping = {
121
  "SysMLRetriever": sysml_retriever,
122
  "WeatherLookup": dummy_weather_lookup,
123
  "TimeLookup": dummy_time_lookup
124
  }
125
+
126
  # Convert chat history
127
  def convert_history_to_messages(history):
128
  messages = []
 
131
  messages.append({"role": "assistant", "content": bot})
132
  return messages
133
 
134
+ # Chatbot logic
 
 
 
 
135
  def sysml_chatbot(message, history):
 
136
  chat_messages = convert_history_to_messages(history)
 
 
137
  full_messages = [
138
+ {"role": "system", "content": "You are a helpful SysML modeling assistant and also a capable smart Assistant"}
139
+ ] + chat_messages + [{"role": "user", "content": message}]
 
 
 
 
 
140
  try:
 
141
  response = client.chat.completions.create(
142
  model=AZURE_OPENAI_LLM_DEPLOYMENT,
143
  messages=full_messages,
144
  tools=tools_definition,
145
  tool_choice={"type": "function", "function": {"name": "SysMLRetriever"}}
146
  )
 
147
  assistant_message = response.choices[0].message
 
 
148
  if assistant_message.tool_calls:
 
149
  tool_call = assistant_message.tool_calls[0]
150
  function_name = tool_call.function.name
151
  function_args = json.loads(tool_call.function.arguments)
 
 
152
  if function_name in tool_mapping:
153
  function_response = tool_mapping[function_name](**function_args)
154
+ full_messages.append({
155
+ "role": "assistant",
156
+ "content": None,
157
+ "tool_calls": [{
158
+ "id": tool_call.id,
159
+ "type": "function",
160
+ "function": {
161
+ "name": function_name,
162
+ "arguments": tool_call.function.arguments
163
+ }
164
+ }]
165
+ })
166
  full_messages.append({
167
  "role": "tool",
168
  "tool_call_id": tool_call.id,
169
  "content": function_response
170
  })
 
 
171
  second_response = client.chat.completions.create(
172
  model=AZURE_OPENAI_LLM_DEPLOYMENT,
173
  messages=full_messages
174
  )
 
175
  answer = second_response.choices[0].message.content
 
 
176
  else:
177
+ answer = f"I tried to use a function '{function_name}' that's not available."
178
  else:
 
179
  answer = assistant_message.content
 
180
  history.append((message, answer))
181
+ return "", history
 
182
  except Exception as e:
183
  print(f"Error in function calling: {str(e)}")
184
+ history.append((message, "Sorry, something went wrong."))
185
+ return "", history
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
+ # === Gradio UI ===
188
  with gr.Blocks(css="""
189
+ #submit-btn {
190
+ height: 100%;
191
+ background-color: #48CAE4;
192
+ color: white;
193
+ font-size: 1.5em;
194
+ }
195
  """) as demo:
196
+
197
  gr.Markdown("## SysModeler Chatbot")
198
 
199
  chatbot = gr.Chatbot(height=600)
 
200
  with gr.Row():
201
  with gr.Column(scale=5):
202
  msg = gr.Textbox(
 
205
  show_label=False
206
  )
207
  with gr.Column(scale=1, min_width=50):
208
+ submit_btn = gr.Button("➤", elem_id="submit-btn")
209
 
210
  clear = gr.Button("Clear")
211
+ state = gr.State([])
212
 
213
+ submit_btn.click(fn=sysml_chatbot, inputs=[msg, state], outputs=[msg, chatbot])
214
+ msg.submit(fn=sysml_chatbot, inputs=[msg, state], outputs=[msg, chatbot])
 
 
 
 
 
 
215
  clear.click(fn=lambda: ([], ""), inputs=None, outputs=[chatbot, msg])
216
 
217
  if __name__ == "__main__":
218
+ demo.launch()