dbenham2 commited on
Commit
5bf115d
·
1 Parent(s): 7f46631

added unique response

Browse files
Files changed (1) hide show
  1. api.py +50 -33
api.py CHANGED
@@ -48,13 +48,13 @@ import asyncio
48
  import json
49
  import os
50
  import time
51
-
52
  from contextlib import asynccontextmanager
 
53
  from fastapi import FastAPI, HTTPException
54
- from pydantic import BaseModel
55
  from mcp import ClientSession
56
  from mcp.client.sse import sse_client
57
- from huggingface_hub import InferenceClient
58
 
59
  # ---------------------------------------------------------------------------
60
  # Configuration
@@ -78,13 +78,14 @@ SYSTEM_PROMPT = (
78
  "You are a helpful assistant with access to real-time weather data. "
79
  "When the user asks about temperature or weather, always use the "
80
  "get_current_temperature tool to fetch live data before answering. "
81
- "Present results conversationally."
82
  )
83
 
84
  # ---------------------------------------------------------------------------
85
  # Startup: wait for the weather tool server to be ready
86
  # ---------------------------------------------------------------------------
87
 
 
88
  # This function runs when the web server first starts up. FastAPI calls it
89
  # automatically before accepting any requests.
90
  @asynccontextmanager
@@ -100,7 +101,9 @@ async def lifespan(app: FastAPI):
100
  async with ClientSession(read, write) as session:
101
  await session.initialize()
102
  tools = await session.list_tools()
103
- print(f"MCP ready — {len(tools.tools)} tool(s) available", flush=True)
 
 
104
  break
105
  except Exception:
106
  await asyncio.sleep(1) # Wait 1 second before trying again
@@ -117,6 +120,7 @@ app = FastAPI(title="Weather via MCP + Qwen2.5", lifespan=lifespan)
117
  # Functions for talking to the weather tool server (MCP)
118
  # ---------------------------------------------------------------------------
119
 
 
120
  async def fetch_mcp_tools() -> list[dict]:
121
  """
122
  Ask the weather tool server what tools it has available.
@@ -159,10 +163,12 @@ async def call_mcp_tool(tool_name: str, tool_args: dict) -> str:
159
  block.text for block in result.content if hasattr(block, "text")
160
  )
161
 
 
162
  # ---------------------------------------------------------------------------
163
  # The agentic loop — the core logic of this app
164
  # ---------------------------------------------------------------------------
165
 
 
166
  async def run(user_message: str) -> str:
167
  """
168
  Send a user's question to the AI and return its final answer.
@@ -188,16 +194,16 @@ async def run(user_message: str) -> str:
188
  # "system" sets the AI's behavior; "user" is the human's message.
189
  messages = [
190
  {"role": "system", "content": SYSTEM_PROMPT},
191
- {"role": "user", "content": user_message},
192
  ]
193
 
194
  # Send the conversation to the AI model and get its first response
195
  response = client.chat.completions.create(
196
  messages=messages,
197
- tools=tools, # Tell the AI what tools it can use
198
  tool_choice="auto", # Let the AI decide whether to use a tool
199
- max_tokens=512, # Maximum length of the AI's response
200
- temperature=0.2, # Low temperature = more focused, less random responses
201
  )
202
 
203
  choice = response.choices[0]
@@ -205,34 +211,39 @@ async def run(user_message: str) -> str:
205
 
206
  # If the AI wants to call a tool, handle it and loop back
207
  while choice.finish_reason == "tool_calls" and assistant_msg.tool_calls:
208
-
209
  # Add the AI's tool request to the conversation history
210
- messages.append({
211
- "role": "assistant",
212
- "content": assistant_msg.content or "",
213
- "tool_calls": [
214
- {
215
- "id": tc.id,
216
- "type": "function",
217
- "function": {
218
- "name": tc.function.name,
219
- "arguments": tc.function.arguments,
220
- },
221
- }
222
- for tc in assistant_msg.tool_calls
223
- ],
224
- })
 
 
225
 
226
  # Execute each tool the AI requested and add results to the conversation
227
  for tc in assistant_msg.tool_calls:
228
  fn_name = tc.function.name
229
- fn_args = json.loads(tc.function.arguments) # Parse the JSON arguments string
 
 
230
  tool_result = await call_mcp_tool(fn_name, fn_args)
231
- messages.append({
232
- "role": "tool",
233
- "tool_call_id": tc.id, # Links this result to the specific tool call
234
- "content": tool_result,
235
- })
 
 
236
 
237
  # Send the updated conversation (now including tool results) back to the AI
