trip-planner / app.py
Amitabhab's picture
Creating dynamic file name
d460cd1
"""
Travel planner based on Agentic AI workflow.
This module deploys a portal which can customize a day to day travel itinerary
for a person using multiple specialized AI crews.
Implemented using Sambanova Cloud, Gradio and Crew AI.
A deployment is available at https://huggingface.co/spaces/sambanovasystems/trip-planner
"""
import datetime
import json
import logging
from typing import List, Tuple
import gradio as gr
import plotly.graph_objects as go
from crew import AddressSummaryCrew, TravelCrew
from db import log_query
from fpdf import FPDF
def export_pdf(input_text:str):
"""
Create a downloadable pdf for the given input text
Args:
input_text: The text that needs to be made a pdf
Result:
Downloadable pdf
"""
current_datetime = datetime.datetime.now()
# Format the current date and time as YYYY-MM-DD_HH-MM-SS
datetime_str = current_datetime.strftime("%Y-%m-%d_%H-%M-%S")
file_name = "itinerary_%s.pdf" % datetime_str
pdf = FPDF()
pdf.add_page()
pdf.set_font("helvetica", size=12)
for line in input_text.split('\n'):
clean_line = line.strip()
if clean_line.startswith('**'):
pdf.set_font("Arial", size=12, style='B')
pdf.multi_cell(0, 5, clean_line[2:].lstrip()[:-2].rstrip())
pdf.set_font("Arial", size=12, style='')
else:
pdf.multi_cell(0, 5, clean_line)
pdf.output(file_name).encode('latin-1')
return file_name
def filter_map(text_list: List[str], lat: List[str], lon: List[str]) -> go.Figure:
"""
Create a Map showing the points specified in the inputs.
Args:
text_list: List of the description of all locations that will be shown on the map.
lat: List of latitude coordinates of the locations.
lon: List of longitude coordinates of the locations.
Returns:
Figure: Map with the points specified in the inputs
"""
# Creating a map with the provided markers using their latitude and longitude coordinates.
fig = go.Figure(
go.Scattermapbox(lat=lat, lon=lon, mode='markers', marker=go.scattermapbox.Marker(size=11), hovertext=text_list)
)
# Update the map by centering it on of the the provided longitude and latitude coordinates
fig.update_layout(
mapbox_style='open-street-map',
hovermode='closest',
mapbox=dict(bearing=0, center=go.layout.mapbox.Center(lat=lat[1], lon=lon[1]), pitch=0, zoom=10),
)
return fig
def run(
origin: str,
destination: str,
arrival_date: str,
age: int,
trip_duration: int,
interests: List[str],
cuisine_preferences: List[str],
children: bool,
budget: int,
model_name:str='Meta-Llama-3.1-70B-Instruct'
) -> Tuple[str, go.Figure]:
"""
Run the specfied query using Crew AI agents.
Args:
origin: Origin city of the traveller.
destination: Destination to which the traveller is going.
arrival_date: Approximate date when the trip will begin in epoch time.
age: Age profile of traveller.
interests: Specific interests of the traveller.
cuisine_preferences: Specific cuisine preferences of the traveller.
children: Whether traveller has children travelling with them.
budget: Total budget of traveller in US Dollars.
Returns:
Returns a tuple containing the itinerary and map
"""
# Gradio Datetime is currently not working on HF
# See https://github.com/gradio-app/gradio/issues/10358
# Hece disabling datetime input and reverting back to string input
"""
if arrival_date:
arrival_date_input = datetime.datetime.fromtimestamp(arrival_date).strftime("%m-%d-%Y")
else:
arrival_date_input = None
"""
if arrival_date:
arrival_date_input = arrival_date.strip()
log_query(origin, destination, age, trip_duration, budget)
logger.info(
f'Origin: {origin}, Destination: {destination}, Arrival Date: {arrival_date_input},'
f' Age: {age}, Duration: {trip_duration},'
f' Interests: {interests}, Cuisines: {cuisine_preferences},'
f' Children: {children}, Daily Budget: {budget}, Model Name: {model_name}'
)
# Creating a dictionary of user provided preferences and providing these to the crew agents
# to work on.
user_preferences = {
'origin': origin,
'destination': destination,
'arrival_date': arrival_date_input,
'age': age,
'trip_duration': trip_duration,
'interests': interests,
'cuisine_preferences': cuisine_preferences,
'children': children,
'budget': budget,
}
result = TravelCrew(model_name).crew().kickoff(inputs=user_preferences)
"""
Now we will pass the result to a address summary crew whose job is to extract position
coordinates of the addresses (latitude and longitude), so that the addresses in the
result can be displayed in map coordinates
"""
inputs_for_address = {'text': str(result)}
addresses = AddressSummaryCrew(model_name).crew().kickoff(inputs=inputs_for_address)
"""
We have requested the crew agent to return latitude, longitude coordinates.
But the exact way the LLMs return varies. Hence we try multiple different ways of
extracting addresses in JSON format from the result.
"""
json_addresses = None
if addresses.json_dict is not None:
json_addresses = addresses.json_dict
if json_addresses is None:
try:
json_addresses = json.loads(addresses.raw)
except json.JSONDecodeError as e:
# Try with different format of result data generated with ```json and ending with ```.
try:
json_addresses = json.loads(addresses.raw[8:-4])
except json.JSONDecodeError as e:
# Try with different format of result data generated with ``` and ending with ```.
try:
json_addresses = json.loads(addresses.raw[4:-4])
except json.JSONDecodeError as e:
logger.error('Error loading Crew Output for addresses')
logger.info(addresses.raw)
return (result, None)
fig = filter_map(json_addresses['name'], json_addresses['lat'], json_addresses['lon'])
return (result, fig)
logger = logging.getLogger()
logger.setLevel(logging.INFO)
with gr.Blocks() as demo:
gr.Markdown('Use this app to create a detailed itinerary on how to explore a new place.'
' Itinerary is customized to your taste. Powered by Sambanova Cloud.')
with gr.Row():
with gr.Column(scale=1):
inp_source = gr.Textbox(label='Where are you travelling from?')
inp_dest = gr.Textbox(label='Where are you going?')
inp_cal = gr.Textbox(label='Approximate arrival date in mm-dd-yyyy')
inp_age = gr.Slider(label='Your age?', value=30, minimum=15, maximum=90, step=5)
inp_days = gr.Slider(label='How many days are you travelling?', value=5, minimum=1, maximum=14, step=1)
inp_interests =\
gr.CheckboxGroup(
[
'Museums',
'Outdoor Adventures',
'Shopping',
'Children\'s Entertainment',
'Off the beat activities',
'Night Life',
],
label='Checkbox your interests.',
)
inp_cuisine =\
gr.CheckboxGroup(
[
'Ethnic',
'American',
'Italian',
'Mexican',
'Chinese',
'Japanese',
'Indian',
'Thai',
'French',
'Vietnamese',
'Vegan',
],
label='Checkbox your cuisine preferences.',
)
inp_children = gr.Checkbox(label='Check if children are travelling with you')
inp_budget =\
gr.Slider(
label='Total budget of trip in USD', show_label=True, value=1000, minimum=500, maximum=10000, step=500
)
inp_model = gr.Textbox(value="Meta-Llama-3.1-70B-Instruct", label='Sambanova Model Name')
button = gr.Button("Plan your Trip")
inputs = [inp_source, inp_dest, inp_cal, inp_age, inp_days, inp_interests, inp_cuisine, inp_children, inp_budget, inp_model]
with gr.Column(scale=2):
output_itinerary =\
gr.Textbox(
label='Complete Personalized Itinerary of your Trip',
show_label=True,
show_copy_button=True,
autoscroll=False,
)
download_btn = gr.Button("Download Itinerary")
output_map = gr.Plot(label='Venues on a Map. Please verify with a Navigation System before traveling.')
output = [output_itinerary, output_map]
button.click(fn=run, inputs=inputs, outputs=output)
download_btn_hidden = gr.DownloadButton(visible=False, elem_id="download_btn_hidden")
download_btn.click(fn=export_pdf, inputs=output_itinerary, outputs=[download_btn_hidden]).then(fn=None, inputs=None, outputs=None, js="() => document.querySelector('#download_btn_hidden').click()")
demo.launch()