Spaces:
Sleeping
Sleeping
Update travel.py
Browse files
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 |
-
#
|
| 74 |
-
# -------------------------------------------------------------------------------
|
| 75 |
-
def
|
| 76 |
-
"""
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
""
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
logging.
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
#
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
)
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
)
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
personality="
|
| 172 |
-
llm=llm,
|
| 173 |
-
)
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
role="
|
| 177 |
-
goal="
|
| 178 |
-
backstory="A
|
| 179 |
-
personality="
|
| 180 |
-
llm=llm,
|
| 181 |
-
)
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
agent=
|
| 229 |
-
expected_output="
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
-
|
| 327 |
-
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
#
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
""
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
print("
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
|