joe4ai commited on
Commit
c7fe5e0
Β·
verified Β·
1 Parent(s): 65d9428

Create api.py

Browse files
Files changed (1) hide show
  1. api.py +542 -0
api.py ADDED
@@ -0,0 +1,542 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ import requests
4
+ import urllib.parse
5
+ import gradio as gr
6
+ import traceback
7
+ from typing import Optional, Dict, Any, AsyncGenerator
8
+
9
+ # Import the core components from the original script
10
+ from main import RAGSystem, geocoding, calculate_additional_transport_times
11
+
12
+ class ProfessionalTransportationPlanner:
13
+ def __init__(self):
14
+ # Global route storage
15
+ self.global_route_data = {
16
+ "origin": None,
17
+ "destination": None,
18
+ "routes": {}
19
+ }
20
+
21
+ # Comprehensive transport mode details
22
+ self.transport_modes = {
23
+ "car": {
24
+ "name": "Car",
25
+ "icon": "πŸš—",
26
+ "avg_speed": 60, # km/h
27
+ "cost_per_km": 0.15, # Estimated fuel and maintenance
28
+ "environmental_impact": "High",
29
+ "best_for": ["Flexibility", "Long distances", "Group travel"]
30
+ },
31
+ "bike": {
32
+ "name": "Bicycle",
33
+ "icon": "🚲",
34
+ "avg_speed": 15, # km/h
35
+ "cost_per_km": 0.01, # Minimal maintenance
36
+ "environmental_impact": "Very Low",
37
+ "best_for": ["Short distances", "Exercise", "Urban areas"]
38
+ },
39
+ "foot": {
40
+ "name": "Walking",
41
+ "icon": "🚢",
42
+ "avg_speed": 5, # km/h
43
+ "cost_per_km": 0,
44
+ "environmental_impact": "Zero",
45
+ "best_for": ["Short distances", "Exploration", "Health"]
46
+ },
47
+ "bus": {
48
+ "name": "Public Bus",
49
+ "icon": "🚌",
50
+ "avg_speed": 25, # km/h
51
+ "cost_per_km": 0.10, # Public transit fare
52
+ "environmental_impact": "Low",
53
+ "best_for": ["Budget travel", "Urban commuting", "No parking hassles"]
54
+ },
55
+ "airplane": {
56
+ "name": "Airplane",
57
+ "icon": "✈️",
58
+ "avg_speed": 800, # km/h
59
+ "cost_per_km": 0.50, # Varies widely
60
+ "environmental_impact": "High",
61
+ "best_for": ["Long distances", "International travel", "Time-sensitive trips"]
62
+ }
63
+ }
64
+
65
+ # Initialize RAG system
66
+ self.rag_system = RAGSystem()
67
+
68
+ def geocode_location(self, location: str) -> Dict[str, Any]:
69
+ """Advanced geocoding with comprehensive details"""
70
+ try:
71
+ key = os.getenv("TRACE") # GraphHopper API key
72
+ status, lat, lng, formatted_loc = geocoding(location, key)
73
+
74
+ if status != 200 or lat == "null" or lng == "null":
75
+ return {
76
+ "status": "error",
77
+ "message": f"Could not geocode location: {location}",
78
+ "details": "Possible reasons: Incomplete address, typo, or unsupported location"
79
+ }
80
+
81
+ return {
82
+ "status": "success",
83
+ "location": formatted_loc,
84
+ "latitude": lat,
85
+ "longitude": lng,
86
+ }
87
+ except Exception as e:
88
+ return {
89
+ "status": "error",
90
+ "message": f"Geocoding error: {str(e)}",
91
+ "details": "Unable to process location. Please check the address and try again."
92
+ }
93
+
94
+ def plan_route(self, origin: str, destination: str) -> str:
95
+ """Comprehensive route planning with detailed insights"""
96
+ try:
97
+ # Geocode locations
98
+ orig_data = self.geocode_location(origin)
99
+ dest_data = self.geocode_location(destination)
100
+
101
+ if orig_data["status"] == "error" or dest_data["status"] == "error":
102
+ return f"""
103
+ <div class="route-error-card">
104
+ <h2>🚨 Route Planning Error</h2>
105
+ <p><strong>Origin:</strong> {orig_data.get('message', 'Unknown origin error')}</p>
106
+ <p><strong>Destination:</strong> {dest_data.get('message', 'Unknown destination error')}</p>
107
+ <p>Please verify your locations and try again.</p>
108
+ </div>
109
+ """
110
+
111
+ # Reset global route data
112
+ self.global_route_data = {
113
+ "origin": orig_data["location"],
114
+ "destination": dest_data["location"],
115
+ "routes": {}
116
+ }
117
+
118
+ # Supported transport modes
119
+ transport_modes = ["car", "bike", "foot", "bus", "airplane"]
120
+
121
+ # HTML for route summary
122
+ route_summary = f"""
123
+ <div class="route-info-card">
124
+ <h2>πŸ—ΊοΈ Route Analysis</h2>
125
+ <div class="route-details">
126
+ <p><strong>From:</strong> {orig_data['location']}</p>
127
+ <p><strong>To:</strong> {dest_data['location']}</p>
128
+ </div>
129
+ </div>
130
+
131
+ <div class="route-comparison-card">
132
+ <h3>🚦 Transport Mode Comparison</h3>
133
+ <div class="route-table-container">
134
+ <table class="route-table">
135
+ <thead>
136
+ <tr>
137
+ <th>Mode</th>
138
+ <th>Duration</th>
139
+ <th>Distance</th>
140
+ <th>Cost Est.</th>
141
+ <th>Environmental Impact</th>
142
+ </tr>
143
+ </thead>
144
+ <tbody>
145
+ """
146
+
147
+ # Route URL and key
148
+ route_url = "https://graphhopper.com/api/1/route?"
149
+ key = os.getenv("TRACE")
150
+
151
+ # Process each transport mode
152
+ for vehicle in transport_modes:
153
+ # Construct URL for API-supported modes
154
+ if vehicle in ["car", "bike", "foot"]:
155
+ op = f"&point={orig_data['latitude']}%2C{orig_data['longitude']}"
156
+ dp = f"&point={dest_data['latitude']}%2C{dest_data['longitude']}"
157
+
158
+ paths_url = route_url + urllib.parse.urlencode({"key": key, "vehicle": vehicle}) + op + dp
159
+ paths_response = requests.get(paths_url)
160
+
161
+ if paths_response.status_code == 200:
162
+ paths_data = paths_response.json()
163
+
164
+ if "paths" in paths_data and len(paths_data["paths"]) > 0:
165
+ path = paths_data["paths"][0]
166
+ miles = path["distance"] / 1000 / 1.61
167
+ km = path["distance"] / 1000
168
+ sec = int(path["time"] / 1000 % 60)
169
+ min = int(path["time"] / 1000 / 60 % 60)
170
+ hr = int(path["time"] / 1000 / 60 / 60)
171
+
172
+ # Estimated cost calculation
173
+ mode_details = self.transport_modes.get(vehicle, {})
174
+ est_cost = km * mode_details.get('cost_per_km', 0)
175
+
176
+ route_summary += f"""
177
+ <tr>
178
+ <td>
179
+ {mode_details.get('icon', '')}
180
+ {mode_details.get('name', vehicle.capitalize())}
181
+ </td>
182
+ <td>{hr:02d}:{min:02d}:{sec:02d}</td>
183
+ <td>{km:.1f} km</td>
184
+ <td>${est_cost:.2f}</td>
185
+ <td>{mode_details.get('environmental_impact', 'Unknown')}</td>
186
+ </tr>
187
+ """
188
+
189
+ # Store route data
190
+ self.global_route_data["routes"][vehicle] = {
191
+ "duration": f"{hr:02d}:{min:02d}:{sec:02d}",
192
+ "distance_km": km,
193
+ "distance_miles": miles,
194
+ "estimated_cost": est_cost
195
+ }
196
+
197
+ # Handle estimated modes (bus, airplane)
198
+ else:
199
+ # Estimate based on car route
200
+ base_car_distance = 100 # Default km
201
+ duration_minutes = calculate_additional_transport_times(base_car_distance, vehicle)
202
+
203
+ hr, min_remainder = divmod(duration_minutes, 60)
204
+ min, sec = divmod(min_remainder * 60, 60)
205
+ miles = base_car_distance / 1.61
206
+
207
+ # Estimated cost calculation
208
+ mode_details = self.transport_modes.get(vehicle, {})
209
+ est_cost = base_car_distance * mode_details.get('cost_per_km', 0)
210
+
211
+ route_summary += f"""
212
+ <tr>
213
+ <td>
214
+ {mode_details.get('icon', '')}
215
+ {mode_details.get('name', vehicle.capitalize())}
216
+ <small>(Estimated)</small>
217
+ </td>
218
+ <td>{int(hr):02d}:{int(min):02d}:{int(sec):02d}</td>
219
+ <td>{base_car_distance:.1f} km</td>
220
+ <td>${est_cost:.2f}</td>
221
+ <td>{mode_details.get('environmental_impact', 'Unknown')}</td>
222
+ </tr>
223
+ """
224
+
225
+ # Store estimated route data
226
+ self.global_route_data["routes"][vehicle] = {
227
+ "duration": f"{int(hr):02d}:{int(min):02d}:{int(sec):02d}",
228
+ "distance_km": base_car_distance,
229
+ "distance_miles": miles,
230
+ "estimated_cost": est_cost,
231
+ "estimated": True
232
+ }
233
+
234
+ route_summary += """
235
+ </tbody>
236
+ </table>
237
+ </div>
238
+ </div>
239
+ """
240
+
241
+ return route_summary
242
+
243
+ except Exception as e:
244
+ error_trace = traceback.format_exc()
245
+ print(f"Comprehensive route planning error: {error_trace}")
246
+ return f"""
247
+ <div class="route-error-card">
248
+ <h2>🚨 Unexpected Error</h2>
249
+ <p>An unexpected error occurred during route planning.</p>
250
+ <p>Error Details: {str(e)}</p>
251
+ <p>Please try again or contact support.</p>
252
+ </div>
253
+ """
254
+
255
+ async def stream_route_query(self, query: str, transport_preference: Optional[str] = None) -> AsyncGenerator[str, None]:
256
+ """Streaming AI-powered route query with enhanced context"""
257
+ try:
258
+ # Check if routes have been planned
259
+ if not self.global_route_data.get("origin") or not self.global_route_data.get("destination"):
260
+ yield """
261
+ <div class="route-warning-card">
262
+ <h2>⚠️ Query Limitation</h2>
263
+ <p>Please plan a route first before asking about it.</p>
264
+ <p>Use the 'Route Planning' tab to create a route, then ask questions.</p>
265
+ </div>
266
+ """
267
+ return
268
+
269
+ # Enhance query with route context
270
+ context = f"Route from {self.global_route_data['origin']} to {self.global_route_data['destination']}"
271
+ full_query = f"{query} ({context})"
272
+
273
+ # Streaming response simulation (replace with actual streaming implementation)
274
+ # Here we'll use the original query method but simulate streaming
275
+ response = self.rag_system.query(full_query, transport_preference)
276
+
277
+ # Start the response
278
+ yield f"""
279
+ <div class="ai-response-card">
280
+ <h3>πŸ€– AI Route Assistant</h3>
281
+ <div class="ai-response-content">
282
+ """
283
+
284
+ # Simulate streaming by yielding chunks of text
285
+ words = response["answer"].split()
286
+ current_chunk = ""
287
+ for word in words:
288
+ current_chunk += word + " "
289
+ # Yield chunks to simulate streaming
290
+ if len(current_chunk.split()) % 5 == 0:
291
+ yield f"{current_chunk}"
292
+ await asyncio.sleep(0.1) # Small delay to simulate streaming
293
+
294
+ # Yield any remaining text
295
+ if current_chunk:
296
+ yield f"{current_chunk}"
297
+
298
+ # Close the response div
299
+ yield """
300
+ </div>
301
+ <div class="ai-response-tip">
302
+ <p>πŸ’‘ Tip: This response is AI-generated based on available route information.</p>
303
+ </div>
304
+ </div>
305
+ """
306
+
307
+ except Exception as e:
308
+ error_trace = traceback.format_exc()
309
+ print(f"Route query error: {error_trace}")
310
+ yield f"""
311
+ <div class="route-error-card">
312
+ <h2>🚨 Query Error</h2>
313
+ <p>Unable to process your query.</p>
314
+ <p>Error Details: {str(e)}</p>
315
+ </div>
316
+ """
317
+
318
+ def create_gradio_interface():
319
+ """Create a professional Gradio interface with enhanced styling"""
320
+ # Comprehensive CSS for professional design
321
+ css = """
322
+ /* Global Styling */
323
+ .gradio-container {
324
+ font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;
325
+ background-color: #0f1429;
326
+ color: #e0e0e0;
327
+ max-width: 1200px;
328
+ margin: 0 auto;
329
+ padding: 20px;
330
+ }
331
+
332
+ /* Typography */
333
+ h1, h2, h3 {
334
+ color: #ffffff;
335
+ font-weight: 600;
336
+ margin-bottom: 15px;
337
+ }
338
+
339
+ /* Input Styling */
340
+ .gradio-container .form .input-text {
341
+ background-color: #1a2138;
342
+ border: 1px solid #2c3e50;
343
+ color: #ffffff;
344
+ padding: 10px;
345
+ border-radius: 6px;
346
+ transition: all 0.3s ease;
347
+ }
348
+
349
+ .gradio-container .form .input-text:focus {
350
+ border-color: #3498db;
351
+ box-shadow: 0 0 10px rgba(52, 152, 219, 0.3);
352
+ }
353
+
354
+ /* Button Styling */
355
+ .gradio-container .button {
356
+ background-color: #3498db;
357
+ color: white;
358
+ border: none;
359
+ padding: 10px 20px;
360
+ border-radius: 6px;
361
+ font-weight: 600;
362
+ transition: all 0.3s ease;
363
+ }
364
+
365
+ .gradio-container .button:hover {
366
+ background-color: #2980b9;
367
+ transform: translateY(-2px);
368
+ }
369
+
370
+ /* Card Styling */
371
+ .route-info-card,
372
+ .route-comparison-card,
373
+ .ai-response-card,
374
+ .route-error-card,
375
+ .route-warning-card {
376
+ background-color: #1a2138;
377
+ border-radius: 10px;
378
+ padding: 20px;
379
+ margin-bottom: 20px;
380
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
381
+ }
382
+
383
+ .route-table {
384
+ width: 100%;
385
+ border-collapse: separate;
386
+ border-spacing: 0;
387
+ }
388
+
389
+ .route-table th {
390
+ background-color: #2c3e50;
391
+ color: #ecf0f1;
392
+ padding: 12px;
393
+ text-align: left;
394
+ border-bottom: 2px solid #34495e;
395
+ }
396
+
397
+ .route-table td {
398
+ padding: 12px;
399
+ border-bottom: 1px solid #2c3e50;
400
+ color: #bdc3c7;
401
+ }
402
+
403
+ .route-table tr:hover {
404
+ background-color: #2c3e50;
405
+ transition: background-color 0.3s ease;
406
+ }
407
+
408
+ /* AI Response Styling */
409
+ .ai-response-content {
410
+ background-color: #252b42;
411
+ padding: 15px;
412
+ border-radius: 8px;
413
+ line-height: 1.6;
414
+ color: #e0e0e0;
415
+ }
416
+
417
+ .ai-response-tip {
418
+ margin-top: 15px;
419
+ font-size: 0.9em;
420
+ color: #7f8c8d;
421
+ font-style: italic;
422
+ }
423
+
424
+ /* Error and Warning Cards */
425
+ .route-error-card {
426
+ border-left: 5px solid #e74c3c;
427
+ }
428
+
429
+ .route-warning-card {
430
+ border-left: 5px solid #f39c12;
431
+ }
432
+
433
+ /* Responsive Design */
434
+ @media (max-width: 768px) {
435
+ .gradio-container {
436
+ padding: 10px;
437
+ }
438
+
439
+ .route-table {
440
+ font-size: 0.9em;
441
+ }
442
+
443
+ .route-table th, .route-table td {
444
+ padding: 8px;
445
+ }
446
+ }
447
+ """
448
+
449
+ # Initialize the planner
450
+ planner = ProfessionalTransportationPlanner()
451
+
452
+ # Create Gradio interface
453
+ with gr.Blocks(css=css, title="Professional Transportation Planner", theme=gr.themes.Soft()) as demo:
454
+ # Header with professional branding
455
+ gr.Markdown("""
456
+ # 🌐 Professional Transportation Planner
457
+ ## Intelligent Route Analysis & AI-Powered Navigation
458
+ """)
459
+
460
+ # Main interface layout with enhanced tabs
461
+ with gr.Tabs() as tabs:
462
+ # Route Planner Tab
463
+ with gr.Tab("Route Planner", id="route-planner"):
464
+ # Input section with improved layout
465
+ with gr.Row():
466
+ origin_input = gr.Textbox(
467
+ label="Origin Location",
468
+ placeholder="Enter precise starting point (e.g., 123 Main St, New York, NY)",
469
+ info="🌍 Provide a detailed, specific location for best results",
470
+ scale=1
471
+ )
472
+ destination_input = gr.Textbox(
473
+ label="Destination Location",
474
+ placeholder="Enter precise destination (e.g., 456 Elm St, Boston, MA)",
475
+ info="🏁 Enter a complete, accurate address",
476
+ scale=1
477
+ )
478
+
479
+ # Advanced options row
480
+ with gr.Row():
481
+ # Plan Route Button with enhanced styling
482
+ plan_route_btn = gr.Button(
483
+ "πŸ“ Analyze Route",
484
+ variant="primary"
485
+ )
486
+
487
+ # Results section
488
+ route_output = gr.HTML(label="πŸ—ΊοΈ Detailed Route Analysis")
489
+
490
+ # AI Insights Tab
491
+ with gr.Tab("Route Insights", id="route-insights"):
492
+ # Query input with context-aware placeholder
493
+ query_input = gr.Textbox(
494
+ label="Ask AI Route Assistant",
495
+ placeholder="What specific insights do you need about your planned route?",
496
+ info="πŸ’‘ Ask questions about transportation, local info, or route details"
497
+ )
498
+
499
+ # Transport mode preference with icons
500
+ transport_pref = gr.Dropdown(
501
+ ["car", "bike", "foot", "bus", "airplane"],
502
+ label="πŸ“‹ Preferred Transport Mode (Optional)",
503
+ info="Narrow down AI insights to a specific mode of transport"
504
+ )
505
+
506
+ # Insights Button with professional styling
507
+ query_btn = gr.Button(
508
+ "πŸ€– Get AI Insights",
509
+ variant="primary"
510
+ )
511
+
512
+ # Streaming AI Response Output
513
+ query_output = gr.HTML(label="🧠 AI Route Insights")
514
+
515
+ # Event Handlers with async support
516
+ plan_route_btn.click(
517
+ fn=planner.plan_route,
518
+ inputs=[origin_input, destination_input],
519
+ outputs=route_output
520
+ )
521
+
522
+ query_btn.click(
523
+ fn=planner.stream_route_query,
524
+ inputs=[query_input, transport_pref],
525
+ outputs=query_output,
526
+ api_name="route_insights"
527
+ )
528
+
529
+ return demo
530
+
531
+ def main():
532
+ # Create and launch the interface with additional configurations
533
+ demo = create_gradio_interface()
534
+ demo.launch(
535
+ server_name="0.0.0.0",
536
+ server_port=8000,
537
+ share=True,
538
+ debug=True # Enable debug mode for development
539
+ )
540
+
541
+ if __name__ == "__main__":
542
+ main()