AI_AQI_Agent / app.py
thisisdev's picture
Update app.py
8147ec1 verified
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")
@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 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)