""" FleetMind MCP - Gradio Web Interface Simple dashboard to interact with the MCP server and database """ import sys from pathlib import Path # Add parent directory to path sys.path.insert(0, str(Path(__file__).parent.parent)) import gradio as gr from database.connection import execute_query, execute_write, test_connection from datetime import datetime, timedelta import json # Import chat functionality from chat.chat_engine import ChatEngine from chat.conversation import ConversationManager from chat.geocoding import GeocodingService import uuid # Global session storage (simple in-memory store) SESSIONS = {} # ============================================ # DATABASE FUNCTIONS # ============================================ def get_database_status(): """Check if database is connected""" try: if test_connection(): return "✅ Connected", "success" else: return "❌ Disconnected", "error" except Exception as e: return f"❌ Error: {str(e)}", "error" def get_orders_summary(): """Get summary of orders by status""" try: query = """ SELECT status, COUNT(*) as count FROM orders GROUP BY status ORDER BY count DESC """ results = execute_query(query) if not results: return "No orders in database" summary = "**Orders Summary:**\n\n" for row in results: summary += f"- {row['status'].upper()}: {row['count']}\n" return summary except Exception as e: return f"Error: {str(e)}" def get_all_orders(): """Get all orders from database""" try: query = """ SELECT order_id, customer_name, delivery_address, status, priority, created_at FROM orders ORDER BY created_at DESC LIMIT 50 """ results = execute_query(query) if not results: return [["No orders found", "", "", "", "", ""]] # Convert to list of lists for Gradio dataframe data = [] for row in results: data.append([ row['order_id'], row['customer_name'], row['delivery_address'][:50] + "..." if len(row['delivery_address']) > 50 else row['delivery_address'], row['status'], row['priority'], str(row['created_at']) ]) return data except Exception as e: return [[f"Error: {str(e)}", "", "", "", "", ""]] def create_sample_order(): """Create a sample order for testing""" try: now = datetime.now() order_id = f"ORD-{now.strftime('%Y%m%d%H%M%S')}" query = """ INSERT INTO orders ( order_id, customer_name, customer_phone, customer_email, delivery_address, delivery_lat, delivery_lng, time_window_start, time_window_end, priority, weight_kg, status ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """ params = ( order_id, "Sample Customer", "+1-555-0100", "sample@example.com", "456 Sample Street, San Francisco, CA 94103", 37.7749, -122.4194, now + timedelta(hours=2), now + timedelta(hours=6), "standard", 10.5, "pending" ) execute_write(query, params) return f"✅ Order {order_id} created successfully!", get_all_orders() except Exception as e: return f"❌ Error: {str(e)}", get_all_orders() def search_orders(search_term): """Search orders by customer name or order ID""" try: if not search_term: return get_all_orders() query = """ SELECT order_id, customer_name, delivery_address, status, priority, created_at FROM orders WHERE order_id ILIKE %s OR customer_name ILIKE %s ORDER BY created_at DESC LIMIT 50 """ search_pattern = f"%{search_term}%" results = execute_query(query, (search_pattern, search_pattern)) if not results: return [["No matching orders found", "", "", "", "", ""]] data = [] for row in results: data.append([ row['order_id'], row['customer_name'], row['delivery_address'][:50] + "..." if len(row['delivery_address']) > 50 else row['delivery_address'], row['status'], row['priority'], str(row['created_at']) ]) return data except Exception as e: return [[f"Error: {str(e)}", "", "", "", "", ""]] # ============================================ # CHAT FUNCTIONS # ============================================ # Initialize chat engine and geocoding service chat_engine = ChatEngine() geocoding_service = GeocodingService() def get_api_status(): """Get API status for chat""" # Get full status for all providers full_status = chat_engine.get_full_status() selected = full_status["selected"] claude_status = full_status["claude"]["status"] gemini_status = full_status["gemini"]["status"] geocoding_status = geocoding_service.get_status() # Mark selected provider claude_marker = "🎯 **ACTIVE** - " if selected == "anthropic" else "" gemini_marker = "🎯 **ACTIVE** - " if selected == "gemini" else "" return f"""### API Status **AI Provider:** **Claude (Anthropic):** {claude_marker}{claude_status} **Gemini (Google):** {gemini_marker}{gemini_status} *💡 Switch provider by setting `AI_PROVIDER=anthropic` or `AI_PROVIDER=gemini` in .env* --- **Geocoding:** **HERE Maps:** {geocoding_status} """ def handle_chat_message(message, session_id): """ Handle chat message from user Args: message: User's message session_id: Session identifier string Returns: Updated chatbot history, tool display, session_id """ # Get or create conversation for this session if session_id not in SESSIONS: SESSIONS[session_id] = ConversationManager() welcome = chat_engine.get_welcome_message() SESSIONS[session_id].add_message("assistant", welcome) conversation = SESSIONS[session_id] if not message.strip(): return conversation.get_formatted_history(), conversation.get_tool_calls(), session_id # Process message through chat engine response, tool_calls = chat_engine.process_message(message, conversation) # Return updated UI return conversation.get_formatted_history(), conversation.get_tool_calls(), session_id def reset_conversation(session_id): """Reset conversation to start fresh""" # Create new session ID for fresh conversation new_session_id = str(uuid.uuid4()) # Create new conversation for this session new_conversation = ConversationManager() # Add welcome message welcome = chat_engine.get_welcome_message() new_conversation.add_message("assistant", welcome) # Store in sessions SESSIONS[new_session_id] = new_conversation return ( new_conversation.get_formatted_history(), [], # Clear tool calls new_session_id ) def get_initial_chat(): """Get initial chat state with welcome message""" # Create new session ID session_id = str(uuid.uuid4()) # Create conversation for this session conversation = ConversationManager() welcome = chat_engine.get_welcome_message() conversation.add_message("assistant", welcome) # Store in sessions SESSIONS[session_id] = conversation return conversation.get_formatted_history(), [], session_id # ============================================ # MCP SERVER INFO # ============================================ def get_mcp_server_info(): """Get MCP server information""" mcp_info = { "server_name": "dispatch-coordinator-mcp", "version": "1.0.0", "status": "Ready", "tools": [ "route_optimizer", "geocoder", "weather_monitor", "traffic_checker", "distance_matrix", "order_manager" ] } return f""" ### MCP Server Information **Name:** {mcp_info['server_name']} **Version:** {mcp_info['version']} **Status:** 🟢 {mcp_info['status']} **Available Tools ({len(mcp_info['tools'])}):** {chr(10).join([f"- {tool}" for tool in mcp_info['tools']])} """ # ============================================ # GRADIO INTERFACE # ============================================ def create_interface(): """Create the Gradio interface""" with gr.Blocks(theme=gr.themes.Soft(), title="FleetMind MCP Dashboard") as app: gr.Markdown("# 🚚 FleetMind MCP Dashboard") gr.Markdown("*Autonomous Dispatch Coordinator powered by MCP and PostgreSQL*") with gr.Tabs(): # ========================================== # TAB 1: OVERVIEW # ========================================== with gr.Tab("📊 Overview"): with gr.Row(): with gr.Column(scale=1): gr.Markdown("### System Status") db_status = gr.Textbox( label="Database Connection", value=get_database_status()[0], interactive=False ) refresh_status_btn = gr.Button("🔄 Refresh Status", size="sm") gr.Markdown("---") orders_summary = gr.Markdown(get_orders_summary()) with gr.Column(scale=2): mcp_info = gr.Markdown(get_mcp_server_info()) # Refresh status button action refresh_status_btn.click( fn=lambda: get_database_status()[0], outputs=db_status ) # ========================================== # TAB 2: ORDERS MANAGEMENT # ========================================== with gr.Tab("📦 Orders"): gr.Markdown("### Orders Management") with gr.Row(): search_box = gr.Textbox( placeholder="Search by Order ID or Customer Name...", label="Search Orders", scale=3 ) search_btn = gr.Button("🔍 Search", scale=1) create_btn = gr.Button("➕ Create Sample Order", scale=1, variant="primary") create_result = gr.Textbox(label="Result", visible=False) orders_table = gr.Dataframe( headers=["Order ID", "Customer", "Delivery Address", "Status", "Priority", "Created At"], datatype=["str", "str", "str", "str", "str", "str"], label="Orders List", value=get_all_orders(), interactive=False, wrap=True ) refresh_orders_btn = gr.Button("🔄 Refresh Orders") # Button actions create_btn.click( fn=create_sample_order, outputs=[create_result, orders_table] ).then( fn=lambda: gr.update(visible=True), outputs=create_result ).then( fn=lambda: get_orders_summary(), outputs=orders_summary ) search_btn.click( fn=search_orders, inputs=search_box, outputs=orders_table ) search_box.submit( fn=search_orders, inputs=search_box, outputs=orders_table ) refresh_orders_btn.click( fn=get_all_orders, outputs=orders_table ).then( fn=lambda: get_orders_summary(), outputs=orders_summary ) # ========================================== # TAB 3: AI CHAT # ========================================== with gr.Tab("💬 Chat"): provider_name = chat_engine.get_provider_name() model_name = chat_engine.get_model_name() gr.Markdown(f"### AI Order Assistant") gr.Markdown(f"*Powered by: **{provider_name}** ({model_name})*") # API Status api_status = gr.Markdown(get_api_status()) # Chat interface chatbot = gr.Chatbot( label="Order Assistant", height=500, type="messages", show_copy_button=True ) msg_input = gr.Textbox( placeholder="e.g., 'Create an order for John Doe at 123 Main St, deliver by 5 PM'", label="Your Message", lines=2 ) with gr.Row(): send_btn = gr.Button("📤 Send", variant="primary", scale=2) clear_btn = gr.Button("🔄 Clear Chat", scale=1) # Tool usage display (reasoning transparency) with gr.Accordion("🔧 Tool Usage (AI Reasoning)", open=False): gr.Markdown("See what tools the AI is using behind the scenes:") tool_display = gr.JSON(label="Tools Called") # Conversation state - use session ID instead of complex object session_id_state = gr.State(value=None) # Initialize with welcome message chatbot.value, tool_display.value, session_id_state.value = get_initial_chat() # Event handlers def send_message(message, sess_id): """Handle send button click""" chat_history, tools, new_sess_id = handle_chat_message(message, sess_id) return chat_history, tools, new_sess_id, "" # Clear input send_btn.click( fn=send_message, inputs=[msg_input, session_id_state], outputs=[chatbot, tool_display, session_id_state, msg_input] ) msg_input.submit( fn=send_message, inputs=[msg_input, session_id_state], outputs=[chatbot, tool_display, session_id_state, msg_input] ) clear_btn.click( fn=reset_conversation, inputs=[session_id_state], outputs=[chatbot, tool_display, session_id_state] ) # ========================================== # TAB 4: MCP TOOLS (Coming Soon) # ========================================== with gr.Tab("🔧 MCP Tools"): gr.Markdown("### MCP Tools") gr.Markdown("*MCP tool integration coming soon...*") gr.Markdown(""" Available tools: - **route_optimizer** - Optimize delivery routes - **geocoder** - Convert addresses to coordinates - **weather_monitor** - Check weather conditions - **traffic_checker** - Monitor traffic conditions - **distance_matrix** - Calculate distances - **order_manager** - Manage orders via MCP """) # ========================================== # TAB 5: DATABASE INFO # ========================================== with gr.Tab("💾 Database"): gr.Markdown("### Database Information") db_info = gr.Markdown(f""" **Database:** PostgreSQL **Name:** fleetmind **Host:** localhost **Port:** 5432 **Tables:** - orders (26 columns) - drivers (coming soon) - assignments (coming soon) - exceptions (coming soon) """) test_db_btn = gr.Button("🧪 Test Connection", variant="primary") test_result = gr.Textbox(label="Test Result", interactive=False) test_db_btn.click( fn=lambda: "✅ Connection successful!" if test_connection() else "❌ Connection failed", outputs=test_result ) gr.Markdown("---") gr.Markdown("*FleetMind MCP v1.0.0 - Built with Gradio, PostgreSQL, and FastMCP*") return app # ============================================ # MAIN # ============================================ if __name__ == "__main__": print("=" * 60) print("FleetMind MCP - Starting Gradio Server") print("=" * 60) # Check database connection print("\nChecking database connection...") if test_connection(): print("✅ Database connected") else: print("❌ Database connection failed") print("Please check your .env file and PostgreSQL server") print("\nStarting Gradio interface...") print("=" * 60) # Create and launch the interface app = create_interface() app.launch( server_name="0.0.0.0", # Allow external connections for HF Spaces server_port=7860, share=False, show_error=True, show_api=False # Disable API docs to avoid schema parsing bug )