LifeFlow-AI / app.py
Marco310's picture
update app.py size
a328f28
raw
history blame
43.7 kB
"""
LifeFlow AI - v11 English + Compact Layout
✅ English version
✅ Compact layout (horizontal on widescreen)
✅ Responsive design
✅ Optimized for HuggingFace Spaces
"""
import gradio as gr
import plotly.graph_objects as go
from datetime import datetime, time
import json
from typing import Dict, List, Optional, Tuple
import time as time_module
class LifeFlowInteractive:
"""LifeFlow AI Interactive Demo v11"""
def __init__(self):
self.team = None
self.current_step = 0
self.task_list = []
self.poi_results = []
self.chat_history = []
self.reasoning_messages = []
self.planning_completed = False
self.agent_raw_output = ""
# Settings
self.settings = {
'google_maps_api_key': '',
'openweather_api_key': '',
'anthropic_api_key': '',
'model': 'claude-sonnet-4-20250514'
}
# Agent info
self.agents_info = {
"planner": {
"name": "Planner",
"avatar": "👔",
"role": "Chief Planning Officer",
"color": "#4A90E2"
},
"scout": {
"name": "Scout",
"avatar": "🕵️",
"role": "Location Scout Expert",
"color": "#50C878"
},
"optimizer": {
"name": "Optimizer",
"avatar": "🤖",
"role": "AI Optimization Engine",
"color": "#F5A623"
},
"validator": {
"name": "Validator",
"avatar": "🛡️",
"role": "Quality Assurance Expert",
"color": "#7ED321"
},
"weather": {
"name": "Weather",
"avatar": "🌈",
"role": "Weather Analyst",
"color": "#50E3C2"
},
"traffic": {
"name": "Traffic",
"avatar": "🚗",
"role": "Traffic Planner",
"color": "#FF6B6B"
}
}
def create_agent_card(self, agent_key: str, status: str = "idle", message: str = ""):
"""Create Agent card (compact version)"""
agent = self.agents_info[agent_key]
status_icons = {
"idle": "⚪",
"thinking": "💭",
"using_tool": "🔧",
"working": "⚙️",
"completed": "✅",
"waiting": "⏸️",
"error": "❌"
}
icon = status_icons.get(status, "⚪")
breathing_class = "breathing" if status in ["working", "using_tool"] else ""
return f"""
<div class="{breathing_class}" style="
background: var(--background-fill-secondary);
border-left: 3px solid {agent['color']};
border-radius: 6px;
padding: 10px;
margin: 6px 0;
">
<div style="display: flex; align-items: center; gap: 8px;">
<div style="font-size: 24px;">{agent['avatar']}</div>
<div style="flex-grow: 1;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<strong style="color: var(--body-text-color); font-size: 14px;">
{agent['name']}
</strong>
<span style="font-size: 16px;">{icon}</span>
</div>
{f'<div style="color: var(--body-text-color); opacity: 0.7; font-size: 12px; margin-top: 4px;">{message}</div>' if message else ''}
</div>
</div>
</div>
"""
def add_reasoning_message(self, agent: str, message: str, msg_type: str = "info"):
"""Add reasoning message"""
icon_map = {
"thinking": "💭",
"tool": "🔧",
"success": "✅",
"info": "ℹ️",
"warning": "⚠️",
"error": "❌"
}
icon = icon_map.get(msg_type, "ℹ️")
agent_name = self.agents_info.get(agent, {}).get("name", agent)
self.reasoning_messages.append({
"agent": agent_name,
"message": message,
"icon": icon,
"timestamp": datetime.now().strftime("%H:%M:%S")
})
def get_reasoning_html(self) -> str:
"""Generate reasoning HTML (compact scrollable)"""
if not self.reasoning_messages:
return """
<div style="
padding: 15px;
text-align: center;
color: var(--body-text-color);
opacity: 0.6;
background: var(--background-fill-secondary);
border-radius: 6px;
">
💬 AI conversation log will appear here
</div>
"""
messages_html = ""
for msg in self.reasoning_messages:
messages_html += f"""
<div style="
padding: 8px;
margin: 6px 0;
background: var(--background-fill-secondary);
border-radius: 6px;
border-left: 2px solid #4A90E2;
">
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 4px;">
<span style="font-size: 14px;">{msg['icon']}</span>
<strong style="color: var(--body-text-color); font-size: 13px;">{msg['agent']}</strong>
<span style="color: var(--body-text-color); opacity: 0.5; font-size: 11px; margin-left: auto;">
{msg['timestamp']}
</span>
</div>
<div style="color: var(--body-text-color); opacity: 0.85; padding-left: 20px; font-size: 12px;">
{msg['message']}
</div>
</div>
"""
return f"""
<div style="
max-height: 400px;
overflow-y: auto;
padding: 10px;
background: var(--background-fill-primary);
border-radius: 6px;
border: 1px solid var(--border-color-primary);
">
{messages_html}
</div>
"""
def create_task_list_ui(self, tasks: List[Dict]) -> str:
"""Create task list UI (compact)"""
if not tasks:
return "<div style='color: var(--body-text-color);'>No tasks yet</div>"
priority_colors = {
"HIGH": "#FF6B6B",
"MEDIUM": "#F5A623",
"LOW": "#7ED321"
}
category_icons = {
"medical": "🏥",
"shopping": "🛒",
"postal": "📮",
"dining": "🍽️",
"financial": "🏦",
"other": "📋"
}
html = "<div style='display: flex; flex-direction: column; gap: 8px;'>"
for i, task in enumerate(tasks, 1):
priority = task.get('priority', 'MEDIUM')
category = task.get('category', 'other')
icon = category_icons.get(category, "📋")
color = priority_colors.get(priority, "#7ED321")
time_window = task.get('time_window', 'Anytime')
html += f"""
<div style="
background: var(--background-fill-secondary);
border-left: 3px solid {color};
border-radius: 6px;
padding: 10px;
">
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 4px;">
<span style="font-size: 20px;">{icon}</span>
<strong style="color: var(--body-text-color); font-size: 13px;">
Task {i}: {task['description']}
</strong>
</div>
<div style="color: var(--body-text-color); opacity: 0.7; font-size: 11px;">
<span style="background: {color}; color: white; padding: 2px 6px; border-radius: 3px; margin-right: 6px;">
{priority}
</span>
<span>⏰ {time_window}</span>
</div>
</div>
"""
html += "</div>"
return html
def create_task_summary(self, tasks: List[Dict]) -> str:
"""Create task summary (compact)"""
high_count = sum(1 for t in tasks if t.get('priority') == 'HIGH')
medium_count = sum(1 for t in tasks if t.get('priority') == 'MEDIUM')
low_count = sum(1 for t in tasks if t.get('priority') == 'LOW')
return f"""
<div style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 15px;
border-radius: 6px;
color: white;
margin-top: 10px;
">
<h4 style="margin: 0 0 10px 0; font-size: 14px;">📊 Task Summary</h4>
<div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px;">
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold;">{len(tasks)}</div>
<div style="opacity: 0.9; font-size: 11px;">Total</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold;">{high_count}</div>
<div style="opacity: 0.9; font-size: 11px;">High Priority</div>
</div>
<div style="text-align: center;">
<div style="font-size: 24px; font-weight: bold;">{medium_count + low_count}</div>
<div style="opacity: 0.9; font-size: 11px;">Med/Low</div>
</div>
</div>
</div>
"""
def create_trip_result_summary(self, result: Dict) -> str:
"""Create trip result summary (compact, left side)"""
stops = result.get('route', {}).get('stops', [])
return f"""
<div style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 8px;
color: white;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
">
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 15px;">
<span style="font-size: 36px;">🎉</span>
<div>
<h3 style="margin: 0; font-size: 18px;">Trip Planning Complete!</h3>
<p style="margin: 5px 0 0 0; opacity: 0.9; font-size: 12px;">Optimized {len(stops)} stops</p>
</div>
</div>
<div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 6px; margin-bottom: 12px;">
<h4 style="margin: 0 0 8px 0; font-size: 13px;">⏰ Schedule</h4>
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; font-size: 11px;">
<div>• Start: <strong>08:30</strong></div>
<div>• Finish: <strong>11:25</strong> ✅</div>
<div>• Deadline: <strong>15:00</strong></div>
<div>• Buffer: <strong>3h 35m</strong> 🎯</div>
</div>
</div>
<div style="background: rgba(255,255,255,0.15); padding: 12px; border-radius: 6px;">
<h4 style="margin: 0 0 8px 0; font-size: 13px;">📍 Route</h4>
<div style="font-size: 11px; line-height: 1.6;">
{' → '.join([stop['name'] for stop in stops])}
</div>
</div>
<div style="margin-top: 12px; padding: 10px; background: rgba(255,255,255,0.1); border-radius: 6px; font-size: 11px;">
💡 Check the <strong>Full Report</strong> tab for detailed analysis
</div>
</div>
"""
def create_agent_text_output(self, result: Dict) -> str:
"""Create Agent text output (for right side tab)"""
stops = result.get('route', {}).get('stops', [])
output = "=" * 80 + "\n"
output += "LIFEFLOW AI - COMPLETE TRIP PLANNING REPORT\n"
output += "=" * 80 + "\n\n"
output += "【PLANNING SUMMARY】\n"
output += f"Total Stops: {len(stops)}\n"
output += f"Start Time: 08:30\n"
output += f"Estimated Completion: 11:25\n"
output += f"Deadline: 15:00\n"
output += f"Buffer Time: 3h 35m\n\n"
output += "=" * 80 + "\n"
output += "【PLANNER AGENT ANALYSIS】\n"
output += "=" * 80 + "\n"
output += "✓ Identified 3 tasks\n"
output += "✓ Task 1: Visit hospital (Medical - HIGH)\n"
output += " - Time window: 08:00-12:00\n"
output += " - Duration: 45 minutes\n"
output += "✓ Task 2: Buy groceries (Shopping - MEDIUM)\n"
output += " - Time window: Anytime\n"
output += " - Duration: 30 minutes\n"
output += "✓ Task 3: Mail package (Postal - HIGH)\n"
output += " - Time window: 09:00-15:00\n"
output += " - Duration: 20 minutes\n\n"
output += "【TIME CONSTRAINT ANALYSIS】\n"
output += "- Hospital time window is tight, must prioritize\n"
output += "- Post office deadline is 15:00, must complete on time\n"
output += "- Supermarket time is flexible, can be used as buffer\n\n"
output += "=" * 80 + "\n"
output += "【SCOUT AGENT SEARCH RESULTS】\n"
output += "=" * 80 + "\n"
output += "✓ POI search complete\n\n"
for i, stop in enumerate(stops, 1):
output += f"Stop {i}: {stop['name']}\n"
output += f" Address: {stop['address']}\n"
output += f" Phone: {stop['phone']}\n"
output += f" Rating: {stop['rating']} ⭐\n"
output += f" Distance: {'Start' if i == 1 else f'{0.5 * i}km'}\n\n"
output += "【DISTANCE MATRIX】\n"
output += "- Start → NTU Hospital: 0.8km (15 min)\n"
output += "- Hospital → PX Mart: 0.6km (12 min)\n"
output += "- PX Mart → Post Office: 0.8km (15 min)\n\n"
output += "=" * 80 + "\n"
output += "【OPTIMIZER AGENT ROUTE OPTIMIZATION】\n"
output += "=" * 80 + "\n"
output += "✓ Using OR-Tools TSPTW solver\n\n"
output += "【OPTIMIZATION PROCESS】\n"
output += "Phase 1: Greedy Construction\n"
output += " - Initial route: Hospital → Supermarket → Post Office\n"
output += " - Total distance: 2.2km\n"
output += " - Total time: 95 minutes\n\n"
output += "Phase 2: 2-opt Improvement\n"
output += " - Attempting order swap...\n"
output += " - Current order is optimal\n"
output += " - No improvement needed\n\n"
output += "Phase 3: Time Window Validation\n"
output += " - Hospital: 09:00 ∈ [08:00, 12:00] ✓\n"
output += " - Supermarket: 10:15 ∈ [Anytime] ✓\n"
output += " - Post Office: 11:05 ∈ [09:00, 15:00] ✓\n\n"
output += "【OPTIMAL ROUTE】\n"
output += "Stop Order: Hospital → Supermarket → Post Office\n"
output += "Total Distance: 2.2km\n"
output += "Total Time: 95 minutes (travel + stops)\n"
output += "Completion: 11:25\n"
output += "Deadline Met: ✓ (3h 35m early)\n\n"
output += "=" * 80 + "\n"
output += "【VALIDATOR AGENT FEASIBILITY CHECK】\n"
output += "=" * 80 + "\n"
output += "✓ Comprehensive feasibility check passed\n\n"
output += "【CHECK ITEMS】\n"
output += "✓ Time window constraints: All satisfied\n"
output += "✓ Deadline constraint: Met (11:25 < 15:00)\n"
output += "✓ Priority handling: All HIGH tasks completed\n"
output += "✓ Business hours: All POIs open during visit\n"
output += "✓ Route continuity: No backtracking\n\n"
output += "【RISK ASSESSMENT】\n"
output += "- Risk Level: LOW\n"
output += "- Sufficient buffer for unexpected delays\n"
output += "- Hospital may have wait time, buffer allocated\n\n"
output += "【SCORE】\n"
output += "Overall Score: 98/100 (Excellent)\n"
output += " - Time Efficiency: 95/100\n"
output += " - Route Optimization: 100/100\n"
output += " - Constraint Satisfaction: 100/100\n"
output += " - Practicality: 95/100\n\n"
output += "=" * 80 + "\n"
output += "【WEATHER AGENT ANALYSIS】\n"
output += "=" * 80 + "\n"
output += "✓ Weather data obtained\n\n"
output += "【CURRENT WEATHER】\n"
output += "- Condition: Sunny ☀️\n"
output += "- Temperature: 23°C\n"
output += "- Precipitation: 10%\n"
output += "- Wind: 5 m/s\n\n"
output += "【WEATHER IMPACT】\n"
output += "- Impact on trip: None\n"
output += "- Recommendation: Good weather for travel\n\n"
output += "=" * 80 + "\n"
output += "【TRAFFIC AGENT ANALYSIS】\n"
output += "=" * 80 + "\n"
output += "✓ Traffic data analyzed\n\n"
output += "【TRAFFIC FORECAST】\n"
output += "- 08:30-09:00: Light congestion (morning rush)\n"
output += "- 09:00-11:00: Good conditions\n"
output += "- 11:00-11:30: Good conditions\n\n"
output += "【RECOMMENDED DEPARTURE】\n"
output += "- Recommended: 08:30 (as planned)\n"
output += "- Alternative: 08:15 (avoid some congestion)\n\n"
output += "【ESTIMATED TRAVEL TIME】\n"
output += "- Start → Hospital: 18 min (with congestion)\n"
output += "- Hospital → Supermarket: 12 min\n"
output += "- Supermarket → Post Office: 15 min\n"
output += "- Total Travel: 45 minutes\n\n"
output += "=" * 80 + "\n"
output += "【TEAM MODEL FINAL DECISION】\n"
output += "=" * 80 + "\n"
output += "✓ All agent reports synthesized\n\n"
output += "【DECISION RATIONALE】\n"
output += "1. Planner identified 3 tasks with clear time constraints\n"
output += "2. Scout found high-quality POIs (all rated 4.3+)\n"
output += "3. Optimizer confirmed current order is optimal\n"
output += "4. Validator verified all constraints satisfied\n"
output += "5. Weather shows good conditions, no adjustments\n"
output += "6. Traffic shows acceptable conditions, proceed as planned\n\n"
output += "【FINAL PLAN】\n"
output += "Adopt Optimizer's route order\n"
output += "Recommend departure at 08:30\n"
output += "Expected completion at 11:25\n"
output += "Safety margin: 3h 35m\n\n"
output += "【RECOMMENDATIONS】\n"
output += "1. Depart at 08:30 to ensure completion of all tasks\n"
output += "2. Hospital visit may take longer, buffer allocated\n"
output += "3. After post office, can have lunch nearby\n"
output += "4. If delayed, can skip supermarket (MEDIUM priority)\n\n"
output += "=" * 80 + "\n"
output += "Planning completed: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\n"
output += "=" * 80 + "\n"
return output
def create_map(self, result: Optional[Dict] = None) -> go.Figure:
"""Create map"""
if not result:
fig = go.Figure()
fig.update_layout(
title="Map will appear after planning",
height=500,
template="plotly_dark"
)
return fig
stops = [
{"name": "Start", "lat": 25.0330, "lng": 121.5654},
{"name": "NTU Hospital", "lat": 25.0408, "lng": 121.5318},
{"name": "PX Mart", "lat": 25.0428, "lng": 121.5298},
{"name": "Post Office", "lat": 25.0468, "lng": 121.5358}
]
fig = go.Figure()
lats = [stop['lat'] for stop in stops]
lngs = [stop['lng'] for stop in stops]
fig.add_trace(go.Scattermapbox(
lat=lats,
lon=lngs,
mode='lines+markers',
marker=dict(size=15, color='red'),
line=dict(width=3, color='blue'),
text=[stop['name'] for stop in stops],
hoverinfo='text'
))
fig.update_layout(
mapbox=dict(
style='open-street-map',
center=dict(lat=25.04, lon=121.53),
zoom=13
),
height=500,
margin=dict(l=0, r=0, t=0, b=0)
)
return fig
def step1_analyze_tasks(self, user_input: str):
"""Step 1: Analyze tasks"""
self.reasoning_messages = []
self.add_reasoning_message("planner", "Analyzing user requirements...", "thinking")
time_module.sleep(0.5)
self.add_reasoning_message("planner", "Using tool: parse_requirements()", "tool")
time_module.sleep(0.5)
self.add_reasoning_message("planner", "✅ Identified 3 tasks: hospital, supermarket, post office", "success")
self.add_reasoning_message("planner", "Using tool: extract_time_windows()", "tool")
time_module.sleep(0.5)
self.add_reasoning_message("planner", "✅ Time windows extracted", "success")
self.task_list = [
{
"description": "Visit NTU Hospital",
"category": "medical",
"priority": "HIGH",
"time_window": "08:00-12:00"
},
{
"description": "Buy groceries at PX Mart",
"category": "shopping",
"priority": "MEDIUM",
"time_window": "Anytime"
},
{
"description": "Mail package at post office",
"category": "postal",
"priority": "HIGH",
"time_window": "09:00-15:00"
}
]
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Analysis complete"),
self.create_agent_card("scout", "idle", "On standby"),
self.create_agent_card("optimizer", "idle", "On standby"),
self.create_agent_card("validator", "idle", "On standby"),
self.create_agent_card("weather", "idle", "On standby"),
self.create_agent_card("traffic", "idle", "On standby")
]
task_list_ui = self.create_task_list_ui(self.task_list)
task_summary = self.create_task_summary(self.task_list)
return (
*agent_updates,
self.get_reasoning_html(),
task_list_ui,
task_summary,
gr.update(visible=True),
gr.update(visible=True),
[],
"✅ Task analysis complete"
)
def step2_search_pois(self):
"""Step 2: Search POIs"""
self.add_reasoning_message("scout", "Starting POI search...", "thinking")
time_module.sleep(0.5)
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "working", "🔧 Searching POIs..."),
self.create_agent_card("optimizer", "waiting", "⏸️ Waiting"),
self.create_agent_card("validator", "waiting", "⏸️ Waiting"),
self.create_agent_card("weather", "waiting", "⏸️ Waiting"),
self.create_agent_card("traffic", "waiting", "⏸️ Waiting")
]
yield (*agent_updates, self.get_reasoning_html(), "", gr.update(), gr.update(), gr.update(), gr.update(),
"🔍 Searching locations...")
self.add_reasoning_message("scout", "Using tool: search_poi(query='hospital')", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("scout", "✅ Found NTU Hospital (0.8km, 4.8★)", "success")
self.add_reasoning_message("scout", "Using tool: search_poi(query='supermarket')", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("scout", "✅ Found PX Mart (1.2km, 24/7)", "success")
self.add_reasoning_message("scout", "Using tool: search_poi(query='post office')", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("scout", "✅ Found Post Office (1.8km, until 17:30)", "success")
self.add_reasoning_message("scout", "Using tool: calculate_distance_matrix()", "tool")
time_module.sleep(0.5)
self.add_reasoning_message("scout", "✅ Distance matrix calculated", "success")
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "completed", "✅ POI search complete"),
self.create_agent_card("optimizer", "waiting", "⏸️ Waiting"),
self.create_agent_card("validator", "waiting", "⏸️ Waiting"),
self.create_agent_card("weather", "waiting", "⏸️ Waiting"),
self.create_agent_card("traffic", "waiting", "⏸️ Waiting")
]
yield (*agent_updates, self.get_reasoning_html(), "", gr.update(), gr.update(), gr.update(), gr.update(),
"✅ POI search complete")
def step3_optimize_route(self):
"""Step 3: Optimize route"""
self.add_reasoning_message("optimizer", "Starting route optimization...", "thinking")
time_module.sleep(0.5)
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "completed", "✅ Complete"),
self.create_agent_card("optimizer", "working", "⚙️ Optimizing..."),
self.create_agent_card("validator", "waiting", "⏸️ Waiting"),
self.create_agent_card("weather", "waiting", "⏸️ Waiting"),
self.create_agent_card("traffic", "waiting", "⏸️ Waiting")
]
yield (*agent_updates, self.get_reasoning_html(), "", gr.update(), gr.update(), gr.update(), gr.update(),
"⚙️ Optimizing route...")
self.add_reasoning_message("optimizer", "Using tool: optimize_with_ortools()", "tool")
time_module.sleep(1.0)
self.add_reasoning_message("optimizer", "✅ TSPTW solved, improved 15%", "success")
self.add_reasoning_message("validator", "Validating feasibility...", "thinking")
time_module.sleep(0.5)
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "completed", "✅ Complete"),
self.create_agent_card("optimizer", "completed", "✅ Optimized"),
self.create_agent_card("validator", "working", "🛡️ Validating..."),
self.create_agent_card("weather", "waiting", "⏸️ Waiting"),
self.create_agent_card("traffic", "waiting", "⏸️ Waiting")
]
yield (*agent_updates, self.get_reasoning_html(), "", gr.update(), gr.update(), gr.update(), gr.update(),
"🛡️ Validating...")
self.add_reasoning_message("validator", "Using tool: validate_time_windows()", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("validator", "✅ All time windows satisfied", "success")
self.add_reasoning_message("validator", "Using tool: check_deadline()", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("validator", "✅ Deadline check passed (3h35m buffer)", "success")
self.add_reasoning_message("weather", "Checking weather...", "thinking")
time_module.sleep(0.5)
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "completed", "✅ Complete"),
self.create_agent_card("optimizer", "completed", "✅ Complete"),
self.create_agent_card("validator", "completed", "✅ Validated"),
self.create_agent_card("weather", "working", "🌈 Checking..."),
self.create_agent_card("traffic", "waiting", "⏸️ Waiting")
]
yield (*agent_updates, self.get_reasoning_html(), "", gr.update(), gr.update(), gr.update(), gr.update(),
"🌈 Checking weather...")
self.add_reasoning_message("weather", "Using tool: check_weather()", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("weather", "✅ Sunny, 23°C, perfect for travel", "success")
self.add_reasoning_message("traffic", "Checking traffic...", "thinking")
time_module.sleep(0.5)
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "completed", "✅ Complete"),
self.create_agent_card("optimizer", "completed", "✅ Complete"),
self.create_agent_card("validator", "completed", "✅ Complete"),
self.create_agent_card("weather", "completed", "✅ Good weather"),
self.create_agent_card("traffic", "working", "🚗 Checking...")
]
yield (*agent_updates, self.get_reasoning_html(), "", gr.update(), gr.update(), gr.update(), gr.update(),
"🚗 Checking traffic...")
self.add_reasoning_message("traffic", "Using tool: check_traffic()", "tool")
time_module.sleep(0.8)
self.add_reasoning_message("traffic", "✅ Good road conditions", "success")
agent_updates = [
self.create_agent_card("planner", "completed", "✅ Complete"),
self.create_agent_card("scout", "completed", "✅ Complete"),
self.create_agent_card("optimizer", "completed", "✅ Complete"),
self.create_agent_card("validator", "completed", "✅ Complete"),
self.create_agent_card("weather", "completed", "✅ Complete"),
self.create_agent_card("traffic", "completed", "✅ Complete")
]
result = {
'route': {
'stops': [
{'name': 'NTU Hospital', 'address': 'No. 1, Changde St., Taipei', 'arrival_time': '09:00',
'duration': '45',
'rating': '4.8', 'phone': '02-2312-3456'},
{'name': 'PX Mart', 'address': 'Roosevelt Rd. Sec. 3, Taipei', 'arrival_time': '10:15',
'duration': '30', 'rating': '4.5', 'phone': '02-2362-7890'},
{'name': 'Main Post Office', 'address': 'Zhongxiao W. Rd. Sec. 1, Taipei', 'arrival_time': '11:05',
'duration': '20', 'rating': '4.3', 'phone': '02-2311-1234'}
]
}
}
trip_result = self.create_trip_result_summary(result)
agent_text = self.create_agent_text_output(result)
map_fig = self.create_map(result)
yield (
*agent_updates,
self.get_reasoning_html(),
trip_result,
agent_text,
map_fig,
gr.update(visible=True),
gr.update(visible=True),
"🎉 Planning complete!"
)
def modify_task_chat(self, message: str, history: List):
"""Modify tasks via chat"""
if not message:
return history, self.create_task_list_ui(self.task_list)
history.append({"role": "user", "content": message})
history.append({"role": "assistant", "content": "Noted. I've recorded your request."})
return history, self.create_task_list_ui(self.task_list)
def save_settings(self, google_key: str, weather_key: str, anthropic_key: str, model: str):
"""Save settings"""
self.settings['google_maps_api_key'] = google_key
self.settings['openweather_api_key'] = weather_key
self.settings['anthropic_api_key'] = anthropic_key
self.settings['model'] = model
return "✅ Settings saved"
def build_interface(self):
"""Build Gradio interface (compact layout)"""
with gr.Blocks(
theme=gr.themes.Soft(
primary_hue="blue",
secondary_hue="green",
neutral_hue="slate",
font=gr.themes.GoogleFont("Inter"),
font_mono=gr.themes.GoogleFont("IBM Plex Mono")
),
css="""
.breathing {
animation: breathing 2s ease-in-out infinite;
}
@keyframes breathing {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.9; transform: scale(1.02); }
}
.compact-container {
max-width: 1400px;
margin: 0 auto;
}
""",
title="LifeFlow AI - Intelligent Trip Planning"
) as demo:
gr.Markdown("""
# 🚀 LifeFlow AI - Intelligent Trip Planning System
**Multi-Agent AI System** for optimizing daily errands and activities
""")
with gr.Row(elem_classes="compact-container"):
# Left: Input + Tasks + Agents + Results (narrower)
with gr.Column(scale=2, min_width=350):
# Input area
with gr.Group(visible=True) as input_area:
gr.Markdown("### 📝 Describe Your Needs")
user_input = gr.Textbox(
label="Requirements",
placeholder="e.g., Visit hospital tomorrow morning, buy groceries, mail package before 3pm",
lines=3
)
with gr.Row():
start_location = gr.Textbox(
label="Start",
placeholder="Taipei",
scale=2
)
start_time = gr.Textbox(
label="Time",
placeholder="08:30",
scale=1
)
deadline = gr.Textbox(
label="Deadline (optional)",
placeholder="e.g., 15:00"
)
analyze_btn = gr.Button("🚀 Start AI Analysis", variant="primary", size="lg")
# Task confirmation
with gr.Group(visible=False) as task_confirm_area:
gr.Markdown("### ✅ Confirm Tasks")
task_list_display = gr.HTML()
task_summary_display = gr.HTML()
with gr.Row():
back_btn = gr.Button("⬅️ Back", variant="secondary", scale=1)
confirm_btn = gr.Button("✅ Confirm & Plan", variant="primary", scale=2)
# Trip result summary
with gr.Group(visible=False) as trip_result_area:
trip_result_display = gr.HTML()
# AI Team
with gr.Group(visible=False) as team_area:
gr.Markdown("### 🤖 AI Expert Team")
agent_displays = []
for agent_key in ['planner', 'scout', 'optimizer', 'validator', 'weather', 'traffic']:
agent_card = gr.HTML(value=self.create_agent_card(agent_key, "idle", "On standby"))
agent_displays.append(agent_card)
# Right: Status + Chat + Tabs (wider)
with gr.Column(scale=3, min_width=500):
# Status bar
with gr.Group():
status_bar = gr.Textbox(
label="📊 Status",
value="Waiting for input...",
interactive=False,
max_lines=1
)
# Chat modification
with gr.Group(visible=False) as chat_modify_area:
gr.Markdown("### 💬 Modify Tasks")
chatbot = gr.Chatbot(
type='messages',
value=[],
height=120,
show_label=False
)
with gr.Row():
chat_input = gr.Textbox(
placeholder="e.g., Change task 2 to high priority",
show_label=False,
scale=4
)
chat_send = gr.Button("Send", variant="primary", scale=1)
# Tabs
with gr.Tabs() as tabs:
# Tab 1: AI Conversation
with gr.Tab("💬 AI Conversation", id="chat_tab"):
reasoning_output = gr.HTML(
value=self.get_reasoning_html()
)
# Tab 2: Full Report
with gr.Tab("📊 Full Report", id="summary_tab", visible=False) as summary_tab:
gr.Markdown("*Team Agent complete analysis (plain text)*")
complete_summary_output = gr.Textbox(
show_label=False,
lines=20,
max_lines=25,
interactive=False
)
# Tab 3: Map
with gr.Tab("🗺️ Route Map", id="map_tab", visible=False) as map_tab:
map_output = gr.Plot(value=self.create_map(), show_label=False)
# Tab 4: Settings
with gr.Tab("⚙️ Settings", id="settings_tab"):
gr.Markdown("### 🔑 API Keys")
google_maps_key = gr.Textbox(
label="Google Maps API Key",
placeholder="AIzaSy...",
type="password"
)
openweather_key = gr.Textbox(
label="OpenWeather API Key",
type="password"
)
anthropic_key = gr.Textbox(
label="Anthropic API Key",
placeholder="sk-ant-...",
type="password"
)
gr.Markdown("### 🤖 Model")
model_choice = gr.Dropdown(
label="Select Model",
choices=[
"claude-sonnet-4-20250514",
"claude-opus-4-20250514",
"gpt-4o",
"gemini-pro"
],
value="claude-sonnet-4-20250514"
)
save_settings_btn = gr.Button("💾 Save", variant="primary")
settings_status = gr.Textbox(label="Status", value="", interactive=False)
# Examples
gr.Examples(
examples=[
["Visit hospital tomorrow morning, buy groceries, mail package before 3pm", "Taipei", "08:30",
"15:00"]
],
inputs=[user_input, start_location, start_time, deadline]
)
# Event bindings
analyze_btn.click(
fn=self.step1_analyze_tasks,
inputs=[user_input],
outputs=[
*agent_displays,
reasoning_output,
task_list_display,
task_summary_display,
task_confirm_area,
chat_modify_area,
chatbot,
status_bar
]
).then(
fn=lambda: (gr.update(visible=False), gr.update(visible=True)),
outputs=[input_area, task_confirm_area]
)
back_btn.click(
fn=lambda: (
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
"Waiting for input..."
),
outputs=[input_area, task_confirm_area, chat_modify_area, status_bar]
)
chat_send.click(
fn=self.modify_task_chat,
inputs=[chat_input, chatbot],
outputs=[chatbot, task_list_display]
).then(
fn=lambda: "",
outputs=[chat_input]
)
chat_input.submit(
fn=self.modify_task_chat,
inputs=[chat_input, chatbot],
outputs=[chatbot, task_list_display]
).then(
fn=lambda: "",
outputs=[chat_input]
)
confirm_btn.click(
fn=lambda: (
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=True)
),
outputs=[task_confirm_area, chat_modify_area, team_area]
).then(
fn=self.step2_search_pois,
outputs=[
*agent_displays,
reasoning_output,
trip_result_display,
complete_summary_output,
map_output,
map_tab,
summary_tab,
status_bar
]
).then(
fn=self.step3_optimize_route,
outputs=[
*agent_displays,
reasoning_output,
trip_result_display,
complete_summary_output,
map_output,
map_tab,
summary_tab,
status_bar
]
).then(
fn=lambda: (
gr.update(visible=True),
gr.update(selected="summary_tab")
),
outputs=[trip_result_area, tabs]
)
save_settings_btn.click(
fn=self.save_settings,
inputs=[google_maps_key, openweather_key, anthropic_key, model_choice],
outputs=[settings_status]
)
return demo
def main():
app = LifeFlowInteractive()
demo = app.build_interface()
demo.launch(
server_name="0.0.0.0",
server_port=7860, # HuggingFace default 7860
share=False,
show_error=True
)
if __name__ == "__main__":
main()