Spaces:
Running
Running
import os | |
import json | |
import logging | |
from datetime import datetime, timedelta | |
from langchain_google_genai import ChatGoogleGenerativeAI | |
from langchain.schema import SystemMessage, HumanMessage | |
# Setup logging configuration | |
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") | |
# ------------------------------------------------------------------------------- | |
# Agent and Task Classes with Type Hints and Docstrings | |
# ------------------------------------------------------------------------------- | |
class Agent: | |
def __init__(self, role: str, goal: str, backstory: str, personality: str = "", llm=None) -> None: | |
""" | |
Initialize an Agent with role, goal, backstory, personality, and assigned LLM. | |
""" | |
self.role = role | |
self.goal = goal | |
self.backstory = backstory | |
self.personality = personality | |
self.tools = [] # Initialize with empty list for future tool integrations | |
self.llm = llm | |
class Task: | |
def __init__(self, description: str, agent: Agent, expected_output: str, context=None) -> None: | |
""" | |
Initialize a Task with its description, the responsible agent, expected output, and optional context. | |
""" | |
self.description = description | |
self.agent = agent | |
self.expected_output = expected_output | |
self.context = context or [] | |
# ------------------------------------------------------------------------------- | |
# Initialize LLM | |
# ------------------------------------------------------------------------------- | |
google_api_key = os.getenv("GEMINI_API_KEY") # μ€μ Google API ν€ μ¬μ© | |
if not google_api_key: | |
logging.error("GEMINI_API_KEY is not set in the environment variables.") | |
llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", google_api_key=google_api_key) | |
# ------------------------------------------------------------------------------- | |
# Define Travel Agents | |
# ------------------------------------------------------------------------------- | |
destination_research_agent = Agent( | |
role="Destination Research Agent", | |
goal=( | |
"Research and provide comprehensive information about the destination including popular attractions, " | |
"local culture, weather patterns, best times to visit, and local transportation options." | |
), | |
backstory=( | |
"An experienced travel researcher with extensive knowledge of global destinations. " | |
"I specialize in uncovering both popular attractions and hidden gems that match travelers' interests." | |
), | |
personality="Curious, detail-oriented, and knowledgeable about global cultures and travel trends.", | |
llm=llm, | |
) | |
accommodation_agent = Agent( | |
role="Accommodation Agent", | |
goal="Find and recommend suitable accommodations based on the traveler's preferences, budget, and location requirements.", | |
backstory="A hospitality expert who understands different types of accommodations and can match travelers with their ideal places to stay.", | |
personality="Attentive, resourceful, and focused on comfort and value.", | |
llm=llm, | |
) | |
transportation_agent = Agent( | |
role="Transportation Agent", | |
goal="Plan efficient transportation between the origin, destination, and all points of interest in the itinerary.", | |
backstory="A logistics specialist with knowledge of global transportation systems, from flights to local transit options.", | |
personality="Efficient, practical, and detail-oriented.", | |
llm=llm, | |
) | |
activities_agent = Agent( | |
role="Activities & Attractions Agent", | |
goal="Curate personalized activities and attractions that align with the traveler's interests, preferences, and time constraints.", | |
backstory="An enthusiastic explorer who has experienced diverse activities around the world and knows how to match experiences to individual preferences.", | |
personality="Enthusiastic, creative, and personable.", | |
llm=llm, | |
) | |
dining_agent = Agent( | |
role="Dining & Culinary Agent", | |
goal="Recommend dining experiences that showcase local cuisine while accommodating dietary preferences and budget considerations.", | |
backstory="A culinary expert with knowledge of global food scenes and an appreciation for authentic local dining experiences.", | |
personality="Passionate about food, culturally aware, and attentive to preferences.", | |
llm=llm, | |
) | |
itinerary_agent = Agent( | |
role="Itinerary Integration Agent", | |
goal="Compile all recommendations into a cohesive, day-by-day itinerary that optimizes time, minimizes travel fatigue, and maximizes enjoyment.", | |
backstory="A master travel planner who understands how to balance activities, rest, and logistics to create the perfect travel experience.", | |
personality="Organized, balanced, and practical.", | |
llm=llm, | |
) | |
# ------------------------------------------------------------------------------- | |
# Define Chatbot Agent and Task for Interactive Conversation | |
# ------------------------------------------------------------------------------- | |
chatbot_agent = Agent( | |
role="Chatbot Agent", | |
goal="Engage in interactive conversation to answer travel-related queries.", | |
backstory="A conversational AI assistant who provides instant, accurate travel information and recommendations.", | |
personality="Friendly, conversational, and knowledgeable about travel.", | |
llm=llm, | |
) | |
chatbot_task = Task( | |
description="Provide a conversational and detailed response to travel-related queries.", | |
agent=chatbot_agent, | |
expected_output="A friendly, helpful response to the user's query." | |
) | |
# ------------------------------------------------------------------------------- | |
# Define Other Travel Tasks | |
# ------------------------------------------------------------------------------- | |
destination_research_task = Task( | |
description="""Research {destination} thoroughly, considering the traveler's interests in {preferences}. | |
Efficient research parameters: | |
- Prioritize research in these critical categories: | |
* Top attractions that match specific {preferences} (not generic lists) | |
* Local transportation systems with cost-efficiency analysis | |
* Neighborhood breakdown with accommodation recommendations by budget tier | |
* Seasonal considerations for the specific travel dates | |
* Safety assessment with specific areas to embrace or avoid | |
* Cultural norms that impact visitor experience (dress codes, tipping, etiquette) | |
- Apply efficiency filters: | |
* Focus exclusively on verified information from official tourism boards, recent travel guides, and reliable local sources | |
* Analyze recent visitor reviews (< 6 months old) to identify changing conditions | |
* Evaluate price-to-experience value for attractions instead of just popularity | |
* Identify logistical clusters where multiple interests can be satisfied efficiently | |
* Research off-peak times for popular attractions to minimize waiting | |
* Evaluate digital tools (apps, passes, reservation systems) that streamline the visit | |
- Create practical knowledge matrices: | |
* Transportation method comparison (cost vs. time vs. convenience) | |
* Weather impact on specific activities | |
* Budget allocation recommendations based on preference priorities | |
* Time-saving opportunity identification""", | |
agent=destination_research_agent, | |
expected_output="""Targeted destination brief containing: | |
1. Executive summary highlighting the 5 most relevant aspects based on {preferences} | |
2. Neighborhood analysis with accommodation recommendations mapped to specific interests | |
3. Transportation efficiency guide with cost/convenience matrix | |
4. Cultural briefing focusing only on need-to-know information that impacts daily activities | |
5. Seasonal advantages and challenges specific to travel dates | |
6. Digital resource toolkit (essential apps, websites, reservation systems) | |
7. Budget optimization strategies with price ranges for key experiences | |
8. Safety and health quick-reference including emergency contacts | |
9. Logistics efficiency map showing optimal activity clustering | |
10. Local insider advantage recommendations that save time or money | |
Format should prioritize scannable information with bullet points, comparison tables, and decision matrices rather than lengthy prose.""" | |
) | |
accommodation_task = Task( | |
description="Find suitable accommodations in {destination} based on a {budget} budget and preferences for {preferences}.", | |
agent=accommodation_agent, | |
expected_output="List of recommended accommodations with details on location, amenities, price range, and availability." | |
) | |
transportation_task = Task( | |
description="Plan transportation from {origin} to {destination} and local transportation options during the stay.", | |
agent=transportation_agent, | |
expected_output="Transportation plan including flights/routes to the destination and recommendations for getting around locally." | |
) | |
activities_task = Task( | |
description="""Suggest activities and attractions in {destination} that align with interests in {preferences}. | |
Detailed requirements: | |
- Categorize activities into: Cultural Experiences, Outdoor Adventures, Culinary Experiences, | |
Entertainment & Nightlife, Family-Friendly Activities, and Local Hidden Gems | |
- For each activity, include: | |
* Detailed description with historical/cultural context where relevant | |
* Precise location with neighborhood information | |
* Operating hours with seasonal variations noted | |
* Pricing information with different ticket options/packages | |
* Accessibility considerations for travelers with mobility limitations | |
* Recommended duration for the activity (minimum and ideal time) | |
* Best time of day/week/year to visit | |
* Crowd levels by season | |
* Photography opportunities and restrictions | |
* Required reservations or booking windows | |
- Include a mix of iconic must-see attractions and off-the-beaten-path experiences | |
- Consider weather patterns in {destination} during travel period | |
- Analyze the {preferences} to match specific personality types and interest levels | |
- Include at least 2-3 rainy day alternatives for outdoor activities | |
- Provide local transportation options to reach each attraction | |
- Note authentic local experiences that provide cultural immersion | |
- Flag any activities requiring special equipment, permits, or physical fitness levels""", | |
agent=activities_agent, | |
expected_output="""Comprehensive curated list of activities and attractions with: | |
1. Clear categorization by type (cultural, outdoor, culinary, entertainment, family-friendly, hidden gems) | |
2. Detailed descriptions that include historical and cultural context | |
3. Complete practical information (hours, pricing, location, accessibility) | |
4. Time optimization recommendations (best time to visit, how to avoid crowds) | |
5. Personalized matches explaining why each activity aligns with specific {preferences} | |
6. Local transportation details to reach each attraction | |
7. Alternative options for inclement weather or unexpected closures | |
8. Insider tips from locals that enhance the experience | |
9. Suggested combinations of nearby activities for efficient itinerary planning | |
10. Risk level assessment and safety considerations where applicable | |
11. Sustainability impact and responsible tourism notes | |
12. Photographic highlights and optimal viewing points | |
Format should include a summary table for quick reference followed by detailed cards for each activity.""" | |
) | |
dining_task = Task( | |
description="Recommend dining experiences in {destination} that showcase local cuisine while considering {preferences}.", | |
agent=dining_agent, | |
expected_output="List of recommended restaurants and food experiences with cuisine types, price ranges, and special notes." | |
) | |
itinerary_task = Task( | |
description="""Create a day-by-day itinerary for a {duration} trip to {destination} from {origin}, incorporating all recommendations. | |
Detailed requirements: | |
- Begin with arrival logistics including airport transfer options, check-in times, and first-day orientation activities | |
- Structure each day with: | |
* Morning, afternoon, and evening activity blocks with precise timing | |
* Estimated travel times between locations using various transportation methods | |
* Buffer time for rest, spontaneous exploration, and unexpected delays | |
* Meal recommendations with reservation details and backup options | |
* Sunset/sunrise opportunities for optimal photography or experiences | |
- Apply intelligent sequencing to: | |
* Group attractions by geographic proximity to minimize transit time | |
* Schedule indoor activities strategically for predicted weather patterns | |
* Balance high-energy activities with relaxation periods | |
* Alternate between cultural immersion and entertainment experiences | |
* Account for opening days/hours of attractions and potential closures | |
- Include practical timing considerations: | |
* Museum/attraction fatigue limitations | |
* Jet lag recovery for first 1-2 days | |
* Time zone adjustment strategies | |
* Local rush hours and traffic patterns to avoid | |
* Cultural norms for meal times and business hours | |
- End with departure logistics including check-out procedures, airport transfer timing, and luggage considerations | |
- Add specialized planning elements: | |
* Local festivals or events coinciding with the travel dates | |
* Free time blocks for personal exploration or shopping | |
* Contingency recommendations for weather disruptions | |
* Early booking requirements for popular attractions/restaurants | |
* Local emergency contacts and nearby medical facilities""", | |
agent=itinerary_agent, | |
expected_output="""Comprehensive day-by-day itinerary featuring: | |
1. Detailed timeline for each day with hour-by-hour scheduling and transit times | |
2. Color-coded activity blocks that visually distinguish between types of activities | |
3. Intelligent geographic clustering to minimize transportation time | |
4. Strategic meal placements with both reservation-required and casual options | |
5. Built-in flexibility with free time blocks and alternative suggestions | |
6. Weather-adaptive scheduling with indoor/outdoor activity balance | |
7. Energy level considerations throughout the trip arc | |
8. Cultural timing adaptations (accommodating local siesta times, religious observances, etc.) | |
9. Practical logistical details (bag storage options, dress code reminders, etc.) | |
10. Local transportation guidance including transit cards, apps, and pre-booking requirements | |
11. Visual map representation showing daily movement patterns | |
12. Key phrases in local language for each day's activities | |
Format should include both a condensed overview calendar and detailed daily breakdowns with time, activity, location, notes, and contingency plans.""" | |
) | |
# ------------------------------------------------------------------------------- | |
# Helper Function to Run a Task with Full Agent & Task Information | |
# ------------------------------------------------------------------------------- | |
def run_task(task: Task, input_text: str) -> str: | |
""" | |
Executes the given task using the associated agent's LLM and returns the response content. | |
""" | |
try: | |
if not isinstance(task, Task): | |
raise ValueError(f"Expected 'task' to be an instance of Task, got {type(task)}") | |
if not hasattr(task, 'agent') or not isinstance(task.agent, Agent): | |
raise ValueError("Task must have a valid 'agent' attribute of type Agent.") | |
system_input = ( | |
f"Agent Details:\n" | |
f"Role: {task.agent.role}\n" | |
f"Goal: {task.agent.goal}\n" | |
f"Backstory: {task.agent.backstory}\n" | |
f"Personality: {task.agent.personality}\n" | |
) | |
task_input = ( | |
f"Task Details:\n" | |
f"Task Description: {task.description}\n" | |
f"Expected Output: {task.expected_output}\n" | |
f"Input for Task:\n{input_text}\n" | |
) | |
messages = [ | |
SystemMessage(content=system_input), | |
HumanMessage(content=task_input) | |
] | |
response = task.agent.llm.invoke(messages) | |
if not response or not response.content: | |
raise ValueError("Empty response from LLM.") | |
return response.content | |
except Exception as e: | |
logging.error(f"Error in task '{task.agent.role}': {e}") | |
return f"Error in {task.agent.role}: {e}" | |
# ------------------------------------------------------------------------------- | |
# User Input Functions | |
# ------------------------------------------------------------------------------- | |
def get_user_input() -> dict: | |
""" | |
Collects user input for travel itinerary generation. | |
""" | |
print("\n=== Travel Itinerary Generator ===\n") | |
origin = input("Enter your origin city/country: ") | |
destination = input("Enter your destination city/country: ") | |
duration = input("Enter trip duration (number of days): ") | |
budget = input("Enter your budget level (budget, moderate, luxury): ") | |
print("\nEnter your travel preferences and interests (comma-separated):") | |
print("Examples: museums, hiking, food, shopping, beaches, history, nightlife, family-friendly, etc.") | |
preferences = input("> ") | |
special_requirements = input("\nAny special requirements or notes (dietary restrictions, accessibility needs, etc.)? ") | |
return { | |
"origin": origin, | |
"destination": destination, | |
"duration": duration, | |
"budget": budget, | |
"preferences": preferences, | |
"special_requirements": special_requirements | |
} | |
# ------------------------------------------------------------------------------- | |
# Main Function to Generate Travel Itinerary | |
# ------------------------------------------------------------------------------- | |
def generate_travel_itinerary(user_input: dict) -> str: | |
""" | |
Generates a personalized travel itinerary by sequentially running defined tasks. | |
""" | |
print("\nGenerating your personalized travel itinerary...\n") | |
# Create input context using f-string formatting | |
input_context = ( | |
f"Travel Request Details:\n" | |
f"Origin: {user_input['origin']}\n" | |
f"Destination: {user_input['destination']}\n" | |
f"Duration: {user_input['duration']} days\n" | |
f"Budget Level: {user_input['budget']}\n" | |
f"Preferences/Interests: {user_input['preferences']}\n" | |
f"Special Requirements: {user_input['special_requirements']}\n" | |
) | |
# Step 1: Destination Research | |
print("Researching your destination...") | |
destination_info = run_task(destination_research_task, input_context) | |
print("β Destination research completed") | |
# Step 2: Accommodation Recommendations | |
print("Finding ideal accommodations...") | |
accommodation_info = run_task(accommodation_task, input_context) | |
print("β Accommodation recommendations completed") | |
# Step 3: Transportation Planning | |
print("Planning transportation...") | |
transportation_info = run_task(transportation_task, input_context) | |
print("β Transportation planning completed") | |
# Step 4: Activities & Attractions | |
print("Curating activities and attractions...") | |
activities_info = run_task(activities_task, input_context) | |
print("β Activities and attractions curated") | |
# Step 5: Dining Recommendations | |
print("Finding dining experiences...") | |
dining_info = run_task(dining_task, input_context) | |
print("β Dining recommendations completed") | |
# Step 6: Create Day-by-Day Itinerary | |
print("Creating your day-by-day itinerary...") | |
combined_info = ( | |
input_context + "\n" | |
"Destination Information:\n" + destination_info + "\n" | |
"Accommodation Options:\n" + accommodation_info + "\n" | |
"Transportation Plan:\n" + transportation_info + "\n" | |
"Recommended Activities:\n" + activities_info + "\n" | |
"Dining Recommendations:\n" + dining_info + "\n" | |
) | |
itinerary = run_task(itinerary_task, combined_info) | |
print("β Itinerary creation completed") | |
print("β Itinerary generation completed") | |
return itinerary | |
# ------------------------------------------------------------------------------- | |
# Save Itinerary to File | |
# ------------------------------------------------------------------------------- | |
def save_itinerary_to_file(itinerary: str, user_input: dict, output_dir: str = None) -> str: | |
""" | |
Saves the generated itinerary to a text file and returns the filepath. | |
""" | |
date_str = datetime.now().strftime("%Y-%m-%d") | |
filename = f"{user_input['destination'].replace(' ', '_')}_{date_str}_itinerary.txt" | |
if output_dir: | |
if not os.path.exists(output_dir): | |
try: | |
os.makedirs(output_dir) | |
logging.info(f"Created output directory: {output_dir}") | |
except Exception as e: | |
logging.error(f"Error creating directory {output_dir}: {e}") | |
return "" | |
filepath = os.path.join(output_dir, filename) | |
else: | |
filepath = filename | |
try: | |
with open(filepath, "w", encoding="utf-8") as f: | |
f.write(itinerary) | |
logging.info(f"Your itinerary has been saved as: {filepath}") | |
return filepath | |
except Exception as e: | |
logging.error(f"Error saving itinerary: {e}") | |
return "" | |
# ------------------------------------------------------------------------------- | |
# Main Function | |
# ------------------------------------------------------------------------------- | |
def main() -> None: | |
""" | |
Main entry point for the travel itinerary generator application. | |
""" | |
print("Welcome to BlockX Travel Itinerary Generator!") | |
print("This AI-powered tool will create a personalized travel itinerary based on your preferences.") | |
user_input = get_user_input() | |
print("\nWhere would you like to save the itinerary?") | |
print("Press Enter to save in the current directory, or specify a path:") | |
output_dir = input("> ").strip() or None | |
itinerary = generate_travel_itinerary(user_input) | |
filepath = save_itinerary_to_file(itinerary, user_input, output_dir) | |
if filepath: | |
print(f"\nYour personalized travel itinerary is ready! Open {filepath} to view it.") | |
print("Thank you for using BlockX Travel Itinerary Generator!") | |
if __name__ == "__main__": | |
main() | |