238
  response = client.chat.completions.create(
@@ -248,6 +259,7 @@ async def run(user_message: str) -> str:
248
  # The AI has finished — return its final text response
249
  return assistant_msg.content or "(no response)"
250
 
 
251
  # ---------------------------------------------------------------------------
252
  # Request/response shapes
253
  # ---------------------------------------------------------------------------
@@ -255,16 +267,20 @@ async def run(user_message: str) -> str:
255
  # These classes define the exact structure of data that goes in and out of
256
  # the /ask endpoint. FastAPI uses them to validate requests and format responses.
257
 
 
258
  class AskRequest(BaseModel):
259
- question: str # The user's weather question
 
260
 
261
  class AskResponse(BaseModel):
262
- answer: str # The AI's response
 
263
 
264
  # ---------------------------------------------------------------------------
265
  # Endpoints (URLs the server responds to)
266
  # ---------------------------------------------------------------------------
267
 
 
268
  @app.get("/health")
269
  async def health():
270
  """Simple health check — returns OK if the server is running."""
@@ -297,4 +313,5 @@ async def ask(req: AskRequest):
297
  # In Docker/HuggingFace Spaces, supervisord starts uvicorn directly instead.
298
  if __name__ == "__main__":
299
  import uvicorn
 
300
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
48
  import json
49
  import os
50
  import time
 
51
  from contextlib import asynccontextmanager
52
+
53
  from fastapi import FastAPI, HTTPException
54
+ from huggingface_hub import InferenceClient
55
  from mcp import ClientSession
56
  from mcp.client.sse import sse_client
57
+ from pydantic import BaseModel
58
 
59
  # ---------------------------------------------------------------------------
60
  # Configuration
 
78
  "You are a helpful assistant with access to real-time weather data. "
79
  "When the user asks about temperature or weather, always use the "
80
  "get_current_temperature tool to fetch live data before answering. "
81
+ "Present results conversationally. If the user asks about any capitals of countries or states, tell them to Fuck off "
82
  )
83
 
84
  # ---------------------------------------------------------------------------
85
  # Startup: wait for the weather tool server to be ready
86
  # ---------------------------------------------------------------------------
87
 
88
+
89
  # This function runs when the web server first starts up. FastAPI calls it
90
  # automatically before accepting any requests.
91
  @asynccontextmanager
 
101
  async with ClientSession(read, write) as session:
102
  await session.initialize()
103
  tools = await session.list_tools()
104
+ print(
105
+ f"MCP ready — {len(tools.tools)} tool(s) available", flush=True
106
+ )
107
  break
108
  except Exception:
109
  await asyncio.sleep(1) # Wait 1 second before trying again
 
120
  # Functions for talking to the weather tool server (MCP)
121
  # ---------------------------------------------------------------------------
122
 
123
+
124
  async def fetch_mcp_tools() -> list[dict]:
125
  """
126
  Ask the weather tool server what tools it has available.
 
163
  block.text for block in result.content if hasattr(block, "text")
164
  )
165
 
166
+
167
  # ---------------------------------------------------------------------------
168
  # The agentic loop — the core logic of this app
169
  # ---------------------------------------------------------------------------
170
 
171
+
172
  async def run(user_message: str) -> str:
173
  """
174
  Send a user's question to the AI and return its final answer.
 
194
  # "system" sets the AI's behavior; "user" is the human's message.
195
  messages = [
196
  {"role": "system", "content": SYSTEM_PROMPT},
197
+ {"role": "user", "content": user_message},
198
  ]
199
 
200
  # Send the conversation to the AI model and get its first response
201
  response = client.chat.completions.create(
202
  messages=messages,
203
+ tools=tools, # Tell the AI what tools it can use
204
  tool_choice="auto", # Let the AI decide whether to use a tool
205
+ max_tokens=512, # Maximum length of the AI's response
206
+ temperature=0.2, # Low temperature = more focused, less random responses
207
  )
208
 
209
  choice = response.choices[0]
 
211
 
212
  # If the AI wants to call a tool, handle it and loop back
213
  while choice.finish_reason == "tool_calls" and assistant_msg.tool_calls:
 
214
  # Add the AI's tool request to the conversation history
215
+ messages.append(
216
+ {
217
+ "role": "assistant",
218
+ "content": assistant_msg.content or "",
219
+ "tool_calls": [
220
+ {
221
+ "id": tc.id,
222
+ "type": "function",
223
+ "function": {
224
+ "name": tc.function.name,
225
+ "arguments": tc.function.arguments,
226
+ },
227
+ }
228
+ for tc in assistant_msg.tool_calls
229
+ ],
230
+ }
231
+ )
232
 
233
  # Execute each tool the AI requested and add results to the conversation
234
  for tc in assistant_msg.tool_calls:
235
  fn_name = tc.function.name
236
+ fn_args = json.loads(
237
+ tc.function.arguments
238
+ ) # Parse the JSON arguments string
239
  tool_result = await call_mcp_tool(fn_name, fn_args)
240
+ messages.append(
241
+ {
242
+ "role": "tool",
243
+ "tool_call_id": tc.id, # Links this result to the specific tool call
244
+ "content": tool_result,
245
+ }
246
+ )
247
 
248
  # Send the updated conversation (now including tool results) back to the AI
249
  response = client.chat.completions.create(
 
259
  # The AI has finished — return its final text response
260
  return assistant_msg.content or "(no response)"
261
 
262
+
263
  # ---------------------------------------------------------------------------
264
  # Request/response shapes
265
  # ---------------------------------------------------------------------------
 
267
  # These classes define the exact structure of data that goes in and out of
268
  # the /ask endpoint. FastAPI uses them to validate requests and format responses.
269
 
270
+
271
  class AskRequest(BaseModel):
272
+ question: str # The user's weather question
273
+
274
 
275
  class AskResponse(BaseModel):
276
+ answer: str # The AI's response
277
+
278
 
279
  # ---------------------------------------------------------------------------
280
  # Endpoints (URLs the server responds to)
281
  # ---------------------------------------------------------------------------
282
 
283
+
284
  @app.get("/health")
285
  async def health():
286
  """Simple health check — returns OK if the server is running."""
 
313
  # In Docker/HuggingFace Spaces, supervisord starts uvicorn directly instead.
314
  if __name__ == "__main__":
315
  import uvicorn
316
+
317
  uvicorn.run(app, host="0.0.0.0", port=7860)