From 8411c90d6901963d1417162f90366eb57e5cd238 Mon Sep 17 00:00:00 2001 From: Madhu Date: Thu, 20 Feb 2025 23:39:07 +0530 Subject: [PATCH 01/12] added AI AQI analysis agent2 --- .../ai_aqi_analysis_agent/README.md | 0 .../ai_aqi_analysis_agent.py | 308 ++++++++++++++++++ .../ai_aqi_analysis_agent/requirements.txt | 0 3 files changed, 308 insertions(+) create mode 100644 ai_agent_tutorials/ai_aqi_analysis_agent/README.md create mode 100644 ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py create mode 100644 ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/README.md b/ai_agent_tutorials/ai_aqi_analysis_agent/README.md new file mode 100644 index 0000000..e69de29 diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py new file mode 100644 index 0000000..91bdd34 --- /dev/null +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -0,0 +1,308 @@ +""" +AQI Analysis Assistant +--------------------- +A Streamlit application that provides health recommendations based on air quality conditions. +Uses Firecrawl for AQI data and OpenAI for health recommendations. +""" + +from typing import Dict, Optional, TypedDict +from dataclasses import dataclass +from pydantic import BaseModel, Field +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from firecrawl import FirecrawlApp +import streamlit as st +import asyncio + +# Data Models +class AQIExtractSchema(BaseModel): + """Schema for AQI data extraction""" + aqi: int = Field(description="Current AQI value") + temperature: float = Field(description="Temperature in Celsius") + humidity: float = Field(description="Humidity percentage") + wind_speed: float = Field(description="Wind speed in km/h") + pm25: float = Field(description="PM2.5 level in µg/m³") + pm10: float = Field(description="PM10 level in µg/m³") + co: float = Field(description="CO level in ppb") + +@dataclass +class UserInput: + """Structure for user input data""" + city: str + state: str + country: str + medical_conditions: Optional[str] + planned_activity: str + +# Agent Classes +class AQIDataAgent: + """Agent responsible for fetching AQI and weather data""" + + def __init__(self, firecrawl_key: str, openai_key: str) -> None: + """Initialize with API keys""" + self.firecrawl = FirecrawlApp(api_key=firecrawl_key) + self.agent = Agent( + model=OpenAIChat( + id="gpt-4o", + api_key=openai_key + ), + description="Expert in analyzing air quality data and weather conditions" + ) + + def _format_url(self, country: str, state: str, city: str) -> str: + """Format location URL with proper formatting for multi-word locations""" + return f"https://www.aqi.in/dashboard/{country.lower().replace(' ', '-')}/{state.lower().replace(' ', '-')}/{city.lower().replace(' ', '-')}" + + async def fetch_data(self, city: str, state: str, country: str) -> AQIExtractSchema: + """Fetch weather and AQI data for given location""" + try: + base_url = self._format_url(country, state, city) + urls = [base_url, f"{base_url}/pm", f"{base_url}/co", f"{base_url}/pm10"] + + extract_prompt = """ + Extract the following air quality and weather metrics from the page: + - Current AQI value as an integer + - Temperature in Celsius as a float + - Humidity percentage as a float + - Wind speed in km/h as a float + - PM2.5 level in µg/m³ as a float + - PM10 level in µg/m³ as a float + - CO level in ppb as a float + + Return these exact metrics with their specified types. + """ + + response = self.firecrawl.extract( + urls=urls, + params={ + 'prompt': extract_prompt, + 'schema': AQIExtractSchema.model_json_schema() + } + ) + + if isinstance(response, dict) and 'error' in response: + raise ValueError(f"Firecrawl error: {response['error']}") + + return AQIExtractSchema(**response) + + except Exception as e: + st.error(f"Error fetching AQI data: {str(e)}") + # Return default values if fetch fails + return AQIExtractSchema( + aqi=0, + temperature=0.0, + humidity=0.0, + wind_speed=0.0, + pm25=0.0, + pm10=0.0, + co=0.0 + ) + +class HealthRecommendationAgent: + """Agent responsible for providing health recommendations""" + + def __init__(self, openai_key: str) -> None: + """Initialize with OpenAI API key""" + self.agent = Agent( + model=OpenAIChat( + id="gpt-4o", + api_key=openai_key + ), + description="Health recommendation expert for air quality conditions" + ) + + async def get_recommendations( + self, + aqi_data: AQIExtractSchema, + user_input: UserInput + ) -> str: + """Generate health recommendations based on conditions""" + prompt = self._create_prompt(aqi_data, user_input) + response = await self.agent.run(prompt) + return response.content + + def _create_prompt( + self, + aqi_data: AQIExtractSchema, + user_input: UserInput + ) -> str: + """Create detailed prompt for health recommendations""" + return f""" + Based on the following air quality conditions in {user_input.city}, {user_input.state}, {user_input.country}: + - Overall AQI: {aqi_data.aqi} + - PM2.5 Level: {aqi_data.pm25} µg/m³ + - PM10 Level: {aqi_data.pm10} µg/m³ + - CO Level: {aqi_data.co} ppb + + Weather conditions: + - Temperature: {aqi_data.temperature}°C + - Humidity: {aqi_data.humidity}% + - Wind Speed: {aqi_data.wind_speed} km/h + + User's Context: + - Medical Conditions: {user_input.medical_conditions or 'None'} + - Planned Activity: {user_input.planned_activity} + + Provide detailed health recommendations considering: + 1. Current air quality impacts on health + 2. Safety precautions needed + 3. Whether the planned activity is advisable + 4. Alternative activity suggestions if needed + 5. Best time to conduct the activity if applicable + """ + +# Main Analysis Function +async def analyze_conditions( + user_input: UserInput, + api_keys: Dict[str, str] +) -> str: + """Main function to analyze conditions and provide recommendations""" + # Initialize agents + aqi_agent = AQIDataAgent( + firecrawl_key=api_keys['firecrawl'], + openai_key=api_keys['openai'] + ) + health_agent = HealthRecommendationAgent( + openai_key=api_keys['openai'] + ) + + # Get data and recommendations + aqi_data = await aqi_agent.fetch_data( + city=user_input.city, + state=user_input.state, + country=user_input.country + ) + + return await health_agent.get_recommendations(aqi_data, user_input) + +# Streamlit UI Components +def initialize_session_state(): + """Initialize Streamlit session state""" + if 'api_keys' not in st.session_state: + st.session_state.api_keys = { + 'firecrawl': '', + 'openai': '' + } + +def setup_page(): + """Configure page settings and styles""" + st.set_page_config( + page_title="AQI Analysis Assistant", + page_icon="🌍", + layout="wide" + ) + + st.markdown(""" + + """, unsafe_allow_html=True) + +def render_sidebar(): + """Render sidebar with API configuration""" + with st.sidebar: + st.header("🔑 API Configuration") + + new_firecrawl_key = st.text_input( + "Firecrawl API Key", + type="password", + value=st.session_state.api_keys['firecrawl'], + help="Enter your Firecrawl API key" + ) + new_openai_key = st.text_input( + "OpenAI API Key", + type="password", + value=st.session_state.api_keys['openai'], + help="Enter your OpenAI API key" + ) + + if (new_firecrawl_key != st.session_state.api_keys['firecrawl'] or + new_openai_key != st.session_state.api_keys['openai']): + st.session_state.api_keys.update({ + 'firecrawl': new_firecrawl_key, + 'openai': new_openai_key + }) + st.success("✅ API keys updated!") + +def render_main_content(): + """Render main content area""" + st.title("🌍 AQI Analysis Assistant") + st.markdown("Get personalized health recommendations based on air quality conditions.") + + col1, col2 = st.columns([2, 1]) + + with col1: + st.header("📍 Location Details") + city = st.text_input("City", placeholder="e.g., Mumbai") + state = st.text_input("State", placeholder="e.g., Maharashtra") + country = st.text_input("Country", value="India") + + st.header("👤 Personal Details") + medical_conditions = st.text_area( + "Medical Conditions (optional)", + placeholder="e.g., asthma, allergies" + ) + planned_activity = st.text_area( + "Planned Activity", + placeholder="e.g., morning jog for 2 hours" + ) + + return UserInput( + city=city, + state=state, + country=country, + medical_conditions=medical_conditions, + planned_activity=planned_activity + ) + +def main(): + """Main application entry point""" + initialize_session_state() + setup_page() + render_sidebar() + user_input = render_main_content() + + if st.button("🔍 Analyze & Get Recommendations"): + if not all([user_input.city, user_input.state, user_input.planned_activity]): + st.error("Please fill in all required fields (medical conditions are optional)") + elif not all(st.session_state.api_keys.values()): + st.error("Please provide both API keys in the sidebar") + else: + try: + with st.spinner("🔄 Analyzing conditions..."): + result = asyncio.run( + analyze_conditions( + user_input=user_input, + api_keys=st.session_state.api_keys + ) + ) + + st.success("✅ Analysis completed!") + st.markdown("### 📊 Recommendations") + st.markdown(result) + + st.download_button( + "💾 Download Recommendations", + data=result, + file_name=f"aqi_recommendations_{user_input.city}_{user_input.state}.txt", + mime="text/plain" + ) + + except Exception as e: + st.error(f"❌ Error: {str(e)}") + +if __name__ == "__main__": + main() diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt b/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt new file mode 100644 index 0000000..e69de29 From 37b9058dff9b096f221777745ac90e54bea8e387 Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 00:50:23 +0530 Subject: [PATCH 02/12] added AI AQI analysis agent3 --- .../ai_aqi_analysis_agent.py | 199 ++++++------------ .../ai_aqi_analysis_agent/test_firecrawl.py | 22 ++ 2 files changed, 85 insertions(+), 136 deletions(-) create mode 100644 ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index 91bdd34..885d832 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -1,11 +1,4 @@ -""" -AQI Analysis Assistant ---------------------- -A Streamlit application that provides health recommendations based on air quality conditions. -Uses Firecrawl for AQI data and OpenAI for health recommendations. -""" - -from typing import Dict, Optional, TypedDict +from typing import Dict, Optional from dataclasses import dataclass from pydantic import BaseModel, Field from agno.agent import Agent @@ -14,130 +7,99 @@ from firecrawl import FirecrawlApp import streamlit as st import asyncio -# Data Models -class AQIExtractSchema(BaseModel): - """Schema for AQI data extraction""" - aqi: int = Field(description="Current AQI value") - temperature: float = Field(description="Temperature in Celsius") +class AQIResponse(BaseModel): + success: bool + data: Dict[str, float] + status: str + expiresAt: str + +class ExtractSchema(BaseModel): + aqi: float = Field(description="Air Quality Index") + temperature: float = Field(description="Temperature in degrees Celsius") humidity: float = Field(description="Humidity percentage") - wind_speed: float = Field(description="Wind speed in km/h") - pm25: float = Field(description="PM2.5 level in µg/m³") - pm10: float = Field(description="PM10 level in µg/m³") - co: float = Field(description="CO level in ppb") + wind_speed: float = Field(description="Wind speed in kilometers per hour") + pm25: float = Field(description="Particulate Matter 2.5 micrometers") + pm10: float = Field(description="Particulate Matter 10 micrometers") + co: float = Field(description="Carbon Monoxide level") @dataclass class UserInput: - """Structure for user input data""" city: str state: str country: str medical_conditions: Optional[str] planned_activity: str -# Agent Classes -class AQIDataAgent: - """Agent responsible for fetching AQI and weather data""" +class AQIAnalyzer: - def __init__(self, firecrawl_key: str, openai_key: str) -> None: - """Initialize with API keys""" + def __init__(self, firecrawl_key: str) -> None: self.firecrawl = FirecrawlApp(api_key=firecrawl_key) - self.agent = Agent( - model=OpenAIChat( - id="gpt-4o", - api_key=openai_key - ), - description="Expert in analyzing air quality data and weather conditions" - ) def _format_url(self, country: str, state: str, city: str) -> str: - """Format location URL with proper formatting for multi-word locations""" return f"https://www.aqi.in/dashboard/{country.lower().replace(' ', '-')}/{state.lower().replace(' ', '-')}/{city.lower().replace(' ', '-')}" - async def fetch_data(self, city: str, state: str, country: str) -> AQIExtractSchema: - """Fetch weather and AQI data for given location""" + def fetch_aqi_data(self, city: str, state: str, country: str) -> Dict[str, float]: try: - base_url = self._format_url(country, state, city) - urls = [base_url, f"{base_url}/pm", f"{base_url}/co", f"{base_url}/pm10"] - - extract_prompt = """ - Extract the following air quality and weather metrics from the page: - - Current AQI value as an integer - - Temperature in Celsius as a float - - Humidity percentage as a float - - Wind speed in km/h as a float - - PM2.5 level in µg/m³ as a float - - PM10 level in µg/m³ as a float - - CO level in ppb as a float - - Return these exact metrics with their specified types. - """ + url = self._format_url(country, state, city) response = self.firecrawl.extract( - urls=urls, + urls=[f"{url}/*"], params={ - 'prompt': extract_prompt, - 'schema': AQIExtractSchema.model_json_schema() + 'prompt': 'Extract the AQI, temperature, humidity, wind speed, PM2.5, PM10, and CO levels from the page.', + 'schema': ExtractSchema.model_json_schema() } ) - if isinstance(response, dict) and 'error' in response: - raise ValueError(f"Firecrawl error: {response['error']}") + aqi_response = AQIResponse(**response) + if not aqi_response.success: + raise ValueError(f"Failed to fetch AQI data: {aqi_response.status}") - return AQIExtractSchema(**response) + return aqi_response.data except Exception as e: st.error(f"Error fetching AQI data: {str(e)}") - # Return default values if fetch fails - return AQIExtractSchema( - aqi=0, - temperature=0.0, - humidity=0.0, - wind_speed=0.0, - pm25=0.0, - pm10=0.0, - co=0.0 - ) + return { + 'aqi': 0, + 'temperature': 0, + 'humidity': 0, + 'wind_speed': 0, + 'pm25': 0, + 'pm10': 0, + 'co': 0 + } class HealthRecommendationAgent: - """Agent responsible for providing health recommendations""" def __init__(self, openai_key: str) -> None: - """Initialize with OpenAI API key""" self.agent = Agent( model=OpenAIChat( id="gpt-4o", + name="Health Recommendation Agent", api_key=openai_key - ), - description="Health recommendation expert for air quality conditions" + ) ) - async def get_recommendations( - self, - aqi_data: AQIExtractSchema, + def get_recommendations( + self, + aqi_data: Dict[str, float], user_input: UserInput ) -> str: - """Generate health recommendations based on conditions""" prompt = self._create_prompt(aqi_data, user_input) - response = await self.agent.run(prompt) + response = self.agent.run(prompt) return response.content - def _create_prompt( - self, - aqi_data: AQIExtractSchema, - user_input: UserInput - ) -> str: - """Create detailed prompt for health recommendations""" + def _create_prompt(self, aqi_data: Dict[str, float], user_input: UserInput) -> str: return f""" Based on the following air quality conditions in {user_input.city}, {user_input.state}, {user_input.country}: - - Overall AQI: {aqi_data.aqi} - - PM2.5 Level: {aqi_data.pm25} µg/m³ - - PM10 Level: {aqi_data.pm10} µg/m³ - - CO Level: {aqi_data.co} ppb + - Overall AQI: {aqi_data['aqi']} + - PM2.5 Level: {aqi_data['pm25']} µg/m³ + - PM10 Level: {aqi_data['pm10']} µg/m³ + - CO Level: {aqi_data['co']} ppb Weather conditions: - - Temperature: {aqi_data.temperature}°C - - Humidity: {aqi_data.humidity}% - - Wind Speed: {aqi_data.wind_speed} km/h + - Temperature: {aqi_data['temperature']}°C + - Humidity: {aqi_data['humidity']}% + - Wind Speed: {aqi_data['wind_speed']} km/h User's Context: - Medical Conditions: {user_input.medical_conditions or 'None'} @@ -151,33 +113,22 @@ class HealthRecommendationAgent: 5. Best time to conduct the activity if applicable """ -# Main Analysis Function -async def analyze_conditions( +def analyze_conditions( user_input: UserInput, api_keys: Dict[str, str] ) -> str: - """Main function to analyze conditions and provide recommendations""" - # Initialize agents - aqi_agent = AQIDataAgent( - firecrawl_key=api_keys['firecrawl'], - openai_key=api_keys['openai'] - ) - health_agent = HealthRecommendationAgent( - openai_key=api_keys['openai'] - ) + aqi_analyzer = AQIAnalyzer(firecrawl_key=api_keys['firecrawl']) + health_agent = HealthRecommendationAgent(openai_key=api_keys['openai']) - # Get data and recommendations - aqi_data = await aqi_agent.fetch_data( + aqi_data = aqi_analyzer.fetch_aqi_data( city=user_input.city, state=user_input.state, country=user_input.country ) - return await health_agent.get_recommendations(aqi_data, user_input) + return health_agent.get_recommendations(aqi_data, user_input) -# Streamlit UI Components def initialize_session_state(): - """Initialize Streamlit session state""" if 'api_keys' not in st.session_state: st.session_state.api_keys = { 'firecrawl': '', @@ -185,34 +136,16 @@ def initialize_session_state(): } def setup_page(): - """Configure page settings and styles""" st.set_page_config( page_title="AQI Analysis Assistant", page_icon="🌍", layout="wide" ) - st.markdown(""" - - """, unsafe_allow_html=True) + st.title("🌍 AQI Analysis Assistant") + st.info("Get personalized health recommendations based on air quality conditions.") def render_sidebar(): - """Render sidebar with API configuration""" with st.sidebar: st.header("🔑 API Configuration") @@ -238,18 +171,15 @@ def render_sidebar(): st.success("✅ API keys updated!") def render_main_content(): - """Render main content area""" - st.title("🌍 AQI Analysis Assistant") - st.markdown("Get personalized health recommendations based on air quality conditions.") - - col1, col2 = st.columns([2, 1]) + st.header("📍 Location Details") + col1, col2 = st.columns(2) with col1: - st.header("📍 Location Details") city = st.text_input("City", placeholder="e.g., Mumbai") state = st.text_input("State", placeholder="e.g., Maharashtra") country = st.text_input("Country", value="India") - + + with col2: st.header("👤 Personal Details") medical_conditions = st.text_area( "Medical Conditions (optional)", @@ -269,7 +199,6 @@ def render_main_content(): ) def main(): - """Main application entry point""" initialize_session_state() setup_page() render_sidebar() @@ -283,16 +212,14 @@ def main(): else: try: with st.spinner("🔄 Analyzing conditions..."): - result = asyncio.run( - analyze_conditions( - user_input=user_input, - api_keys=st.session_state.api_keys - ) + result = analyze_conditions( + user_input=user_input, + api_keys=st.session_state.api_keys ) st.success("✅ Analysis completed!") st.markdown("### 📊 Recommendations") - st.markdown(result) + st.info(result) st.download_button( "💾 Download Recommendations", diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py b/ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py new file mode 100644 index 0000000..3829df3 --- /dev/null +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py @@ -0,0 +1,22 @@ +from firecrawl import FirecrawlApp +from pydantic import BaseModel, Field + +# Initialize the FirecrawlApp with your API key +app = FirecrawlApp(api_key='') + +class ExtractSchema(BaseModel): + aqi: float = Field(description="Air Quality Index") + temperature: float = Field(description="Temperature in degrees Celsius") + humidity: float = Field(description="Humidity percentage") + wind_speed: float = Field(description="Wind speed in kilometers per hour") + pm25: float = Field(description="Particulate Matter 2.5 micrometers") + pm10: float = Field(description="Particulate Matter 10 micrometers") + co: float = Field(description="Carbon Monoxide level") + +data = app.extract([ + 'https://www.aqi.in/dashboard/india/andhra-pradesh/kakinada/*' +], { + 'prompt': 'Extract the AQI, temperature, humidity, wind speed, PM2.5, PM10, and CO levels from the page.', + 'schema': ExtractSchema.model_json_schema(), +}) +print(data) \ No newline at end of file From eb17161e0470926348491fc35d8ecc3cc5e0d99c Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 00:52:43 +0530 Subject: [PATCH 03/12] api keys update st.success change --- .../ai_aqi_analysis_agent/ai_aqi_analysis_agent.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index 885d832..c8d9f36 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -146,6 +146,7 @@ def setup_page(): st.info("Get personalized health recommendations based on air quality conditions.") def render_sidebar(): + """Render sidebar with API configuration""" with st.sidebar: st.header("🔑 API Configuration") @@ -162,8 +163,10 @@ def render_sidebar(): help="Enter your OpenAI API key" ) - if (new_firecrawl_key != st.session_state.api_keys['firecrawl'] or - new_openai_key != st.session_state.api_keys['openai']): + # Update session state only if both keys are provided + if (new_firecrawl_key and new_openai_key and + (new_firecrawl_key != st.session_state.api_keys['firecrawl'] or + new_openai_key != st.session_state.api_keys['openai'])): st.session_state.api_keys.update({ 'firecrawl': new_firecrawl_key, 'openai': new_openai_key From 2e8b254391366e286304d8d8655da6f31cf993af Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 01:06:33 +0530 Subject: [PATCH 04/12] few changes --- .../ai_aqi_analysis_agent.py | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index c8d9f36..45180e2 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -39,6 +39,7 @@ class AQIAnalyzer: return f"https://www.aqi.in/dashboard/{country.lower().replace(' ', '-')}/{state.lower().replace(' ', '-')}/{city.lower().replace(' ', '-')}" def fetch_aqi_data(self, city: str, state: str, country: str) -> Dict[str, float]: + """Fetch AQI data using Firecrawl""" try: url = self._format_url(country, state, city) @@ -50,9 +51,14 @@ class AQIAnalyzer: } ) + # Parse Firecrawl response aqi_response = AQIResponse(**response) if not aqi_response.success: raise ValueError(f"Failed to fetch AQI data: {aqi_response.status}") + + # Display raw data in expander + with st.expander("📊 Raw AQI Data", expanded=True): + st.json(aqi_response.data) return aqi_response.data @@ -107,10 +113,9 @@ class HealthRecommendationAgent: Provide detailed health recommendations considering: 1. Current air quality impacts on health - 2. Safety precautions needed - 3. Whether the planned activity is advisable - 4. Alternative activity suggestions if needed - 5. Best time to conduct the activity if applicable + 2. Safety precautions needed for the planned activity of User based on the air quality conditions + 3. Whether the planned activity is advisable or not, if its not advisable then provide alternative activity suggestions + 4. Best time to conduct the activity if applicable """ def analyze_conditions( @@ -202,11 +207,14 @@ def render_main_content(): ) def main(): + """Main application entry point""" initialize_session_state() setup_page() render_sidebar() user_input = render_main_content() + result = None # Initialize result variable + if st.button("🔍 Analyze & Get Recommendations"): if not all([user_input.city, user_input.state, user_input.planned_activity]): st.error("Please fill in all required fields (medical conditions are optional)") @@ -219,20 +227,22 @@ def main(): user_input=user_input, api_keys=st.session_state.api_keys ) - st.success("✅ Analysis completed!") - st.markdown("### 📊 Recommendations") - st.info(result) - - st.download_button( - "💾 Download Recommendations", - data=result, - file_name=f"aqi_recommendations_{user_input.city}_{user_input.state}.txt", - mime="text/plain" - ) except Exception as e: st.error(f"❌ Error: {str(e)}") + # Display recommendations if available + if result: + st.markdown("### 📊 Recommendations") + st.markdown(result) + + st.download_button( + "💾 Download Recommendations", + data=result, + file_name=f"aqi_recommendations_{user_input.city}_{user_input.state}.txt", + mime="text/plain" + ) + if __name__ == "__main__": main() From 85f953c1cee180d25b905d555b06973cdfe2227d Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 01:18:33 +0530 Subject: [PATCH 05/12] Placeholder changes --- .../ai_aqi_analysis_agent.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index 45180e2..e78d564 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -36,7 +36,18 @@ class AQIAnalyzer: self.firecrawl = FirecrawlApp(api_key=firecrawl_key) def _format_url(self, country: str, state: str, city: str) -> str: - return f"https://www.aqi.in/dashboard/{country.lower().replace(' ', '-')}/{state.lower().replace(' ', '-')}/{city.lower().replace(' ', '-')}" + """Format URL based on location, handling cases with and without state""" + # Clean and format the components + country_clean = country.lower().replace(' ', '-') + city_clean = city.lower().replace(' ', '-') + + # If state is None or empty, use direct city URL + if not state or state.lower() == 'none': + return f"https://www.aqi.in/dashboard/{country_clean}/{city_clean}" + + # Regular case with state included + state_clean = state.lower().replace(' ', '-') + return f"https://www.aqi.in/dashboard/{country_clean}/{state_clean}/{city_clean}" def fetch_aqi_data(self, city: str, state: str, country: str) -> Dict[str, float]: """Fetch AQI data using Firecrawl""" @@ -184,8 +195,8 @@ def render_main_content(): with col1: city = st.text_input("City", placeholder="e.g., Mumbai") - state = st.text_input("State", placeholder="e.g., Maharashtra") - country = st.text_input("Country", value="India") + state = st.text_input("State", placeholder="If it's a Union Territory or a city in the US, leave it blank") + country = st.text_input("Country", value="India", placeholder="United States") with col2: st.header("👤 Personal Details") From 2a7e2a0fe212a2374d0a9542319a25f4e31dddc5 Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 01:26:00 +0530 Subject: [PATCH 06/12] flow change --- .../ai_aqi_analysis_agent.py | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index e78d564..80f539c 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -53,11 +53,12 @@ class AQIAnalyzer: """Fetch AQI data using Firecrawl""" try: url = self._format_url(country, state, city) + st.info(f"Accessing URL: {url}") # Display URL being accessed response = self.firecrawl.extract( urls=[f"{url}/*"], params={ - 'prompt': 'Extract the AQI, temperature, humidity, wind speed, PM2.5, PM10, and CO levels from the page.', + 'prompt': 'Extract the current real-time AQI, temperature, humidity, wind speed, PM2.5, PM10, and CO levels from the page. Also extract the timestamp of the data.', 'schema': ExtractSchema.model_json_schema() } ) @@ -67,9 +68,24 @@ class AQIAnalyzer: if not aqi_response.success: raise ValueError(f"Failed to fetch AQI data: {aqi_response.status}") - # Display raw data in expander + # Display raw data in expander with timestamp with st.expander("📊 Raw AQI Data", expanded=True): - st.json(aqi_response.data) + st.json({ + "url_accessed": url, + "timestamp": aqi_response.expiresAt, + "data": aqi_response.data + }) + + # Add warning if data seems stale or inconsistent + st.warning(""" + ⚠️ Note: The data shown may not match real-time values on the website. + This could be due to: + - Cached data in Firecrawl + - Rate limiting + - Website updates not being captured + + Consider refreshing or checking the website directly for real-time values. + """) return aqi_response.data @@ -224,11 +240,11 @@ def main(): render_sidebar() user_input = render_main_content() - result = None # Initialize result variable + result = None if st.button("🔍 Analyze & Get Recommendations"): - if not all([user_input.city, user_input.state, user_input.planned_activity]): - st.error("Please fill in all required fields (medical conditions are optional)") + if not all([user_input.city, user_input.planned_activity]): + st.error("Please fill in all required fields (state and medical conditions are optional)") elif not all(st.session_state.api_keys.values()): st.error("Please provide both API keys in the sidebar") else: @@ -246,7 +262,7 @@ def main(): # Display recommendations if available if result: st.markdown("### 📊 Recommendations") - st.markdown(result) + st.info(result) st.download_button( "💾 Download Recommendations", From 396d95ad8b21a1a5e6d02c23a6a55da19c3cf000 Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 01:43:58 +0530 Subject: [PATCH 07/12] changes --- .../ai_aqi_analysis_agent.py | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index 80f539c..d96d5cd 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -37,15 +37,12 @@ class AQIAnalyzer: def _format_url(self, country: str, state: str, city: str) -> str: """Format URL based on location, handling cases with and without state""" - # Clean and format the components country_clean = country.lower().replace(' ', '-') city_clean = city.lower().replace(' ', '-') - # If state is None or empty, use direct city URL if not state or state.lower() == 'none': return f"https://www.aqi.in/dashboard/{country_clean}/{city_clean}" - # Regular case with state included state_clean = state.lower().replace(' ', '-') return f"https://www.aqi.in/dashboard/{country_clean}/{state_clean}/{city_clean}" @@ -63,20 +60,17 @@ class AQIAnalyzer: } ) - # Parse Firecrawl response aqi_response = AQIResponse(**response) if not aqi_response.success: raise ValueError(f"Failed to fetch AQI data: {aqi_response.status}") - # Display raw data in expander with timestamp - with st.expander("📊 Raw AQI Data", expanded=True): + with st.expander("📦 Raw AQI Data", expanded=True): st.json({ "url_accessed": url, "timestamp": aqi_response.expiresAt, "data": aqi_response.data }) - # Add warning if data seems stale or inconsistent st.warning(""" ⚠️ Note: The data shown may not match real-time values on the website. This could be due to: @@ -137,12 +131,11 @@ class HealthRecommendationAgent: User's Context: - Medical Conditions: {user_input.medical_conditions or 'None'} - Planned Activity: {user_input.planned_activity} - - Provide detailed health recommendations considering: - 1. Current air quality impacts on health - 2. Safety precautions needed for the planned activity of User based on the air quality conditions - 3. Whether the planned activity is advisable or not, if its not advisable then provide alternative activity suggestions - 4. Best time to conduct the activity if applicable + **Comprehensive Health Recommendations:** + 1. **Impact of Current Air Quality on Health:** + 2. **Necessary Safety Precautions for Planned Activity:** + 3. **Advisability of Planned Activity:** + 4. **Best Time to Conduct the Activity:** """ def analyze_conditions( @@ -169,12 +162,12 @@ def initialize_session_state(): def setup_page(): st.set_page_config( - page_title="AQI Analysis Assistant", + page_title="AQI Analysis Agent", page_icon="🌍", layout="wide" ) - st.title("🌍 AQI Analysis Assistant") + st.title("🌍 AQI Analysis Agent") st.info("Get personalized health recommendations based on air quality conditions.") def render_sidebar(): @@ -195,7 +188,6 @@ def render_sidebar(): help="Enter your OpenAI API key" ) - # Update session state only if both keys are provided if (new_firecrawl_key and new_openai_key and (new_firecrawl_key != st.session_state.api_keys['firecrawl'] or new_openai_key != st.session_state.api_keys['openai'])): @@ -259,10 +251,9 @@ def main(): except Exception as e: st.error(f"❌ Error: {str(e)}") - # Display recommendations if available if result: - st.markdown("### 📊 Recommendations") - st.info(result) + st.markdown("### 📦 Recommendations") + st.markdown(result) st.download_button( "💾 Download Recommendations", From 732b68852a0e3236cac2a59c54d2a9c86a35879a Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 01:46:31 +0530 Subject: [PATCH 08/12] readme and requirements.txt --- .../ai_aqi_analysis_agent/README.md | 74 +++++++++++++++++++ .../ai_aqi_analysis_agent.py | 1 - .../ai_aqi_analysis_agent/requirements.txt | 3 + .../ai_aqi_analysis_agent/test_firecrawl.py | 22 ------ 4 files changed, 77 insertions(+), 23 deletions(-) delete mode 100644 ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/README.md b/ai_agent_tutorials/ai_aqi_analysis_agent/README.md index e69de29..055f004 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/README.md +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/README.md @@ -0,0 +1,74 @@ +# 🌍 AQI Analysis Agent + +The AQI Analysis Agent is a powerful air quality monitoring and health recommendation tool powered by Firecrawl and Agno's AI Agent framework. This app helps users make informed decisions about outdoor activities by analyzing real-time air quality data and providing personalized health recommendations. + +## Features + +- **Multi-Agent System** + - **AQI Analyzer**: Fetches and processes real-time air quality data + - **Health Recommendation Agent**: Generates personalized health advice + +- **Air Quality Metrics**: + - Overall Air Quality Index (AQI) + - Particulate Matter (PM2.5 and PM10) + - Carbon Monoxide (CO) levels + - Temperature + - Humidity + - Wind Speed + +- **Comprehensive Analysis**: + - Real-time data visualization + - Health impact assessment + - Activity safety recommendations + - Best time suggestions for outdoor activities + - Weather condition correlations + +- **Interactive Features**: + - Location-based analysis + - Medical condition considerations + - Activity-specific recommendations + - Downloadable reports + +## How to Run + +Follow these steps to set up and run the application: + +1. **Clone the Repository**: + ```bash + git clone https://github.com/Shubhamsaboo/awesome-llm-apps.git + cd ai_agent_tutorials/ai_aqi_analysis_agent + ``` + +2. **Install the dependencies**: + ```bash + pip install -r requirements.txt + ``` + +3. **Set up your API keys**: + - Get an OpenAI API key from: https://platform.openai.com/api-keys + - Get a Firecrawl API key from: [Firecrawl website](https://www.firecrawl.dev/app/api-keys) + +4. **Run the Streamlit app**: + ```bash + streamlit run ai_aqi_analysis_agent.py + ``` + +## Usage + +1. Enter your API keys in the sidebar +2. Input location details: + - City name + - State (optional) + - Country +3. Provide personal information: + - Medical conditions (optional) + - Planned outdoor activity +4. Click "Analyze & Get Recommendations" to receive: + - Current air quality data + - Health impact analysis + - Activity safety recommendations + - Downloadable report + +## Note + +The air quality data is fetched using Firecrawl's web scraping capabilities. Due to caching and rate limiting, the data might not always match real-time values on the website. For the most accurate real-time data, consider checking the source website directly. diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index d96d5cd..a0a0689 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -5,7 +5,6 @@ from agno.agent import Agent from agno.models.openai import OpenAIChat from firecrawl import FirecrawlApp import streamlit as st -import asyncio class AQIResponse(BaseModel): success: bool diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt b/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt index e69de29..af9678d 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt @@ -0,0 +1,3 @@ +agno +openai +firecrawl-py==1.9.0 diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py b/ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py deleted file mode 100644 index 3829df3..0000000 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/test_firecrawl.py +++ /dev/null @@ -1,22 +0,0 @@ -from firecrawl import FirecrawlApp -from pydantic import BaseModel, Field - -# Initialize the FirecrawlApp with your API key -app = FirecrawlApp(api_key='') - -class ExtractSchema(BaseModel): - aqi: float = Field(description="Air Quality Index") - temperature: float = Field(description="Temperature in degrees Celsius") - humidity: float = Field(description="Humidity percentage") - wind_speed: float = Field(description="Wind speed in kilometers per hour") - pm25: float = Field(description="Particulate Matter 2.5 micrometers") - pm10: float = Field(description="Particulate Matter 10 micrometers") - co: float = Field(description="Carbon Monoxide level") - -data = app.extract([ - 'https://www.aqi.in/dashboard/india/andhra-pradesh/kakinada/*' -], { - 'prompt': 'Extract the AQI, temperature, humidity, wind speed, PM2.5, PM10, and CO levels from the page.', - 'schema': ExtractSchema.model_json_schema(), -}) -print(data) \ No newline at end of file From 1bab3f4b6163c75ccd884b69dd588876678913eb Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 02:04:03 +0530 Subject: [PATCH 09/12] Gradio UI --- .../ai_aqi_analysis_agent.py | 286 +++++++++--------- 1 file changed, 146 insertions(+), 140 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index a0a0689..6d07148 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -4,7 +4,8 @@ from pydantic import BaseModel, Field from agno.agent import Agent from agno.models.openai import OpenAIChat from firecrawl import FirecrawlApp -import streamlit as st +import gradio as gr +import json class AQIResponse(BaseModel): success: bool @@ -45,11 +46,11 @@ class AQIAnalyzer: state_clean = state.lower().replace(' ', '-') return f"https://www.aqi.in/dashboard/{country_clean}/{state_clean}/{city_clean}" - def fetch_aqi_data(self, city: str, state: str, country: str) -> Dict[str, float]: + def fetch_aqi_data(self, city: str, state: str, country: str) -> tuple[Dict[str, float], str]: """Fetch AQI data using Firecrawl""" try: url = self._format_url(country, state, city) - st.info(f"Accessing URL: {url}") # Display URL being accessed + info_msg = f"Accessing URL: {url}" response = self.firecrawl.extract( urls=[f"{url}/*"], @@ -63,27 +64,10 @@ class AQIAnalyzer: if not aqi_response.success: raise ValueError(f"Failed to fetch AQI data: {aqi_response.status}") - with st.expander("📦 Raw AQI Data", expanded=True): - st.json({ - "url_accessed": url, - "timestamp": aqi_response.expiresAt, - "data": aqi_response.data - }) - - st.warning(""" - ⚠️ Note: The data shown may not match real-time values on the website. - This could be due to: - - Cached data in Firecrawl - - Rate limiting - - Website updates not being captured - - Consider refreshing or checking the website directly for real-time values. - """) - - return aqi_response.data + return aqi_response.data, info_msg except Exception as e: - st.error(f"Error fetching AQI data: {str(e)}") + error_msg = f"Error fetching AQI data: {str(e)}" return { 'aqi': 0, 'temperature': 0, @@ -92,7 +76,7 @@ class AQIAnalyzer: 'pm25': 0, 'pm10': 0, 'co': 0 - } + }, error_msg class HealthRecommendationAgent: @@ -138,128 +122,150 @@ class HealthRecommendationAgent: """ def analyze_conditions( - user_input: UserInput, - api_keys: Dict[str, str] -) -> str: - aqi_analyzer = AQIAnalyzer(firecrawl_key=api_keys['firecrawl']) - health_agent = HealthRecommendationAgent(openai_key=api_keys['openai']) - - aqi_data = aqi_analyzer.fetch_aqi_data( - city=user_input.city, - state=user_input.state, - country=user_input.country - ) - - return health_agent.get_recommendations(aqi_data, user_input) - -def initialize_session_state(): - if 'api_keys' not in st.session_state: - st.session_state.api_keys = { - 'firecrawl': '', - 'openai': '' - } - -def setup_page(): - st.set_page_config( - page_title="AQI Analysis Agent", - page_icon="🌍", - layout="wide" - ) - - st.title("🌍 AQI Analysis Agent") - st.info("Get personalized health recommendations based on air quality conditions.") - -def render_sidebar(): - """Render sidebar with API configuration""" - with st.sidebar: - st.header("🔑 API Configuration") + city: str, + state: str, + country: str, + medical_conditions: str, + planned_activity: str, + firecrawl_key: str, + openai_key: str +) -> tuple[str, str, str, str]: + """Analyze conditions and return AQI data, recommendations, and status messages""" + try: + # Initialize analyzers + aqi_analyzer = AQIAnalyzer(firecrawl_key=firecrawl_key) + health_agent = HealthRecommendationAgent(openai_key=openai_key) - new_firecrawl_key = st.text_input( - "Firecrawl API Key", - type="password", - value=st.session_state.api_keys['firecrawl'], - help="Enter your Firecrawl API key" - ) - new_openai_key = st.text_input( - "OpenAI API Key", - type="password", - value=st.session_state.api_keys['openai'], - help="Enter your OpenAI API key" + # Create user input + user_input = UserInput( + city=city, + state=state, + country=country, + medical_conditions=medical_conditions, + planned_activity=planned_activity ) - if (new_firecrawl_key and new_openai_key and - (new_firecrawl_key != st.session_state.api_keys['firecrawl'] or - new_openai_key != st.session_state.api_keys['openai'])): - st.session_state.api_keys.update({ - 'firecrawl': new_firecrawl_key, - 'openai': new_openai_key - }) - st.success("✅ API keys updated!") - -def render_main_content(): - st.header("📍 Location Details") - col1, col2 = st.columns(2) - - with col1: - city = st.text_input("City", placeholder="e.g., Mumbai") - state = st.text_input("State", placeholder="If it's a Union Territory or a city in the US, leave it blank") - country = st.text_input("Country", value="India", placeholder="United States") - - with col2: - st.header("👤 Personal Details") - medical_conditions = st.text_area( - "Medical Conditions (optional)", - placeholder="e.g., asthma, allergies" + # Get AQI data + aqi_data, info_msg = aqi_analyzer.fetch_aqi_data( + city=user_input.city, + state=user_input.state, + country=user_input.country ) - planned_activity = st.text_area( - "Planned Activity", - placeholder="e.g., morning jog for 2 hours" - ) - - return UserInput( - city=city, - state=state, - country=country, - medical_conditions=medical_conditions, - planned_activity=planned_activity - ) - -def main(): - """Main application entry point""" - initialize_session_state() - setup_page() - render_sidebar() - user_input = render_main_content() - - result = None - - if st.button("🔍 Analyze & Get Recommendations"): - if not all([user_input.city, user_input.planned_activity]): - st.error("Please fill in all required fields (state and medical conditions are optional)") - elif not all(st.session_state.api_keys.values()): - st.error("Please provide both API keys in the sidebar") - else: - try: - with st.spinner("🔄 Analyzing conditions..."): - result = analyze_conditions( - user_input=user_input, - api_keys=st.session_state.api_keys - ) - st.success("✅ Analysis completed!") - - except Exception as e: - st.error(f"❌ Error: {str(e)}") - - if result: - st.markdown("### 📦 Recommendations") - st.markdown(result) - st.download_button( - "💾 Download Recommendations", - data=result, - file_name=f"aqi_recommendations_{user_input.city}_{user_input.state}.txt", - mime="text/plain" + # Format AQI data for display + aqi_json = json.dumps({ + "Air Quality Index (AQI)": aqi_data['aqi'], + "PM2.5": f"{aqi_data['pm25']} µg/m³", + "PM10": f"{aqi_data['pm10']} µg/m³", + "Carbon Monoxide (CO)": f"{aqi_data['co']} ppb", + "Temperature": f"{aqi_data['temperature']}°C", + "Humidity": f"{aqi_data['humidity']}%", + "Wind Speed": f"{aqi_data['wind_speed']} km/h" + }, indent=2) + + # Get recommendations + recommendations = health_agent.get_recommendations(aqi_data, user_input) + + warning_msg = """ + ⚠️ Note: The data shown may not match real-time values on the website. + This could be due to: + - Cached data in Firecrawl + - Rate limiting + - Website updates not being captured + + Consider refreshing or checking the website directly for real-time values. + """ + + return aqi_json, recommendations, info_msg, warning_msg + + except Exception as e: + error_msg = f"Error occurred: {str(e)}" + return "", "Analysis failed", error_msg, "" + +def create_demo() -> gr.Blocks: + """Create and configure the Gradio interface""" + with gr.Blocks(title="AQI Analysis Agent") as demo: + gr.Markdown( + """ + # 🌍 AQI Analysis Agent + Get personalized health recommendations based on air quality conditions. + """ ) + + # API Configuration + with gr.Accordion("API Configuration", open=False): + firecrawl_key = gr.Textbox( + label="Firecrawl API Key", + type="password", + placeholder="Enter your Firecrawl API key" + ) + openai_key = gr.Textbox( + label="OpenAI API Key", + type="password", + placeholder="Enter your OpenAI API key" + ) + + # Location Details + with gr.Row(): + with gr.Column(): + city = gr.Textbox(label="City", placeholder="e.g., Mumbai") + state = gr.Textbox( + label="State", + placeholder="Leave blank for Union Territories or US cities", + value="" + ) + country = gr.Textbox(label="Country", value="India") + + # Personal Details + with gr.Row(): + with gr.Column(): + medical_conditions = gr.Textbox( + label="Medical Conditions (optional)", + placeholder="e.g., asthma, allergies", + lines=2 + ) + planned_activity = gr.Textbox( + label="Planned Activity", + placeholder="e.g., morning jog for 2 hours", + lines=2 + ) + + # Status Messages + info_box = gr.Textbox(label="ℹ️ Status", interactive=False) + warning_box = gr.Textbox(label="⚠️ Warning", interactive=False) + + # Output Areas + aqi_data_json = gr.JSON(label="📊 Current Air Quality Data") + recommendations = gr.Markdown(label="🏥 Health Recommendations") + + # Analyze Button + analyze_btn = gr.Button("🔍 Analyze & Get Recommendations", variant="primary") + analyze_btn.click( + fn=analyze_conditions, + inputs=[ + city, + state, + country, + medical_conditions, + planned_activity, + firecrawl_key, + openai_key + ], + outputs=[aqi_data_json, recommendations, info_box, warning_box] + ) + + # Examples + gr.Examples( + examples=[ + ["Mumbai", "Maharashtra", "India", "asthma", "morning walk for 30 minutes"], + ["Delhi", "", "India", "", "outdoor yoga session"], + ["New York", "", "United States", "allergies", "afternoon run"] + ], + inputs=[city, state, country, medical_conditions, planned_activity] + ) + + return demo if __name__ == "__main__": - main() + demo = create_demo() + demo.launch(share=True) From ef9d223cd6e70e97ab36748957abee028c62ae85 Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 02:10:05 +0530 Subject: [PATCH 10/12] Gradio readme changes --- .../ai_aqi_analysis_agent/README.md | 17 ++++++++++++----- .../ai_aqi_analysis_agent/requirements.txt | 3 +++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/README.md b/ai_agent_tutorials/ai_aqi_analysis_agent/README.md index 055f004..cae38eb 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/README.md +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/README.md @@ -28,6 +28,7 @@ The AQI Analysis Agent is a powerful air quality monitoring and health recommend - Medical condition considerations - Activity-specific recommendations - Downloadable reports + - Example queries for quick testing ## How to Run @@ -48,17 +49,23 @@ Follow these steps to set up and run the application: - Get an OpenAI API key from: https://platform.openai.com/api-keys - Get a Firecrawl API key from: [Firecrawl website](https://www.firecrawl.dev/app/api-keys) -4. **Run the Streamlit app**: +4. **Run the Gradio app**: ```bash - streamlit run ai_aqi_analysis_agent.py + python ai_aqi_analysis_agent.py ``` +5. **Access the Web Interface**: + - The terminal will display two URLs: + - Local URL: `http://127.0.0.1:7860` (for local access) + - Public URL: `https://xxx-xxx-xxx.gradio.live` (for temporary public access) + - Click on either URL to open the web interface in your browser + ## Usage -1. Enter your API keys in the sidebar +1. Enter your API keys in the API Configuration section 2. Input location details: - City name - - State (optional) + - State (optional for Union Territories/US cities) - Country 3. Provide personal information: - Medical conditions (optional) @@ -67,7 +74,7 @@ Follow these steps to set up and run the application: - Current air quality data - Health impact analysis - Activity safety recommendations - - Downloadable report +5. Try the example queries for quick testing ## Note diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt b/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt index af9678d..034b309 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/requirements.txt @@ -1,3 +1,6 @@ agno openai firecrawl-py==1.9.0 +gradio==5.9.1 +pydantic +dataclasses From 23342f60d48aece8e59e9ff8f229f59c1d6eb817 Mon Sep 17 00:00:00 2001 From: Madhu Date: Fri, 21 Feb 2025 23:51:04 +0530 Subject: [PATCH 11/12] extra examples --- .../ai_aqi_analysis_agent/ai_aqi_analysis_agent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py index 6d07148..609d027 100644 --- a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py @@ -259,7 +259,8 @@ def create_demo() -> gr.Blocks: examples=[ ["Mumbai", "Maharashtra", "India", "asthma", "morning walk for 30 minutes"], ["Delhi", "", "India", "", "outdoor yoga session"], - ["New York", "", "United States", "allergies", "afternoon run"] + ["New York", "", "United States", "allergies", "afternoon run"], + ["Kakinada", "Andhra Pradesh", "India", "none", "Tennis for 2 hours"] ], inputs=[city, state, country, medical_conditions, planned_activity] ) From fa7bf0f4d24a443055ad20e7dd636148698e4cf6 Mon Sep 17 00:00:00 2001 From: Madhu Date: Sun, 23 Feb 2025 02:17:07 +0530 Subject: [PATCH 12/12] added streamlit version along with gradio version too --- ...ent.py => ai_aqi_analysis_agent_gradio.py} | 0 .../ai_aqi_analysis_agent_streamlit.py | 265 ++++++++++++++++++ 2 files changed, 265 insertions(+) rename ai_agent_tutorials/ai_aqi_analysis_agent/{ai_aqi_analysis_agent.py => ai_aqi_analysis_agent_gradio.py} (100%) create mode 100644 ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent_streamlit.py diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent_gradio.py similarity index 100% rename from ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent.py rename to ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent_gradio.py diff --git a/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent_streamlit.py b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent_streamlit.py new file mode 100644 index 0000000..b8acaeb --- /dev/null +++ b/ai_agent_tutorials/ai_aqi_analysis_agent/ai_aqi_analysis_agent_streamlit.py @@ -0,0 +1,265 @@ +from typing import Dict, Optional +from dataclasses import dataclass +from pydantic import BaseModel, Field +from agno.agent import Agent +from agno.models.openai import OpenAIChat +from firecrawl import FirecrawlApp +import streamlit as st + +class AQIResponse(BaseModel): + success: bool + data: Dict[str, float] + status: str + expiresAt: str + +class ExtractSchema(BaseModel): + aqi: float = Field(description="Air Quality Index") + temperature: float = Field(description="Temperature in degrees Celsius") + humidity: float = Field(description="Humidity percentage") + wind_speed: float = Field(description="Wind speed in kilometers per hour") + pm25: float = Field(description="Particulate Matter 2.5 micrometers") + pm10: float = Field(description="Particulate Matter 10 micrometers") + co: float = Field(description="Carbon Monoxide level") + +@dataclass +class UserInput: + city: str + state: str + country: str + medical_conditions: Optional[str] + planned_activity: str + +class AQIAnalyzer: + + def __init__(self, firecrawl_key: str) -> None: + self.firecrawl = FirecrawlApp(api_key=firecrawl_key) + + def _format_url(self, country: str, state: str, city: str) -> str: + """Format URL based on location, handling cases with and without state""" + country_clean = country.lower().replace(' ', '-') + city_clean = city.lower().replace(' ', '-') + + if not state or state.lower() == 'none': + return f"https://www.aqi.in/dashboard/{country_clean}/{city_clean}" + + state_clean = state.lower().replace(' ', '-') + return f"https://www.aqi.in/dashboard/{country_clean}/{state_clean}/{city_clean}" + + def fetch_aqi_data(self, city: str, state: str, country: str) -> Dict[str, float]: + """Fetch AQI data using Firecrawl""" + try: + url = self._format_url(country, state, city) + st.info(f"Accessing URL: {url}") # Display URL being accessed + + response = self.firecrawl.extract( + urls=[f"{url}/*"], + params={ + 'prompt': 'Extract the current real-time AQI, temperature, humidity, wind speed, PM2.5, PM10, and CO levels from the page. Also extract the timestamp of the data.', + 'schema': ExtractSchema.model_json_schema() + } + ) + + aqi_response = AQIResponse(**response) + if not aqi_response.success: + raise ValueError(f"Failed to fetch AQI data: {aqi_response.status}") + + with st.expander("📦 Raw AQI Data", expanded=True): + st.json({ + "url_accessed": url, + "timestamp": aqi_response.expiresAt, + "data": aqi_response.data + }) + + st.warning(""" + ⚠️ Note: The data shown may not match real-time values on the website. + This could be due to: + - Cached data in Firecrawl + - Rate limiting + - Website updates not being captured + + Consider refreshing or checking the website directly for real-time values. + """) + + return aqi_response.data + + except Exception as e: + st.error(f"Error fetching AQI data: {str(e)}") + return { + 'aqi': 0, + 'temperature': 0, + 'humidity': 0, + 'wind_speed': 0, + 'pm25': 0, + 'pm10': 0, + 'co': 0 + } + +class HealthRecommendationAgent: + + def __init__(self, openai_key: str) -> None: + self.agent = Agent( + model=OpenAIChat( + id="gpt-4o", + name="Health Recommendation Agent", + api_key=openai_key + ) + ) + + def get_recommendations( + self, + aqi_data: Dict[str, float], + user_input: UserInput + ) -> str: + prompt = self._create_prompt(aqi_data, user_input) + response = self.agent.run(prompt) + return response.content + + def _create_prompt(self, aqi_data: Dict[str, float], user_input: UserInput) -> str: + return f""" + Based on the following air quality conditions in {user_input.city}, {user_input.state}, {user_input.country}: + - Overall AQI: {aqi_data['aqi']} + - PM2.5 Level: {aqi_data['pm25']} µg/m³ + - PM10 Level: {aqi_data['pm10']} µg/m³ + - CO Level: {aqi_data['co']} ppb + + Weather conditions: + - Temperature: {aqi_data['temperature']}°C + - Humidity: {aqi_data['humidity']}% + - Wind Speed: {aqi_data['wind_speed']} km/h + + User's Context: + - Medical Conditions: {user_input.medical_conditions or 'None'} + - Planned Activity: {user_input.planned_activity} + **Comprehensive Health Recommendations:** + 1. **Impact of Current Air Quality on Health:** + 2. **Necessary Safety Precautions for Planned Activity:** + 3. **Advisability of Planned Activity:** + 4. **Best Time to Conduct the Activity:** + """ + +def analyze_conditions( + user_input: UserInput, + api_keys: Dict[str, str] +) -> str: + aqi_analyzer = AQIAnalyzer(firecrawl_key=api_keys['firecrawl']) + health_agent = HealthRecommendationAgent(openai_key=api_keys['openai']) + + aqi_data = aqi_analyzer.fetch_aqi_data( + city=user_input.city, + state=user_input.state, + country=user_input.country + ) + + return health_agent.get_recommendations(aqi_data, user_input) + +def initialize_session_state(): + if 'api_keys' not in st.session_state: + st.session_state.api_keys = { + 'firecrawl': '', + 'openai': '' + } + +def setup_page(): + st.set_page_config( + page_title="AQI Analysis Agent", + page_icon="🌍", + layout="wide" + ) + + st.title("🌍 AQI Analysis Agent") + st.info("Get personalized health recommendations based on air quality conditions.") + +def render_sidebar(): + """Render sidebar with API configuration""" + with st.sidebar: + st.header("🔑 API Configuration") + + new_firecrawl_key = st.text_input( + "Firecrawl API Key", + type="password", + value=st.session_state.api_keys['firecrawl'], + help="Enter your Firecrawl API key" + ) + new_openai_key = st.text_input( + "OpenAI API Key", + type="password", + value=st.session_state.api_keys['openai'], + help="Enter your OpenAI API key" + ) + + if (new_firecrawl_key and new_openai_key and + (new_firecrawl_key != st.session_state.api_keys['firecrawl'] or + new_openai_key != st.session_state.api_keys['openai'])): + st.session_state.api_keys.update({ + 'firecrawl': new_firecrawl_key, + 'openai': new_openai_key + }) + st.success("✅ API keys updated!") + +def render_main_content(): + st.header("📍 Location Details") + col1, col2 = st.columns(2) + + with col1: + city = st.text_input("City", placeholder="e.g., Mumbai") + state = st.text_input("State", placeholder="If it's a Union Territory or a city in the US, leave it blank") + country = st.text_input("Country", value="India", placeholder="United States") + + with col2: + st.header("👤 Personal Details") + medical_conditions = st.text_area( + "Medical Conditions (optional)", + placeholder="e.g., asthma, allergies" + ) + planned_activity = st.text_area( + "Planned Activity", + placeholder="e.g., morning jog for 2 hours" + ) + + return UserInput( + city=city, + state=state, + country=country, + medical_conditions=medical_conditions, + planned_activity=planned_activity + ) + +def main(): + """Main application entry point""" + initialize_session_state() + setup_page() + render_sidebar() + user_input = render_main_content() + + result = None + + if st.button("🔍 Analyze & Get Recommendations"): + if not all([user_input.city, user_input.planned_activity]): + st.error("Please fill in all required fields (state and medical conditions are optional)") + elif not all(st.session_state.api_keys.values()): + st.error("Please provide both API keys in the sidebar") + else: + try: + with st.spinner("🔄 Analyzing conditions..."): + result = analyze_conditions( + user_input=user_input, + api_keys=st.session_state.api_keys + ) + st.success("✅ Analysis completed!") + + except Exception as e: + st.error(f"❌ Error: {str(e)}") + + if result: + st.markdown("### 📦 Recommendations") + st.markdown(result) + + st.download_button( + "💾 Download Recommendations", + data=result, + file_name=f"aqi_recommendations_{user_input.city}_{user_input.state}.txt", + mime="text/plain" + ) + +if __name__ == "__main__": + main() \ No newline at end of file