Spaces:
Sleeping
Sleeping
| import os | |
| import json | |
| import requests | |
| import gradio as gr | |
| from agno.agent import Agent | |
| from dotenv import load_dotenv | |
| from dataclasses import dataclass | |
| from typing import Dict, Optional | |
| from firecrawl import FirecrawlApp | |
| from pydantic import BaseModel, Field | |
| from agno.models.openai import OpenAIChat | |
| load_dotenv() | |
| 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 Degree Celsius") | |
| humidity: float = Field(description = "Humidity Percentage") | |
| wind_speed: float = Field(description = "") | |
| pm25:float = Field(description = "Particulate Matter 2.5 micrometers") | |
| pm10:float = Field(description = "Particulate Matter 10 micrometers") | |
| co: float = Field(description = "Carbon Monoxide Level") | |
| 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 URLs 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().replace(" ","-"): | |
| 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) -> tuple[Dict[str, float], str]: | |
| """Fetch API data using Firecrawl""" | |
| try: | |
| url = self._format_url(country, state, city) | |
| info_msg = f"Accessing URL: {url}" | |
| resp = 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(**resp) | |
| if not aqi_response.success: | |
| raise requests.HTTPError(f"Failed to fetch AQI Data: {aqi_response.status}") | |
| return aqi_response.data, info_msg | |
| except Exception as e: | |
| error_msg = f"Error Fetching AQI Data: {str(e)}" | |
| return { | |
| "api": 0, | |
| "temperature": 0, | |
| "humidity": 0, | |
| "wind_speed": 0, | |
| "pm25": 0, | |
| "pm10": 0, | |
| "co": 0 | |
| }, error_msg | |
| class HealthRecommendationAgent: | |
| def __init__(self, openai_key: str) -> Agent: | |
| self.agent = Agent( | |
| model = OpenAIChat( | |
| id = "gpt-4.1-nano", | |
| name = "Health Recommendation Agent", | |
| api_key = openai_key | |
| ) | |
| ) | |
| def _create_prompt(self, aqi_data: Dict[str, float], user_input: UserInput) -> str: | |
| return f""" | |
| Based on the following air quality condition 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["co"]} ppb | |
| """ | |
| def get_recommendation(self, aqi_data: Dict[str, float], user_input: UserInput) -> str: | |
| prompt = self._create_prompt(prompt) | |
| resp = self.agent.run(prompt) | |
| return resp.content | |
| def analyze_conditions(city: str, state: str, country: str, medical_condition: str, planned_activity: str, firecrawl_key: str, openai_key: str) -> tuple[str, str, str, str]: | |
| """Analyze condition and return AQI data, recommendation, and status messages""" | |
| try: | |
| # initialize the analyzer | |
| aqi_analyzer = AQIAnalyzer(firecrawl_key=firecrawl_key) | |
| health_agent = HealthRecommendationAgent(openai_key = openai_key) | |
| # Create user input | |
| user_input = UserInput( | |
| city = city, | |
| state = state, | |
| country = country, | |
| medical_conditions = medical_condition, | |
| planned_activity = planned_activity | |
| ) | |
| # Get AQI Data | |
| aqi_data, info_msg = aqi_analyzer.fetch_aqi_data( | |
| city = user_input.city, | |
| state = user_input.state, | |
| country = user_input.country | |
| ) | |
| # 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_recommendation(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 Occured: {str(e)}" | |
| return "", "Analysis Failed", error_msg, "" | |
| def create_demo() -> gr.Blocks: | |
| """Create and configure the gradio interface""" | |
| with gr.Blocks(title = "AQL Analysis and Recommendation Agent") as demo: | |
| gr.Markdown( | |
| """ | |
| AQI Analysis Agent | |
| Get personalized health recommendations based on air quality conditions. | |
| """ | |
| ) | |
| # API Configurations | |
| 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="eg. Mumbai") | |
| state = gr.Textbox( | |
| label="State", | |
| placeholder="Leave blank for UT 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"], | |
| ["Kakinada", "Andhra Pradesh", "India", "none", "Tennis for 2 hours"] | |
| ], | |
| inputs=[city, state, country, medical_conditions, planned_activity] | |
| ) | |
| return demo | |
| if __name__ == "__main__": | |
| demo = create_demo() | |
| demo.launch(share=True) |