diff --git a/starter_ai_agents/ai_travel_agent/README.MD b/starter_ai_agents/ai_travel_agent/README.MD index d9b492f..cc19db7 100644 --- a/starter_ai_agents/ai_travel_agent/README.MD +++ b/starter_ai_agents/ai_travel_agent/README.MD @@ -5,6 +5,7 @@ This Streamlit app is an AI-powered travel Agent that generates personalized tra - Research and discover exciting travel destinations, activities, and accommodations - Customize your itinerary based on the number of days you want to travel - Utilize the power of GPT-4o to generate intelligent and personalized travel plans +- Download your itinerary as a calendar (.ics) file to import into Google Calendar, Apple Calendar, or other calendar apps ### How to get Started? @@ -32,8 +33,29 @@ pip install -r requirements.txt streamlit run travel_agent.py ``` +For local LLM usage (with Ollama): +```bash +streamlit run local_travel_agent.py +``` + ### How it Works? The AI Travel Agent has two main components: -- Researcher: Responsible for generating search terms based on the user's destination and travel duration, and searching the web for relevant activities and accommodations using SerpAPI. -- Planner: Takes the research results and user preferences to generate a personalized draft itinerary that includes suggested activiti \ No newline at end of file +- **Researcher:** Responsible for generating search terms based on the user's destination and travel duration, and searching the web for relevant activities and accommodations using SerpAPI. +- **Planner:** Takes the research results and user preferences to generate a personalized draft itinerary that includes suggested activities, dining options, and accommodations. + +### Using the Calendar Download Feature + +After generating your travel itinerary: +1. Click the "Download Itinerary as Calendar (.ics)" button that appears next to the "Generate Itinerary" button +2. Save the .ics file to your computer +3. Import the file into your preferred calendar application (Google Calendar, Apple Calendar, Outlook, etc.) +4. Each day of your itinerary will appear as an all-day event in your calendar +5. The complete details for each day's activities are included in the event description + +This feature makes it easy to keep track of your travel plans and have your itinerary available on all your devices, even offline. + +### Local vs Cloud Version + +- **travel_agent.py**: Uses OpenAI's GPT-4o for high-quality itineraries (requires OpenAI API key) +- **local_travel_agent.py**: Uses Ollama for local LLM inference without sending data to external APIs (requires Ollama to be installed and running) \ No newline at end of file diff --git a/starter_ai_agents/ai_travel_agent/local_travel_agent.py b/starter_ai_agents/ai_travel_agent/local_travel_agent.py index e56a91c..25f086d 100644 --- a/starter_ai_agents/ai_travel_agent/local_travel_agent.py +++ b/starter_ai_agents/ai_travel_agent/local_travel_agent.py @@ -2,12 +2,70 @@ from textwrap import dedent from agno.agent import Agent from agno.tools.serpapi import SerpApiTools import streamlit as st +import re from agno.models.ollama import Ollama +from icalendar import Calendar, Event +from datetime import datetime, timedelta + + +def generate_ics_content(plan_text:str, start_date: datetime = None) -> bytes: + """ + Generate an ICS calendar file from a travel itinerary text. + + Args: + plan_text: The travel itinerary text + start_date: Optional start date for the itinerary (defaults to today) + + Returns: + bytes: The ICS file content as bytes + """ + cal = Calendar() + cal.add('prodid','-//AI Travel Planner//github.com//' ) + cal.add('version', '2.0') + + if start_date is None: + start_date = datetime.today() + + # Split the plan into days + day_pattern = re.compile(r'Day (\d+)[:\s]+(.*?)(?=Day \d+|$)', re.DOTALL) + days = day_pattern.findall(plan_text) + + if not days: # If no day pattern found, create a single all-day event with the entire content + event = Event() + event.add('summary', "Travel Itinerary") + event.add('description', plan_text) + event.add('dtstart', start_date.date()) + event.add('dtend', start_date.date()) + event.add("dtstamp", datetime.now()) + cal.add_component(event) + else: + # Process each day + for day_num, day_content in days: + day_num = int(day_num) + current_date = start_date + timedelta(days=day_num - 1) + + # Create a single event for the entire day + event = Event() + event.add('summary', f"Day {day_num} Itinerary") + event.add('description', day_content.strip()) + + # Make it an all-day event + event.add('dtstart', current_date.date()) + event.add('dtend', current_date.date()) + event.add("dtstamp", datetime.now()) + cal.add_component(event) + + return cal.to_ical() + # Set up the Streamlit app -st.title("AI Travel Planner using Llama-3.2 ✈️") +st.title("AI Travel Planner using Llama-3.2 ") st.caption("Plan your next adventure with AI Travel Planner by researching and planning a personalized itinerary on autopilot using local Llama-3") +# Initialize session state to store the generated itinerary +if 'itinerary' not in st.session_state: + st.session_state.itinerary = None + # Get SerpAPI key from the user serp_api_key = st.text_input("Enter Serp API Key for Search functionality", type="password") @@ -15,7 +73,7 @@ if serp_api_key: researcher = Agent( name="Researcher", role="Searches for travel destinations, activities, and accommodations based on user preferences", - model=Ollama(id="llama3.2", max_tokens=1024), + model=Ollama(id="llama3.2"), description=dedent( """\ You are a world-class travel researcher. Given a travel destination and the number of days the user wants to travel for, @@ -35,7 +93,7 @@ if serp_api_key: planner = Agent( name="Planner", role="Generates a draft itinerary based on user preferences and research results", - model=Ollama(id="llama3.2", max_tokens=1024), + model=Ollama(id="llama3.2"), description=dedent( """\ You are a senior travel planner. Given a travel destination, the number of days the user wants to travel for, and a list of research results, @@ -57,8 +115,27 @@ if serp_api_key: destination = st.text_input("Where do you want to go?") num_days = st.number_input("How many days do you want to travel for?", min_value=1, max_value=30, value=7) - if st.button("Generate Itinerary"): - with st.spinner("Processing..."): - # Get the response from the assistant - response = planner.run(f"{destination} for {num_days} days", stream=False) - st.write(response.content) \ No newline at end of file + col1, col2 = st.columns(2) + + with col1: + if st.button("Generate Itinerary"): + with st.spinner("Processing..."): + # Get the response from the assistant + response = planner.run(f"{destination} for {num_days} days", stream=False) + # Store the response in session state + st.session_state.itinerary = response.content + st.write(response.content) + + # Only show download button if there's an itinerary + with col2: + if st.session_state.itinerary: + # Generate the ICS file + ics_content = generate_ics_content(st.session_state.itinerary) + + # Provide the file for download + st.download_button( + label="Download Itinerary as Calendar (.ics)", + data=ics_content, + file_name="travel_itinerary.ics", + mime="text/calendar" + ) \ No newline at end of file diff --git a/starter_ai_agents/ai_travel_agent/requirements.txt b/starter_ai_agents/ai_travel_agent/requirements.txt index ffff278..2e438f6 100644 --- a/starter_ai_agents/ai_travel_agent/requirements.txt +++ b/starter_ai_agents/ai_travel_agent/requirements.txt @@ -1,4 +1,5 @@ streamlit agno openai -google-search-results \ No newline at end of file +google-search-results +icalendar \ No newline at end of file diff --git a/starter_ai_agents/ai_travel_agent/travel_agent.py b/starter_ai_agents/ai_travel_agent/travel_agent.py index eab9bbf..b43dc53 100644 --- a/starter_ai_agents/ai_travel_agent/travel_agent.py +++ b/starter_ai_agents/ai_travel_agent/travel_agent.py @@ -2,12 +2,69 @@ from textwrap import dedent from agno.agent import Agent from agno.tools.serpapi import SerpApiTools import streamlit as st +import re from agno.models.openai import OpenAIChat +from icalendar import Calendar, Event +from datetime import datetime, timedelta + + +def generate_ics_content(plan_text:str, start_date: datetime = None) -> bytes: + """ + Generate an ICS calendar file from a travel itinerary text. + + Args: + plan_text: The travel itinerary text + start_date: Optional start date for the itinerary (defaults to today) + + Returns: + bytes: The ICS file content as bytes + """ + cal = Calendar() + cal.add('prodid','-//AI Travel Planner//github.com//' ) + cal.add('version', '2.0') + + if start_date is None: + start_date = datetime.today() + + # Split the plan into days + day_pattern = re.compile(r'Day (\d+)[:\s]+(.*?)(?=Day \d+|$)', re.DOTALL) + days = day_pattern.findall(plan_text) + + if not days: # If no day pattern found, create a single all-day event with the entire content + event = Event() + event.add('summary', "Travel Itinerary") + event.add('description', plan_text) + event.add('dtstart', start_date.date()) + event.add('dtend', start_date.date()) + event.add("dtstamp", datetime.now()) + cal.add_component(event) + else: + # Process each day + for day_num, day_content in days: + day_num = int(day_num) + current_date = start_date + timedelta(days=day_num - 1) + + # Create a single event for the entire day + event = Event() + event.add('summary', f"Day {day_num} Itinerary") + event.add('description', day_content.strip()) + + # Make it an all-day event + event.add('dtstart', current_date.date()) + event.add('dtend', current_date.date()) + event.add("dtstamp", datetime.now()) + cal.add_component(event) + + return cal.to_ical() # Set up the Streamlit app -st.title("AI Travel Planner ✈️") +st.title("AI Travel Planner ") st.caption("Plan your next adventure with AI Travel Planner by researching and planning a personalized itinerary on autopilot using GPT-4o") +# Initialize session state to store the generated itinerary +if 'itinerary' not in st.session_state: + st.session_state.itinerary = None + # Get OpenAI API key from user openai_api_key = st.text_input("Enter OpenAI API Key to access GPT-4o", type="password") @@ -60,22 +117,41 @@ if openai_api_key and serp_api_key: destination = st.text_input("Where do you want to go?") num_days = st.number_input("How many days do you want to travel for?", min_value=1, max_value=30, value=7) - if st.button("Generate Itinerary"): - with st.spinner("Researching your destination..."): - # First get research results - research_results = researcher.run(f"Research {destination} for a {num_days} day trip", stream=False) + col1, col2 = st.columns(2) + + with col1: + if st.button("Generate Itinerary"): + with st.spinner("Researching your destination..."): + # First get research results + research_results = researcher.run(f"Research {destination} for a {num_days} day trip", stream=False) + + # Show research progress + st.write(" Research completed") + + with st.spinner("Creating your personalized itinerary..."): + # Pass research results to planner + prompt = f""" + Destination: {destination} + Duration: {num_days} days + Research Results: {research_results.content} + + Please create a detailed itinerary based on this research. + """ + response = planner.run(prompt, stream=False) + # Store the response in session state + st.session_state.itinerary = response.content + st.write(response.content) + + # Only show download button if there's an itinerary + with col2: + if st.session_state.itinerary: + # Generate the ICS file + ics_content = generate_ics_content(st.session_state.itinerary) - # Show research progress - st.write("✓ Research completed") - - with st.spinner("Creating your personalized itinerary..."): - # Pass research results to planner - prompt = f""" - Destination: {destination} - Duration: {num_days} days - Research Results: {research_results.content} - - Please create a detailed itinerary based on this research. - """ - response = planner.run(prompt, stream=False) - st.write(response.content) \ No newline at end of file + # Provide the file for download + st.download_button( + label="Download Itinerary as Calendar (.ics)", + data=ics_content, + file_name="travel_itinerary.ics", + mime="text/calendar" + ) \ No newline at end of file