aiagent / app.py
Sakshi2005's picture
Update app.py
9c1d0e0 verified
import math
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from typing import Dict, List, Tuple, Optional
import gradio as gr
from dateutil.parser import parse as parse_dt
# Optional LLM (nice-to-have)
LLM_AVAILABLE = True
try:
from transformers import pipeline
except Exception:
LLM_AVAILABLE = False
# --------------------------
# Data + Helpers
# --------------------------
VIBES = [
"Relax & Chill",
"Adventure",
"Food & Cafes",
"Culture & Museums",
"Nature & Scenic",
"Nightlife",
"Budget Backpacking",
"Luxury",
]
TRAVEL_STYLE_TEMPLATES = {
"Relax & Chill": [
"Late breakfast at a local cafe",
"Leisurely walk + scenic viewpoint",
"Spa / beach / slow afternoon",
"Sunset spot",
"Early dinner + light dessert"
],
"Adventure": [
"Early start + main adventure activity",
"Local lunch (high-energy)",
"Second activity (short hike / water sport)",
"Rest + recovery",
"Dinner near your stay"
],
"Food & Cafes": [
"Famous breakfast spot",
"Street food crawl / market",
"Specialty cafe break",
"Food experience (cooking class / tasting)",
"Dinner at a highly rated local place"
],
"Culture & Museums": [
"Main museum/heritage site",
"Neighborhood walk (old town)",
"Local lunch near cultural area",
"Second museum/gallery/temple",
"Evening performance / calm dinner"
],
"Nature & Scenic": [
"Sunrise viewpoint / park",
"Nature trail / lake / botanical garden",
"Local lunch",
"Scenic drive / cable car / waterfall",
"Sunset + dinner"
],
"Nightlife": [
"Late start brunch",
"Light sightseeing",
"Rest",
"Evening bar/club district",
"Late-night food"
],
"Budget Backpacking": [
"Free walking tour / public park",
"Local cheap eats",
"Museum on discount day / free attractions",
"Sunset viewpoint",
"Affordable dinner"
],
"Luxury": [
"Brunch + premium experience",
"Private tour / signature attraction",
"Fine dining lunch",
"High-end shopping / spa",
"Chef’s tasting / rooftop dinner"
],
}
PACKING_BASE = {
"Essentials": [
"ID/passport, tickets, hotel confirmation",
"Phone + charger + power bank",
"Cards + some cash",
"Reusable water bottle",
"Basic medicines (painkiller, ORS, band-aids)",
],
"Clothing": [
"Comfortable outfits (mix & match)",
"Light jacket / hoodie",
"Sleepwear, innerwear",
"Comfortable walking shoes",
],
"Extras": [
"Sunglasses / cap",
"Small daypack",
"Toiletries kit",
]
}
FOOD_SUGGESTIONS = {
"India": [
"Try a local thali / meals-on-a-plate place",
"Street food (only busy stalls + bottled water)",
"Regional breakfast specialty (poha/idli/paratha etc.)",
"One ‘famous’ dessert spot (jalebi, kulfi, etc.)"
],
"Generic": [
"Find a local market/food hall for cheap + variety",
"Try one signature dish of the region",
"Do one fancy meal, keep others simple",
"Carry snacks to avoid impulse spending"
]
}
def parse_start_date(s: str) -> date:
if not s or not s.strip():
return date.today()
try:
return parse_dt(s).date()
except Exception:
return date.today()
def money(x: float) -> str:
return f"{x:,.0f}" if abs(x) >= 100 else f"{x:,.2f}"
@dataclass
class TripInputs:
destination: str
days: int
start_date: date
budget: float
vibe: str
people: int
pace: str # Slow / Balanced / Packed
food_pref: str # Veg/Non-veg/Any
country_hint: str # India/Other
# --------------------------
# Optional LLM (for nicer phrasing)
# --------------------------
from functools import lru_cache
@lru_cache(maxsize=1)
def load_llm():
return pipeline("text2text-generation", model="google/flan-t5-base", device=-1)
def maybe_polish_with_llm(text: str, use_llm: bool) -> str:
if not use_llm or not LLM_AVAILABLE:
return text
try:
gen = load_llm()
prompt = (
"Rewrite this travel plan more clearly with headings and bullet points. "
"Keep all details and don't add new places. Text:\n\n" + text
)
out = gen(prompt, max_new_tokens=350, do_sample=False)[0]["generated_text"]
return out.strip() if out else text
except Exception:
return text
# --------------------------
# Core Planner (deterministic + fast)
# --------------------------
def budget_breakdown(total: float, vibe: str, pace: str) -> Dict[str, float]:
"""
Simple envelope budget. Tune these as you like.
"""
# Defaults
stay = 0.35
food = 0.25
local = 0.15
activities = 0.15
buffer = 0.10
if vibe in ["Luxury"]:
stay, food, activities = 0.45, 0.22, 0.18
local, buffer = 0.10, 0.05
elif vibe in ["Budget Backpacking"]:
stay, food, local = 0.25, 0.25, 0.20
activities, buffer = 0.15, 0.15
elif vibe in ["Nightlife"]:
food, activities = 0.22, 0.22
stay, local, buffer = 0.33, 0.13, 0.10
if pace == "Packed":
activities += 0.05
buffer -= 0.05
elif pace == "Slow":
local += 0.03
buffer += 0.02
activities -= 0.05
# normalize
parts = {"Stay": stay, "Food": food, "Local Transport": local, "Activities": activities, "Buffer": buffer}
s = sum(parts.values())
for k in parts:
parts[k] = (parts[k] / s) * total
return parts
def day_plan_blocks(vibe: str, pace: str) -> List[str]:
blocks = TRAVEL_STYLE_TEMPLATES.get(vibe, TRAVEL_STYLE_TEMPLATES["Relax & Chill"]).copy()
if pace == "Packed":
# add one extra micro-block
blocks.insert(2, "Quick extra stop (photo spot / short local experience)")
elif pace == "Slow":
# remove one block and add rest
if len(blocks) > 4:
blocks.pop(2)
blocks.insert(3, "Rest / free time")
return blocks
def packing_list(days: int, vibe: str, food_pref: str) -> str:
clothing_count = max(2, min(days, 7)) # suggest rotating laundry
lines = []
lines.append("### Packing list\n")
for section, items in PACKING_BASE.items():
lines.append(f"**{section}**")
for it in items:
lines.append(f"- {it}")
lines.append("")
lines.append("**Trip-specific**")
lines.append(f"- Tops: ~{clothing_count}, Bottoms: ~{max(2, clothing_count//2)} (repeatable)")
lines.append("- Socks/innerwear: enough for 4–5 days + quick wash plan")
if vibe in ["Adventure", "Nature & Scenic"]:
lines.append("- Light rain jacket / poncho")
lines.append("- Sunscreen + insect repellent")
lines.append("- Small first-aid kit upgrade (crepe bandage, antiseptic)")
if vibe in ["Nightlife"]:
lines.append("- One nice outfit + comfortable going-out shoes")
if food_pref.lower().startswith("veg"):
lines.append("- Dry snacks you trust (protein bars / nuts) for backup")
lines.append("")
return "\n".join(lines)
def food_suggestions(country_hint: str, food_pref: str, vibe: str) -> str:
tips = FOOD_SUGGESTIONS["India"] if country_hint == "India" else FOOD_SUGGESTIONS["Generic"]
lines = ["### Food suggestions\n"]
if food_pref.lower().startswith("veg"):
lines.append("- Filter: pure-veg / veg-friendly places; check reviews for cross-contamination if needed.")
elif food_pref.lower().startswith("non"):
lines.append("- Try regional non-veg specialties, but keep one simple meal/day to manage budget.")
else:
lines.append("- Mix local specialties + simple meals to keep spending stable.")
if vibe == "Food & Cafes":
lines.append("- Do one food crawl day: 4–6 small items instead of 1 big expensive meal.")
lines.append("- Save cafe-hopping for afternoons; mornings are better for popular breakfast spots.")
for t in tips:
lines.append(f"- {t}")
lines.append("")
return "\n".join(lines)
def build_itinerary(inp: TripInputs) -> str:
per_day = inp.budget / max(inp.days, 1)
env = budget_breakdown(inp.budget, inp.vibe, inp.pace)
lines = []
lines.append(f"# ✈️ Travel Itinerary Bot\n")
lines.append(f"**Destination:** {inp.destination}")
lines.append(f"**Days:** {inp.days} | **Start:** {inp.start_date.isoformat()} | **People:** {inp.people}")
lines.append(f"**Vibe:** {inp.vibe} | **Pace:** {inp.pace} | **Food preference:** {inp.food_pref}")
lines.append(f"**Total budget:** {money(inp.budget)} | **Budget/day (avg):** {money(per_day)}")
lines.append("")
lines.append("## Budget breakdown (envelopes)")
for k, v in env.items():
lines.append(f"- **{k}**: {money(v)}")
lines.append("")
blocks = day_plan_blocks(inp.vibe, inp.pace)
lines.append("## Day-by-day plan")
for d in range(inp.days):
day_date = inp.start_date + timedelta(days=d)
lines.append(f"### Day {d+1}{day_date.strftime('%a, %d %b %Y')}")
lines.append(f"- Morning: {blocks[0]}")
lines.append(f"- Late morning: {blocks[1]}")
lines.append(f"- Afternoon: {blocks[2]}")
lines.append(f"- Evening: {blocks[3]}")
lines.append(f"- Night: {blocks[4]}")
lines.append(f"- **Spend target today:** ~{money(per_day)} (adjust using envelopes above)")
lines.append("")
lines.append(packing_list(inp.days, inp.vibe, inp.food_pref))
lines.append(food_suggestions(inp.country_hint, inp.food_pref, inp.vibe))
lines.append("## Quick checklist")
lines.append("- Book stays near transit / main area to save time & money.")
lines.append("- Keep 10% buffer for surprises (cabs, tickets, snacks).")
lines.append("- If a day goes over budget: compensate next day by choosing free attractions + simpler meals.")
lines.append("")
lines.append("> Note: This is a planning assistant. Always verify opening hours, local rules, and safety advisories.")
return "\n".join(lines)
# --------------------------
# Gradio App
# --------------------------
def generate_plan(destination, days, start_date_str, budget, vibe, people, pace, food_pref, country_hint, use_llm):
days = int(days)
people = int(people)
budget = float(budget)
start = parse_start_date(start_date_str)
inp = TripInputs(
destination=destination.strip() or "Your destination",
days=max(1, min(days, 30)),
start_date=start,
budget=max(0.0, budget),
vibe=vibe,
people=max(1, min(people, 12)),
pace=pace,
food_pref=food_pref,
country_hint=country_hint,
)
raw = build_itinerary(inp)
final = maybe_polish_with_llm(raw, use_llm=use_llm)
return final
with gr.Blocks(title="Travel Itinerary Bot") as demo:
gr.Markdown(
"# 🧳 Travel Itinerary Bot\n"
"Enter **destination + days + budget + vibe** → get a **day-by-day itinerary**, **packing list**, and **food suggestions**.\n"
)
with gr.Row():
with gr.Column(scale=1):
destination = gr.Textbox(label="Destination", placeholder="Goa / Manali / Jaipur / Bangkok ...", value="Goa")
days = gr.Slider(1, 14, value=5, step=1, label="Number of days")
start_date_str = gr.Textbox(label="Start date (optional)", placeholder="2025-12-20 or 'next Monday'", value="")
budget = gr.Number(label="Total budget", value=20000)
vibe = gr.Dropdown(VIBES, value="Relax & Chill", label="Vibe")
pace = gr.Radio(["Slow", "Balanced", "Packed"], value="Balanced", label="Pace")
people = gr.Slider(1, 6, value=1, step=1, label="People")
food_pref = gr.Radio(["Veg", "Non-veg", "Any"], value="Any", label="Food preference")
country_hint = gr.Radio(["India", "Other"], value="India", label="Country hint (for food tips)")
use_llm = gr.Checkbox(label="Use FLAN-T5 to polish text (optional, slower)", value=False)
btn = gr.Button("Generate itinerary", variant="primary")
with gr.Column(scale=2):
output = gr.Markdown()
btn.click(
generate_plan,
inputs=[destination, days, start_date_str, budget, vibe, people, pace, food_pref, country_hint, use_llm],
outputs=[output],
)
demo.launch(share=True)