aakashdg commited on
Commit
d0bbed8
·
verified ·
1 Parent(s): 0493c5a

feat (reverting back from test)

Browse files
Files changed (1) hide show
  1. app.py +160 -696
app.py CHANGED
@@ -1,615 +1,33 @@
1
- # """
2
- # Farmer.Chat Backend - FastAPI Application
3
- # Deploy to Hugging Face Space: https://huggingface.co/spaces/aakashdg/farmer-chat-backend
4
- # """
5
-
6
- # from fastapi import FastAPI, HTTPException
7
- # from fastapi.middleware.cors import CORSMiddleware
8
- # from fastapi.responses import FileResponse, JSONResponse
9
- # from pydantic import BaseModel, Field
10
- # from typing import Optional, Dict, Any
11
- # import os
12
- # import asyncio
13
- # import time
14
- # from datetime import datetime
15
-
16
- # # Import pipeline components
17
- # from src.pipeline import FarmerChatPipeline
18
- # from src.pdf_generator import generate_pdf_report
19
-
20
- # from openai import OpenAI
21
- # import httpx
22
-
23
- # # Initialize FastAPI
24
- # app = FastAPI(
25
- # title="Farmer.Chat Backend",
26
- # description="Multi-stage MCP pipeline for agricultural intelligence",
27
- # version="2.0.0"
28
- # )
29
-
30
- # # CORS - Allow all origins for demo (restrict in production)
31
- # app.add_middleware(
32
- # CORSMiddleware,
33
- # allow_origins=["*"],
34
- # allow_credentials=True,
35
- # allow_methods=["*"],
36
- # allow_headers=["*"],
37
- # )
38
-
39
- # # Initialize OpenAI client with FIXED httpx configuration
40
- # OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
41
- # if not OPENAI_API_KEY:
42
- # raise ValueError("OPENAI_API_KEY environment variable not set!")
43
-
44
- # # FIX: Create httpx client without proxy support
45
- # http_client = httpx.Client(
46
- # timeout=httpx.Timeout(60.0),
47
- # limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
48
- # )
49
-
50
- # # Initialize OpenAI with custom http client (bypasses proxy issues)
51
- # openai_client = OpenAI(
52
- # api_key=OPENAI_API_KEY,
53
- # http_client=http_client
54
- # )
55
-
56
- # print("✅ OpenAI client initialized with custom httpx client")
57
- # print(f" Model: gpt-4o")
58
-
59
- # # Default location (Bangalore Agricultural Region)
60
- # DEFAULT_LOCATION = {
61
- # "name": "Bangalore Agricultural Region",
62
- # "lat": 12.8716,
63
- # "lon": 77.4946
64
- # }
65
-
66
- # # Initialize pipeline
67
- # pipeline = FarmerChatPipeline(openai_client, DEFAULT_LOCATION)
68
-
69
- # # Request/Response Models
70
- # class QueryRequest(BaseModel):
71
- # query: str = Field(..., min_length=3, max_length=500, description="Farmer's question")
72
- # location: Optional[Dict[str, Any]] = Field(None, description="Custom location (lat, lon, name)")
73
-
74
- # class Config:
75
- # json_schema_extra = {
76
- # "example": {
77
- # "query": "Should I plant rice today?",
78
- # "location": {
79
- # "name": "Bangalore",
80
- # "lat": 12.8716,
81
- # "lon": 77.4946
82
- # }
83
- # }
84
- # }
85
-
86
-
87
- # class QueryResponse(BaseModel):
88
- # success: bool
89
- # query: str
90
- # advice: str
91
- # routing: Dict[str, Any]
92
- # data: Dict[str, Any]
93
- # execution_time_seconds: float
94
- # timestamp: str
95
-
96
-
97
- # # Health check
98
- # @app.get("/")
99
- # async def root():
100
- # return {
101
- # "service": "Farmer.Chat Backend",
102
- # "status": "operational",
103
- # "version": "2.0.0",
104
- # "endpoints": {
105
- # "query": "/api/query",
106
- # "health": "/api/health",
107
- # "servers": "/api/servers"
108
- # }
109
- # }
110
-
111
-
112
- # @app.get("/api/health")
113
- # async def health_check():
114
- # """Health check endpoint"""
115
- # return {
116
- # "status": "healthy",
117
- # "timestamp": datetime.now().isoformat(),
118
- # "openai_configured": bool(OPENAI_API_KEY),
119
- # "location": DEFAULT_LOCATION
120
- # }
121
-
122
-
123
- # @app.get("/api/servers")
124
- # async def list_servers():
125
- # """List available MCP servers"""
126
- # from src.executor import MCP_SERVER_REGISTRY
127
-
128
- # return {
129
- # "total_servers": len(MCP_SERVER_REGISTRY),
130
- # "servers": MCP_SERVER_REGISTRY
131
- # }
132
-
133
-
134
- # @app.post("/api/query", response_model=QueryResponse)
135
- # async def process_query(request: QueryRequest):
136
- # """
137
- # Main query endpoint - processes farmer questions through MCP pipeline
138
- # """
139
- # try:
140
- # start_time = time.time()
141
-
142
- # # Use custom location if provided, otherwise default
143
- # location = request.location if request.location else DEFAULT_LOCATION
144
-
145
- # # Update pipeline location if changed
146
- # if request.location:
147
- # pipeline.location = location
148
-
149
- # # Process query through pipeline
150
- # result = await pipeline.process_query(request.query, verbose=False)
151
-
152
- # execution_time = time.time() - start_time
153
-
154
- # return QueryResponse(
155
- # success=True,
156
- # query=request.query,
157
- # advice=result["advice"],
158
- # routing=result["routing"],
159
- # data=result["compiled_data"],
160
- # execution_time_seconds=round(execution_time, 2),
161
- # timestamp=datetime.now().isoformat()
162
- # )
163
-
164
- # except Exception as e:
165
- # raise HTTPException(status_code=500, detail=str(e))
166
-
167
-
168
- # @app.post("/api/export-pdf")
169
- # async def export_pdf(request: QueryRequest):
170
- # """
171
- # Export query result as PDF
172
- # """
173
- # try:
174
- # # Process query
175
- # result = await pipeline.process_query(request.query, verbose=False)
176
-
177
- # # Generate PDF
178
- # pdf_path = generate_pdf_report(
179
- # query=request.query,
180
- # advice=result["advice"],
181
- # data=result["compiled_data"],
182
- # location=pipeline.location
183
- # )
184
-
185
- # # Return PDF file
186
- # return FileResponse(
187
- # pdf_path,
188
- # media_type="application/pdf",
189
- # filename=f"farmer-chat-report-{int(time.time())}.pdf"
190
- # )
191
-
192
- # except Exception as e:
193
- # raise HTTPException(status_code=500, detail=str(e))
194
-
195
-
196
- # # Error handlers
197
- # @app.exception_handler(404)
198
- # async def not_found_handler(request, exc):
199
- # return JSONResponse(
200
- # status_code=404,
201
- # content={"error": "Endpoint not found", "path": str(request.url)}
202
- # )
203
-
204
-
205
- # @app.exception_handler(500)
206
- # async def server_error_handler(request, exc):
207
- # return JSONResponse(
208
- # status_code=500,
209
- # content={"error": "Internal server error", "detail": str(exc)}
210
- # )
211
-
212
-
213
- # if __name__ == "__main__":
214
- # import uvicorn
215
- # uvicorn.run(app, host="0.0.0.0", port=7860)
216
-
217
  """
218
- Alert Summary Generator Backend - Standalone Version
219
- FastAPI app with embedded pipeline and location data
220
  """
