Spaces:
Running
Running
Upload 3 files
Browse files- app.py +254 -0
- referral_codes.py +57 -0
- requirements.txt +5 -0
app.py
ADDED
@@ -0,0 +1,254 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import os
|
3 |
+
from dotenv import load_dotenv
|
4 |
+
from crewai import Agent, Task, Crew, Process
|
5 |
+
from crewai import LLM
|
6 |
+
import litellm
|
7 |
+
from crewai_tools import SerperDevTool
|
8 |
+
from typing import Any, Dict
|
9 |
+
import sys
|
10 |
+
from datetime import datetime
|
11 |
+
from queue import Queue
|
12 |
+
import threading
|
13 |
+
from referral_codes import validate_referral_code
|
14 |
+
|
15 |
+
# Page config
|
16 |
+
st.set_page_config(
|
17 |
+
page_title="Travel Planning Agent",
|
18 |
+
page_icon="🌍",
|
19 |
+
layout="wide"
|
20 |
+
)
|
21 |
+
|
22 |
+
# Load environment variables
|
23 |
+
load_dotenv()
|
24 |
+
|
25 |
+
def initialize_llm():
|
26 |
+
# Get API key
|
27 |
+
api_key = os.getenv("GEMINI_API_KEY")
|
28 |
+
if not api_key:
|
29 |
+
raise ValueError("GEMINI_API_KEY not found in environment variables")
|
30 |
+
|
31 |
+
st.write(f"Debug: API Key found: {api_key[:10]}...")
|
32 |
+
|
33 |
+
# Initialize Gemini LLM with API Key
|
34 |
+
return LLM(
|
35 |
+
model="gemini/gemini-1.5-pro-latest",
|
36 |
+
api_key=api_key,
|
37 |
+
temperature=0.7
|
38 |
+
)
|
39 |
+
|
40 |
+
def create_travel_agent(llm):
|
41 |
+
return Agent(
|
42 |
+
role="Elite AI Travel Strategist",
|
43 |
+
goal=(
|
44 |
+
"Design the most optimized, personalized, and budget-conscious travel plans based on the user's unique preferences, "
|
45 |
+
"including origin, destination, budget, travel dates, group size, preferred activities, and travel style. "
|
46 |
+
"Leverage cutting-edge AI with real-time data from flights, hotels, transport services, local attractions, and events "
|
47 |
+
"to craft seamless, time-efficient, and experience-rich itineraries. "
|
48 |
+
"Ensure dynamic adaptability to handle disruptions, availability changes, and user modifications, creating a hassle-free travel experience."
|
49 |
+
),
|
50 |
+
backstory=(
|
51 |
+
"I am an AI-powered **Master Travel Architect** with deep expertise in curating extraordinary journeys tailored to individual tastes, "
|
52 |
+
"whether for solo travelers, families, business professionals, or adventure seekers. "
|
53 |
+
"With an extensive knowledge of global destinations, cultural insights, seasonal trends, and logistical intricacies, "
|
54 |
+
"I meticulously craft itineraries that blend adventure, relaxation, and cultural immersion. "
|
55 |
+
"By integrating real-time APIs for flights, accommodations, activities, and local events, "
|
56 |
+
"I ensure each recommendation is accurate, cost-effective, and aligned with the traveler’s needs."
|
57 |
+
),
|
58 |
+
personality=(
|
59 |
+
"Hyper-intelligent, intuitive, and proactive. I am a meticulous planner with a deep understanding of travel logistics, "
|
60 |
+
"capable of anticipating potential issues before they arise. "
|
61 |
+
"I balance **comfort, adventure, and budget efficiency**, ensuring every itinerary is customized for a truly unique experience. "
|
62 |
+
"I dynamically adjust plans based on live travel updates, offer personalized recommendations based on user preferences, "
|
63 |
+
"and present a seamless blend of structured scheduling with flexibility. "
|
64 |
+
"Whether it’s a luxury escape, an immersive cultural journey, or an action-packed adventure, I craft the **perfect** travel blueprint."
|
65 |
+
),
|
66 |
+
tools=[SerperDevTool()],
|
67 |
+
allow_delegation=True,
|
68 |
+
humaninput=True,
|
69 |
+
llm=llm,
|
70 |
+
verbose=True
|
71 |
+
)
|
72 |
+
|
73 |
+
def create_travel_task(agent, user_inputs):
|
74 |
+
return Task(
|
75 |
+
description=f"""
|
76 |
+
You are an **AI Travel Planner** responsible for crafting a **personalized, budget-conscious, and time-efficient travel plan** using advanced tools for data retrieval, analysis, and real-time updates.
|
77 |
+
|
78 |
+
The user has provided the following details:
|
79 |
+
- **Origin:** {user_inputs['origin']}
|
80 |
+
- **Destination:** {user_inputs['destination']}
|
81 |
+
- **Budget:** {user_inputs['budget']} (in local currency)
|
82 |
+
- **Preferred Activities:** {user_inputs['activities']}
|
83 |
+
- **Transport Preference:** {user_inputs['transport']}
|
84 |
+
- **Time Available:** {user_inputs['time']}
|
85 |
+
|
86 |
+
## **Step 1: Generate an Optimized Itinerary**
|
87 |
+
- **Retrieve real-time data** (e.g., weather, crowd levels, and local events) to suggest the best times to visit attractions.
|
88 |
+
- Create a **structured, multi-day travel plan** that maximizes available time while balancing **sightseeing, relaxation, and local experiences**.
|
89 |
+
- Incorporate **seasonal and cultural insights** to enhance the travel experience.
|
90 |
+
- Prioritize **time-efficient routes** using mapping tools.
|
91 |
+
|
92 |
+
## **Step 2: Smart Transport & Accommodation Planning**
|
93 |
+
- Suggest the most **efficient transport options** based on {user_inputs['transport']}.
|
94 |
+
- Compare **flights, trains, buses, or car rentals** for cost, duration, and convenience.
|
95 |
+
- Recommend **accommodations** within budget, balancing comfort, location, and safety.
|
96 |
+
- Provide estimated travel times and **real-time availability** for transport & hotels.
|
97 |
+
- Include **relevant booking links** for:
|
98 |
+
- **Flights:** Skyscanner, Google Flights, Kayak
|
99 |
+
- **Trains & Buses:** Omio, Rail Europe, FlixBus
|
100 |
+
- **Car Rentals:** Rentalcars, Turo, Enterprise
|
101 |
+
- **Hotels & Stays:** Booking.com, Airbnb, Agoda
|
102 |
+
|
103 |
+
## **Step 3: Cost Estimation & Budgeting**
|
104 |
+
- **Break down estimated costs** for:
|
105 |
+
- **Transport:** (flights, public transport, or car rentals)
|
106 |
+
- **Accommodation:** (hotels, hostels, or rentals)
|
107 |
+
- **Meals & Dining:** (local food spots vs. premium dining)
|
108 |
+
- **Activities & Attractions:** (ticket prices, entry fees, and reservations)
|
109 |
+
- Offer **budget-friendly vs. premium options** while ensuring the plan stays within {user_inputs['budget']}.
|
110 |
+
- Suggest **discount passes, travel cards, and best booking platforms** for savings.
|
111 |
+
- Provide **direct booking links for tours, attractions, and dining** using:
|
112 |
+
- **Attractions & Tours:** GetYourGuide, Viator, Klook
|
113 |
+
- **Restaurant Reservations:** OpenTable, TheFork, Zomato
|
114 |
+
|
115 |
+
## **Step 4: Flexibility & Real-Time Adjustments**
|
116 |
+
- **Retrieve live updates** on:
|
117 |
+
- **Transport delays, strikes, or disruptions.**
|
118 |
+
- **Weather conditions affecting travel plans.**
|
119 |
+
- **Local event schedules & closures.**
|
120 |
+
- Include **alternative activity suggestions** in case of last-minute changes.
|
121 |
+
- Provide **emergency contacts, visa requirements, safety tips**, and **last-minute booking options**.
|
122 |
+
- Offer a **summary dashboard** with real-time insights for easy decision-making.
|
123 |
+
|
124 |
+
## **Step 5: AI-Powered Personalized Enhancements**
|
125 |
+
- Utilize **LLM-based recommendation engines** to personalize the itinerary based on travel history, reviews, and preferences.
|
126 |
+
- Suggest **AI-curated food experiences, offbeat destinations, and hidden gems**.
|
127 |
+
- Implement **interactive chatbot support** for instant Q&A and local guides.
|
128 |
+
""",
|
129 |
+
agent=agent,
|
130 |
+
expected_output="""
|
131 |
+
A structured, **highly optimized travel plan**, including:
|
132 |
+
- **Day-by-day itinerary** tailored to user preferences.
|
133 |
+
- **Real-time transport & accommodation recommendations** with direct booking links.
|
134 |
+
- **Estimated cost breakdown** with budget-conscious choices.
|
135 |
+
- **Dining & event recommendations** with reservation links.
|
136 |
+
- **Alternative options & live travel insights** for flexibility.
|
137 |
+
- **Safety, visa, and emergency contact details.**
|
138 |
+
- **Interactive AI-driven suggestions for a hassle-free experience.**
|
139 |
+
-**Links to the booking webistes for travel and hotel returned from the serperDevtool**
|
140 |
+
"""
|
141 |
+
)
|
142 |
+
|
143 |
+
def log_status(container, message):
|
144 |
+
"""Simple function to log status messages using Streamlit's native components"""
|
145 |
+
timestamp = datetime.now().strftime("%H:%M:%S")
|
146 |
+
container.info(f"[{timestamp}] {message}")
|
147 |
+
|
148 |
+
def main():
|
149 |
+
# Ensure environment variables are loaded
|
150 |
+
load_dotenv()
|
151 |
+
|
152 |
+
st.title("🌍 AI Travel Planning Assistant")
|
153 |
+
|
154 |
+
# Session state for referral code validation
|
155 |
+
if 'referral_validated' not in st.session_state:
|
156 |
+
st.session_state.referral_validated = False
|
157 |
+
st.session_state.user_type = None
|
158 |
+
|
159 |
+
# Debug information in sidebar
|
160 |
+
st.sidebar.title("Debug Information")
|
161 |
+
st.sidebar.write(f"GEMINI_API_KEY present: {bool(os.getenv('GEMINI_API_KEY'))}")
|
162 |
+
|
163 |
+
# Referral code validation
|
164 |
+
if not st.session_state.referral_validated:
|
165 |
+
st.write("Welcome! Please enter your referral code to access the travel planner.")
|
166 |
+
with st.form("referral_form"):
|
167 |
+
referral_code = st.text_input("Referral Code", key="referral_input")
|
168 |
+
submit_code = st.form_submit_button("Submit")
|
169 |
+
|
170 |
+
if submit_code:
|
171 |
+
is_valid, user_type, message = validate_referral_code(referral_code)
|
172 |
+
if is_valid:
|
173 |
+
st.session_state.referral_validated = True
|
174 |
+
st.session_state.user_type = user_type
|
175 |
+
st.success(f"Welcome! You have {user_type.title()} access.")
|
176 |
+
st.experimental_rerun()
|
177 |
+
else:
|
178 |
+
st.error(message)
|
179 |
+
return
|
180 |
+
|
181 |
+
# Show user type in sidebar
|
182 |
+
st.sidebar.write(f"Access Level: {st.session_state.user_type.title()}")
|
183 |
+
|
184 |
+
st.write("Let me help you plan your perfect trip!")
|
185 |
+
|
186 |
+
# Input form
|
187 |
+
with st.form("travel_form"):
|
188 |
+
col1, col2 = st.columns(2)
|
189 |
+
|
190 |
+
with col1:
|
191 |
+
origin = st.text_input("Origin", placeholder="e.g., New York")
|
192 |
+
destination = st.text_input("Destination", placeholder="e.g., Paris")
|
193 |
+
budget = st.number_input("Budget (in local currency)", min_value=0.0, value=1000.0, step=100.0)
|
194 |
+
|
195 |
+
with col2:
|
196 |
+
activities = st.text_input("Preferred Activities", placeholder="e.g., cultural, adventure, leisure")
|
197 |
+
transport = st.text_input("Preferred Transport", placeholder="e.g., flight, train, bus")
|
198 |
+
time = st.text_input("Time Available", placeholder="e.g., 5 days")
|
199 |
+
|
200 |
+
submit_button = st.form_submit_button("Generate Travel Plan")
|
201 |
+
|
202 |
+
if submit_button:
|
203 |
+
if not all([origin, destination, activities, transport, time]):
|
204 |
+
st.error("Please fill in all the fields!")
|
205 |
+
return
|
206 |
+
|
207 |
+
user_inputs = {
|
208 |
+
"origin": origin,
|
209 |
+
"destination": destination,
|
210 |
+
"budget": budget,
|
211 |
+
"activities": activities,
|
212 |
+
"transport": transport,
|
213 |
+
"time": time
|
214 |
+
}
|
215 |
+
|
216 |
+
with st.spinner("🤖 Generating your personalized travel plan... This may take a few minutes."):
|
217 |
+
try:
|
218 |
+
# Create an expander for logs
|
219 |
+
with st.expander("Progress Updates", expanded=True):
|
220 |
+
status_container = st.empty()
|
221 |
+
|
222 |
+
# Initialize components
|
223 |
+
log_status(status_container, "Initializing LLM...")
|
224 |
+
llm = initialize_llm()
|
225 |
+
|
226 |
+
log_status(status_container, "Creating travel agent...")
|
227 |
+
travel_agent = create_travel_agent(llm)
|
228 |
+
|
229 |
+
log_status(status_container, "Creating travel task...")
|
230 |
+
travel_task = create_travel_task(travel_agent, user_inputs)
|
231 |
+
|
232 |
+
log_status(status_container, "Setting up crew...")
|
233 |
+
crew = Crew(
|
234 |
+
agents=[travel_agent],
|
235 |
+
tasks=[travel_task],
|
236 |
+
verbose=True,
|
237 |
+
process=Process.sequential
|
238 |
+
)
|
239 |
+
|
240 |
+
log_status(status_container, "Starting travel plan generation...")
|
241 |
+
results = crew.kickoff()
|
242 |
+
|
243 |
+
log_status(status_container, "✨ Travel plan generation completed!")
|
244 |
+
|
245 |
+
# Display results
|
246 |
+
st.success("✨ Your travel plan is ready!")
|
247 |
+
st.markdown(results)
|
248 |
+
|
249 |
+
except Exception as e:
|
250 |
+
st.error(f"An error occurred: {str(e)}")
|
251 |
+
st.error("Please try again or contact support if the problem persists.")
|
252 |
+
|
253 |
+
if __name__ == "__main__":
|
254 |
+
main()
|
referral_codes.py
ADDED
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Store and validate referral codes for application access.
|
3 |
+
"""
|
4 |
+
|
5 |
+
# List of valid referral codes
|
6 |
+
VALID_REFERRAL_CODES = {
|
7 |
+
"TRAVEL2025BETA1": {"uses_left": 50, "type": "beta"},
|
8 |
+
"WANDERLUST25A": {"uses_left": 30, "type": "premium"},
|
9 |
+
"EXPLORER2025B": {"uses_left": 30, "type": "premium"},
|
10 |
+
"JOURNEY25VIP": {"uses_left": 20, "type": "vip"},
|
11 |
+
"GLOBETROTTER1": {"uses_left": 25, "type": "standard"},
|
12 |
+
"ADVENTURE25C": {"uses_left": 25, "type": "standard"},
|
13 |
+
"DISCOVER25D": {"uses_left": 25, "type": "standard"},
|
14 |
+
"VOYAGE2025E": {"uses_left": 25, "type": "standard"},
|
15 |
+
"PASSPORT25F": {"uses_left": 25, "type": "standard"},
|
16 |
+
"COMPASS2025G": {"uses_left": 25, "type": "standard"},
|
17 |
+
"NOMAD2025H": {"uses_left": 25, "type": "standard"},
|
18 |
+
"WANDER25VIP": {"uses_left": 20, "type": "vip"},
|
19 |
+
"TRAVEL25PRO": {"uses_left": 30, "type": "premium"},
|
20 |
+
"EXPLORE25J": {"uses_left": 25, "type": "standard"},
|
21 |
+
"JOURNEY25K": {"uses_left": 25, "type": "standard"},
|
22 |
+
"VOYAGE25VIP": {"uses_left": 20, "type": "vip"},
|
23 |
+
"ATLAS2025M": {"uses_left": 25, "type": "standard"},
|
24 |
+
"COMPASS25N": {"uses_left": 25, "type": "standard"},
|
25 |
+
"DISCOVER25P": {"uses_left": 25, "type": "standard"},
|
26 |
+
"PASSPORT25Q": {"uses_left": 25, "type": "standard"},
|
27 |
+
"WANDER25PRO": {"uses_left": 30, "type": "premium"},
|
28 |
+
"TRAVEL25VIP": {"uses_left": 20, "type": "vip"},
|
29 |
+
"EXPLORE25T": {"uses_left": 25, "type": "standard"},
|
30 |
+
"JOURNEY25U": {"uses_left": 25, "type": "standard"},
|
31 |
+
"VOYAGE25PRO": {"uses_left": 30, "type": "premium"}
|
32 |
+
}
|
33 |
+
|
34 |
+
def validate_referral_code(code):
|
35 |
+
"""
|
36 |
+
Validate a referral code and return its type if valid.
|
37 |
+
|
38 |
+
Args:
|
39 |
+
code (str): The referral code to validate
|
40 |
+
|
41 |
+
Returns:
|
42 |
+
tuple: (is_valid, code_type, message)
|
43 |
+
"""
|
44 |
+
if not code:
|
45 |
+
return False, None, "Please enter a referral code"
|
46 |
+
|
47 |
+
code = code.upper().strip()
|
48 |
+
if code not in VALID_REFERRAL_CODES:
|
49 |
+
return False, None, "Invalid referral code"
|
50 |
+
|
51 |
+
code_data = VALID_REFERRAL_CODES[code]
|
52 |
+
if code_data["uses_left"] <= 0:
|
53 |
+
return False, None, "This referral code has expired"
|
54 |
+
|
55 |
+
# Decrease the number of uses left
|
56 |
+
VALID_REFERRAL_CODES[code]["uses_left"] -= 1
|
57 |
+
return True, code_data["type"], "Valid referral code"
|
requirements.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
streamlit==1.31.1
|
2 |
+
crewai==0.12.2
|
3 |
+
python-dotenv==1.0.0
|
4 |
+
litellm==1.16.9
|
5 |
+
crewai-tools==0.0.15
|