""" Farmer.Chat Backend - FastAPI Application Deploy to Hugging Face Space: https://huggingface.co/spaces/aakashdg/farmer-chat-backend """ from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse, JSONResponse from pydantic import BaseModel, Field from typing import Optional, Dict, Any import os import asyncio import time from datetime import datetime # Import pipeline components from src.pipeline import FarmerChatPipeline from src.pdf_generator import generate_pdf_report from openai import OpenAI import httpx # Initialize FastAPI app = FastAPI( title="Farmer.Chat Backend", description="Multi-stage MCP pipeline for agricultural intelligence", version="2.0.0" ) # CORS - Allow all origins for demo (restrict in production) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Initialize OpenAI client with FIXED httpx configuration OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") if not OPENAI_API_KEY: raise ValueError("OPENAI_API_KEY environment variable not set!") # FIX: Create httpx client without proxy support http_client = httpx.Client( timeout=httpx.Timeout(60.0), limits=httpx.Limits(max_keepalive_connections=5, max_connections=10) ) # Initialize OpenAI with custom http client (bypasses proxy issues) openai_client = OpenAI( api_key=OPENAI_API_KEY, http_client=http_client ) print("✅ OpenAI client initialized with custom httpx client") print(f" Model: gpt-4o") # Default location (Bangalore Agricultural Region) DEFAULT_LOCATION = { "name": "Bangalore Agricultural Region", "lat": 12.8716, "lon": 77.4946 } # Initialize pipeline pipeline = FarmerChatPipeline(openai_client, DEFAULT_LOCATION) # Request/Response Models class QueryRequest(BaseModel): query: str = Field(..., min_length=3, max_length=500, description="Farmer's question") location: Optional[Dict[str, Any]] = Field(None, description="Custom location (lat, lon, name)") class Config: json_schema_extra = { "example": { "query": "Should I plant rice today?", "location": { "name": "Bangalore", "lat": 12.8716, "lon": 77.4946 } } } class QueryResponse(BaseModel): success: bool query: str advice: str routing: Dict[str, Any] data: Dict[str, Any] execution_time_seconds: float timestamp: str # Health check @app.get("/") async def root(): return { "service": "Farmer.Chat Backend", "status": "operational", "version": "2.0.0", "endpoints": { "query": "/api/query", "health": "/api/health", "servers": "/api/servers" } } @app.get("/api/health") async def health_check(): """Health check endpoint""" return { "status": "healthy", "timestamp": datetime.now().isoformat(), "openai_configured": bool(OPENAI_API_KEY), "location": DEFAULT_LOCATION } @app.get("/api/servers") async def list_servers(): """List available MCP servers""" from src.executor import MCP_SERVER_REGISTRY return { "total_servers": len(MCP_SERVER_REGISTRY), "servers": MCP_SERVER_REGISTRY } @app.post("/api/query", response_model=QueryResponse) async def process_query(request: QueryRequest): """ Main query endpoint - processes farmer questions through MCP pipeline """ try: start_time = time.time() # Use custom location if provided, otherwise default location = request.location if request.location else DEFAULT_LOCATION # Update pipeline location if changed if request.location: pipeline.location = location # Process query through pipeline result = await pipeline.process_query(request.query, verbose=False) execution_time = time.time() - start_time return QueryResponse( success=True, query=request.query, advice=result["advice"], routing=result["routing"], data=result["compiled_data"], execution_time_seconds=round(execution_time, 2), timestamp=datetime.now().isoformat() ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @app.post("/api/export-pdf") async def export_pdf(request: QueryRequest): """ Export query result as PDF """ try: # Process query result = await pipeline.process_query(request.query, verbose=False) # Generate PDF pdf_path = generate_pdf_report( query=request.query, advice=result["advice"], data=result["compiled_data"], location=pipeline.location ) # Return PDF file return FileResponse( pdf_path, media_type="application/pdf", filename=f"farmer-chat-report-{int(time.time())}.pdf" ) except Exception as e: raise HTTPException(status_code=500, detail=str(e)) # Error handlers @app.exception_handler(404) async def not_found_handler(request, exc): return JSONResponse( status_code=404, content={"error": "Endpoint not found", "path": str(request.url)} ) @app.exception_handler(500) async def server_error_handler(request, exc): return JSONResponse( status_code=500, content={"error": "Internal server error", "detail": str(exc)} ) if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=7860)