221
 
222
  from fastapi import FastAPI, HTTPException
223
  from fastapi.middleware.cors import CORSMiddleware
224
- from pydantic import BaseModel
225
- from typing import Optional, Dict, Any, List
 
226
  import os
 
 
227
  from datetime import datetime
228
- from openai import OpenAI
229
- import httpx
230
-
231
- # ============================================================================
232
- # OPENAI CLIENT SETUP
233
- # ============================================================================
234
-
235
- def get_openai_client():
236
- """Initialize OpenAI client with API key from environment"""
237
- api_key = os.getenv("OPENAI_API_KEY")
238
- if not api_key:
239
- raise ValueError("OPENAI_API_KEY environment variable not set")
240
- http_client = httpx.Client(
241
- timeout=httpx.Timeout(60.0, connect=10.0),
242
- limits=httpx.Limits(max_keepalive_connections=10, max_connections=20)
243
- )
244
- return OpenAI(api_key=api_key, http_client=http_client)
245
-
246
-
247
- # ============================================================================
248
- # PIPELINE COMPONENTS
249
- # ============================================================================
250
-
251
- class QueryRouter:
252
- """Routes queries to appropriate MCP servers"""
253
-
254
- def __init__(self, client):
255
- self.client = client
256
-
257
- def route_query(self, query: str, location: Dict[str, float]) -> Dict[str, Any]:
258
- """Determine which servers to call based on query"""
259
- prompt = f"""Given the farmer query: "{query}"
260
- Location: {location['latitude']}, {location['longitude']}
261
-
262
- Determine which data sources are needed. Return JSON:
263
- {{
264
- "weather": true/false,
265
- "soil": true/false,
266
- "water": true/false,
267
- "elevation": true/false,
268
- "pests": true/false
269
- }}"""
270
-
271
- response = self.client.chat.completions.create(
272
- model="gpt-4",
273
- messages=[{"role": "user", "content": prompt}],
274
- temperature=0
275
- )
276
-
277
- # Parse routing decision
278
- import json
279
- try:
280
- routing = json.loads(response.choices[0].message.content)
281
- except:
282
- # Default: query all servers for alerts
283
- routing = {
284
- "weather": True,
285
- "soil": True,
286
- "water": True,
287
- "elevation": True,
288
- "pests": True
289
- }
290
-
291
- return routing
292
-
293
-
294
- class MCPExecutor:
295
- """Executes parallel calls to MCP servers"""
296
-
297
- def __init__(self, client):
298
- self.client = client
299
-
300
- def execute_parallel(self, routing: Dict[str, bool], location: Dict[str, float]) -> Dict[str, str]:
301
- """Execute MCP server calls in parallel"""
302
- results = {}
303
-
304
- # Simulate MCP server calls (replace with actual server calls)
305
- if routing.get("weather"):
306
- results["weather"] = f"Weather data for {location['latitude']}, {location['longitude']}"
307
-
308
- if routing.get("soil"):
309
- results["soil"] = f"Soil data for {location['latitude']}, {location['longitude']}"
310
-
311
- if routing.get("water"):
312
- results["water"] = f"Water availability for {location['latitude']}, {location['longitude']}"
313
-
314
- if routing.get("elevation"):
315
- results["elevation"] = f"Elevation data for {location['latitude']}, {location['longitude']}"
316
-
317
- if routing.get("pests"):
318
- results["pests"] = f"Pest risk data for {location['latitude']}, {location['longitude']}"
319
-
320
- return results
321
-
322
-
323
- class ResponseCompiler:
324
- """Compiles MCP results into coherent response"""
325
-
326
- def __init__(self, client):
327
- self.client = client
328
-
329
- def compile_response(self, query: str, mcp_results: Dict[str, str], location: Dict[str, float]) -> str:
330
- """Compile MCP results into final response"""
331
-
332
- # Format MCP results for context
333
- context = "\n\n".join([f"{k.upper()}: {v}" for k, v in mcp_results.items()])
334
-
335
- prompt = f"""You are an agricultural assistant. Compile this data into a comprehensive alert summary.
336
-
337
- FARMER QUERY: {query}
338
- LOCATION: {location['latitude']}, {location['longitude']}
339
-
340
- DATA FROM SOURCES:
341
- {context}
342
-
343
- Provide a comprehensive agricultural alert covering:
344
- 1. Current weather conditions and forecast
345
- 2. Soil health and recommendations
346
- 3. Water availability status
347
- 4. Elevation/topography considerations
348
- 5. Pest risks and preventive measures
349
-
350
- Be specific, actionable, and farmer-friendly."""
351
-
352
- response = self.client.chat.completions.create(
353
- model="gpt-4",
354
- messages=[{"role": "user", "content": prompt}],
355
- temperature=0.7
356
- )
357
-
358
- return response.choices[0].message.content
359
 
 
 
 
360
 
