mashrur950's picture
Initial commit: FleetMind MCP with GitHub Actions auto-sync
d69447e
raw
history blame
17.3 kB
"""
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
# ============================================
# 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",
"[email protected]",
"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, conversation_state):
"""
Handle chat message from user
Args:
message: User's message
conversation_state: ConversationManager instance
Returns:
Updated chatbot history, tool display, conversation state
"""
if not message.strip():
return conversation_state.get_formatted_history(), conversation_state.get_tool_calls(), conversation_state
# Process message through chat engine
response, tool_calls = chat_engine.process_message(message, conversation_state)
# Return updated UI
return conversation_state.get_formatted_history(), conversation_state.get_tool_calls(), conversation_state
def reset_conversation():
"""Reset conversation to start fresh"""
new_conversation = ConversationManager()
# Add welcome message
welcome = chat_engine.get_welcome_message()
new_conversation.add_message("assistant", welcome)
return (
new_conversation.get_formatted_history(),
[], # Clear tool calls
new_conversation
)
def get_initial_chat():
"""Get initial chat state with welcome message"""
conversation = ConversationManager()
welcome = chat_engine.get_welcome_message()
conversation.add_message("assistant", welcome)
return conversation.get_formatted_history(), [], conversation
# ============================================
# 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
conversation_state = gr.State(value=None)
# Initialize with welcome message
chatbot.value, tool_display.value, conversation_state.value = get_initial_chat()
# Event handlers
def send_message(message, conv_state):
"""Handle send button click"""
chat_history, tools, new_state = handle_chat_message(message, conv_state)
return chat_history, tools, new_state, "" # Clear input
send_btn.click(
fn=send_message,
inputs=[msg_input, conversation_state],
outputs=[chatbot, tool_display, conversation_state, msg_input]
)
msg_input.submit(
fn=send_message,
inputs=[msg_input, conversation_state],
outputs=[chatbot, tool_display, conversation_state, msg_input]
)
clear_btn.click(
fn=reset_conversation,
outputs=[chatbot, tool_display, conversation_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
)