Bhupen commited on
Commit
6672eba
·
1 Parent(s): 95806b4

weather app using pydanticai

Browse files
Files changed (2) hide show
  1. app.py +525 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,525 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+
3
+ st.markdown("""
4
+ **Weather agent**
5
+
6
+ Example of PydanticAI with `multiple tools` which the LLM needs to call in turn to answer a question.
7
+ """)
8
+
9
+ with st.expander("🎯 Objectives"):
10
+ st.markdown("""
11
+ - Use **OpenAI GPT-4o-mini** agent to `process natural language queries` about the weather.
12
+ - Fetch **geolocation** from a location string using the `Maps.co API`.
13
+ - Retrieve **real-time weather** using the Tomorrow.io API.
14
+ - Handle `retries`, `backoff`, and `logging` using **Logfire**.
15
+ - Integrate all parts in a clean, async-compatible **Streamlit UI**.
16
+ - Ensuring `concise` and `structured` responses.
17
+ """)
18
+
19
+ with st.expander("🧰 Pre-requisites"):
20
+ st.markdown("""
21
+ - Python 3.10+
22
+ - Streamlit
23
+ - AsyncClient (httpx)
24
+ - OpenAI `pydantic_ai` Agent
25
+ - Logfire for tracing/debugging
26
+ - Valid API Keys:
27
+ - [https://geocode.maps.co/](https://geocode.maps.co/)
28
+ - [https://www.tomorrow.io/](https://www.tomorrow.io/)
29
+ """)
30
+
31
+ st.code("""
32
+ pip install streamlit httpx logfire pydantic_ai
33
+ """)
34
+
35
+ with st.expander("⚙️ Step-by-Step Setup"):
36
+ st.markdown("**Imports and Global Client**")
37
+ st.code("""
38
+ import os
39
+ import asyncio
40
+ import streamlit as st
41
+ from dataclasses import dataclass
42
+ from typing import Any
43
+ import logfire
44
+ from httpx import AsyncClient
45
+ from pydantic_ai import Agent, RunContext, ModelRetry
46
+
47
+ logfire.configure(send_to_logfire='if-token-present')
48
+ client = AsyncClient()
49
+ """)
50
+
51
+ st.markdown("**Declare Dependencies**")
52
+ st.code("""
53
+ @dataclass
54
+ class Deps:
55
+ client: AsyncClient # client is an instance of AsyncClient (from httpx).
56
+ weather_api_key: str | None
57
+ geo_api_key: str | None
58
+ """)
59
+
60
+ st.markdown("**Setup Weather Agent**")
61
+ st.code("""
62
+ weather_agent = Agent(
63
+ 'openai:gpt-4o-mini',
64
+ system_prompt=(
65
+ 'Be concise, reply with one sentence. '
66
+ 'Use the `get_lat_lng` tool to get the latitude and longitude of the locations, '
67
+ 'then use the `get_weather` tool to get the weather.'
68
+ ),
69
+ deps_type= Deps,
70
+ retries = 2,
71
+ )
72
+ """)
73
+
74
+ st.markdown("**Define Geocoding Tool with Retry**")
75
+ st.code("""
76
+ @weather_agent.tool
77
+ async def get_lat_lng(ctx: RunContext[Deps],
78
+ location_description: str,
79
+ max_retries: int = 5,
80
+ base_delay: int = 2) -> dict[str, float]:
81
+ "Get the latitude and longitude of a location with retry handling for rate limits."
82
+
83
+ if ctx.deps.geo_api_key is None:
84
+ return {'lat': 51.1, 'lng': -0.1} # Default to London
85
+
86
+ # Sets up API request parameters.
87
+ params = {'q': location_description, 'api_key': ctx.deps.geo_api_key}
88
+
89
+ # Loops for a maximum number of retries.
90
+ for attempt in range(max_retries):
91
+ try:
92
+
93
+ # Logs API call span with parameters.
94
+ with logfire.span('calling geocode API', params=params) as span:
95
+
96
+ # Sends async GET request.
97
+ r = await ctx.deps.client.get('https://geocode.maps.co/search', params=params)
98
+
99
+ # Checks if API rate limit is exceeded.
100
+ if r.status_code == 429:
101
+
102
+ # Exponential backoff
103
+ wait_time = base_delay * (2 ** attempt)
104
+
105
+ # Waits before retrying.
106
+ await asyncio.sleep(wait_time)
107
+
108
+ # Continues to the next retry attempt.
109
+ continue
110
+
111
+ r.raise_for_status()
112
+ data = r.json()
113
+ span.set_attribute('response', data)
114
+
115
+ if data:
116
+ # Extracts and returns latitude & longitude.
117
+ return {'lat': float(data[0]['lat']), 'lng': float(data[0]['lon'])}
118
+ else:
119
+ # Raises an error if no valid data is found.
120
+ raise ModelRetry('Could not find the location')
121
+
122
+ except Exception as e: # Catches HTTP errors.
123
+ print(f"Request failed: {e}") # Logs the failure.
124
+
125
+ raise ModelRetry('Failed after multiple retries')
126
+
127
+ """)
128
+
129
+ st.markdown("**Define Weather Tool**")
130
+ st.code("""
131
+ @weather_agent.tool
132
+ async def get_weather(ctx: RunContext[Deps], lat: float, lng: float) -> dict[str, Any]:
133
+ if ctx.deps.weather_api_key is None:
134
+ return {'temperature': '21 °C', 'description': 'Sunny'}
135
+ params = {'apikey': ctx.deps.weather_api_key, 'location': f'{lat},{lng}', 'units': 'metric'}
136
+
137
+ r = await ctx.deps.client.get('https://api.tomorrow.io/v4/weather/realtime', params=params)
138
+ r.raise_for_status()
139
+
140
+ data = r.json()
141
+ values = data['data']['values']
142
+
143
+ code_lookup = {
144
+ 1000: 'Clear, Sunny', 1001: 'Cloudy', 1100: 'Mostly Clear', 1101: 'Partly Cloudy',
145
+ 1102: 'Mostly Cloudy', 2000: 'Fog', 2100: 'Light Fog', 4000: 'Drizzle', 4001: 'Rain',
146
+ 4200: 'Light Rain', 4201: 'Heavy Rain', 5000: 'Snow', 5001: 'Flurries',
147
+ 5100: 'Light Snow', 5101: 'Heavy Snow', 6000: 'Freezing Drizzle', 6001: 'Freezing Rain',
148
+ 6200: 'Light Freezing Rain', 6201: 'Heavy Freezing Rain', 7000: 'Ice Pellets',
149
+ 7101: 'Heavy Ice Pellets', 7102: 'Light Ice Pellets', 8000: 'Thunderstorm',
150
+ }
151
+
152
+ return {
153
+ 'temperature': f'{values["temperatureApparent"]:0.0f}°C',
154
+ 'description': code_lookup.get(values['weatherCode'], 'Unknown'),
155
+ }
156
+ """)
157
+
158
+ st.markdown("**Wrapper to Run the Agent**")
159
+ st.code("""
160
+ async def run_weather_agent(user_input: str):
161
+ deps = Deps(
162
+ client=client,
163
+ weather_api_key = os.getenv("TOMORROW_IO_API_KEY"),
164
+ geo_api_key = os.getenv("GEOCODE_API_KEY")
165
+ )
166
+ result = await weather_agent.run(user_input, deps=deps)
167
+ return result.data
168
+ """)
169
+
170
+ st.markdown("**Streamlit UI with Async Handling**")
171
+ st.code("""
172
+ st.set_page_config(page_title="Weather Application", page_icon="🚀")
173
+
174
+ if "weather_response" not in st.session_state:
175
+ st.session_state.weather_response = None
176
+
177
+ st.title("Weather Agent App")
178
+ user_input = st.text_area("Enter a sentence with locations:", "What is the weather like in Bangalore, Chennai and Delhi?")
179
+
180
+ if st.button("Get Weather"):
181
+ with st.spinner("Fetching weather..."):
182
+ loop = asyncio.new_event_loop()
183
+ asyncio.set_event_loop(loop)
184
+ response = loop.run_until_complete(run_weather_agent(user_input))
185
+ st.session_state.weather_response = response
186
+
187
+ if st.session_state.weather_response:
188
+ st.info(st.session_state.weather_response)
189
+ """)
190
+
191
+ with st.expander("Description of Each Step"):
192
+ st.markdown("""
193
+ - **Imports**: Brings in all required packages including `httpx`, `logfire`, and `streamlit`.
194
+ - **`Deps` Dataclass**: Encapsulates dependencies injected into the agent like the API keys and shared HTTP client.
195
+ - **Weather Agent**: Configures an OpenAI GPT-4o-mini agent with tools for geolocation and weather.
196
+ - **Tools**:
197
+ - `get_lat_lng`: Geocodes a location using a free Maps.co API. Implements retry with exponential backoff.
198
+ - `get_weather`: Fetches live weather info from Tomorrow.io using lat/lng.
199
+ - **Agent Runner**: Wraps the interaction to run asynchronously with injected dependencies.
200
+ - **Streamlit UI**: Captures user input, triggers agent execution, and displays response with `asyncio`.
201
+ """)
202
+
203
+ st.image("https://raw.githubusercontent.com/gridflowai/gridflowAI-datasets-icons/862001d5ac107780b38f96eca34cefcb98c7f3e3/AI-icons-images/get_weather_app.png",
204
+ caption="Agentic Weather App Flow",
205
+ use_column_width=True)
206
+
207
+
208
+ import os
209
+ import asyncio
210
+ import streamlit as st
211
+ from dataclasses import dataclass
212
+ from typing import Any
213
+
214
+ import logfire
215
+ from httpx import AsyncClient
216
+ from pydantic_ai import Agent, RunContext, ModelRetry
217
+
218
+ # Configure logfire
219
+ logfire.configure(send_to_logfire='if-token-present')
220
+
221
+ @dataclass
222
+ class Deps:
223
+ client: AsyncClient
224
+ weather_api_key: str | None
225
+ geo_api_key: str | None
226
+
227
+ weather_agent = Agent(
228
+ 'openai:gpt-4o-mini',
229
+ system_prompt=(
230
+ 'Be concise, reply with one sentence. '
231
+ 'Use the `get_lat_lng` tool to get the latitude and longitude of the locations, '
232
+ 'then use the `get_weather` tool to get the weather.'
233
+ ),
234
+ deps_type=Deps,
235
+ retries=2,
236
+ )
237
+
238
+ # Create a single global AsyncClient instance
239
+ client = AsyncClient()
240
+
241
+ @weather_agent.tool
242
+ async def get_lat_lng(ctx: RunContext[Deps],
243
+ location_description: str,
244
+ max_retries: int = 5,
245
+ base_delay: int = 2) -> dict[str, float]:
246
+ """Get the latitude and longitude of a location."""
247
+
248
+ if ctx.deps.geo_api_key is None:
249
+ return {'lat': 51.1, 'lng': -0.1} # Default to London
250
+
251
+ # Sets up API request parameters.
252
+ params = {'q': location_description, 'api_key': ctx.deps.geo_api_key}
253
+
254
+ # Loops for a maximum number of retries.
255
+ for attempt in range(max_retries):
256
+ try:
257
+ # Logs API call span with parameters.
258
+ with logfire.span('calling geocode API', params=params) as span:
259
+
260
+ # Sends async GET request.
261
+ r = await ctx.deps.client.get('https://geocode.maps.co/search', params=params)
262
+
263
+ # Checks if API rate limit is exceeded.
264
+ if r.status_code == 429: # Too Many Requests
265
+ wait_time = base_delay * (2 ** attempt) # Exponential backoff
266
+ print(f"Rate limited. Retrying in {wait_time} seconds...")
267
+
268
+ # Waits before retrying.
269
+ await asyncio.sleep(wait_time)
270
+
271
+ # Continues to the next retry attempt.
272
+ continue # Retry the request
273
+
274
+ # Raises an exception for HTTP errors.
275
+ r.raise_for_status()
276
+
277
+ # Parses the API response as JSON.
278
+ data = r.json()
279
+
280
+ # Logs the response data.
281
+ span.set_attribute('response', data)
282
+
283
+ if data:
284
+ # Extracts and returns latitude & longitude.
285
+ return {'lat': float(data[0]['lat']), 'lng': float(data[0]['lon'])}
286
+ else:
287
+ # Raises an error if no valid data is found.
288
+ raise ModelRetry('Could not find the location')
289
+
290
+ except Exception as e: # Catches HTTP errors.
291
+ print(f"Request failed: {e}") # Logs the failure.
292
+
293
+ raise ModelRetry('Failed after multiple retries')
294
+
295
+ @weather_agent.tool
296
+ async def get_weather(ctx: RunContext[Deps], lat: float, lng: float) -> dict[str, Any]:
297
+ """Get the weather at a location."""
298
+ if ctx.deps.weather_api_key is None:
299
+ return {'temperature': '21 °C', 'description': 'Sunny'}
300
+ params = {'apikey': ctx.deps.weather_api_key, 'location': f'{lat},{lng}', 'units': 'metric'}
301
+
302
+ r = await ctx.deps.client.get('https://api.tomorrow.io/v4/weather/realtime', params=params)
303
+
304
+ r.raise_for_status()
305
+
306
+ data = r.json()
307
+
308
+ values = data['data']['values']
309
+
310
+ code_lookup = {
311
+ 1000: 'Clear, Sunny', 1001: 'Cloudy', 1100: 'Mostly Clear', 1101: 'Partly Cloudy',
312
+ 1102: 'Mostly Cloudy', 2000: 'Fog', 2100: 'Light Fog', 4000: 'Drizzle', 4001: 'Rain',
313
+ 4200: 'Light Rain', 4201: 'Heavy Rain', 5000: 'Snow', 5001: 'Flurries',
314
+ 5100: 'Light Snow', 5101: 'Heavy Snow', 6000: 'Freezing Drizzle', 6001: 'Freezing Rain',
315
+ 6200: 'Light Freezing Rain', 6201: 'Heavy Freezing Rain', 7000: 'Ice Pellets',
316
+ 7101: 'Heavy Ice Pellets', 7102: 'Light Ice Pellets', 8000: 'Thunderstorm',
317
+ }
318
+ return {
319
+ 'temperature': f'{values["temperatureApparent"]:0.0f}°C',
320
+ 'description': code_lookup.get(values['weatherCode'], 'Unknown'),
321
+ }
322
+
323
+ async def run_weather_agent(user_input: str):
324
+ deps = Deps(
325
+ client=client, # Use global client
326
+ weather_api_key=os.getenv("TOMORROW_IO_API_KEY"),
327
+ geo_api_key=os.getenv("GEOCODE_API_KEY")
328
+ )
329
+ result = await weather_agent.run(user_input, deps=deps)
330
+ return result.data
331
+
332
+ # Initialize session state for storing weather responses
333
+ if "weather_response" not in st.session_state:
334
+ st.session_state.weather_response = None
335
+
336
+ # Set the page title
337
+ #st.set_page_config(page_title="Weather Application", page_icon="🚀")
338
+
339
+ # Streamlit UI
340
+ with st.expander(f"**Example prompts**"):
341
+
342
+ st.markdown(f"""
343
+
344
+ Prompt : If I were in Sydney today, would I need a jacket?
345
+ Bot : No, you likely wouldn't need a jacket as it's clear and sunny with a temperature of 22°C in Sydney.
346
+
347
+ Prompt : Tell me whether it's beach weather in Bali and Phuket.
348
+ Bot : Bali is too cold at 7°C and partly cloudy for beach weather, while Phuket is warm at 26°C with drizzle, making it more suitable for beach activities.
349
+
350
+ Prompt : If I had a meeting in Dubai, should I wear light clothing?
351
+ Bot : Yes, you should wear light clothing as the temperature in Dubai is currently 25°C and mostly clear.
352
+
353
+ Prompt : How does today’s temperature in Tokyo compare to the same time last week?
354
+ Bot : Today's temperature in Tokyo is 14°C, which is the same as the temperature at the same time last week.
355
+
356
+ Prompt : Is the current weather suitable for air travel in London and New York?
357
+ Bot : The current weather in London is 5°C and cloudy, and in New York, it is -0°C and clear; both conditions are generally suitable for air travel.
358
+
359
+ """)
360
+
361
+ user_input = st.text_area("Enter a sentence with locations:", "What is the weather like in Bangalore, Chennai and Delhi?")
362
+
363
+ # Button to trigger weather fetch
364
+ if st.button("Get Weather"):
365
+ with st.spinner("Fetching weather..."):
366
+ loop = asyncio.new_event_loop()
367
+ asyncio.set_event_loop(loop)
368
+ response = loop.run_until_complete(run_weather_agent(user_input))
369
+ st.session_state.weather_response = response
370
+
371
+ # Display stored response
372
+ if st.session_state.weather_response:
373
+ st.info(st.session_state.weather_response)
374
+
375
+
376
+ with st.expander("🧠 How is this app Agentic?"):
377
+ st.markdown("""
378
+ ###### ✅ How this App is Agentic
379
+
380
+ This weather app demonstrates **Agentic AI** because:
381
+
382
+ 1. **Goal-Oriented Autonomy**
383
+ The user provides a natural language request (e.g., *“What’s the weather in Bangalore and Delhi?”*).
384
+ The agent autonomously figures out *how* to fulfill it.
385
+
386
+ 2. **Tool Usage by the Agent**
387
+ The `Agent` uses two tools:
388
+ - `get_lat_lng()` – to fetch coordinates via a geocoding API.
389
+ - `get_weather()` – to get real-time weather for those coordinates.
390
+ The agent determines when and how to use these tools.
391
+
392
+ 3. **Context + Dependency Injection**
393
+ The app uses the `Deps` dataclass to provide the agent with shared dependencies like HTTP clients and API keys—just like a human agent accessing internal tools.
394
+
395
+ 4. **Retries and Adaptive Behavior**
396
+ The agent handles failures and retries via `ModelRetry`, showing resilience and smart retry logic.
397
+
398
+ 5. **Structured Interactions via `RunContext`**
399
+ Each tool runs with access to structured context, enabling better coordination and reuse of shared state.
400
+
401
+ 6. **LLM-Orchestrated Actions**
402
+ At the core, a GPT-4o-mini model orchestrates:
403
+ - Understanding the user intent,
404
+ - Selecting and invoking the right tools,
405
+ - Synthesizing the final response.
406
+
407
+ > 🧠 **In essence**: This is not just a chatbot, but an *autonomous reasoning engine* that uses real tools to complete real-world goals.
408
+ """)
409
+
410
+ with st.expander("🧪 Example Prompts: Handling Complex Queries"):
411
+ st.markdown("""
412
+ This app can understand **natural, varied, and multi-part prompts** thanks to the LLM-based agent at its core.
413
+ It intelligently uses `get_lat_lng()` and `get_weather()` tools based on user intent.
414
+
415
+ ###### 🗣️ Complex Prompt Examples & Responses:
416
+
417
+ **Prompt:**
418
+ *If I were in Sydney today, would I need a jacket?*
419
+ **Response:**
420
+ *No, you likely wouldn't need a jacket as it's clear and sunny with a temperature of 22°C in Sydney.*
421
+
422
+ ---
423
+
424
+ **Prompt:**
425
+ *Tell me whether it's beach weather in Bali and Phuket.*
426
+ **Response:**
427
+ *Bali is too cold at 7°C and partly cloudy for beach weather, while Phuket is warm at 26°C with drizzle, making it more suitable for beach activities.*
428
+
429
+ ---
430
+
431
+ **Prompt:**
432
+ *If I had a meeting in Dubai, should I wear light clothing?*
433
+ **Response:**
434
+ *Yes, you should wear light clothing as the temperature in Dubai is currently 25°C and mostly clear.*
435
+
436
+ ---
437
+
438
+ **Prompt:**
439
+ *How does today’s temperature in Tokyo compare to the same time last week?*
440
+ **Response:**
441
+ *Today's temperature in Tokyo is 14°C, which is the same as the temperature at the same time last week.*
442
+ *(Note: This would require historical API support to be accurate in a real app.)*
443
+
444
+ ---
445
+
446
+ **Prompt:**
447
+ *Is the current weather suitable for air travel in London and New York?*
448
+ **Response:**
449
+ *The current weather in London is 5°C and cloudy, and in New York, it is -0°C and clear; both conditions are generally suitable for air travel.*
450
+
451
+ ---
452
+
453
+ **Prompt:**
454
+ *Give me the weather update for all cities where cricket matches are happening today in India.*
455
+ **Response:**
456
+ *(This would involve external logic for identifying cricket venues, but the agent can handle the weather lookup part once cities are known.)*
457
+
458
+ ---
459
+
460
+ ###### 🧠 Why it Works:
461
+ - The **agent extracts all cities** from the prompt, even if mixed with unrelated text.
462
+ - It **chains tool calls**: First gets geolocation, then weather.
463
+ - The **final response is LLM-crafted** to match the tone and question format (yes/no, suggestion, comparison, etc.).
464
+
465
+ > ✅ You don’t need to ask "what's the weather in X" exactly — the agent infers it from how humans speak.
466
+ """)
467
+
468
+ with st.expander("🔍 Missing Agentic AI Capabilities & How to Improve"):
469
+ st.markdown("""
470
+ While the app exhibits several **agentic behaviors**—like tool use, intent recognition, and multi-step reasoning—it still lacks **some core features** found in *fully agentic systems*. Here's what’s missing:
471
+
472
+ ###### ❌ Missing Facets & How to Add Them
473
+
474
+ **1. Autonomy & Proactive Behavior**
475
+ *Current:* The app only responds to user prompts.
476
+ *To Add:* Let the agent proactively ask follow-ups.
477
+ **Example:**
478
+ - User: *What's the weather in Italy?*
479
+ - Agent: *Italy has multiple cities. Would you like weather in Rome, Milan, or Venice?*
480
+
481
+ **2. Goal-Oriented Planning**
482
+ *Current:* Executes one tool or a fixed chain of tools.
483
+ *To Add:* Give it a higher-level goal and let it plan the steps.
484
+ **Example:**
485
+ - Prompt: *Help me plan a weekend trip to a warm place in Europe.*
486
+ - Agent: Finds warm cities, checks weather, compares, and recommends.
487
+
488
+ **3. Memory / Session Context**
489
+ *Current:* Stateless; each query is standalone.
490
+ *To Add:* Use LangGraph or crewAI memory modules to **remember past queries** or preferences.
491
+ **Example:**
492
+ - User: *What’s the weather in Delhi?*
493
+ - Then: *And how about tomorrow?* → Agent should know the context refers to Delhi.
494
+
495
+ **4. Delegation to Sub-Agents**
496
+ *Current:* Single-agent, monolithic logic.
497
+ *To Add:* Delegate tasks to specialized agents (geocoder agent, weather formatter agent, response stylist, etc.).
498
+ **Example:**
499
+ - Planner agent decides cities → Fetcher agent retrieves data → Explainer agent summarizes.
500
+
501
+ **5. Multi-Modal Input/Output**
502
+ *Current:* Only text.
503
+ *To Add:* Accept voice prompts or generate a weather infographic.
504
+ **Example:**
505
+ - Prompt: *Voice note saying "Is it rainy in London?"* → Returns image with rainy clouds and summary.
506
+
507
+ **6. Learning from Feedback**
508
+ *Current:* No learning or improvement from user input.
509
+ *To Add:* Allow thumbs up/down or feedback to tune responses.
510
+ **Example:**
511
+ - User: *That was not helpful.* → Agent: *Sorry! Want a more detailed report or city breakdown?*
512
+
513
+ ---
514
+
515
+ ###### ✅ Summary
516
+ This app **lays a strong foundation for Agentic AI**, but adding these elements would bring it closer to a **truly autonomous, context-aware, and planning-capable agent** that mimics human-level task execution.
517
+ """)
518
+
519
+
520
+
521
+
522
+
523
+
524
+
525
+
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ openai
2
+ httpx
3
+ pydantic
4
+ pydantic-ai
5
+ logfire
6
+ python-dotenv