Spaces:
Running
Running
import streamlit as st | |
import os | |
import json | |
from datetime import datetime, timedelta | |
import base64 | |
from travel import ( | |
destination_research_task, accommodation_task, transportation_task, | |
activities_task, dining_task, itinerary_task, final_report_task, | |
run_task | |
) | |
# Set page configuration | |
st.set_page_config( | |
page_title="BlockX Travel Itinerary Generator", | |
page_icon="✈️", | |
layout="wide", | |
initial_sidebar_state="expanded" | |
) | |
# Custom CSS for better styling | |
st.markdown(""" | |
<style> | |
.main-header { | |
font-size: 2.5rem; | |
color: #1E88E5; | |
text-align: center; | |
margin-bottom: 1rem; | |
} | |
.sub-header { | |
font-size: 1.5rem; | |
color: #0D47A1; | |
margin-top: 2rem; | |
margin-bottom: 1rem; | |
} | |
.info-box { | |
background-color: #E3F2FD; | |
padding: 1rem; | |
border-radius: 0.5rem; | |
margin-bottom: 1rem; | |
} | |
.success-box { | |
background-color: #E8F5E9; | |
padding: 1rem; | |
border-radius: 0.5rem; | |
margin-bottom: 1rem; | |
border-left: 5px solid #4CAF50; | |
} | |
.progress-container { | |
margin: 1rem 0; | |
} | |
.step-complete { | |
color: #4CAF50; | |
font-weight: bold; | |
} | |
.step-pending { | |
color: #9E9E9E; | |
} | |
.step-active { | |
color: #1E88E5; | |
font-weight: bold; | |
} | |
.agent-log { | |
background-color: #F5F5F5; | |
border-left: 3px solid #1E88E5; | |
padding: 0.5rem; | |
margin-bottom: 0.5rem; | |
font-family: monospace; | |
} | |
.agent-output { | |
background-color: #E8F5E9; | |
border-left: 5px solid #4CAF50; | |
padding: 1rem; | |
margin: 1rem 0; | |
border-radius: 0.5rem; | |
max-height: 400px; | |
overflow-y: auto; | |
} | |
.footer { | |
text-align: center; | |
margin-top: 3rem; | |
color: #757575; | |
font-size: 0.8rem; | |
} | |
.stButton button { | |
background-color: #1E88E5; | |
color: white; | |
font-weight: bold; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Helper function to download HTML file | |
def get_download_link(html_content, filename): | |
b64 = base64.b64encode(html_content.encode()).decode() | |
href = f'<a href="data:text/html;base64,{b64}" download="{filename}">Download Itinerary as HTML</a>' | |
return href | |
# Helper function to display progress | |
def display_progress(current_step, total_steps=7): | |
progress_html = '<div class="progress-container">' | |
steps = [ | |
"Destination Research", | |
"Accommodation", | |
"Transportation", | |
"Activities", | |
"Dining", | |
"Itinerary Creation", | |
"Final Report" | |
] | |
for i, step in enumerate(steps): | |
if i < current_step: | |
progress_html += f'<p class="step-complete">✓ {step} - Complete</p>' | |
elif i == current_step: | |
progress_html += f'<p class="step-active">⟳ {step} - In Progress...</p>' | |
else: | |
progress_html += f'<p class="step-pending">○ {step} - Pending</p>' | |
progress_html += '</div>' | |
return progress_html | |
# Custom run_task function that updates the UI with logs and shows live agent outputs | |
def run_task_with_logs(task, input_text, log_container, output_container, results_key=None): | |
# Add log message | |
log_message = f"🤖 Starting {task.agent.role}..." | |
st.session_state.log_messages.append(log_message) | |
# Update the log container | |
with log_container: | |
st.markdown("### Agent Activity") | |
for msg in st.session_state.log_messages: | |
st.markdown(msg) | |
# Run the actual task | |
result = run_task(task, input_text) | |
# Store result if needed | |
if results_key: | |
st.session_state.results[results_key] = result | |
# Add completion log message | |
log_message = f"✅ {task.agent.role} completed!" | |
st.session_state.log_messages.append(log_message) | |
# Update the log container again | |
with log_container: | |
st.markdown("### Agent Activity") | |
for msg in st.session_state.log_messages: | |
st.markdown(msg) | |
# Display the agent's output in the output container | |
with output_container: | |
st.markdown(f"### {task.agent.role} Output") | |
st.markdown("""<div class='agent-output'>""" + result + """</div>""", unsafe_allow_html=True) | |
return result | |
# Initialize session state | |
if 'generated_itinerary' not in st.session_state: | |
st.session_state.generated_itinerary = None | |
if 'generation_complete' not in st.session_state: | |
st.session_state.generation_complete = False | |
if 'current_step' not in st.session_state: | |
st.session_state.current_step = 0 | |
if 'results' not in st.session_state: | |
st.session_state.results = { | |
"destination_info": "", | |
"accommodation_info": "", | |
"transportation_info": "", | |
"activities_info": "", | |
"dining_info": "", | |
"itinerary": "", | |
"final_itinerary": "" | |
} | |
if 'log_messages' not in st.session_state: | |
st.session_state.log_messages = [] | |
if 'current_output' not in st.session_state: | |
st.session_state.current_output = None | |
# Header | |
st.markdown('<h1 class="main-header">BlockX Travel Itinerary Generator</h1>', unsafe_allow_html=True) | |
st.markdown('<p style="text-align: center;">Create your personalized AI-powered travel itinerary in minutes!</p>', unsafe_allow_html=True) | |
# Sidebar with information | |
with st.sidebar: | |
st.image("https://img.icons8.com/fluency/96/travel-card.png", width=80) | |
st.markdown("### About") | |
st.info( | |
"This AI-powered tool creates a personalized travel itinerary based on your preferences. " | |
"Fill in the form and let our specialized travel agents plan your perfect trip!" | |
) | |
st.markdown("### How it works") | |
st.markdown( | |
"1. Enter your travel details\n" | |
"2. Our AI agents analyze your preferences\n" | |
"3. Receive a comprehensive itinerary\n" | |
"4. Download and enjoy your trip!" | |
) | |
st.markdown("### Travel Agents") | |
st.markdown( | |
"- 🔍 Destination Research Agent\n" | |
"- 🏨 Accommodation Agent\n" | |
"- 🚗 Transportation Agent\n" | |
"- 🎭 Activities & Attractions Agent\n" | |
"- 🍽️ Dining & Culinary Agent\n" | |
"- 📅 Itinerary Integration Agent\n" | |
"- 📄 Final Report Generation Agent" | |
) | |
# Input Form | |
if not st.session_state.generation_complete: | |
st.markdown('<h2 class="sub-header">Enter Your Travel Details</h2>', unsafe_allow_html=True) | |
col1, col2 = st.columns(2) | |
with col1: | |
origin = st.text_input("Origin City/Country", placeholder="e.g., New York, USA") | |
destination = st.text_input("Destination City/Country", placeholder="e.g., Paris, France") | |
duration = st.number_input("Trip Duration (days)", min_value=1, max_value=30, value=7) | |
with col2: | |
budget_options = ["Budget", "Moderate", "Luxury"] | |
budget = st.selectbox("Budget Level", budget_options) | |
preferences = st.text_area("Travel Preferences & Interests", | |
placeholder="e.g., museums, hiking, food, shopping, beaches, history, nightlife, family-friendly") | |
special_requirements = st.text_area("Special Requirements", | |
placeholder="e.g., dietary restrictions, accessibility needs, etc.") | |
# Generate button | |
if st.button("Generate My Travel Itinerary"): | |
if not origin or not destination: | |
st.error("Please enter both origin and destination.") | |
else: | |
user_input = { | |
"origin": origin, | |
"destination": destination, | |
"duration": str(duration), | |
"budget": budget.lower(), | |
"preferences": preferences, | |
"special_requirements": special_requirements | |
} | |
# Format the user input for tasks | |
input_context = f"""Travel Request Details: | |
Origin: {user_input['origin']} | |
Destination: {user_input['destination']} | |
Duration: {user_input['duration']} days | |
Budget Level: {user_input['budget']} | |
Preferences/Interests: {user_input['preferences']} | |
Special Requirements: {user_input['special_requirements']} | |
""" | |
# Create containers for progress, logs, and live output | |
col1, col2 = st.columns([1, 2]) | |
with col1: | |
progress_placeholder = st.empty() | |
progress_placeholder.markdown(display_progress(0), unsafe_allow_html=True) | |
log_container = st.container() | |
st.session_state.log_messages = [] | |
with col2: | |
output_container = st.container() | |
with output_container: | |
st.markdown("### Live Agent Outputs") | |
st.info("Agent outputs will appear here as they are generated") | |
# Step 1: Destination Research | |
st.session_state.current_step = 0 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
destination_info = run_task_with_logs( | |
destination_research_task, | |
input_context.format( | |
destination=user_input['destination'], | |
preferences=user_input['preferences'] | |
), | |
log_container, | |
output_container, | |
"destination_info" | |
) | |
st.session_state.current_step = 1 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
# Step 2: Accommodation Recommendations | |
accommodation_info = run_task_with_logs( | |
accommodation_task, | |
input_context.format( | |
destination=user_input['destination'], | |
budget=user_input['budget'], | |
preferences=user_input['preferences'] | |
), | |
log_container, | |
output_container, | |
"accommodation_info" | |
) | |
st.session_state.current_step = 2 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
# Step 3: Transportation Planning | |
transportation_info = run_task_with_logs( | |
transportation_task, | |
input_context.format( | |
origin=user_input['origin'], | |
destination=user_input['destination'] | |
), | |
log_container, | |
output_container, | |
"transportation_info" | |
) | |
st.session_state.current_step = 3 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
# Step 4: Activities & Attractions | |
activities_info = run_task_with_logs( | |
activities_task, | |
input_context.format( | |
destination=user_input['destination'], | |
preferences=user_input['preferences'] | |
), | |
log_container, | |
output_container, | |
"activities_info" | |
) | |
st.session_state.current_step = 4 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
# Step 5: Dining Recommendations | |
dining_info = run_task_with_logs( | |
dining_task, | |
input_context.format( | |
destination=user_input['destination'], | |
preferences=user_input['preferences'] | |
), | |
log_container, | |
output_container, | |
"dining_info" | |
) | |
st.session_state.current_step = 5 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
# Step 6: Create Day-by-Day Itinerary | |
combined_info = f"""{input_context} | |
Destination Information: | |
{destination_info} | |
Accommodation Options: | |
{accommodation_info} | |
Transportation Plan: | |
{transportation_info} | |
Recommended Activities: | |
{activities_info} | |
Dining Recommendations: | |
{dining_info} | |
""" | |
itinerary = run_task_with_logs( | |
itinerary_task, | |
combined_info.format( | |
duration=user_input['duration'], | |
origin=user_input['origin'], | |
destination=user_input['destination'] | |
), | |
log_container, | |
output_container, | |
"itinerary" | |
) | |
st.session_state.current_step = 6 | |
progress_placeholder.markdown(display_progress(st.session_state.current_step), unsafe_allow_html=True) | |
# Step 7: Generate Final Report | |
final_context = f"""{combined_info} | |
Day-by-Day Itinerary: | |
{itinerary} | |
""" | |
final_itinerary = run_task_with_logs( | |
final_report_task, | |
final_context.format( | |
duration=user_input['duration'], | |
origin=user_input['origin'], | |
destination=user_input['destination'] | |
), | |
log_container, | |
output_container, | |
"final_itinerary" | |
) | |
# Save the generated itinerary to session state | |
st.session_state.generated_itinerary = final_itinerary | |
st.session_state.generation_complete = True | |
# Create a filename based on the destination and date | |
date_str = datetime.now().strftime("%Y-%m-%d") | |
st.session_state.filename = f"{user_input['destination'].replace(' ', '_')}_{date_str}_itinerary.html" | |
# No need to rerun, we'll update the UI directly | |
# Display results if generation is complete | |
if st.session_state.generation_complete: | |
st.markdown('<h2 class="sub-header">Your Travel Itinerary is Ready! 🎉</h2>', unsafe_allow_html=True) | |
# Display download link | |
st.markdown( | |
get_download_link(st.session_state.generated_itinerary, st.session_state.filename), | |
unsafe_allow_html=True | |
) | |
# Option to view the itinerary directly | |
with st.expander("Preview Your Itinerary", expanded=True): | |
st.components.v1.html(st.session_state.generated_itinerary, height=600, scrolling=True) | |
# Option to view individual agent outputs | |
with st.expander("View Detailed Agent Outputs"): | |
agent_tabs = st.tabs([ | |
"Destination", "Accommodation", "Transportation", | |
"Activities", "Dining", "Day-by-Day" | |
]) | |
with agent_tabs[0]: | |
st.markdown("### Destination Research") | |
st.markdown(st.session_state.results["destination_info"]) | |
with agent_tabs[1]: | |
st.markdown("### Accommodation Options") | |
st.markdown(st.session_state.results["accommodation_info"]) | |
with agent_tabs[2]: | |
st.markdown("### Transportation Plan") | |
st.markdown(st.session_state.results["transportation_info"]) | |
with agent_tabs[3]: | |
st.markdown("### Recommended Activities") | |
st.markdown(st.session_state.results["activities_info"]) | |
with agent_tabs[4]: | |
st.markdown("### Dining Recommendations") | |
st.markdown(st.session_state.results["dining_info"]) | |
with agent_tabs[5]: | |
st.markdown("### Day-by-Day Itinerary") | |
st.markdown(st.session_state.results["itinerary"]) | |
# Option to start over | |
if st.button("Create Another Itinerary"): | |
# Reset session state | |
st.session_state.generated_itinerary = None | |
st.session_state.generation_complete = False | |
st.session_state.current_step = 0 | |
st.session_state.results = { | |
"destination_info": "", | |
"accommodation_info": "", | |
"transportation_info": "", | |
"activities_info": "", | |
"dining_info": "", | |
"itinerary": "", | |
"final_itinerary": "" | |
} | |
st.rerun() | |
# Footer | |
st.markdown('<div class="footer">BlockX Travel Itinerary Generator © 2025</div>', unsafe_allow_html=True) | |