361
- class HindiTranslator:
362
- """Translates responses to Hindi"""
363
-
364
- def __init__(self, client):
365
- self.client = client
366
-
367
- def translate(self, text: str, target_lang: str = "en") -> str:
368
- """Translate text if needed"""
369
- if target_lang == "en":
370
- return text
371
-
372
- # Add Hindi translation logic here
373
- return text
374
-
375
-
376
- class FarmerChatPipeline:
377
- """Main pipeline orchestrating all stages"""
378
-
379
- def __init__(self, client, location: Dict[str, float]):
380
- self.client = client
381
- self.location = location
382
- self.router = QueryRouter(client)
383
- self.executor = MCPExecutor(client)
384
- self.compiler = ResponseCompiler(client)
385
- self.translator = HindiTranslator(client)
386
-
387
- def process_query(self, query: str, language: str = "en") -> str:
388
- """Process query through full pipeline"""
389
-
390
- # Stage 1: Route query
391
- routing = self.router.route_query(query, self.location)
392
-
393
- # Stage 2: Execute MCP calls
394
- mcp_results = self.executor.execute_parallel(routing, self.location)
395
-
396
- # Stage 3: Compile response
397
- response = self.compiler.compile_response(query, mcp_results, self.location)
398
-
399
- # Stage 4: Translate if needed
400
- final_response = self.translator.translate(response, language)
401
-
402
- return final_response
403
-
404
-
405
- # ============================================================================
406
- # BIHAR LOCATION DATA
407
- # ============================================================================
408
-
409
- BIHAR_DATA = {
410
- "Araria": ["Araria", "Forbesganj", "Jokihat", "Raniganj"],
411
- "Arwal": ["Arwal", "Kaler", "Karpi", "Kurtha"],
412
- "Aurangabad": ["Aurangabad", "Daudnagar", "Obra", "Nabinagar"],
413
- "Banka": ["Banka", "Amarpur", "Barahat", "Belhar"],
414
- "Begusarai": ["Begusarai", "Bakhri", "Barauni", "Teghra"],
415
- "Bhagalpur": ["Bhagalpur", "Sabour", "Nathnagar", "Kahalgaon"],
416
- "Bhojpur": ["Arrah", "Jagdishpur", "Piro", "Shahpur"],
417
- "Buxar": ["Buxar", "Dumraon", "Chausa", "Simri"],
418
- "Darbhanga": ["Darbhanga", "Baheri", "Jale", "Benipur"],
419
- "East Champaran": ["Motihari", "Raxaul", "Chakia", "Dhaka"],
420
- "Gaya": ["Gaya", "Bodh Gaya", "Tekari", "Sherghati"],
421
- "Gopalganj": ["Gopalganj", "Barauli", "Baikunthpur", "Kateya"],
422
- "Jamui": ["Jamui", "Jhajha", "Sikandra", "Sono"],
423
- "Jehanabad": ["Jehanabad", "Ghoshi", "Makhdumpur", "Modanganj"],
424
- "Kaimur": ["Bhabua", "Mohania", "Ramgarh", "Chainpur"],
425
- "Katihar": ["Katihar", "Barsoi", "Manihari", "Pranpur"],
426
- "Khagaria": ["Khagaria", "Parbatta", "Alauli", "Beldaur"],
427
- "Kishanganj": ["Kishanganj", "Bahadurganj", "Thakurganj", "Dighalbank"],
428
- "Lakhisarai": ["Lakhisarai", "Halsi", "Suryagarha", "Pipariya"],
429
- "Madhepura": ["Madhepura", "Udakishanganj", "Murliganj", "Alamnagar"],
430
- "Madhubani": ["Madhubani", "Jhanjharpur", "Benipatti", "Jainagar"],
431
- "Munger": ["Munger", "Jamalpur", "Asarganj", "Tarapur"],
432
- "Muzaffarpur": ["Muzaffarpur", "Sitamarhi", "Minapur", "Bochaha"],
433
- "Nalanda": ["Bihar Sharif", "Rajgir", "Hilsa", "Biharsharif"],
434
- "Nawada": ["Nawada", "Rajauli", "Akbarpur", "Hisua"],
435
- "Patna": ["Patna", "Danapur", "Fatuha", "Khagaul"],
436
- "Purnia": ["Purnia", "Dhamdaha", "Kasba", "Banmankhi"],
437
- "Rohtas": ["Sasaram", "Dehri", "Bikramganj", "Nasriganj"],
438
- "Saharsa": ["Saharsa", "Sonbarsa", "Simri Bakhtiarpur", "Mahishi"],
439
- "Samastipur": ["Samastipur", "Rosera", "Dalsinghsarai", "Pusa"],
440
- "Saran": ["Chapra", "Marhaura", "Amnour", "Sonepur"],
441
- "Sheikhpura": ["Sheikhpura", "Barbigha", "Ariari", "Shekhopur"],
442
- "Sheohar": ["Sheohar", "Dumri Katsari", "Piprahi", "Tariyani"],
443
- "Sitamarhi": ["Sitamarhi", "Pupri", "Belsand", "Bathnaha"],
444
- "Siwan": ["Siwan", "Maharajganj", "Mairwa", "Darauli"],
445
- "Supaul": ["Supaul", "Nirmali", "Triveniganj", "Chhatapur"],
446
- "Vaishali": ["Hajipur", "Mahua", "Lalganj", "Desri"],
447
- "West Champaran": ["Bettiah", "Bagaha", "Narkatiaganj", "Lauriya"]
448
- }
449
-
450
- LOCATIONS = {
451
- "Araria": {"latitude": 26.1523, "longitude": 87.5167},
452
- "Forbesganj": {"latitude": 26.3023, "longitude": 87.2664},
453
- "Jokihat": {"latitude": 25.8998, "longitude": 87.2686},
454
- "Raniganj": {"latitude": 26.0537, "longitude": 87.5333},
455
- "Arwal": {"latitude": 25.2560, "longitude": 84.6819},
456
- "Kaler": {"latitude": 25.1960, "longitude": 84.6219},
457
- "Karpi": {"latitude": 25.2360, "longitude": 84.7019},
458
- "Kurtha": {"latitude": 25.3160, "longitude": 84.6619},
459
- "Aurangabad": {"latitude": 24.7521, "longitude": 84.3742},
460
- "Daudnagar": {"latitude": 25.0337, "longitude": 84.4007},
461
- "Obra": {"latitude": 24.9923, "longitude": 84.4342},
462
- "Nabinagar": {"latitude": 24.6087, "longitude": 84.1269},
463
- "Banka": {"latitude": 24.8893, "longitude": 86.9220},
464
- "Amarpur": {"latitude": 25.0393, "longitude": 86.9020},
465
- "Barahat": {"latitude": 24.8393, "longitude": 87.0020},
466
- "Belhar": {"latitude": 24.9393, "longitude": 86.9620},
467
- "Begusarai": {"latitude": 25.4182, "longitude": 86.1347},
468
- "Bakhri": {"latitude": 25.4582, "longitude": 86.0547},
469
- "Barauni": {"latitude": 25.4751, "longitude": 86.0458},
470
- "Teghra": {"latitude": 25.5082, "longitude": 85.9347},
471
- "Bhagalpur": {"latitude": 25.2425, "longitude": 86.9842},
472
- "Sabour": {"latitude": 25.2375, "longitude": 87.0542},
473
- "Nathnagar": {"latitude": 25.1225, "longitude": 87.0042},
474
- "Kahalgaon": {"latitude": 25.1925, "longitude": 87.2142},
475
- "Arrah": {"latitude": 25.5560, "longitude": 84.6631},
476
- "Jagdishpur": {"latitude": 25.4660, "longitude": 84.4231},
477
- "Piro": {"latitude": 25.3260, "longitude": 84.4031},
478
- "Shahpur": {"latitude": 25.6060, "longitude": 84.4031},
479
- "Buxar": {"latitude": 25.5641, "longitude": 83.9778},
480
- "Dumraon": {"latitude": 25.5541, "longitude": 84.1478},
481
- "Chausa": {"latitude": 25.5241, "longitude": 83.9178},
482
- "Simri": {"latitude": 25.6141, "longitude": 84.0478},
483
- "Darbhanga": {"latitude": 26.1542, "longitude": 85.8978},
484
- "Baheri": {"latitude": 26.0442, "longitude": 85.8378},
485
- "Jale": {"latitude": 26.2042, "longitude": 85.8578},
486
- "Benipur": {"latitude": 26.1142, "longitude": 85.9478},
487
- "Motihari": {"latitude": 26.6484, "longitude": 84.9194},
488
- "Raxaul": {"latitude": 26.9784, "longitude": 84.8494},
489
- "Chakia": {"latitude": 26.4184, "longitude": 85.0494},
490
- "Dhaka": {"latitude": 26.6784, "longitude": 85.1694},
491
- "Gaya": {"latitude": 24.7955, "longitude": 85.0002},
492
- "Bodh Gaya": {"latitude": 24.6955, "longitude": 84.9902},
493
- "Tekari": {"latitude": 24.9455, "longitude": 85.0402},
494
- "Sherghati": {"latitude": 24.5655, "longitude": 84.7902},
495
- "Gopalganj": {"latitude": 26.4685, "longitude": 84.4388},
496
- "Barauli": {"latitude": 26.3785, "longitude": 84.5788},
497
- "Baikunthpur": {"latitude": 26.5285, "longitude": 84.3588},
498
- "Kateya": {"latitude": 26.4285, "longitude": 84.6388},
499
- "Jamui": {"latitude": 24.9272, "longitude": 86.2231},
500
- "Jhajha": {"latitude": 24.7772, "longitude": 86.3731},
501
- "Sikandra": {"latitude": 24.9672, "longitude": 86.0631},
502
- "Sono": {"latitude": 24.8372, "longitude": 86.1431},
503
- "Jehanabad": {"latitude": 25.2078, "longitude": 84.9869},
504
- "Ghoshi": {"latitude": 25.1478, "longitude": 84.9269},
505
- "Makhdumpur": {"latitude": 25.2478, "longitude": 85.0469},
506
- "Modanganj": {"latitude": 25.2678, "longitude": 84.9069},
507
- "Bhabua": {"latitude": 25.0405, "longitude": 83.6074},
508
- "Mohania": {"latitude": 25.1305, "longitude": 83.4774},
509
- "Ramgarh": {"latitude": 24.9505, "longitude": 83.6874},
510
- "Chainpur": {"latitude": 25.2005, "longitude": 83.7474},
511
- "Katihar": {"latitude": 25.5394, "longitude": 87.5839},
512
- "Barsoi": {"latitude": 25.3794, "longitude": 87.8839},
513
- "Manihari": {"latitude": 25.3394, "longitude": 87.6239},
514
- "Pranpur": {"latitude": 25.6894, "longitude": 87.7239},
515
- "Khagaria": {"latitude": 25.5022, "longitude": 86.4665},
516
- "Parbatta": {"latitude": 25.5422, "longitude": 86.5865},
517
- "Alauli": {"latitude": 25.4622, "longitude": 86.3465},
518
- "Beldaur": {"latitude": 25.5622, "longitude": 86.4265},
519
- "Kishanganj": {"latitude": 26.1046, "longitude": 87.9475},
520
- "Bahadurganj": {"latitude": 26.2646, "longitude": 88.1175},
521
- "Thakurganj": {"latitude": 26.0446, "longitude": 87.8275},
522
- "Dighalbank": {"latitude": 25.9046, "longitude": 87.9875},
523
- "Lakhisarai": {"latitude": 25.1678, "longitude": 86.0927},
524
- "Halsi": {"latitude": 25.2278, "longitude": 86.0327},
525
- "Suryagarha": {"latitude": 25.1078, "longitude": 86.1527},
526
- "Pipariya": {"latitude": 25.2078, "longitude": 86.1327},
527
- "Madhepura": {"latitude": 25.9207, "longitude": 86.7940},
528
- "Udakishanganj": {"latitude": 25.9807, "longitude": 86.6740},
529
- "Murliganj": {"latitude": 25.8907, "longitude": 86.9940},
530
- "Alamnagar": {"latitude": 25.9607, "longitude": 86.7340},
531
- "Madhubani": {"latitude": 26.3561, "longitude": 86.0644},
532
- "Jhanjharpur": {"latitude": 26.2661, "longitude": 86.2844},
533
- "Benipatti": {"latitude": 26.5961, "longitude": 86.1444},
534
- "Jainagar": {"latitude": 26.2061, "longitude": 86.1644},
535
- "Munger": {"latitude": 25.3753, "longitude": 86.4734},
536
- "Jamalpur": {"latitude": 25.3153, "longitude": 86.4934},
537
- "Asarganj": {"latitude": 25.1453, "longitude": 86.6834},
538
- "Tarapur": {"latitude": 25.0253, "longitude": 86.6334},
539
- "Muzaffarpur": {"latitude": 26.1225, "longitude": 85.3906},
540
- "Sitamarhi": {"latitude": 26.5925, "longitude": 85.4806},
541
- "Minapur": {"latitude": 26.0625, "longitude": 85.2906},
542
- "Bochaha": {"latitude": 26.0025, "longitude": 85.5306},
543
- "Bihar Sharif": {"latitude": 25.1979, "longitude": 85.5238},
544
- "Rajgir": {"latitude": 25.0279, "longitude": 85.4238},
545
- "Hilsa": {"latitude": 25.3179, "longitude": 85.2838},
546
- "Biharsharif": {"latitude": 25.1979, "longitude": 85.5238},
547
- "Nawada": {"latitude": 24.8834, "longitude": 85.5387},
548
- "Rajauli": {"latitude": 25.0634, "longitude": 85.6387},
549
- "Akbarpur": {"latitude": 24.8234, "longitude": 85.4587},
550
- "Hisua": {"latitude": 24.8334, "longitude": 85.4187},
551
- "Patna": {"latitude": 25.5941, "longitude": 85.1376},
552
- "Danapur": {"latitude": 25.6341, "longitude": 85.0476},
553
- "Fatuha": {"latitude": 25.5041, "longitude": 85.3076},
554
- "Khagaul": {"latitude": 25.5741, "longitude": 85.0476},
555
- "Purnia": {"latitude": 25.7771, "longitude": 87.4753},
556
- "Dhamdaha": {"latitude": 25.8871, "longitude": 87.5853},
557
- "Kasba": {"latitude": 25.8471, "longitude": 87.5353},
558
- "Banmankhi": {"latitude": 25.8871, "longitude": 87.1953},
559
- "Sasaram": {"latitude": 24.9520, "longitude": 84.0328},
560
- "Dehri": {"latitude": 24.9020, "longitude": 84.1828},
561
- "Bikramganj": {"latitude": 25.2120, "longitude": 84.2628},
562
- "Nasriganj": {"latitude": 25.0520, "longitude": 84.1228},
563
- "Saharsa": {"latitude": 25.8769, "longitude": 86.5956},
564
- "Sonbarsa": {"latitude": 25.9269, "longitude": 86.7356},
565
- "Simri Bakhtiarpur": {"latitude": 25.9569, "longitude": 86.3556},
566
- "Mahishi": {"latitude": 25.9969, "longitude": 86.4756},
567
- "Samastipur": {"latitude": 25.8647, "longitude": 85.7817},
568
- "Rosera": {"latitude": 25.7947, "longitude": 85.9317},
569
- "Dalsinghsarai": {"latitude": 25.6647, "longitude": 85.8317},
570
- "Pusa": {"latitude": 25.9847, "longitude": 85.6717},
571
- "Chapra": {"latitude": 25.7805, "longitude": 84.7477},
572
- "Marhaura": {"latitude": 25.9705, "longitude": 84.8677},
573
- "Amnour": {"latitude": 25.8905, "longitude": 84.9077},
574
- "Sonepur": {"latitude": 25.6905, "longitude": 85.1777},
575
- "Sheikhpura": {"latitude": 25.1391, "longitude": 85.8354},
576
- "Barbigha": {"latitude": 25.2191, "longitude": 85.7354},
577
- "Ariari": {"latitude": 25.0591, "longitude": 85.9554},
578
- "Shekhopur": {"latitude": 25.1391, "longitude": 85.8354},
579
- "Sheohar": {"latitude": 26.5184, "longitude": 85.2959},
580
- "Dumri Katsari": {"latitude": 26.5784, "longitude": 85.1959},
581
- "Piprahi": {"latitude": 26.4684, "longitude": 85.4159},
582
- "Tariyani": {"latitude": 26.5584, "longitude": 85.2359},
583
- "Sitamarhi": {"latitude": 26.5925, "longitude": 85.4806},
584
- "Pupri": {"latitude": 26.4725, "longitude": 85.7006},
585
- "Belsand": {"latitude": 26.4425, "longitude": 85.4006},
586
- "Bathnaha": {"latitude": 26.5925, "longitude": 85.5306},
587
- "Siwan": {"latitude": 26.2195, "longitude": 84.3564},
588
- "Maharajganj": {"latitude": 26.1095, "longitude": 84.5064},
589
- "Mairwa": {"latitude": 26.2295, "longitude": 84.1664},
590
- "Darauli": {"latitude": 26.1595, "longitude": 84.1464},
591
- "Supaul": {"latitude": 26.1260, "longitude": 86.6050},
592
- "Nirmali": {"latitude": 26.3160, "longitude": 86.5850},
593
- "Triveniganj": {"latitude": 26.2160, "longitude": 87.0250},
594
- "Chhatapur": {"latitude": 26.2160, "longitude": 86.9050},
595
- "Hajipur": {"latitude": 25.6851, "longitude": 85.2095},
596
- "Mahua": {"latitude": 25.9651, "longitude": 85.2895},
597
- "Lalganj": {"latitude": 25.8751, "longitude": 85.1695},
598
- "Desri": {"latitude": 25.6051, "longitude": 85.4895},
599
- "Bettiah": {"latitude": 26.8022, "longitude": 84.5025},
600
- "Bagaha": {"latitude": 27.0922, "longitude": 84.0925},
601
- "Narkatiaganj": {"latitude": 26.4322, "longitude": 84.7925},
602
- "Lauriya": {"latitude": 26.9822, "longitude": 84.3125}
603
- }
604
-
605
-
606
- # ============================================================================
607
- # FASTAPI APP
608
- # ============================================================================
609
 
