Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -41,6 +41,7 @@ from tempfile import mkdtemp
|
|
| 41 |
from langchain.schema import AIMessage
|
| 42 |
from datetime import datetime
|
| 43 |
from zoneinfo import ZoneInfo
|
|
|
|
| 44 |
import numexpr as ne
|
| 45 |
import pandas as pd
|
| 46 |
|
|
@@ -53,6 +54,7 @@ from geopy.geocoders import Nominatim
|
|
| 53 |
from timezonefinder import TimezoneFinder
|
| 54 |
from langchain_experimental.agents import create_pandas_dataframe_agent
|
| 55 |
|
|
|
|
| 56 |
session_retriever = None
|
| 57 |
session_qa_chain = None
|
| 58 |
csv_dataframe = None # CSV tool will use this
|
|
@@ -450,83 +452,148 @@ weather_api_key = os.environ.get("WEATHER_API_KEY")
|
|
| 450 |
|
| 451 |
def weather_agent_tool(query: str) -> str:
|
| 452 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 453 |
location_prompt = f"""
|
| 454 |
You are a location extractor. Given a user's query about weather, return the location mentioned in it. If not found, return "London".
|
| 455 |
-
|
| 456 |
Examples:
|
| 457 |
- "What's the weather in Tokyo now?" → Tokyo
|
| 458 |
- "紐約天氣如何?" → New York
|
| 459 |
- "今天台北下雨嗎?" → Taipei
|
| 460 |
-
- "
|
| 461 |
-
- "Is it
|
| 462 |
-
|
| 463 |
Now process this query: "{query}"
|
| 464 |
"""
|
| 465 |
location_response = llm_gpt4.invoke(location_prompt)
|
| 466 |
-
if isinstance(location_response, AIMessage)
|
| 467 |
-
location = location_response.content.strip()
|
| 468 |
-
else:
|
| 469 |
-
location = str(location_response).strip()
|
| 470 |
-
|
| 471 |
-
url = f"http://api.weatherapi.com/v1/current.json?key={weather_api_key}&q={location}&aqi=no"
|
| 472 |
-
response = requests.get(url)
|
| 473 |
-
data = response.json()
|
| 474 |
-
|
| 475 |
-
if "current" not in data:
|
| 476 |
-
return f"Sorry, I couldn't find the weather info for '{location}'."
|
| 477 |
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 486 |
|
| 487 |
except Exception as e:
|
| 488 |
-
return f"Weather
|
| 489 |
|
|
|
|
| 490 |
@tool("weather")
|
| 491 |
def weather_tool(query: str) -> str:
|
| 492 |
-
"""Weather Agent: Provide real-time weather info
|
| 493 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
location_prompt = f"""
|
| 495 |
You are a location extractor. Given a user's query about weather, return the location mentioned in it. If not found, return "London".
|
| 496 |
-
|
| 497 |
Examples:
|
| 498 |
- "What's the weather in Tokyo now?" → Tokyo
|
| 499 |
- "紐約天氣如何?" → New York
|
| 500 |
- "今天台北下雨嗎?" → Taipei
|
| 501 |
-
- "
|
| 502 |
-
- "Is it
|
| 503 |
-
|
| 504 |
Now process this query: "{query}"
|
| 505 |
"""
|
| 506 |
location_response = llm_gpt4.invoke(location_prompt)
|
| 507 |
-
if isinstance(location_response, AIMessage)
|
| 508 |
-
location = location_response.content.strip()
|
| 509 |
-
else:
|
| 510 |
-
location = str(location_response).strip()
|
| 511 |
-
|
| 512 |
-
url = f"http://api.weatherapi.com/v1/current.json?key={weather_api_key}&q={location}&aqi=no"
|
| 513 |
-
response = requests.get(url)
|
| 514 |
-
data = response.json()
|
| 515 |
-
|
| 516 |
-
if "current" not in data:
|
| 517 |
-
return f"Sorry, I couldn't find the weather info for '{location}'."
|
| 518 |
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 525 |
|
| 526 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
|
| 528 |
except Exception as e:
|
| 529 |
-
return f"Weather
|
| 530 |
|
| 531 |
@tool("summarise")
|
| 532 |
def summarise_tool(query: str) -> str:
|
|
|
|
| 41 |
from langchain.schema import AIMessage
|
| 42 |
from datetime import datetime
|
| 43 |
from zoneinfo import ZoneInfo
|
| 44 |
+
from dateutil import parser as date_parser
|
| 45 |
import numexpr as ne
|
| 46 |
import pandas as pd
|
| 47 |
|
|
|
|
| 54 |
from timezonefinder import TimezoneFinder
|
| 55 |
from langchain_experimental.agents import create_pandas_dataframe_agent
|
| 56 |
|
| 57 |
+
|
| 58 |
session_retriever = None
|
| 59 |
session_qa_chain = None
|
| 60 |
csv_dataframe = None # CSV tool will use this
|
|
|
|
| 452 |
|
| 453 |
def weather_agent_tool(query: str) -> str:
|
| 454 |
try:
|
| 455 |
+
from dateutil import parser as date_parser
|
| 456 |
+
from zoneinfo import ZoneInfo
|
| 457 |
+
|
| 458 |
+
weather_api_key = os.environ.get("WEATHER_API_KEY")
|
| 459 |
+
if not weather_api_key:
|
| 460 |
+
return "Weather API key not found. Please set WEATHER_API_KEY env variable."
|
| 461 |
+
|
| 462 |
+
# GPT get location
|
| 463 |
location_prompt = f"""
|
| 464 |
You are a location extractor. Given a user's query about weather, return the location mentioned in it. If not found, return "London".
|
|
|
|
| 465 |
Examples:
|
| 466 |
- "What's the weather in Tokyo now?" → Tokyo
|
| 467 |
- "紐約天氣如何?" → New York
|
| 468 |
- "今天台北下雨嗎?" → Taipei
|
| 469 |
+
- "How's the weather?" → London
|
| 470 |
+
- "Is it gon rain in ldn" → London
|
| 471 |
+
|
| 472 |
Now process this query: "{query}"
|
| 473 |
"""
|
| 474 |
location_response = llm_gpt4.invoke(location_prompt)
|
| 475 |
+
location = location_response.content.strip() if isinstance(location_response, AIMessage) else str(location_response).strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 476 |
|
| 477 |
+
# get local time
|
| 478 |
+
geo = Nominatim(user_agent="weather_agent_demo")
|
| 479 |
+
loc = geo.geocode(location)
|
| 480 |
+
tf = TimezoneFinder()
|
| 481 |
+
tz_str = tf.timezone_at(lat=loc.latitude, lng=loc.longitude) if loc else "Europe/London"
|
| 482 |
+
local_now = datetime.now(ZoneInfo(tz_str))
|
| 483 |
+
local_now_str = local_now.strftime("%Y-%m-%d %H:%M:%S")
|
| 484 |
+
|
| 485 |
+
# GPT get query time(transfer to Y-M-D)
|
| 486 |
+
time_prompt = f"""
|
| 487 |
+
It is currently {local_now_str} in {location}.
|
| 488 |
+
Based on the query: "{query}", return the intended local date (YYYY-MM-DD).
|
| 489 |
+
Return only a date like 2025-04-04. If unclear, use today.
|
| 490 |
+
"""
|
| 491 |
+
date_response = llm_gpt4.invoke(time_prompt)
|
| 492 |
+
date_str = date_response.content.strip() if isinstance(date_response, AIMessage) else str(date_response).strip()
|
| 493 |
+
query_date = date_parser.parse(date_str).date()
|
| 494 |
+
today = local_now.date()
|
| 495 |
+
|
| 496 |
+
# choose to use current / forecast / history API according to time
|
| 497 |
+
if query_date == today:
|
| 498 |
+
url = f"http://api.weatherapi.com/v1/current.json?key={weather_api_key}&q={location}&aqi=no"
|
| 499 |
+
data = requests.get(url).json()
|
| 500 |
+
cur = data["current"]
|
| 501 |
+
return f"The current weather in {location.title()} is {cur['condition']['text']} with {cur['temp_c']}°C (feels like {cur['feelslike_c']}°C), humidity {cur['humidity']}%, wind {cur['wind_kph']} kph."
|
| 502 |
+
|
| 503 |
+
elif query_date > today:
|
| 504 |
+
url = f"http://api.weatherapi.com/v1/forecast.json?key={weather_api_key}&q={location}&days=3"
|
| 505 |
+
data = requests.get(url).json()
|
| 506 |
+
forecast_days = data.get("forecast", {}).get("forecastday", [])
|
| 507 |
+
for day in forecast_days:
|
| 508 |
+
if day["date"] == date_str:
|
| 509 |
+
d = day["day"]
|
| 510 |
+
return f"Forecast for {location.title()} on {query_date.strftime('%A, %B %d')}: {d['condition']['text']}, avg {d['avgtemp_c']}°C, humidity {d['avghumidity']}%, max {d['maxtemp_c']}°C, min {d['mintemp_c']}°C."
|
| 511 |
+
return f"Forecast unavailable for {query_date} (WeatherAPI supports up to 3 days ahead)."
|
| 512 |
|
| 513 |
+
else:
|
| 514 |
+
url = f"http://api.weatherapi.com/v1/history.json?key={weather_api_key}&q={location}&dt={date_str}"
|
| 515 |
+
data = requests.get(url).json()
|
| 516 |
+
hist = data.get("forecast", {}).get("forecastday", [{}])[0].get("day", {})
|
| 517 |
+
if not hist:
|
| 518 |
+
return f"Historical weather not found for {location.title()} on {query_date}."
|
| 519 |
+
return f"The weather in {location.title()} on {query_date.strftime('%A, %B %d')}: {hist['condition']['text']}, avg {hist['avgtemp_c']}°C, humidity {hist['avghumidity']}%, max {hist['maxtemp_c']}°C, min {hist['mintemp_c']}°C."
|
| 520 |
|
| 521 |
except Exception as e:
|
| 522 |
+
return f"Weather Agent Error: {e}"
|
| 523 |
|
| 524 |
+
|
| 525 |
@tool("weather")
|
| 526 |
def weather_tool(query: str) -> str:
|
| 527 |
+
"""Weather Agent: Provide real-time, future, or historical weather info (max 3 days forecast, 7 days history)."""
|
| 528 |
try:
|
| 529 |
+
from dateutil import parser as date_parser
|
| 530 |
+
from zoneinfo import ZoneInfo
|
| 531 |
+
|
| 532 |
+
weather_api_key = os.environ.get("WEATHER_API_KEY")
|
| 533 |
+
if not weather_api_key:
|
| 534 |
+
return "Weather API key not found. Please set WEATHER_API_KEY env variable."
|
| 535 |
+
|
| 536 |
+
# GPT get location
|
| 537 |
location_prompt = f"""
|
| 538 |
You are a location extractor. Given a user's query about weather, return the location mentioned in it. If not found, return "London".
|
|
|
|
| 539 |
Examples:
|
| 540 |
- "What's the weather in Tokyo now?" → Tokyo
|
| 541 |
- "紐約天氣如何?" → New York
|
| 542 |
- "今天台北下雨嗎?" → Taipei
|
| 543 |
+
- "How's the weather?" → London
|
| 544 |
+
- "Is it gon rain in ldn" → London
|
| 545 |
+
|
| 546 |
Now process this query: "{query}"
|
| 547 |
"""
|
| 548 |
location_response = llm_gpt4.invoke(location_prompt)
|
| 549 |
+
location = location_response.content.strip() if isinstance(location_response, AIMessage) else str(location_response).strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 550 |
|
| 551 |
+
# get local time
|
| 552 |
+
geo = Nominatim(user_agent="weather_agent_demo")
|
| 553 |
+
loc = geo.geocode(location)
|
| 554 |
+
tf = TimezoneFinder()
|
| 555 |
+
tz_str = tf.timezone_at(lat=loc.latitude, lng=loc.longitude) if loc else "Europe/London"
|
| 556 |
+
local_now = datetime.now(ZoneInfo(tz_str))
|
| 557 |
+
local_now_str = local_now.strftime("%Y-%m-%d %H:%M:%S")
|
| 558 |
+
|
| 559 |
+
# GPT get query time(transfer to Y-M-D)
|
| 560 |
+
time_prompt = f"""
|
| 561 |
+
It is currently {local_now_str} in {location}.
|
| 562 |
+
Based on the query: "{query}", return the intended local date (YYYY-MM-DD).
|
| 563 |
+
Return only a date like 2025-04-04. If unclear, use today.
|
| 564 |
+
"""
|
| 565 |
+
date_response = llm_gpt4.invoke(time_prompt)
|
| 566 |
+
date_str = date_response.content.strip() if isinstance(date_response, AIMessage) else str(date_response).strip()
|
| 567 |
+
query_date = date_parser.parse(date_str).date()
|
| 568 |
+
today = local_now.date()
|
| 569 |
+
|
| 570 |
+
# chose to use current / forecast / history API according to time
|
| 571 |
+
if query_date == today:
|
| 572 |
+
url = f"http://api.weatherapi.com/v1/current.json?key={weather_api_key}&q={location}&aqi=no"
|
| 573 |
+
data = requests.get(url).json()
|
| 574 |
+
cur = data["current"]
|
| 575 |
+
return f"The current weather in {location.title()} is {cur['condition']['text']} with {cur['temp_c']}°C (feels like {cur['feelslike_c']}°C), humidity {cur['humidity']}%, wind {cur['wind_kph']} kph."
|
| 576 |
+
|
| 577 |
+
elif query_date > today:
|
| 578 |
+
url = f"http://api.weatherapi.com/v1/forecast.json?key={weather_api_key}&q={location}&days=3"
|
| 579 |
+
data = requests.get(url).json()
|
| 580 |
+
forecast_days = data.get("forecast", {}).get("forecastday", [])
|
| 581 |
+
for day in forecast_days:
|
| 582 |
+
if day["date"] == date_str:
|
| 583 |
+
d = day["day"]
|
| 584 |
+
return f"Forecast for {location.title()} on {query_date.strftime('%A, %B %d')}: {d['condition']['text']}, avg {d['avgtemp_c']}°C, humidity {d['avghumidity']}%, max {d['maxtemp_c']}°C, min {d['mintemp_c']}°C."
|
| 585 |
+
return f"Forecast unavailable for {query_date} (WeatherAPI supports up to 3 days ahead)."
|
| 586 |
|
| 587 |
+
else:
|
| 588 |
+
url = f"http://api.weatherapi.com/v1/history.json?key={weather_api_key}&q={location}&dt={date_str}"
|
| 589 |
+
data = requests.get(url).json()
|
| 590 |
+
hist = data.get("forecast", {}).get("forecastday", [{}])[0].get("day", {})
|
| 591 |
+
if not hist:
|
| 592 |
+
return f"Historical weather not found for {location.title()} on {query_date}."
|
| 593 |
+
return f"The weather in {location.title()} on {query_date.strftime('%A, %B %d')}: {hist['condition']['text']}, avg {hist['avgtemp_c']}°C, humidity {hist['avghumidity']}%, max {hist['maxtemp_c']}°C, min {hist['mintemp_c']}°C."
|
| 594 |
|
| 595 |
except Exception as e:
|
| 596 |
+
return f"Weather Agent Error: {e}"
|
| 597 |
|
| 598 |
@tool("summarise")
|
| 599 |
def summarise_tool(query: str) -> str:
|