Spaces:
Running
Running
| """ | |
| Farmer.chat Alert Summary Generator - FastAPI Application | |
| """ | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from pydantic import BaseModel | |
| from typing import Optional, Dict | |
| from datetime import datetime | |
| import os | |
| # Import pipeline components | |
| from src.pipeline import FarmerChatPipeline | |
| # Import MCP servers | |
| from src.servers.weather import WeatherServer | |
| from src.servers.soil import SoilPropertiesServer | |
| from src.servers.water import WaterServer | |
| from src.servers.elevation import ElevationServer | |
| from src.servers.pests import PestsServer | |
| from mcp_server import sse_app | |
| from starlette.applications import Starlette | |
| # ============================================================================ | |
| # PYDANTIC MODELS | |
| # ============================================================================ | |
| class LocationRequest(BaseModel): | |
| """Request model for alert generation""" | |
| location_name: str | |
| district: Optional[str] = None | |
| class AlertResponse(BaseModel): | |
| """Response model for generated alerts""" | |
| location: str | |
| coordinates: Dict[str, float] | |
| district: Optional[str] | |
| alert_summary: str | |
| timestamp: str | |
| class QueryRequest(BaseModel): | |
| """Request model for specific queries""" | |
| query: str | |
| location_name: Optional[str] = None | |
| district: Optional[str] = None | |
| # ============================================================================ | |
| # BIHAR LOCATION DATA | |
| # ============================================================================ | |
| BIHAR_DATA = { | |
| "Araria": ["Araria", "Forbesganj", "Jokihat", "Raniganj"], | |
| "Arwal": ["Arwal", "Kaler", "Karpi", "Kurtha"], | |
| "Aurangabad": ["Aurangabad", "Daudnagar", "Obra", "Nabinagar"], | |
| "Banka": ["Banka", "Amarpur", "Barahat", "Belhar"], | |
| "Begusarai": ["Begusarai", "Bakhri", "Barauni", "Teghra"], | |
| "Bhagalpur": ["Bhagalpur", "Sabour", "Nathnagar", "Kahalgaon"], | |
| "Bhojpur": ["Arrah", "Jagdishpur", "Piro", "Shahpur"], | |
| "Buxar": ["Buxar", "Dumraon", "Chausa", "Simri"], | |
| "Darbhanga": ["Darbhanga", "Baheri", "Jale", "Benipur"], | |
| "East Champaran": ["Motihari", "Raxaul", "Chakia", "Dhaka"], | |
| "Gaya": ["Gaya", "Bodh Gaya", "Tekari", "Sherghati"], | |
| "Gopalganj": ["Gopalganj", "Barauli", "Baikunthpur", "Kateya"], | |
| "Jamui": ["Jamui", "Jhajha", "Sikandra", "Sono"], | |
| "Jehanabad": ["Jehanabad", "Ghoshi", "Makhdumpur", "Modanganj"], | |
| "Kaimur": ["Bhabua", "Mohania", "Ramgarh", "Chainpur"], | |
| "Katihar": ["Katihar", "Barsoi", "Manihari", "Pranpur"], | |
| "Khagaria": ["Khagaria", "Parbatta", "Alauli", "Beldaur"], | |
| "Kishanganj": ["Kishanganj", "Bahadurganj", "Thakurganj", "Dighalbank"], | |
| "Lakhisarai": ["Lakhisarai", "Halsi", "Suryagarha", "Pipariya"], | |
| "Madhepura": ["Madhepura", "Udakishanganj", "Murliganj", "Alamnagar"], | |
| "Madhubani": ["Madhubani", "Jhanjharpur", "Benipatti", "Jainagar"], | |
| "Munger": ["Munger", "Jamalpur", "Asarganj", "Tarapur"], | |
| "Muzaffarpur": ["Muzaffarpur", "Sitamarhi", "Minapur", "Bochaha"], | |
| "Nalanda": ["Bihar Sharif", "Rajgir", "Hilsa", "Biharsharif"], | |
| "Nawada": ["Nawada", "Rajauli", "Akbarpur", "Hisua"], | |
| "Patna": ["Patna", "Danapur", "Fatuha", "Khagaul"], | |
| "Purnia": ["Purnia", "Dhamdaha", "Kasba", "Banmankhi"], | |
| "Rohtas": ["Sasaram", "Dehri", "Bikramganj", "Nasriganj"], | |
| "Saharsa": ["Saharsa", "Sonbarsa", "Simri Bakhtiarpur", "Mahishi"], | |
| "Samastipur": ["Samastipur", "Rosera", "Dalsinghsarai", "Pusa"], | |
| "Saran": ["Chapra", "Marhaura", "Amnour", "Sonepur"], | |
| "Sheikhpura": ["Sheikhpura", "Barbigha", "Ariari", "Shekhopur"], | |
| "Sheohar": ["Sheohar", "Dumri Katsari", "Piprahi", "Tariyani"], | |
| "Sitamarhi": ["Sitamarhi", "Pupri", "Belsand", "Bathnaha"], | |
| "Siwan": ["Siwan", "Maharajganj", "Mairwa", "Darauli"], | |
| "Supaul": ["Supaul", "Nirmali", "Triveniganj", "Chhatapur"], | |
| "Vaishali": ["Hajipur", "Mahua", "Lalganj", "Desri"], | |
| "West Champaran": ["Bettiah", "Bagaha", "Narkatiaganj", "Lauriya"] | |
| } | |
| LOCATIONS = { | |
| "Araria": {"latitude": 26.1523, "longitude": 87.5167}, | |
| "Forbesganj": {"latitude": 26.3023, "longitude": 87.2664}, | |
| "Jokihat": {"latitude": 25.8998, "longitude": 87.2686}, | |
| "Raniganj": {"latitude": 26.0537, "longitude": 87.5333}, | |
| "Arwal": {"latitude": 25.2560, "longitude": 84.6819}, | |
| "Kaler": {"latitude": 25.1960, "longitude": 84.6219}, | |
| "Karpi": {"latitude": 25.2360, "longitude": 84.7019}, | |
| "Kurtha": {"latitude": 25.3160, "longitude": 84.6619}, | |
| "Aurangabad": {"latitude": 24.7521, "longitude": 84.3742}, | |
| "Daudnagar": {"latitude": 25.0337, "longitude": 84.4007}, | |
| "Obra": {"latitude": 24.9923, "longitude": 84.4342}, | |
| "Nabinagar": {"latitude": 24.6087, "longitude": 84.1269}, | |
| "Banka": {"latitude": 24.8893, "longitude": 86.9220}, | |
| "Amarpur": {"latitude": 25.0393, "longitude": 86.9020}, | |
| "Barahat": {"latitude": 24.8393, "longitude": 87.0020}, | |
| "Belhar": {"latitude": 24.9393, "longitude": 86.9620}, | |
| "Begusarai": {"latitude": 25.4182, "longitude": 86.1347}, | |
| "Bakhri": {"latitude": 25.4582, "longitude": 86.0547}, | |
| "Barauni": {"latitude": 25.4751, "longitude": 86.0458}, | |
| "Teghra": {"latitude": 25.5082, "longitude": 85.9347}, | |
| "Bhagalpur": {"latitude": 25.2425, "longitude": 86.9842}, | |
| "Sabour": {"latitude": 25.2375, "longitude": 87.0542}, | |
| "Nathnagar": {"latitude": 25.1225, "longitude": 87.0042}, | |
| "Kahalgaon": {"latitude": 25.1925, "longitude": 87.2142}, | |
| "Arrah": {"latitude": 25.5560, "longitude": 84.6631}, | |
| "Jagdishpur": {"latitude": 25.4660, "longitude": 84.4231}, | |
| "Piro": {"latitude": 25.3260, "longitude": 84.4031}, | |
| "Shahpur": {"latitude": 25.6060, "longitude": 84.4031}, | |
| "Buxar": {"latitude": 25.5641, "longitude": 83.9778}, | |
| "Dumraon": {"latitude": 25.5541, "longitude": 84.1478}, | |
| "Chausa": {"latitude": 25.5241, "longitude": 83.9178}, | |
| "Simri": {"latitude": 25.6141, "longitude": 84.0478}, | |
| "Darbhanga": {"latitude": 26.1542, "longitude": 85.8978}, | |
| "Baheri": {"latitude": 26.0442, "longitude": 85.8378}, | |
| "Jale": {"latitude": 26.2042, "longitude": 85.8578}, | |
| "Benipur": {"latitude": 26.1142, "longitude": 85.9478}, | |
| "Motihari": {"latitude": 26.6484, "longitude": 84.9194}, | |
| "Raxaul": {"latitude": 26.9784, "longitude": 84.8494}, | |
| "Chakia": {"latitude": 26.4184, "longitude": 85.0494}, | |
| "Dhaka": {"latitude": 26.6784, "longitude": 85.1694}, | |
| "Gaya": {"latitude": 24.7955, "longitude": 85.0002}, | |
| "Bodh Gaya": {"latitude": 24.6955, "longitude": 84.9902}, | |
| "Tekari": {"latitude": 24.9455, "longitude": 85.0402}, | |
| "Sherghati": {"latitude": 24.5655, "longitude": 84.7902}, | |
| "Gopalganj": {"latitude": 26.4685, "longitude": 84.4388}, | |
| "Barauli": {"latitude": 26.3785, "longitude": 84.5788}, | |
| "Baikunthpur": {"latitude": 26.5285, "longitude": 84.3588}, | |
| "Kateya": {"latitude": 26.4285, "longitude": 84.6388}, | |
| "Jamui": {"latitude": 24.9272, "longitude": 86.2231}, | |
| "Jhajha": {"latitude": 24.7772, "longitude": 86.3731}, | |
| "Sikandra": {"latitude": 24.9672, "longitude": 86.0631}, | |
| "Sono": {"latitude": 24.8372, "longitude": 86.1431}, | |
| "Jehanabad": {"latitude": 25.2078, "longitude": 84.9869}, | |
| "Ghoshi": {"latitude": 25.1478, "longitude": 84.9269}, | |
| "Makhdumpur": {"latitude": 25.2478, "longitude": 85.0469}, | |
| "Modanganj": {"latitude": 25.2678, "longitude": 84.9069}, | |
| "Bhabua": {"latitude": 25.0405, "longitude": 83.6074}, | |
| "Mohania": {"latitude": 25.1305, "longitude": 83.4774}, | |
| "Ramgarh": {"latitude": 24.9505, "longitude": 83.6874}, | |
| "Chainpur": {"latitude": 25.2005, "longitude": 83.7474}, | |
| "Katihar": {"latitude": 25.5394, "longitude": 87.5839}, | |
| "Barsoi": {"latitude": 25.3794, "longitude": 87.8839}, | |
| "Manihari": {"latitude": 25.3394, "longitude": 87.6239}, | |
| "Pranpur": {"latitude": 25.6894, "longitude": 87.7239}, | |
| "Khagaria": {"latitude": 25.5022, "longitude": 86.4665}, | |
| "Parbatta": {"latitude": 25.5422, "longitude": 86.5865}, | |
| "Alauli": {"latitude": 25.4622, "longitude": 86.3465}, | |
| "Beldaur": {"latitude": 25.5622, "longitude": 86.4265}, | |
| "Kishanganj": {"latitude": 26.1046, "longitude": 87.9475}, | |
| "Bahadurganj": {"latitude": 26.2646, "longitude": 88.1175}, | |
| "Thakurganj": {"latitude": 26.0446, "longitude": 87.8275}, | |
| "Dighalbank": {"latitude": 25.9046, "longitude": 87.9875}, | |
| "Lakhisarai": {"latitude": 25.1678, "longitude": 86.0927}, | |
| "Halsi": {"latitude": 25.2278, "longitude": 86.0327}, | |
| "Suryagarha": {"latitude": 25.1078, "longitude": 86.1527}, | |
| "Pipariya": {"latitude": 25.2078, "longitude": 86.1327}, | |
| "Madhepura": {"latitude": 25.9207, "longitude": 86.7940}, | |
| "Udakishanganj": {"latitude": 25.9807, "longitude": 86.6740}, | |
| "Murliganj": {"latitude": 25.8907, "longitude": 86.9940}, | |
| "Alamnagar": {"latitude": 25.9607, "longitude": 86.7340}, | |
| "Madhubani": {"latitude": 26.3561, "longitude": 86.0644}, | |
| "Jhanjharpur": {"latitude": 26.2661, "longitude": 86.2844}, | |
| "Benipatti": {"latitude": 26.5961, "longitude": 86.1444}, | |
| "Jainagar": {"latitude": 26.2061, "longitude": 86.1644}, | |
| "Munger": {"latitude": 25.3753, "longitude": 86.4734}, | |
| "Jamalpur": {"latitude": 25.3153, "longitude": 86.4934}, | |
| "Asarganj": {"latitude": 25.1453, "longitude": 86.6834}, | |
| "Tarapur": {"latitude": 25.0253, "longitude": 86.6334}, | |
| "Muzaffarpur": {"latitude": 26.1225, "longitude": 85.3906}, | |
| "Sitamarhi": {"latitude": 26.5925, "longitude": 85.4806}, | |
| "Minapur": {"latitude": 26.0625, "longitude": 85.2906}, | |
| "Bochaha": {"latitude": 26.0025, "longitude": 85.5306}, | |
| "Bihar Sharif": {"latitude": 25.1979, "longitude": 85.5238}, | |
| "Rajgir": {"latitude": 25.0279, "longitude": 85.4238}, | |
| "Hilsa": {"latitude": 25.3179, "longitude": 85.2838}, | |
| "Biharsharif": {"latitude": 25.1979, "longitude": 85.5238}, | |
| "Nawada": {"latitude": 24.8834, "longitude": 85.5387}, | |
| "Rajauli": {"latitude": 25.0634, "longitude": 85.6387}, | |
| "Akbarpur": {"latitude": 24.8234, "longitude": 85.4587}, | |
| "Hisua": {"latitude": 24.8334, "longitude": 85.4187}, | |
| "Patna": {"latitude": 25.5941, "longitude": 85.1376}, | |
| "Danapur": {"latitude": 25.6341, "longitude": 85.0476}, | |
| "Fatuha": {"latitude": 25.5041, "longitude": 85.3076}, | |
| "Khagaul": {"latitude": 25.5741, "longitude": 85.0476}, | |
| "Purnia": {"latitude": 25.7771, "longitude": 87.4753}, | |
| "Dhamdaha": {"latitude": 25.8871, "longitude": 87.5853}, | |
| "Kasba": {"latitude": 25.8471, "longitude": 87.5353}, | |
| "Banmankhi": {"latitude": 25.8871, "longitude": 87.1953}, | |
| "Sasaram": {"latitude": 24.9520, "longitude": 84.0328}, | |
| "Dehri": {"latitude": 24.9020, "longitude": 84.1828}, | |
| "Bikramganj": {"latitude": 25.2120, "longitude": 84.2628}, | |
| "Nasriganj": {"latitude": 25.0520, "longitude": 84.1228}, | |
| "Saharsa": {"latitude": 25.8769, "longitude": 86.5956}, | |
| "Sonbarsa": {"latitude": 25.9269, "longitude": 86.7356}, | |
| "Simri Bakhtiarpur": {"latitude": 25.9569, "longitude": 86.3556}, | |
| "Mahishi": {"latitude": 25.9969, "longitude": 86.4756}, | |
| "Samastipur": {"latitude": 25.8647, "longitude": 85.7817}, | |
| "Rosera": {"latitude": 25.7947, "longitude": 85.9317}, | |
| "Dalsinghsarai": {"latitude": 25.6647, "longitude": 85.8317}, | |
| "Pusa": {"latitude": 25.9847, "longitude": 85.6717}, | |
| "Chapra": {"latitude": 25.7805, "longitude": 84.7477}, | |
| "Marhaura": {"latitude": 25.9705, "longitude": 84.8677}, | |
| "Amnour": {"latitude": 25.8905, "longitude": 84.9077}, | |
| "Sonepur": {"latitude": 25.6905, "longitude": 85.1777}, | |
| "Sheikhpura": {"latitude": 25.1391, "longitude": 85.8354}, | |
| "Barbigha": {"latitude": 25.2191, "longitude": 85.7354}, | |
| "Ariari": {"latitude": 25.0591, "longitude": 85.9554}, | |
| "Shekhopur": {"latitude": 25.1391, "longitude": 85.8354}, | |
| "Sheohar": {"latitude": 26.5184, "longitude": 85.2959}, | |
| "Dumri Katsari": {"latitude": 26.5784, "longitude": 85.1959}, | |
| "Piprahi": {"latitude": 26.4684, "longitude": 85.4159}, | |
| "Tariyani": {"latitude": 26.5584, "longitude": 85.2359}, | |
| "Sitamarhi": {"latitude": 26.5925, "longitude": 85.4806}, | |
| "Pupri": {"latitude": 26.4725, "longitude": 85.7006}, | |
| "Belsand": {"latitude": 26.4425, "longitude": 85.4006}, | |
| "Bathnaha": {"latitude": 26.5925, "longitude": 85.5306}, | |
| "Siwan": {"latitude": 26.2195, "longitude": 84.3564}, | |
| "Maharajganj": {"latitude": 26.1095, "longitude": 84.5064}, | |
| "Mairwa": {"latitude": 26.2295, "longitude": 84.1664}, | |
| "Darauli": {"latitude": 26.1595, "longitude": 84.1464}, | |
| "Supaul": {"latitude": 26.1260, "longitude": 86.6050}, | |
| "Nirmali": {"latitude": 26.3160, "longitude": 86.5850}, | |
| "Triveniganj": {"latitude": 26.2160, "longitude": 87.0250}, | |
| "Chhatapur": {"latitude": 26.2160, "longitude": 86.9050}, | |
| "Hajipur": {"latitude": 25.6851, "longitude": 85.2095}, | |
| "Mahua": {"latitude": 25.9651, "longitude": 85.2895}, | |
| "Lalganj": {"latitude": 25.8751, "longitude": 85.1695}, | |
| "Desri": {"latitude": 25.6051, "longitude": 85.4895}, | |
| "Bettiah": {"latitude": 26.8022, "longitude": 84.5025}, | |
| "Bagaha": {"latitude": 27.0922, "longitude": 84.0925}, | |
| "Narkatiaganj": {"latitude": 26.4322, "longitude": 84.7925}, | |
| "Lauriya": {"latitude": 26.9822, "longitude": 84.3125} | |
| } | |
| # ============================================================================ | |
| # FASTAPI APPLICATION | |
| # ============================================================================ | |
| # app = FastAPI( | |
| # title="Farmer.chat Alert Summary API", | |
| # description="Agricultural intelligence system with alert-focused MCP pipeline", | |
| # version="2.0.0" | |
| # ) | |
| # # CORS configuration - allow all for development | |
| # app.add_middleware( | |
| # CORSMiddleware, | |
| # allow_origins=["*"], | |
| # allow_credentials=True, | |
| # allow_methods=["*"], | |
| # allow_headers=["*"], | |
| # ) | |
| # ============================================================================ | |
| # GLOBAL STATE | |
| # ============================================================================ | |
| pipeline: Optional[FarmerChatPipeline] = None | |
| servers: Dict = {} | |
| # ============================================================================ | |
| # STARTUP & SHUTDOWN (using lifespan - modern FastAPI pattern) | |
| # ============================================================================ | |
| from contextlib import asynccontextmanager | |
| async def lifespan(app: FastAPI): | |
| """Initialize on startup, cleanup on shutdown""" | |
| global pipeline, servers | |
| # STARTUP | |
| print("\n" + "="*60) | |
| print("Initializing Farmer.chat Alert System") | |
| print("="*60 + "\n") | |
| try: | |
| # Initialize MCP servers | |
| print("Initializing MCP servers...") | |
| servers = { | |
| "weather": WeatherServer(), | |
| "soil": SoilPropertiesServer(), | |
| "water": WaterServer(), | |
| "elevation": ElevationServer(), | |
| "pests": PestsServer() | |
| } | |
| print("✓ All MCP servers initialized successfully\n") | |
| # Initialize pipeline with default location (Patna) | |
| default_location = {"latitude": 25.6093, "longitude": 85.1235} | |
| pipeline = FarmerChatPipeline( | |
| servers=servers, | |
| location=default_location | |
| ) | |
| print("\n" + "="*60) | |
| print("✓ Farmer.chat Alert System Ready") | |
| print("="*60 + "\n") | |
| except Exception as e: | |
| print(f"✗ Pipeline initialization failed: {str(e)}") | |
| raise | |
| yield # Application runs here | |
| # SHUTDOWN | |
| print("\nShutting down Farmer.chat Alert System...") | |
| # Apply lifespan to app | |
| app = FastAPI( | |
| title="Farmer.chat Alert Summary API", | |
| description="Agricultural intelligence system with alert-focused MCP pipeline", | |
| version="2.0.0", | |
| lifespan=lifespan | |
| ) | |
| # Re-add CORS after recreating app | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Exposing endpoint for sse | |
| app.mount("/mcp", sse_app) | |
| # ============================================================================ | |
| # API ENDPOINTS | |
| # ============================================================================ | |
| async def root(): | |
| """Root endpoint with API information""" | |
| return { | |
| "service": "Farmer.chat Alert Summary Generator", | |
| "version": "2.0.0", | |
| "status": "operational", | |
| "endpoints": { | |
| "locations": "/locations", | |
| "generate_alert": "/generate-alert", | |
| "query": "/query", | |
| "health": "/health" | |
| } | |
| } | |
| async def health_check(): | |
| """Health check endpoint""" | |
| if pipeline is None: | |
| raise HTTPException(status_code=503, detail="Pipeline not initialized") | |
| return { | |
| "status": "healthy", | |
| "pipeline": "operational", | |
| "servers": len(servers), | |
| "timestamp": datetime.utcnow().isoformat() | |
| } | |
| async def get_locations(): | |
| """Get all available locations""" | |
| return { | |
| "districts": BIHAR_DATA, | |
| "total_locations": len(LOCATIONS), | |
| "coverage": "Bihar, India" | |
| } | |
| async def generate_alert(request: LocationRequest): | |
| """ | |
| Generate comprehensive alert summary for a location. | |
| FIX: Now properly awaits the async pipeline method. | |
| """ | |
| if pipeline is None: | |
| raise HTTPException(status_code=503, detail="Pipeline not initialized") | |
| # Lookup location (case-insensitive) | |
| location_key = request.location_name.lower().strip() | |
| if location_key not in LOCATIONS: | |
| # Try partial match | |
| matches = [k for k in LOCATIONS.keys() if location_key in k.lower()] | |
| if matches: | |
| location_key = matches[0] | |
| else: | |
| raise HTTPException( | |
| status_code=404, | |
| detail=f"Location '{request.location_name}' not found" | |
| ) | |
| coordinates = LOCATIONS[location_key] | |
| try: | |
| # FIX: AWAIT the async method! | |
| result = await pipeline.generate_alert( | |
| location=coordinates, | |
| location_name=request.location_name | |
| ) | |
| return AlertResponse( | |
| location=request.location_name, | |
| coordinates=coordinates, | |
| district=request.district, | |
| alert_summary=result["alert_summary"], | |
| timestamp=datetime.utcnow().isoformat() | |
| ) | |
| except Exception as e: | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"Failed to generate alert: {str(e)}" | |
| ) | |
| async def process_query(request: QueryRequest): | |
| """ | |
| Process a specific farmer query. | |
| FIX: Now properly awaits the async pipeline method. | |
| """ | |
| if pipeline is None: | |
| raise HTTPException(status_code=503, detail="Pipeline not initialized") | |
| # Determine location | |
| location = None | |
| if request.location_name: | |
| location_key = request.location_name.lower().strip() | |
| if location_key in LOCATIONS: | |
| location = LOCATIONS[location_key] | |
| else: | |
| matches = [k for k in LOCATIONS.keys() if location_key in k.lower()] | |
| if matches: | |
| location = LOCATIONS[matches[0]] | |
| try: | |
| # FIX: AWAIT the async method! | |
| result = await pipeline.process_query( | |
| query=request.query, | |
| location=location | |
| ) | |
| return { | |
| "query": request.query, | |
| "response": result["response"], | |
| "location": result["location"], | |
| "servers_queried": list(result["mcp_results"].keys()), | |
| "timestamp": datetime.utcnow().isoformat() | |
| } | |
| except Exception as e: | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"Failed to process query: {str(e)}" | |
| ) | |
| # ============================================================================ | |
| # MAIN | |
| # ============================================================================ | |
| if __name__ == "__main__": | |
| import uvicorn | |
| port = int(os.getenv("PORT", 7860)) | |
| uvicorn.run(app, host="0.0.0.0", port=port) | |