610
- app = FastAPI(title="Farmer.chat Alert Summary API")
 
 
 
 
 
611
 
612
- # CORS middleware
613
  app.add_middleware(
614
  CORSMiddleware,
615
  allow_origins=["*"],
@@ -618,135 +36,181 @@ app.add_middleware(
618
  allow_headers=["*"],
619
  )
620
 
621
- # Initialize pipeline
622
- openai_client = None
623
- pipeline = None
 
624
 
625
- @app.on_event("startup")
626
- async def startup_event():
627
- """Initialize OpenAI client and pipeline on startup"""
628
- global openai_client, pipeline
629
- try:
630
- openai_client = get_openai_client()
631
- DEFAULT_LOCATION = {"latitude": 25.5941, "longitude": 85.1376}
632
- pipeline = FarmerChatPipeline(openai_client, DEFAULT_LOCATION)
633
- print("✓ Pipeline initialized successfully")
634
- except Exception as e:
635
- print(f"✗ Pipeline initialization failed: {e}")
636
- raise
637
 
 
 
 
 
 
638
 
639
- # ============================================================================
640
- # API MODELS
641
- # ============================================================================
642
 
643
- class LocationRequest(BaseModel):
644
- location_name: str
645
- district: Optional[str] = None
 
 
 
646
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
647
 
648
- class AlertResponse(BaseModel):
649
- location: str
650
- coordinates: Dict[str, float]
651
- district: Optional[str] = None
652
- alert_summary: str
653
- timestamp: str
654
 
 
 
 
 
 
 
 
 
655
 
656
- # ============================================================================
657
- # API ENDPOINTS
658
- # ============================================================================
659
 
 
660
  @app.get("/")
661
  async def root():
662
- """Root endpoint"""
663
  return {
664
- "message": "Farmer.chat Alert Summary API",
665
- "version": "1.0.0",
 
666
  "endpoints": {
667
- "/locations": "GET - List all districts and villages",
668
- "/generate-alert": "POST - Generate alert for location",
669
- "/health": "GET - Health check"
670
  }
671
  }
672
 
673
 
674
- @app.get("/health")
675
  async def health_check():
676
  """Health check endpoint"""
677
  return {
678
  "status": "healthy",
679
- "pipeline": "initialized" if pipeline else "not_initialized",
680
- "timestamp": datetime.now().isoformat()
 
681
  }
682
 
683
 
684
- @app.get("/locations")
685
- async def get_locations():
686
- """Get all districts and villages"""
687
- return {"districts": BIHAR_DATA}
688
-
689
-
690
- @app.post("/generate-alert", response_model=AlertResponse)
691
- async def generate_alert(request: LocationRequest):
692
- """Generate alert summary for selected location"""
693
-
694
- if not pipeline:
695
- raise HTTPException(status_code=500, detail="Pipeline not initialized")
696
-
697
- # Find location coordinates
698
- location_name = request.location_name.strip()
699
- coordinates = None
700
 
701
- # Case-insensitive search
702
- for loc_key, loc_coords in LOCATIONS.items():
703
- if loc_key.lower() == location_name.lower():
704
- coordinates = loc_coords
705
- break
706
-
707
- if not coordinates:
708
- raise HTTPException(
709
- status_code=404,
710
- detail=f"Location '{location_name}' not found in database"
711
- )
712
-
713
- # Update pipeline location
714
- pipeline.location = coordinates
715
-
716
- # Create comprehensive query for all MCP servers
717
- query = f"""Generate a comprehensive agricultural alert summary for {location_name} covering:
718
- 1. Current weather conditions and 7-day forecast
719
- 2. Soil health analysis and fertilizer recommendations
720
- 3. Groundwater availability and irrigation status
721
- 4. Elevation and topography considerations for farming
722
- 5. Current pest risks and preventive measures
723
 
724
- Provide actionable insights for farmers."""
725
-
 
 
 
 
726
  try:
727
- # Generate alert
728
- alert_summary = pipeline.process_query(query)
 
 
729
 
730
- return AlertResponse(
731
- location=location_name,
732
- coordinates=coordinates,
733
- district=request.district,
734
- alert_summary=alert_summary,
 
 
 
 
 
 
 
 
 
 
 
735
  timestamp=datetime.now().isoformat()
736
  )
737
-
738
  except Exception as e:
739
- raise HTTPException(
740
- status_code=500,
741
- detail=f"Error generating alert: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
742
  )
 
 
 
743
 
744
 
745
- # ============================================================================
746
- # RUN SERVER
747
- # ============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
 
748
 
749
  if __name__ == "__main__":
750
  import uvicorn
751
- port = int(os.getenv("PORT", 7860))
752
- uvicorn.run(app, host="0.0.0.0", port=port)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
+ Farmer.Chat Backend - FastAPI Application
3
+ Deploy to Hugging Face Space: https://huggingface.co/spaces/aakashdg/farmer-chat-backend
4
  """
5
 
6
  from fastapi import FastAPI, HTTPException
7
  from fastapi.middleware.cors import CORSMiddleware
8
+ from fastapi.responses import FileResponse, JSONResponse
9
+ from pydantic import BaseModel, Field
10
+ from typing import Optional, Dict, Any
11
  import os
12
+ import asyncio
13
+ import time
14
  from datetime import datetime
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ # Import pipeline components
17
+ from src.pipeline import FarmerChatPipeline
18
+ from src.pdf_generator import generate_pdf_report
19
 
20
+ from openai import OpenAI
21
+ import httpx
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ # Initialize FastAPI
24
+ app = FastAPI(
25
+ title="Farmer.Chat Backend",
26
+ description="Multi-stage MCP pipeline for agricultural intelligence",
27
+ version="2.0.0"
28
+ )
29
 
30
+ # CORS - Allow all origins for demo (restrict in production)
31
  app.add_middleware(
32
  CORSMiddleware,
33
  allow_origins=["*"],
 
36
  allow_headers=["*"],
37
  )
38
 
39
+ # Initialize OpenAI client with FIXED httpx configuration
40
+ OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
41
+ if not OPENAI_API_KEY:
42
+ raise ValueError("OPENAI_API_KEY environment variable not set!")
43
 
44
+ # FIX: Create httpx client without proxy support
45
+ http_client = httpx.Client(
46
+ timeout=httpx.Timeout(60.0),
47
+ limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
48
+ )
 
 
 
 
 
 
 
49
 
50
+ # Initialize OpenAI with custom http client (bypasses proxy issues)
51
+ openai_client = OpenAI(
52
+ api_key=OPENAI_API_KEY,
53
+ http_client=http_client
54
+ )
55
 
56
+ print("✅ OpenAI client initialized with custom httpx client")
57
+ print(f" Model: gpt-4o")
 
58
 
59
+ # Default location (Bangalore Agricultural Region)
60
+ DEFAULT_LOCATION = {
61
+ "name": "Bangalore Agricultural Region",
62
+ "lat": 12.8716,
63
+ "lon": 77.4946
64
+ }
65
 
66
+ # Initialize pipeline
67
+ pipeline = FarmerChatPipeline(openai_client, DEFAULT_LOCATION)
68
+
69
+ # Request/Response Models
70
+ class QueryRequest(BaseModel):
71
+ query: str = Field(..., min_length=3, max_length=500, description="Farmer's question")
72
+ location: Optional[Dict[str, Any]] = Field(None, description="Custom location (lat, lon, name)")
73
+
74
+ class Config:
75
+ json_schema_extra = {
76
+ "example": {
77
+ "query": "Should I plant rice today?",
78
+ "location": {
79
+ "name": "Bangalore",
80
+ "lat": 12.8716,
81
+ "lon": 77.4946
82
+ }
83
+ }
84
+ }
85
 
 
 
 
 
 
 
86
 
87
+ class QueryResponse(BaseModel):
88
+ success: bool
89
+ query: str
90
+ advice: str
91
+ routing: Dict[str, Any]
92
+ data: Dict[str, Any]
93
+ execution_time_seconds: float
94
+ timestamp: str
95
 
 
 
 
96
 
97
+ # Health check
98
  @app.get("/")
99
  async def root():
 
100
  return {
101
+ "service": "Farmer.Chat Backend",
102
+ "status": "operational",
103
+ "version": "2.0.0",
104
  "endpoints": {
105
+ "query": "/api/query",
106
+ "health": "/api/health",
107
+ "servers": "/api/servers"
108
  }
109
  }
110
 
111
 
112
+ @app.get("/api/health")
113
  async def health_check():
114
  """Health check endpoint"""
115
  return {
116
  "status": "healthy",
117
+ "timestamp": datetime.now().isoformat(),
118
+ "openai_configured": bool(OPENAI_API_KEY),
119
+ "location": DEFAULT_LOCATION
120
  }
121
 
122
 
123
+ @app.get("/api/servers")
124
+ async def list_servers():
125
+ """List available MCP servers"""
126
+ from src.executor import MCP_SERVER_REGISTRY
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
+ return {
129
+ "total_servers": len(MCP_SERVER_REGISTRY),
130
+ "servers": MCP_SERVER_REGISTRY
131
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
 
133
+
134
+ @app.post("/api/query", response_model=QueryResponse)
135
+ async def process_query(request: QueryRequest):
136
+ """
137
+ Main query endpoint - processes farmer questions through MCP pipeline
138
+ """
139
  try:
140
+ start_time = time.time()
141
+
142
+ # Use custom location if provided, otherwise default
143
+ location = request.location if request.location else DEFAULT_LOCATION
144
 
145
+ # Update pipeline location if changed
146
+ if request.location:
147
+ pipeline.location = location
148
+
149
+ # Process query through pipeline
150
+ result = await pipeline.process_query(request.query, verbose=False)
151
+
152
+ execution_time = time.time() - start_time
153
+
154
+ return QueryResponse(
155
+ success=True,
156
+ query=request.query,
157
+ advice=result["advice"],
158
+ routing=result["routing"],
159
+ data=result["compiled_data"],
160
+ execution_time_seconds=round(execution_time, 2),
161
  timestamp=datetime.now().isoformat()
162
  )
163
+
164
  except Exception as e:
165
+ raise HTTPException(status_code=500, detail=str(e))
166
+
167
+
168
+ @app.post("/api/export-pdf")
169
+ async def export_pdf(request: QueryRequest):
170
+ """
171
+ Export query result as PDF
172
+ """
173
+ try:
174
+ # Process query
175
+ result = await pipeline.process_query(request.query, verbose=False)
176
+
177
+ # Generate PDF
178
+ pdf_path = generate_pdf_report(
179
+ query=request.query,
180
+ advice=result["advice"],
181
+ data=result["compiled_data"],
182
+ location=pipeline.location
183
+ )
184
+
185
+ # Return PDF file
186
+ return FileResponse(
187
+ pdf_path,
188
+ media_type="application/pdf",
189
+ filename=f"farmer-chat-report-{int(time.time())}.pdf"
190
  )
191
+
192
+ except Exception as e:
193
+ raise HTTPException(status_code=500, detail=str(e))
194
 
195
 
196
+ # Error handlers
197
+ @app.exception_handler(404)
198
+ async def not_found_handler(request, exc):
199
+ return JSONResponse(
200
+ status_code=404,
201
+ content={"error": "Endpoint not found", "path": str(request.url)}
202
+ )
203
+
204
+
205
+ @app.exception_handler(500)
206
+ async def server_error_handler(request, exc):
207
+ return JSONResponse(
208
+ status_code=500,
209
+ content={"error": "Internal server error", "detail": str(exc)}
210
+ )
211
+
212
 
213
  if __name__ == "__main__":
214
  import uvicorn
215
+ uvicorn.run(app, host="0.0.0.0", port=7860)
216
+