Abs6187 commited on
Commit
5cae8a7
·
verified ·
1 Parent(s): c49780b

Update travel.py

Browse files
Files changed (1) hide show
  1. travel.py +629 -556
travel.py CHANGED
@@ -1,556 +1,629 @@
1
- """
2
- # travel.py - AI Agent System for Travel Planning
3
- # ----------------------------------------------
4
- #
5
- # This module implements a sophisticated multi-agent system for travel planning
6
- # with a specific focus on Indian travel destinations and experiences.
7
- #
8
- # ARCHITECTURE:
9
- # The system uses a collection of specialized AI agents, each responsible for
10
- # a different aspect of travel planning:
11
- #
12
- # 1. Destination Research Agent - Researches destinations based on user preferences
13
- # 2. Accommodation Agent - Suggests suitable accommodations meeting budget and preference constraints
14
- # 3. Transportation Agent - Plans optimal transportation between locations
15
- # 4. Activities Agent - Curates personalized activities based on interests
16
- # 5. Dining Agent - Recommends dining experiences showcasing local cuisine
17
- # 6. Itinerary Agent - Compiles all recommendations into a cohesive day-by-day plan
18
- # 7. Chatbot Agent - Provides conversational responses to travel queries
19
- #
20
- # Each agent is powered by Google's Generative AI (Gemini) and is specialized through
21
- # careful system prompting that defines its role, goals, and expected outputs.
22
- #
23
- # WORKFLOW:
24
- # 1. User submits travel preferences
25
- # 2. Each agent processes the information in sequence
26
- # 3. Final itinerary is compiled from all agent outputs
27
- # 4. Results are presented to the user as a complete travel plan
28
- #
29
- # PRIMARY FUNCTIONS:
30
- # - run_task(): Core function to execute a specific agent task
31
- # - generate_travel_itinerary(): Orchestrates the full planning process
32
- # - save_itinerary_to_file(): Saves the generated itinerary for the user
33
- #
34
- # Created by TechMatrix Solvers for IIITDMJ HackByte3.0
35
- """
36
-
37
- import os
38
- import json
39
- import logging
40
- from datetime import datetime, timedelta
41
- from langchain_google_genai import ChatGoogleGenerativeAI
42
- from langchain.schema import SystemMessage, HumanMessage
43
-
44
- # Setup logging configuration
45
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
46
-
47
- # -------------------------------------------------------------------------------
48
- # Agent and Task Classes with Type Hints and Docstrings
49
- # -------------------------------------------------------------------------------
50
- class Agent:
51
- def __init__(self, role: str, goal: str, backstory: str, personality: str = "", llm=None) -> None:
52
- """
53
- Initialize an Agent with role, goal, backstory, personality, and assigned LLM.
54
- """
55
- self.role = role
56
- self.goal = goal
57
- self.backstory = backstory
58
- self.personality = personality
59
- self.tools = [] # Initialize with empty list for future tool integrations
60
- self.llm = llm
61
-
62
- class Task:
63
- def __init__(self, description: str, agent: Agent, expected_output: str, context=None) -> None:
64
- """
65
- Initialize a Task with its description, the responsible agent, expected output, and optional context.
66
- """
67
- self.description = description
68
- self.agent = agent
69
- self.expected_output = expected_output
70
- self.context = context or []
71
-
72
- # -------------------------------------------------------------------------------
73
- # Initialize LLM
74
- # -------------------------------------------------------------------------------
75
- def initialize_llm(api_key=None):
76
- """Initialize the LLM with the provided API key or from environment variables.
77
-
78
- Args:
79
- api_key (str, optional): API key for Google Generative AI.
80
- If None, will try to get from environment variables.
81
-
82
- Returns:
83
- ChatGoogleGenerativeAI or None: Initialized LLM instance or None if initialization failed.
84
- """
85
- # First try the provided API key
86
- if api_key:
87
- google_api_key = api_key
88
- else:
89
- # Fall back to environment variable
90
- google_api_key = os.getenv("GEMINI_API_KEY")
91
-
92
- if not google_api_key:
93
- logging.warning("GEMINI_API_KEY is not set. AI functionality will be limited.")
94
- return None
95
-
96
- # Basic API key format validation
97
- if not google_api_key.startswith("AI"):
98
- logging.warning("API key format appears incorrect. Should start with 'AI'.")
99
-
100
- try:
101
- # Attempt to initialize the LLM
102
- llm_instance = ChatGoogleGenerativeAI(
103
- model="gemini-2.0-flash",
104
- google_api_key=google_api_key,
105
- temperature=0.7,
106
- top_p=0.95,
107
- top_k=40,
108
- max_output_tokens=8192
109
- )
110
- logging.info("LLM initialized successfully.")
111
- return llm_instance
112
- except Exception as e:
113
- logging.error(f"Error initializing LLM: {e}")
114
- # More detailed error message for common issues
115
- if "403" in str(e) or "401" in str(e):
116
- logging.error("Authentication error: Your API key may be invalid or expired.")
117
- elif "429" in str(e):
118
- logging.error("Rate limit exceeded: Too many requests to the API.")
119
- elif "timeout" in str(e).lower():
120
- logging.error("Request timed out: Network issue or service unavailability.")
121
- return None
122
-
123
- # Initialize with environment variable for now
124
- llm = initialize_llm()
125
-
126
- # -------------------------------------------------------------------------------
127
- # Define Travel Agents
128
- # -------------------------------------------------------------------------------
129
- destination_research_agent = Agent(
130
- role="Destination Research Agent",
131
- goal=(
132
- "Research and provide comprehensive information about the destination including popular attractions, "
133
- "local culture, weather patterns, best times to visit, and local transportation options with special focus on Indian context."
134
- ),
135
- backstory=(
136
- "An experienced travel researcher with extensive knowledge of Indian destinations. "
137
- "I specialize in uncovering both popular attractions and hidden gems that match travelers' interests."
138
- ),
139
- personality="Curious, detail-oriented, and knowledgeable about Indian cultures and travel trends.",
140
- llm=llm,
141
- )
142
-
143
- accommodation_agent = Agent(
144
- role="Accommodation Agent",
145
- goal="Find and recommend suitable accommodations based on the traveler's preferences, budget, and location requirements with focus on Indian hospitality options.",
146
- backstory="A hospitality expert who understands different types of accommodations in India and can match travelers with their ideal places to stay.",
147
- personality="Attentive, resourceful, and focused on comfort and value.",
148
- llm=llm,
149
- )
150
-
151
- transportation_agent = Agent(
152
- role="Transportation Agent",
153
- goal="Plan efficient transportation between the origin, destination, and all points of interest in the itinerary using Indian transportation networks.",
154
- backstory="A logistics specialist with knowledge of India's transportation systems, from flights to local transit options including trains, buses, and auto-rickshaws.",
155
- personality="Efficient, practical, and detail-oriented.",
156
- llm=llm,
157
- )
158
-
159
- activities_agent = Agent(
160
- role="Activities & Attractions Agent",
161
- goal="Curate personalized activities and attractions that align with the traveler's interests, preferences, and time constraints in Indian destinations.",
162
- backstory="An enthusiastic explorer who has experienced diverse activities across India and knows how to match experiences to individual preferences.",
163
- personality="Enthusiastic, creative, and personable.",
164
- llm=llm,
165
- )
166
-
167
- dining_agent = Agent(
168
- role="Dining & Culinary Agent",
169
- goal="Recommend dining experiences that showcase local Indian cuisine while accommodating dietary preferences and budget considerations.",
170
- backstory="A culinary expert with knowledge of India's diverse food scenes and an appreciation for authentic local dining experiences.",
171
- personality="Passionate about food, culturally aware, and attentive to preferences.",
172
- llm=llm,
173
- )
174
-
175
- itinerary_agent = Agent(
176
- role="Itinerary Integration Agent",
177
- goal="Compile all recommendations into a cohesive, day-by-day itinerary that optimizes time, minimizes travel fatigue, and maximizes enjoyment for travel in India.",
178
- backstory="A master travel planner who understands how to balance activities, rest, and logistics to create the perfect Indian travel experience.",
179
- personality="Organized, balanced, and practical.",
180
- llm=llm,
181
- )
182
-
183
- # -------------------------------------------------------------------------------
184
- # Define Chatbot Agent and Task for Interactive Conversation
185
- # -------------------------------------------------------------------------------
186
- chatbot_agent = Agent(
187
- role="Chatbot Agent",
188
- goal="Engage in interactive conversation to answer travel-related queries about India.",
189
- backstory="A conversational AI assistant who provides instant, accurate travel information and recommendations for Indian destinations.",
190
- personality="Friendly, conversational, and knowledgeable about travel in India.",
191
- llm=llm,
192
- )
193
-
194
- chatbot_task = Task(
195
- description="Provide a conversational and detailed response to travel-related queries about Indian destinations.",
196
- agent=chatbot_agent,
197
- expected_output="A friendly, helpful response to the user's query about travel in India."
198
- )
199
-
200
- # -------------------------------------------------------------------------------
201
- # Define Other Travel Tasks
202
- # -------------------------------------------------------------------------------
203
- destination_research_task = Task(
204
- description="""Research {destination} thoroughly, considering the traveler's interests in {preferences}.
205
-
206
- Efficient research parameters:
207
- - Prioritize research in these critical categories:
208
- * Top attractions that match specific {preferences} (not generic lists)
209
- * Local transportation systems with cost-efficiency analysis
210
- * Neighborhood breakdown with accommodation recommendations by budget tier
211
- * Seasonal considerations for the specific travel dates
212
- * Safety assessment with specific areas to embrace or avoid
213
- * Cultural norms that impact visitor experience (dress codes, tipping, etiquette)
214
-
215
- - Apply efficiency filters:
216
- * Focus exclusively on verified information from official tourism boards, recent travel guides, and reliable local sources
217
- * Analyze recent visitor reviews (< 6 months old) to identify changing conditions
218
- * Evaluate price-to-experience value for attractions instead of just popularity
219
- * Identify logistical clusters where multiple interests can be satisfied efficiently
220
- * Research off-peak times for popular attractions to minimize waiting
221
- * Evaluate digital tools (apps, passes, reservation systems) that streamline the visit
222
-
223
- - Create practical knowledge matrices:
224
- * Transportation method comparison (cost vs. time vs. convenience)
225
- * Weather impact on specific activities
226
- * Budget allocation recommendations based on preference priorities
227
- * Time-saving opportunity identification""",
228
- agent=destination_research_agent,
229
- expected_output="""Targeted destination brief containing:
230
- 1. Executive summary highlighting the 5 most relevant aspects based on {preferences}
231
- 2. Neighborhood analysis with accommodation recommendations mapped to specific interests
232
- 3. Transportation efficiency guide with cost/convenience matrix
233
- 4. Cultural briefing focusing only on need-to-know information that impacts daily activities
234
- 5. Seasonal advantages and challenges specific to travel dates
235
- 6. Digital resource toolkit (essential apps, websites, reservation systems)
236
- 7. Budget optimization strategies with price ranges for key experiences
237
- 8. Safety and health quick-reference including emergency contacts
238
- 9. Logistics efficiency map showing optimal activity clustering
239
- 10. Local insider advantage recommendations that save time or money
240
-
241
- Format should prioritize scannable information with bullet points, comparison tables, and decision matrices rather than lengthy prose."""
242
- )
243
-
244
- accommodation_task = Task(
245
- description="Find suitable accommodations in {destination} based on a {budget} budget and preferences for {preferences}. Focus on Indian accommodations including heritage hotels, homestays, and modern options.",
246
- agent=accommodation_agent,
247
- expected_output="List of recommended accommodations with details on location, amenities, price range, and availability."
248
- )
249
-
250
- transportation_task = Task(
251
- description="Plan transportation from {origin} to {destination} and local transportation options during the stay. Include Indian railways, local buses, metro systems, and ride-hailing services where available.",
252
- agent=transportation_agent,
253
- expected_output="Transportation plan including flights/routes to the destination and recommendations for getting around locally."
254
- )
255
-
256
- activities_task = Task(
257
- description="""Suggest activities and attractions in {destination} that align with interests in {preferences}.
258
-
259
- Detailed requirements:
260
- - Categorize activities into: Cultural Experiences, Outdoor Adventures, Culinary Experiences,
261
- Entertainment & Nightlife, Family-Friendly Activities, and Local Hidden Gems
262
- - For each activity, include:
263
- * Detailed description with historical/cultural context where relevant
264
- * Precise location with neighborhood information
265
- * Operating hours with seasonal variations noted
266
- * Pricing information with different ticket options/packages
267
- * Accessibility considerations for travelers with mobility limitations
268
- * Recommended duration for the activity (minimum and ideal time)
269
- * Best time of day/week/year to visit
270
- * Crowd levels by season
271
- * Photography opportunities and restrictions
272
- * Required reservations or booking windows
273
- - Include a mix of iconic must-see attractions and off-the-beaten-path experiences
274
- - Consider weather patterns in {destination} during travel period
275
- - Analyze the {preferences} to match specific personality types and interest levels
276
- - Include at least 2-3 rainy day alternatives for outdoor activities
277
- - Provide local transportation options to reach each attraction
278
- - Note authentic local experiences that provide cultural immersion
279
- - Flag any activities requiring special equipment, permits, or physical fitness levels""",
280
- agent=activities_agent,
281
- expected_output="""Comprehensive curated list of activities and attractions with:
282
- 1. Clear categorization by type (cultural, outdoor, culinary, entertainment, family-friendly, hidden gems)
283
- 2. Essential details for planning (location, timing, cost, requirements)
284
- 3. Special recommendations based on {preferences} with personalized suggestions
285
- 4. Weather contingency options
286
- 5. Insider tips for maximizing experience value
287
- 6. Logistical guidance for efficient navigation between attractions"""
288
- )
289
-
290
- dining_task = Task(
291
- description="""Recommend dining options in {destination} that showcase local cuisine while accommodating {preferences} with special attention to authentic Indian culinary experiences.
292
-
293
- For each recommended dining establishment, provide:
294
- - Name, location, and cuisine type
295
- - Signature dishes and culinary specialties
296
- - Price range and value assessment
297
- - Ambiance description and dress code if applicable
298
- - Operating hours and reservation policies
299
- - Local popularity vs. tourist popularity
300
- - Special dietary accommodations (vegetarian, vegan, gluten-free, etc.)
301
- - Authentic cultural dining experiences that provide insight into local life""",
302
- agent=dining_agent,
303
- expected_output="""Curated dining guide for {destination} with:
304
- 1. Top restaurant recommendations categorized by meal type, cuisine, and price point
305
- 2. Must-try local dishes specific to the region
306
- 3. Unique dining experiences that reflect local culture
307
- 4. Budget-friendly options with exceptional value
308
- 5. Strategic meal timing to complement activity schedule
309
- 6. Advance reservation requirements where applicable
310
- 7. Special dietary consideration options
311
- 8. Hidden gems frequented by locals"""
312
- )
313
-
314
- itinerary_task = Task(
315
- description="""Create a day-by-day itinerary for a {duration}-day trip to {destination} based on all previous research and recommendations. Focus on authentic experiences that reflect true Indian culture and heritage.
316
-
317
- The itinerary should:
318
- - Balance activities with rest and travel time
319
- - Group attractions by geographic proximity to minimize transit time
320
- - Consider opening hours, best times to visit, and seasonal factors
321
- - Include meal recommendations that complement the day's activities
322
- - Provide contingency options for weather or unexpected closures
323
- - Balance must-see attractions with authentic local experiences
324
- - Account for {preferences} in prioritizing experiences
325
- - Include precise timing, transportation methods, and logistical details
326
- - Optimize for the traveler's interests while maintaining a realistic pace
327
- - Incorporate free time for exploration and spontaneity""",
328
- agent=itinerary_agent,
329
- expected_output="""Complete day-by-day itinerary containing:
330
- 1. Daily schedule with timing, locations, and activities
331
- 2. Transportation details between all points
332
- 3. Meal recommendations with timing and cuisine type
333
- 4. Activity duration estimates with buffer time included
334
- 5. Alternative options for flexibility
335
- 6. Booking/reservation requirements with deadlines
336
- 7. Cost estimates for budgeting purposes
337
- 8. Local cultural insights for each experience
338
- 9. Strategic planning notes (best photo spots, crowd avoidance, etc.)
339
- 10. Evening entertainment options
340
-
341
- Format should be scannable with clear headings, timing, and logistical details for easy reference during travel."""
342
- )
343
-
344
- # -------------------------------------------------------------------------------
345
- # Helper Function to Run a Task with Full Agent & Task Information
346
- # -------------------------------------------------------------------------------
347
- def run_task(task: Task, input_text: str, api_key=None) -> str:
348
- """
349
- Run an agent task with the given input text and API key.
350
-
351
- Args:
352
- task: The Task to run
353
- input_text: User input text
354
- api_key: Optional Gemini API key to use
355
-
356
- Returns:
357
- str: The generated response or error message
358
- """
359
- # Check if we need to initialize or reinitialize the LLM
360
- current_llm = initialize_llm(api_key)
361
- if current_llm:
362
- # Update the agent's LLM
363
- task.agent.llm = current_llm
364
- elif not task.agent.llm:
365
- logging.error("No valid API key provided")
366
- return "⚠️ API Key Error: Please enter a valid Gemini API key in the settings to access AI features."
367
-
368
- # Prepare the system prompt
369
- system_prompt = f"""
370
- # Role: {task.agent.role}
371
- # Goal: {task.agent.goal}
372
- # Backstory: {task.agent.backstory}
373
-
374
- Instructions for output:
375
- {task.expected_output}
376
- """
377
-
378
- messages = [
379
- SystemMessage(content=system_prompt),
380
- HumanMessage(content=input_text)
381
- ]
382
-
383
- try:
384
- # Start tracking time for possible timeout issues
385
- start_time = datetime.now()
386
-
387
- # Make the API call with timeout handling
388
- response = task.agent.llm.invoke(messages).content
389
-
390
- # Calculate response time for logging
391
- response_time = (datetime.now() - start_time).total_seconds()
392
- logging.info(f"Task '{task.description[:30]}...' completed in {response_time:.2f} seconds")
393
-
394
- return response
395
- except Exception as e:
396
- error_msg = str(e)
397
- logging.error(f"Error running task: {error_msg}")
398
-
399
- # Create user-friendly error messages based on the exception
400
- if "timeout" in error_msg.lower():
401
- return "⚠️ Request timed out. The service might be experiencing high traffic. Please try again later."
402
- elif "429" in error_msg:
403
- return "⚠️ Rate limit exceeded. Please try again in a few minutes."
404
- elif "403" in error_msg or "401" in error_msg or "authentication" in error_msg.lower():
405
- return "⚠️ API Key Error: Your API key appears to be invalid or has expired. Please update it in settings."
406
- elif "quota" in error_msg.lower():
407
- return "⚠️ API quota exceeded. Your Gemini API key has reached its usage limit."
408
- else:
409
- # Generic error message for other types of errors
410
- return f"⚠️ Error processing your request. Please try again or check your API key settings."
411
-
412
- # -------------------------------------------------------------------------------
413
- # User Input Functions
414
- # -------------------------------------------------------------------------------
415
- def get_user_input() -> dict:
416
- """
417
- Collects user input for travel itinerary generation.
418
- """
419
- print("\n=== Travel Itinerary Generator ===\n")
420
- origin = input("Enter your origin: ")
421
- destination = input("Enter your destination: ")
422
- duration = input("Enter duration in days: ")
423
-
424
- current_date = datetime.now()
425
- start_date = current_date + timedelta(days=7)
426
- end_date = start_date + timedelta(days=int(duration))
427
-
428
- preferences = input("Enter your preferences (comma separated): ")
429
- budget = input("Enter your budget: ")
430
-
431
- return {
432
- "origin": origin,
433
- "destination": destination,
434
- "duration": duration,
435
- "start_date": start_date.strftime("%Y-%m-%d"),
436
- "end_date": end_date.strftime("%Y-%m-%d"),
437
- "preferences": preferences,
438
- "budget": budget
439
- }
440
-
441
- # -------------------------------------------------------------------------------
442
- # Main Function to Generate Travel Itinerary
443
- # -------------------------------------------------------------------------------
444
- def generate_travel_itinerary(user_input: dict) -> str:
445
- """
446
- Generates a personalized travel itinerary by sequentially running defined tasks.
447
- """
448
- print("\nGenerating your personalized travel itinerary...\n")
449
-
450
- # Create input context using f-string formatting
451
- input_context = (
452
- f"Travel Request Details:\n"
453
- f"Origin: {user_input['origin']}\n"
454
- f"Destination: {user_input['destination']}\n"
455
- f"Duration: {user_input['duration']} days\n"
456
- f"Budget Level: {user_input['budget']}\n"
457
- f"Preferences/Interests: {user_input['preferences']}\n"
458
- f"Special Requirements: {user_input['special_requirements']}\n"
459
- )
460
-
461
- # Step 1: Destination Research
462
- print("Researching your destination...")
463
- destination_info = run_task(destination_research_task, input_context)
464
- print(" Destination research completed")
465
-
466
- # Step 2: Accommodation Recommendations
467
- print("Finding ideal accommodations...")
468
- accommodation_info = run_task(accommodation_task, input_context)
469
- print(" Accommodation recommendations completed")
470
-
471
- # Step 3: Transportation Planning
472
- print("Planning transportation...")
473
- transportation_info = run_task(transportation_task, input_context)
474
- print("✓ Transportation planning completed")
475
-
476
- # Step 4: Activities & Attractions
477
- print("Curating activities and attractions...")
478
- activities_info = run_task(activities_task, input_context)
479
- print("✓ Activities and attractions curated")
480
-
481
- # Step 5: Dining Recommendations
482
- print("Finding dining experiences...")
483
- dining_info = run_task(dining_task, input_context)
484
- print("✓ Dining recommendations completed")
485
-
486
- # Step 6: Create Day-by-Day Itinerary
487
- print("Creating your day-by-day itinerary...")
488
- combined_info = (
489
- input_context + "\n"
490
- "Destination Information:\n" + destination_info + "\n"
491
- "Accommodation Options:\n" + accommodation_info + "\n"
492
- "Transportation Plan:\n" + transportation_info + "\n"
493
- "Recommended Activities:\n" + activities_info + "\n"
494
- "Dining Recommendations:\n" + dining_info + "\n"
495
- )
496
- itinerary = run_task(itinerary_task, combined_info)
497
- print("✓ Itinerary creation completed")
498
- print("✓ Itinerary generation completed")
499
-
500
- return itinerary
501
-
502
- # -------------------------------------------------------------------------------
503
- # Save Itinerary to File
504
- # -------------------------------------------------------------------------------
505
- def save_itinerary_to_file(itinerary: str, user_input: dict, output_dir: str = None) -> str:
506
- """
507
- Saves the generated itinerary to a text file and returns the filepath.
508
- """
509
- date_str = datetime.now().strftime("%Y-%m-%d")
510
- filename = f"India_Travel_Itinerary_{user_input['destination']}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
511
-
512
- if output_dir:
513
- if not os.path.exists(output_dir):
514
- try:
515
- os.makedirs(output_dir)
516
- logging.info(f"Created output directory: {output_dir}")
517
- except Exception as e:
518
- logging.error(f"Error creating directory {output_dir}: {e}")
519
- return ""
520
- filepath = os.path.join(output_dir, filename)
521
- else:
522
- filepath = filename
523
-
524
- try:
525
- with open(filepath, "w", encoding="utf-8") as f:
526
- f.write(itinerary)
527
- logging.info(f"Your itinerary has been saved as: {filepath}")
528
- return filepath
529
- except Exception as e:
530
- logging.error(f"Error saving itinerary: {e}")
531
- return ""
532
-
533
- # -------------------------------------------------------------------------------
534
- # Main Function
535
- # -------------------------------------------------------------------------------
536
- def main() -> None:
537
- """
538
- Main entry point for the travel itinerary generator application.
539
- """
540
- print("Welcome to the India Travel Planner! Let's create your perfect itinerary.")
541
- user_input = get_user_input()
542
-
543
- print("\nGenerating your personalized travel itinerary...\n")
544
- itinerary = generate_travel_itinerary(user_input)
545
-
546
- print("\n" + "=" * 50)
547
- print("Your travel itinerary is ready!")
548
- print("=" * 50 + "\n")
549
-
550
- print(itinerary)
551
-
552
- output_file = save_itinerary_to_file(itinerary, user_input)
553
- print(f"\nYour itinerary has been saved to: {output_file}")
554
-
555
- if __name__ == "__main__":
556
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ # travel.py - AI Agent System for Travel Planning
3
+ # ----------------------------------------------
4
+ #
5
+ # This module implements a sophisticated multi-agent system for travel planning
6
+ # with a specific focus on Indian travel destinations and experiences.
7
+ #
8
+ # ARCHITECTURE:
9
+ # The system uses a collection of specialized AI agents, each responsible for
10
+ # a different aspect of travel planning:
11
+ #
12
+ # 1. Destination Research Agent - Researches destinations based on user preferences
13
+ # 2. Accommodation Agent - Suggests suitable accommodations meeting budget and preference constraints
14
+ # 3. Transportation Agent - Plans optimal transportation between locations
15
+ # 4. Activities Agent - Curates personalized activities based on interests
16
+ # 5. Dining Agent - Recommends dining experiences showcasing local cuisine
17
+ # 6. Itinerary Agent - Compiles all recommendations into a cohesive day-by-day plan
18
+ # 7. Chatbot Agent - Provides conversational responses to travel queries
19
+ #
20
+ # Each agent is powered by Google's Generative AI (Gemini) and is specialized through
21
+ # careful system prompting that defines its role, goals, and expected outputs.
22
+ #
23
+ # WORKFLOW:
24
+ # 1. User submits travel preferences
25
+ # 2. Each agent processes the information in sequence
26
+ # 3. Final itinerary is compiled from all agent outputs
27
+ # 4. Results are presented to the user as a complete travel plan
28
+ #
29
+ # PRIMARY FUNCTIONS:
30
+ # - run_task(): Core function to execute a specific agent task
31
+ # - generate_travel_itinerary(): Orchestrates the full planning process
32
+ # - save_itinerary_to_file(): Saves the generated itinerary for the user
33
+ #
34
+ # Created by TechMatrix Solvers for IIITDMJ HackByte3.0
35
+ """
36
+
37
+ import os
38
+ import json
39
+ import logging
40
+ from datetime import datetime, timedelta
41
+ from langchain_google_genai import ChatGoogleGenerativeAI
42
+ from langchain.schema import SystemMessage, HumanMessage
43
+
44
+ # Setup logging configuration
45
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
46
+
47
+ # -------------------------------------------------------------------------------
48
+ # Agent and Task Classes with Type Hints and Docstrings
49
+ # -------------------------------------------------------------------------------
50
+ class Agent:
51
+ def __init__(self, role: str, goal: str, backstory: str, personality: str = "", llm=None) -> None:
52
+ """
53
+ Initialize an Agent with role, goal, backstory, personality, and assigned LLM.
54
+ """
55
+ self.role = role
56
+ self.goal = goal
57
+ self.backstory = backstory
58
+ self.personality = personality
59
+ self.tools = [] # Initialize with empty list for future tool integrations
60
+ self.llm = llm
61
+
62
+ class Task:
63
+ def __init__(self, description: str, agent: Agent, expected_output: str, context=None) -> None:
64
+ """
65
+ Initialize a Task with its description, the responsible agent, expected output, and optional context.
66
+ """
67
+ self.description = description
68
+ self.agent = agent
69
+ self.expected_output = expected_output
70
+ self.context = context or []
71
+
72
+ # -------------------------------------------------------------------------------
73
+ # API Key Management
74
+ # -------------------------------------------------------------------------------
75
+ def get_api_keys():
76
+ """Get all available Gemini API keys from environment variables.
77
+
78
+ Returns:
79
+ list: List of API keys found in environment variables.
80
+ """
81
+ api_keys = []
82
+
83
+ # Check for GEMINI_API_KEY_1 and GEMINI_API_KEY_2
84
+ key1 = os.getenv("GEMINI_API_KEY_1")
85
+ key2 = os.getenv("GEMINI_API_KEY_2")
86
+
87
+ if key1:
88
+ api_keys.append(key1)
89
+ logging.info("Found GEMINI_API_KEY_1")
90
+
91
+ if key2:
92
+ api_keys.append(key2)
93
+ logging.info("Found GEMINI_API_KEY_2")
94
+
95
+ # Fall back to GEMINI_API_KEY if no numbered keys found
96
+ if not api_keys:
97
+ fallback_key = os.getenv("GEMINI_API_KEY")
98
+ if fallback_key:
99
+ api_keys.append(fallback_key)
100
+ logging.info("Using fallback GEMINI_API_KEY")
101
+
102
+ return api_keys
103
+
104
+ # -------------------------------------------------------------------------------
105
+ # Initialize LLM
106
+ # -------------------------------------------------------------------------------
107
+ def initialize_llm(api_key=None):
108
+ """Initialize the LLM with the provided API key or from environment variables.
109
+
110
+ Args:
111
+ api_key (str, optional): API key for Google Generative AI.
112
+ If None, will try to get from environment variables.
113
+
114
+ Returns:
115
+ ChatGoogleGenerativeAI or None: Initialized LLM instance or None if initialization failed.
116
+ """
117
+ # First try the provided API key
118
+ if api_key:
119
+ google_api_key = api_key
120
+ else:
121
+ # Fall back to environment variable
122
+ google_api_key = os.getenv("GEMINI_API_KEY")
123
+
124
+ if not google_api_key:
125
+ logging.warning("GEMINI_API_KEY is not set. AI functionality will be limited.")
126
+ return None
127
+
128
+ # Basic API key format validation
129
+ if not google_api_key.startswith("AI"):
130
+ logging.warning("API key format appears incorrect. Should start with 'AI'.")
131
+
132
+ try:
133
+ # Attempt to initialize the LLM
134
+ llm_instance = ChatGoogleGenerativeAI(
135
+ model="gemini-2.5-flash",
136
+ google_api_key=google_api_key,
137
+ temperature=0.7,
138
+ top_p=0.95,
139
+ top_k=40,
140
+ max_output_tokens=8192
141
+ )
142
+ logging.info("LLM initialized successfully.")
143
+ return llm_instance
144
+ except Exception as e:
145
+ logging.error(f"Error initializing LLM: {e}")
146
+ # More detailed error message for common issues
147
+ if "403" in str(e) or "401" in str(e):
148
+ logging.error("Authentication error: Your API key may be invalid or expired.")
149
+ elif "429" in str(e):
150
+ logging.error("Rate limit exceeded: Too many requests to the API.")
151
+ elif "timeout" in str(e).lower():
152
+ logging.error("Request timed out: Network issue or service unavailability.")
153
+ return None
154
+
155
+ # Initialize with environment variable for now
156
+ llm = initialize_llm()
157
+
158
+ # -------------------------------------------------------------------------------
159
+ # Define Travel Agents
160
+ # -------------------------------------------------------------------------------
161
+ destination_research_agent = Agent(
162
+ role="Destination Research Agent",
163
+ goal=(
164
+ "Research and provide comprehensive information about the destination including popular attractions, "
165
+ "local culture, weather patterns, best times to visit, and local transportation options with special focus on Indian context."
166
+ ),
167
+ backstory=(
168
+ "An experienced travel researcher with extensive knowledge of Indian destinations. "
169
+ "I specialize in uncovering both popular attractions and hidden gems that match travelers' interests."
170
+ ),
171
+ personality="Curious, detail-oriented, and knowledgeable about Indian cultures and travel trends.",
172
+ llm=llm,
173
+ )
174
+
175
+ accommodation_agent = Agent(
176
+ role="Accommodation Agent",
177
+ goal="Find and recommend suitable accommodations based on the traveler's preferences, budget, and location requirements with focus on Indian hospitality options.",
178
+ backstory="A hospitality expert who understands different types of accommodations in India and can match travelers with their ideal places to stay.",
179
+ personality="Attentive, resourceful, and focused on comfort and value.",
180
+ llm=llm,
181
+ )
182
+
183
+ transportation_agent = Agent(
184
+ role="Transportation Agent",
185
+ goal="Plan efficient transportation between the origin, destination, and all points of interest in the itinerary using Indian transportation networks.",
186
+ backstory="A logistics specialist with knowledge of India's transportation systems, from flights to local transit options including trains, buses, and auto-rickshaws.",
187
+ personality="Efficient, practical, and detail-oriented.",
188
+ llm=llm,
189
+ )
190
+
191
+ activities_agent = Agent(
192
+ role="Activities & Attractions Agent",
193
+ goal="Curate personalized activities and attractions that align with the traveler's interests, preferences, and time constraints in Indian destinations.",
194
+ backstory="An enthusiastic explorer who has experienced diverse activities across India and knows how to match experiences to individual preferences.",
195
+ personality="Enthusiastic, creative, and personable.",
196
+ llm=llm,
197
+ )
198
+
199
+ dining_agent = Agent(
200
+ role="Dining & Culinary Agent",
201
+ goal="Recommend dining experiences that showcase local Indian cuisine while accommodating dietary preferences and budget considerations.",
202
+ backstory="A culinary expert with knowledge of India's diverse food scenes and an appreciation for authentic local dining experiences.",
203
+ personality="Passionate about food, culturally aware, and attentive to preferences.",
204
+ llm=llm,
205
+ )
206
+
207
+ itinerary_agent = Agent(
208
+ role="Itinerary Integration Agent",
209
+ goal="Compile all recommendations into a cohesive, day-by-day itinerary that optimizes time, minimizes travel fatigue, and maximizes enjoyment for travel in India.",
210
+ backstory="A master travel planner who understands how to balance activities, rest, and logistics to create the perfect Indian travel experience.",
211
+ personality="Organized, balanced, and practical.",
212
+ llm=llm,
213
+ )
214
+
215
+ # -------------------------------------------------------------------------------
216
+ # Define Chatbot Agent and Task for Interactive Conversation
217
+ # -------------------------------------------------------------------------------
218
+ chatbot_agent = Agent(
219
+ role="Chatbot Agent",
220
+ goal="Engage in interactive conversation to answer travel-related queries about India.",
221
+ backstory="A conversational AI assistant who provides instant, accurate travel information and recommendations for Indian destinations.",
222
+ personality="Friendly, conversational, and knowledgeable about travel in India.",
223
+ llm=llm,
224
+ )
225
+
226
+ chatbot_task = Task(
227
+ description="Provide a conversational and detailed response to travel-related queries about Indian destinations.",
228
+ agent=chatbot_agent,
229
+ expected_output="A friendly, helpful response to the user's query about travel in India."
230
+ )
231
+
232
+ # -------------------------------------------------------------------------------
233
+ # Define Other Travel Tasks
234
+ # -------------------------------------------------------------------------------
235
+ destination_research_task = Task(
236
+ description="""Research {destination} thoroughly, considering the traveler's interests in {preferences}.
237
+
238
+ Efficient research parameters:
239
+ - Prioritize research in these critical categories:
240
+ * Top attractions that match specific {preferences} (not generic lists)
241
+ * Local transportation systems with cost-efficiency analysis
242
+ * Neighborhood breakdown with accommodation recommendations by budget tier
243
+ * Seasonal considerations for the specific travel dates
244
+ * Safety assessment with specific areas to embrace or avoid
245
+ * Cultural norms that impact visitor experience (dress codes, tipping, etiquette)
246
+
247
+ - Apply efficiency filters:
248
+ * Focus exclusively on verified information from official tourism boards, recent travel guides, and reliable local sources
249
+ * Analyze recent visitor reviews (< 6 months old) to identify changing conditions
250
+ * Evaluate price-to-experience value for attractions instead of just popularity
251
+ * Identify logistical clusters where multiple interests can be satisfied efficiently
252
+ * Research off-peak times for popular attractions to minimize waiting
253
+ * Evaluate digital tools (apps, passes, reservation systems) that streamline the visit
254
+
255
+ - Create practical knowledge matrices:
256
+ * Transportation method comparison (cost vs. time vs. convenience)
257
+ * Weather impact on specific activities
258
+ * Budget allocation recommendations based on preference priorities
259
+ * Time-saving opportunity identification""",
260
+ agent=destination_research_agent,
261
+ expected_output="""Targeted destination brief containing:
262
+ 1. Executive summary highlighting the 5 most relevant aspects based on {preferences}
263
+ 2. Neighborhood analysis with accommodation recommendations mapped to specific interests
264
+ 3. Transportation efficiency guide with cost/convenience matrix
265
+ 4. Cultural briefing focusing only on need-to-know information that impacts daily activities
266
+ 5. Seasonal advantages and challenges specific to travel dates
267
+ 6. Digital resource toolkit (essential apps, websites, reservation systems)
268
+ 7. Budget optimization strategies with price ranges for key experiences
269
+ 8. Safety and health quick-reference including emergency contacts
270
+ 9. Logistics efficiency map showing optimal activity clustering
271
+ 10. Local insider advantage recommendations that save time or money
272
+
273
+ Format should prioritize scannable information with bullet points, comparison tables, and decision matrices rather than lengthy prose."""
274
+ )
275
+
276
+ accommodation_task = Task(
277
+ description="Find suitable accommodations in {destination} based on a {budget} budget and preferences for {preferences}. Focus on Indian accommodations including heritage hotels, homestays, and modern options.",
278
+ agent=accommodation_agent,
279
+ expected_output="List of recommended accommodations with details on location, amenities, price range, and availability."
280
+ )
281
+
282
+ transportation_task = Task(
283
+ description="Plan transportation from {origin} to {destination} and local transportation options during the stay. Include Indian railways, local buses, metro systems, and ride-hailing services where available.",
284
+ agent=transportation_agent,
285
+ expected_output="Transportation plan including flights/routes to the destination and recommendations for getting around locally."
286
+ )
287
+
288
+ activities_task = Task(
289
+ description="""Suggest activities and attractions in {destination} that align with interests in {preferences}.
290
+
291
+ Detailed requirements:
292
+ - Categorize activities into: Cultural Experiences, Outdoor Adventures, Culinary Experiences,
293
+ Entertainment & Nightlife, Family-Friendly Activities, and Local Hidden Gems
294
+ - For each activity, include:
295
+ * Detailed description with historical/cultural context where relevant
296
+ * Precise location with neighborhood information
297
+ * Operating hours with seasonal variations noted
298
+ * Pricing information with different ticket options/packages
299
+ * Accessibility considerations for travelers with mobility limitations
300
+ * Recommended duration for the activity (minimum and ideal time)
301
+ * Best time of day/week/year to visit
302
+ * Crowd levels by season
303
+ * Photography opportunities and restrictions
304
+ * Required reservations or booking windows
305
+ - Include a mix of iconic must-see attractions and off-the-beaten-path experiences
306
+ - Consider weather patterns in {destination} during travel period
307
+ - Analyze the {preferences} to match specific personality types and interest levels
308
+ - Include at least 2-3 rainy day alternatives for outdoor activities
309
+ - Provide local transportation options to reach each attraction
310
+ - Note authentic local experiences that provide cultural immersion
311
+ - Flag any activities requiring special equipment, permits, or physical fitness levels""",
312
+ agent=activities_agent,
313
+ expected_output="""Comprehensive curated list of activities and attractions with:
314
+ 1. Clear categorization by type (cultural, outdoor, culinary, entertainment, family-friendly, hidden gems)
315
+ 2. Essential details for planning (location, timing, cost, requirements)
316
+ 3. Special recommendations based on {preferences} with personalized suggestions
317
+ 4. Weather contingency options
318
+ 5. Insider tips for maximizing experience value
319
+ 6. Logistical guidance for efficient navigation between attractions"""
320
+ )
321
+
322
+ dining_task = Task(
323
+ description="""Recommend dining options in {destination} that showcase local cuisine while accommodating {preferences} with special attention to authentic Indian culinary experiences.
324
+
325
+ For each recommended dining establishment, provide:
326
+ - Name, location, and cuisine type
327
+ - Signature dishes and culinary specialties
328
+ - Price range and value assessment
329
+ - Ambiance description and dress code if applicable
330
+ - Operating hours and reservation policies
331
+ - Local popularity vs. tourist popularity
332
+ - Special dietary accommodations (vegetarian, vegan, gluten-free, etc.)
333
+ - Authentic cultural dining experiences that provide insight into local life""",
334
+ agent=dining_agent,
335
+ expected_output="""Curated dining guide for {destination} with:
336
+ 1. Top restaurant recommendations categorized by meal type, cuisine, and price point
337
+ 2. Must-try local dishes specific to the region
338
+ 3. Unique dining experiences that reflect local culture
339
+ 4. Budget-friendly options with exceptional value
340
+ 5. Strategic meal timing to complement activity schedule
341
+ 6. Advance reservation requirements where applicable
342
+ 7. Special dietary consideration options
343
+ 8. Hidden gems frequented by locals"""
344
+ )
345
+
346
+ itinerary_task = Task(
347
+ description="""Create a day-by-day itinerary for a {duration}-day trip to {destination} based on all previous research and recommendations. Focus on authentic experiences that reflect true Indian culture and heritage.
348
+
349
+ The itinerary should:
350
+ - Balance activities with rest and travel time
351
+ - Group attractions by geographic proximity to minimize transit time
352
+ - Consider opening hours, best times to visit, and seasonal factors
353
+ - Include meal recommendations that complement the day's activities
354
+ - Provide contingency options for weather or unexpected closures
355
+ - Balance must-see attractions with authentic local experiences
356
+ - Account for {preferences} in prioritizing experiences
357
+ - Include precise timing, transportation methods, and logistical details
358
+ - Optimize for the traveler's interests while maintaining a realistic pace
359
+ - Incorporate free time for exploration and spontaneity""",
360
+ agent=itinerary_agent,
361
+ expected_output="""Complete day-by-day itinerary containing:
362
+ 1. Daily schedule with timing, locations, and activities
363
+ 2. Transportation details between all points
364
+ 3. Meal recommendations with timing and cuisine type
365
+ 4. Activity duration estimates with buffer time included
366
+ 5. Alternative options for flexibility
367
+ 6. Booking/reservation requirements with deadlines
368
+ 7. Cost estimates for budgeting purposes
369
+ 8. Local cultural insights for each experience
370
+ 9. Strategic planning notes (best photo spots, crowd avoidance, etc.)
371
+ 10. Evening entertainment options
372
+
373
+ Format should be scannable with clear headings, timing, and logistical details for easy reference during travel."""
374
+ )
375
+
376
+ # -------------------------------------------------------------------------------
377
+ # Helper Function to Run a Task with Full Agent & Task Information
378
+ # -------------------------------------------------------------------------------
379
+ def run_task(task: Task, input_text: str, api_key=None) -> str:
380
+ """
381
+ Run an agent task with the given input text and API key.
382
+ Automatically switches between multiple API keys if rate limits are hit.
383
+
384
+ Args:
385
+ task: The Task to run
386
+ input_text: User input text
387
+ api_key: Optional Gemini API key to use
388
+
389
+ Returns:
390
+ str: The generated response or error message
391
+ """
392
+ # Get all available API keys
393
+ available_api_keys = get_api_keys()
394
+
395
+ # If a specific API key is provided, use it first
396
+ if api_key:
397
+ api_keys_to_try = [api_key] + [k for k in available_api_keys if k != api_key]
398
+ else:
399
+ api_keys_to_try = available_api_keys
400
+
401
+ if not api_keys_to_try:
402
+ logging.error("No valid API key provided")
403
+ return "⚠️ API Key Error: Please set GEMINI_API_KEY_1 or GEMINI_API_KEY_2 in environment variables to access AI features."
404
+
405
+ # Prepare the system prompt
406
+ system_prompt = f"""
407
+ # Role: {task.agent.role}
408
+ # Goal: {task.agent.goal}
409
+ # Backstory: {task.agent.backstory}
410
+
411
+ Instructions for output:
412
+ {task.expected_output}
413
+ """
414
+
415
+ messages = [
416
+ SystemMessage(content=system_prompt),
417
+ HumanMessage(content=input_text)
418
+ ]
419
+
420
+ # Try each API key until one succeeds
421
+ last_error = None
422
+ for idx, current_api_key in enumerate(api_keys_to_try):
423
+ try:
424
+ # Initialize LLM with current API key
425
+ current_llm = initialize_llm(current_api_key)
426
+ if not current_llm:
427
+ continue
428
+
429
+ # Update the agent's LLM
430
+ task.agent.llm = current_llm
431
+
432
+ # Start tracking time for possible timeout issues
433
+ start_time = datetime.now()
434
+
435
+ # Make the API call with timeout handling
436
+ response = task.agent.llm.invoke(messages).content
437
+
438
+ # Calculate response time for logging
439
+ response_time = (datetime.now() - start_time).total_seconds()
440
+ logging.info(f"Task '{task.description[:30]}...' completed in {response_time:.2f} seconds using API key #{idx+1}")
441
+
442
+ return response
443
+
444
+ except Exception as e:
445
+ error_msg = str(e)
446
+ last_error = error_msg
447
+
448
+ # Check if it's a rate limit error
449
+ if "429" in error_msg or "rate limit" in error_msg.lower() or "quota" in error_msg.lower():
450
+ logging.warning(f"Rate limit hit on API key #{idx+1}. Trying next API key...")
451
+
452
+ # If this is not the last API key, try the next one
453
+ if idx < len(api_keys_to_try) - 1:
454
+ logging.info(f"Switching to API key #{idx+2}")
455
+ continue
456
+ else:
457
+ logging.error("All API keys have hit rate limits")
458
+ return "⚠️ Rate limit exceeded on all API keys. Please try again in a few minutes."
459
+
460
+ # For non-rate-limit errors, log and try next key or return error
461
+ logging.error(f"Error running task with API key #{idx+1}: {error_msg}")
462
+
463
+ # For authentication errors, try next key
464
+ if "403" in error_msg or "401" in error_msg or "authentication" in error_msg.lower():
465
+ if idx < len(api_keys_to_try) - 1:
466
+ logging.info(f"Authentication failed, trying API key #{idx+2}")
467
+ continue
468
+ else:
469
+ return "⚠️ API Key Error: All API keys appear to be invalid or expired. Please update them in environment variables."
470
+
471
+ # For other errors, try next key or return error
472
+ if idx < len(api_keys_to_try) - 1:
473
+ continue
474
+
475
+ # If we've exhausted all API keys, return a user-friendly error
476
+ if last_error:
477
+ if "timeout" in last_error.lower():
478
+ return "⚠️ Request timed out. The service might be experiencing high traffic. Please try again later."
479
+ else:
480
+ return f"⚠️ Error processing your request with all available API keys. Please try again or check your API key settings."
481
+
482
+ return "⚠️ Unable to complete the request. Please check your API keys."
483
+
484
+ # -------------------------------------------------------------------------------
485
+ # User Input Functions
486
+ # -------------------------------------------------------------------------------
487
+ def get_user_input() -> dict:
488
+ """
489
+ Collects user input for travel itinerary generation.
490
+ """
491
+ print("\n=== Travel Itinerary Generator ===\n")
492
+ origin = input("Enter your origin: ")
493
+ destination = input("Enter your destination: ")
494
+ duration = input("Enter duration in days: ")
495
+
496
+ current_date = datetime.now()
497
+ start_date = current_date + timedelta(days=7)
498
+ end_date = start_date + timedelta(days=int(duration))
499
+
500
+ preferences = input("Enter your preferences (comma separated): ")
501
+ budget = input("Enter your budget: ")
502
+
503
+ return {
504
+ "origin": origin,
505
+ "destination": destination,
506
+ "duration": duration,
507
+ "start_date": start_date.strftime("%Y-%m-%d"),
508
+ "end_date": end_date.strftime("%Y-%m-%d"),
509
+ "preferences": preferences,
510
+ "budget": budget
511
+ }
512
+
513
+ # -------------------------------------------------------------------------------
514
+ # Main Function to Generate Travel Itinerary
515
+ # -------------------------------------------------------------------------------
516
+ def generate_travel_itinerary(user_input: dict) -> str:
517
+ """
518
+ Generates a personalized travel itinerary by sequentially running defined tasks.
519
+ """
520
+ print("\nGenerating your personalized travel itinerary...\n")
521
+
522
+ # Create input context using f-string formatting
523
+ input_context = (
524
+ f"Travel Request Details:\n"
525
+ f"Origin: {user_input['origin']}\n"
526
+ f"Destination: {user_input['destination']}\n"
527
+ f"Duration: {user_input['duration']} days\n"
528
+ f"Budget Level: {user_input['budget']}\n"
529
+ f"Preferences/Interests: {user_input['preferences']}\n"
530
+ f"Special Requirements: {user_input['special_requirements']}\n"
531
+ )
532
+
533
+ # Step 1: Destination Research
534
+ print("Researching your destination...")
535
+ destination_info = run_task(destination_research_task, input_context)
536
+ print("✓ Destination research completed")
537
+
538
+ # Step 2: Accommodation Recommendations
539
+ print("Finding ideal accommodations...")
540
+ accommodation_info = run_task(accommodation_task, input_context)
541
+ print("✓ Accommodation recommendations completed")
542
+
543
+ # Step 3: Transportation Planning
544
+ print("Planning transportation...")
545
+ transportation_info = run_task(transportation_task, input_context)
546
+ print(" Transportation planning completed")
547
+
548
+ # Step 4: Activities & Attractions
549
+ print("Curating activities and attractions...")
550
+ activities_info = run_task(activities_task, input_context)
551
+ print("✓ Activities and attractions curated")
552
+
553
+ # Step 5: Dining Recommendations
554
+ print("Finding dining experiences...")
555
+ dining_info = run_task(dining_task, input_context)
556
+ print("✓ Dining recommendations completed")
557
+
558
+ # Step 6: Create Day-by-Day Itinerary
559
+ print("Creating your day-by-day itinerary...")
560
+ combined_info = (
561
+ input_context + "\n"
562
+ "Destination Information:\n" + destination_info + "\n"
563
+ "Accommodation Options:\n" + accommodation_info + "\n"
564
+ "Transportation Plan:\n" + transportation_info + "\n"
565
+ "Recommended Activities:\n" + activities_info + "\n"
566
+ "Dining Recommendations:\n" + dining_info + "\n"
567
+ )
568
+ itinerary = run_task(itinerary_task, combined_info)
569
+ print("✓ Itinerary creation completed")
570
+ print("✓ Itinerary generation completed")
571
+
572
+ return itinerary
573
+
574
+ # -------------------------------------------------------------------------------
575
+ # Save Itinerary to File
576
+ # -------------------------------------------------------------------------------
577
+ def save_itinerary_to_file(itinerary: str, user_input: dict, output_dir: str = None) -> str:
578
+ """
579
+ Saves the generated itinerary to a text file and returns the filepath.
580
+ """
581
+ date_str = datetime.now().strftime("%Y-%m-%d")
582
+ filename = f"India_Travel_Itinerary_{user_input['destination']}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
583
+
584
+ if output_dir:
585
+ if not os.path.exists(output_dir):
586
+ try:
587
+ os.makedirs(output_dir)
588
+ logging.info(f"Created output directory: {output_dir}")
589
+ except Exception as e:
590
+ logging.error(f"Error creating directory {output_dir}: {e}")
591
+ return ""
592
+ filepath = os.path.join(output_dir, filename)
593
+ else:
594
+ filepath = filename
595
+
596
+ try:
597
+ with open(filepath, "w", encoding="utf-8") as f:
598
+ f.write(itinerary)
599
+ logging.info(f"Your itinerary has been saved as: {filepath}")
600
+ return filepath
601
+ except Exception as e:
602
+ logging.error(f"Error saving itinerary: {e}")
603
+ return ""
604
+
605
+ # -------------------------------------------------------------------------------
606
+ # Main Function
607
+ # -------------------------------------------------------------------------------
608
+ def main() -> None:
609
+ """
610
+ Main entry point for the travel itinerary generator application.
611
+ """
612
+ print("Welcome to the India Travel Planner! Let's create your perfect itinerary.")
613
+ user_input = get_user_input()
614
+
615
+ print("\nGenerating your personalized travel itinerary...\n")
616
+ itinerary = generate_travel_itinerary(user_input)
617
+
618
+ print("\n" + "=" * 50)
619
+ print("Your travel itinerary is ready!")
620
+ print("=" * 50 + "\n")
621
+
622
+ print(itinerary)
623
+
624
+ output_file = save_itinerary_to_file(itinerary, user_input)
625
+ print(f"\nYour itinerary has been saved to: {output_file}")
626
+
627
+ if __name__ == "__main__":
628
+ main()
629
+