Weather_Agent / app.py
MathWizard1729's picture
Update app.py
b14190d verified
raw
history blame
7.79 kB
###############################################################################
# app.py – Weather Umbrella Advisor (Streamlit + OpenWeatherMap + Claude 3) #
###############################################################################
import os, json, requests, boto3, streamlit as st
from dotenv import load_dotenv
# --------------------------------------------------------------------------- #
# 1) Load env vars (local .env or HF Space “Secrets”) #
# --------------------------------------------------------------------------- #
load_dotenv()
OPENWEATHERMAP_API_KEY = os.getenv("OPENWEATHERMAP_API_KEY")
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID") # may be None
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY") # may be None
# --- Heuristic: auto-swap if ID looks like a secret (contains “/”) ----------
if AWS_ACCESS_KEY_ID and "/" in AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY and "/" not in AWS_SECRET_ACCESS_KEY:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY = AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID
# --------------------------------------------------------------------------- #
# 2) Create Bedrock client, preferring explicit keys if they’re present #
# --------------------------------------------------------------------------- #
if AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY:
session = boto3.Session(
aws_access_key_id = AWS_ACCESS_KEY_ID,
aws_secret_access_key = AWS_SECRET_ACCESS_KEY,
region_name = AWS_REGION,
)
else:
# Falls back to IAM role or default credentials chain
session = boto3.Session(region_name=AWS_REGION)
bedrock = session.client("bedrock-runtime")
# Quick sanity-check – will raise if creds are still wrong
try:
_ = bedrock.meta.region_name # simple attribute access forces no call
except Exception as e:
st.error(f"Credential problem: {e}")
st.stop()
# --------------------------------------------------------------------------- #
# 3) Streamlit page style #
# --------------------------------------------------------------------------- #
st.set_page_config(page_title="🌤️ Umbrella Advisor", page_icon="☔", layout="centered")
st.markdown("""
<div style="text-align:center">
<h1 style="color:#3c79f5;">☔ Weather Umbrella Advisor</h1>
<p style="font-size:18px">Ask if you need an umbrella tomorrow – powered by <b>Claude 3</b> &amp; <b>OpenWeatherMap</b>.</p>
</div>
""", unsafe_allow_html=True)
# --------------------------------------------------------------------------- #
# 4) Conversation state #
# --------------------------------------------------------------------------- #
if "messages" not in st.session_state:
st.session_state.messages = []
for m in st.session_state.messages:
with st.chat_message(m["role"]):
st.markdown(m["content"])
# --------------------------------------------------------------------------- #
# 5) Helper – get weather #
# --------------------------------------------------------------------------- #
def get_weather(city: str):
if not city.strip():
return {"error": "Please give me a city name."}
geo = f"http://api.openweathermap.org/geo/1.0/direct?q={city}&limit=1&appid={OPENWEATHERMAP_API_KEY}"
try:
loc = requests.get(geo, timeout=10).json()
if not loc:
return {"error": f"City “{city}” not found."}
lat, lon = loc[0]["lat"], loc[0]["lon"]
wurl = f"http://api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&appid={OPENWEATHERMAP_API_KEY}&units=metric"
data = requests.get(wurl, timeout=10).json()
if "list" not in data:
return {"error": f"No forecast for “{city}”."}
fc = [{
"time": f["dt_txt"],
"description": f["weather"][0]["description"].capitalize(),
"rain_probability": round(f.get("pop", 0)*100, 1),
"temp": f["main"]["temp"]
} for f in data["list"][:8]]
return {"location": city.title(), "forecast": fc}
except Exception as e:
return {"error": str(e)}
# --------------------------------------------------------------------------- #
# 6) Helper – talk to Claude (ReAct) #
# --------------------------------------------------------------------------- #
SYSTEM_PROMPT = """You are a helpful umbrella advisor using ReAct:
1. Think about the question.
2. If needed, act with get_weather(location).
3. Observe results.
4. Reason and answer.
When you need weather data, respond EXACTLY:
{"thought":"…","action":"get_weather","action_input":{"location":"City"}}"""
def ask_claude(user, history=""):
# First call
body = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1000,
"temperature": 0.7,
"top_p": 0.9,
"messages": [{
"role": "user",
"content": f"{SYSTEM_PROMPT}\n\nHistory:\n{history}\n\nUser: {user}"
}]
}
raw = bedrock.invoke_model(
modelId="anthropic.claude-3-sonnet-20240229-v1:0",
contentType="application/json",
accept="application/json",
body=json.dumps(body)
)
txt = json.loads(raw["body"].read())["content"][0]["text"].strip()
# Try to parse ReAct JSON
try:
j = json.loads(txt)
if j.get("action") == "get_weather":
city = j["action_input"]["location"]
wx = get_weather(city)
if "error" in wx: return wx["error"]
# Second call – reasoning
reason_prompt = f"""Here is the forecast for {city}:
{json.dumps(wx, indent=2)}
Give a friendly answer: YES/NO umbrella, with reasoning."""
body2 = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 500,
"temperature": 0.7,
"messages":[{"role":"user","content":reason_prompt}]
}
raw2 = bedrock.invoke_model(
modelId="anthropic.claude-3-sonnet-20240229-v1:0",
contentType="application/json",
accept="application/json",
body=json.dumps(body2)
)
return json.loads(raw2["body"].read())["content"][0]["text"].strip()
except json.JSONDecodeError:
pass
return txt
# --------------------------------------------------------------------------- #
# 7) Chat input #
# --------------------------------------------------------------------------- #
def last_history(n=4):
return "\n".join(f"{m['role'].capitalize()}: {m['content']}" for m in st.session_state.messages[-n:])
if prompt := st.chat_input("Ask: Do I need an umbrella tomorrow?"):
st.session_state.messages.append({"role":"user","content":prompt})
with st.chat_message("user"): st.markdown(prompt)
with st.chat_message("assistant"):
with st.spinner("Thinking…"):
reply = ask_claude(prompt, last_history())
st.markdown(reply)
st.session_state.messages.append({"role":"assistant","content":reply})
# --------------------------------------------------------------------------- #
# 8) Sidebar #
# --------------------------------------------------------------------------- #
with st.sidebar:
st.image("https://img.icons8.com/clouds/100/umbrella.png", width=100)
st.markdown("### About\nUses **Claude 3 Sonnet (Bedrock)** + **OpenWeatherMap**")