Spaces:
Sleeping
Sleeping
Trip Planner Agent using CrewAI
Browse files- LICENSE +21 -0
- README.md +16 -0
- agents/__pycache__/trip_agents.cpython-311.pyc +0 -0
- agents/trip_agents.py +125 -0
- app.py +152 -0
- images/trip_planner.jpg +0 -0
- pyproject.toml +18 -0
- tasks/__pycache__/trip_tasks.cpython-311.pyc +0 -0
- tasks/trip_tasks.py +91 -0
- tools/__init__.py +0 -0
- tools/__pycache__/__init__.cpython-311.pyc +0 -0
- tools/__pycache__/browser_tools.cpython-311.pyc +0 -0
- tools/__pycache__/calculator_tools.cpython-311.pyc +0 -0
- tools/__pycache__/search_tools.cpython-311.pyc +0 -0
- tools/browser_tools.py +59 -0
- tools/calculator_tools.py +18 -0
- tools/search_tools.py +52 -0
- uv.lock +0 -0
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2024 Sourangshu Pal
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Trip Planner
|
| 3 |
emoji: 🏃
|
|
|
|
| 1 |
+

|
| 2 |
+
|
| 3 |
+
# 🏖️ Trip Planner: Streamlit with CrewAI
|
| 4 |
+
|
| 5 |
+

|
| 6 |
+
|
| 7 |
+
## Introduction
|
| 8 |
+
|
| 9 |
+
Trip Planner leverages the CrewAI framework to automate and enhance the trip planning experience, a user-friendly Streamlit interface.
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
## CrewAI Framework
|
| 13 |
+
|
| 14 |
+
CrewAI simplifies the orchestration of role-playing AI agents. In VacAIgent, these agents collaboratively decide on cities and craft a complete itinerary for your trip based on specified preferences, all accessible via a streamlined Streamlit user interface.
|
| 15 |
+
|
| 16 |
+
|
| 17 |
---
|
| 18 |
title: Trip Planner
|
| 19 |
emoji: 🏃
|
agents/__pycache__/trip_agents.cpython-311.pyc
ADDED
|
Binary file (6.63 kB). View file
|
|
|
agents/trip_agents.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
from crewai import Agent
|
| 3 |
+
import re
|
| 4 |
+
import streamlit as st
|
| 5 |
+
from langchain_core.language_models.chat_models import BaseChatModel
|
| 6 |
+
from crewai import LLM
|
| 7 |
+
from tools.browser_tools import BrowserTools
|
| 8 |
+
from tools.calculator_tools import CalculatorTools
|
| 9 |
+
from tools.search_tools import SearchTools
|
| 10 |
+
from typing import Any
|
| 11 |
+
|
| 12 |
+
class TripAgents():
|
| 13 |
+
def __init__(self, llm: BaseChatModel = None,
|
| 14 |
+
gemini_api_key: str = None,
|
| 15 |
+
serper_api_key :str = None,
|
| 16 |
+
browserless_api_key: str = None):
|
| 17 |
+
|
| 18 |
+
if llm is None:
|
| 19 |
+
self.llm = LLM(model="gemini/gemini-2.0-flash", api_key=gemini_api_key)
|
| 20 |
+
else:
|
| 21 |
+
self.llm = llm
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# Initialize tools once
|
| 25 |
+
self.search_tool = SearchTools(serper_api_key = serper_api_key)
|
| 26 |
+
self.browser_tool = BrowserTools(browserless_api_key = browserless_api_key)
|
| 27 |
+
self.calculator_tool = CalculatorTools()
|
| 28 |
+
|
| 29 |
+
def city_selection_agent(self):
|
| 30 |
+
return Agent(
|
| 31 |
+
role='City Selection Expert',
|
| 32 |
+
goal='Select the best city based on weather, season, and prices',
|
| 33 |
+
backstory='An expert in analyzing travel data to pick ideal destinations',
|
| 34 |
+
tools=[self.search_tool, self.browser_tool],
|
| 35 |
+
allow_delegation=False,
|
| 36 |
+
llm=self.llm,
|
| 37 |
+
verbose=True
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
def local_expert(self):
|
| 41 |
+
return Agent(
|
| 42 |
+
role='Local Expert at this city',
|
| 43 |
+
goal='Provide the BEST insights about the selected city',
|
| 44 |
+
backstory="""A knowledgeable local guide with extensive information
|
| 45 |
+
about the city, it's attractions and customs""",
|
| 46 |
+
tools=[self.search_tool, self.browser_tool],
|
| 47 |
+
allow_delegation=False,
|
| 48 |
+
llm=self.llm,
|
| 49 |
+
verbose=True
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
def travel_concierge(self):
|
| 53 |
+
return Agent(
|
| 54 |
+
role='Amazing Travel Concierge',
|
| 55 |
+
goal="""Create the most amazing travel itineraries with budget and
|
| 56 |
+
packing suggestions for the city""",
|
| 57 |
+
backstory="""Specialist in travel planning and logistics with
|
| 58 |
+
decades of experience""",
|
| 59 |
+
tools=[self.search_tool, self.browser_tool, self.calculator_tool],
|
| 60 |
+
allow_delegation=False,
|
| 61 |
+
llm=self.llm,
|
| 62 |
+
verbose=True
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
###########################################################################################
|
| 66 |
+
# Print agent process to Streamlit app container #
|
| 67 |
+
# This portion of the code is adapted from @AbubakrChan; thank you! #
|
| 68 |
+
# https://github.com/AbubakrChan/crewai-UI-business-product-launch/blob/main/main.py#L210 #
|
| 69 |
+
###########################################################################################
|
| 70 |
+
class StreamToExpander:
|
| 71 |
+
def __init__(self, expander):
|
| 72 |
+
self.expander = expander
|
| 73 |
+
self.buffer = []
|
| 74 |
+
self.colors = ['red', 'green', 'blue', 'orange']
|
| 75 |
+
self.color_index = 0
|
| 76 |
+
|
| 77 |
+
def write(self, data):
|
| 78 |
+
# Filter out ANSI escape codes using a regular expression
|
| 79 |
+
cleaned_data = re.sub(r'\x1B\[[0-9;]*[mK]', '', data)
|
| 80 |
+
|
| 81 |
+
# Check if the data contains 'task' information
|
| 82 |
+
task_match_object = re.search(r'\"task\"\s*:\s*\"(.*?)\"', cleaned_data, re.IGNORECASE)
|
| 83 |
+
task_match_input = re.search(r'task\s*:\s*([^\n]*)', cleaned_data, re.IGNORECASE)
|
| 84 |
+
task_value = None
|
| 85 |
+
if task_match_object:
|
| 86 |
+
task_value = task_match_object.group(1)
|
| 87 |
+
elif task_match_input:
|
| 88 |
+
task_value = task_match_input.group(1).strip()
|
| 89 |
+
|
| 90 |
+
if task_value:
|
| 91 |
+
st.toast(":robot_face: " + task_value)
|
| 92 |
+
|
| 93 |
+
# Check if the text contains the specified phrase and apply color
|
| 94 |
+
if "Entering new CrewAgentExecutor chain" in cleaned_data:
|
| 95 |
+
self.color_index = (self.color_index + 1) % len(self.colors)
|
| 96 |
+
cleaned_data = cleaned_data.replace("Entering new CrewAgentExecutor chain",
|
| 97 |
+
f":{self.colors[self.color_index]}[Entering new CrewAgentExecutor chain]")
|
| 98 |
+
|
| 99 |
+
if "City Selection Expert" in cleaned_data:
|
| 100 |
+
cleaned_data = cleaned_data.replace("City Selection Expert",
|
| 101 |
+
f":{self.colors[self.color_index]}[City Selection Expert]")
|
| 102 |
+
if "Local Expert at this city" in cleaned_data:
|
| 103 |
+
cleaned_data = cleaned_data.replace("Local Expert at this city",
|
| 104 |
+
f":{self.colors[self.color_index]}[Local Expert at this city]")
|
| 105 |
+
if "Amazing Travel Concierge" in cleaned_data:
|
| 106 |
+
cleaned_data = cleaned_data.replace("Amazing Travel Concierge",
|
| 107 |
+
f":{self.colors[self.color_index]}[Amazing Travel Concierge]")
|
| 108 |
+
if "Finished chain." in cleaned_data:
|
| 109 |
+
cleaned_data = cleaned_data.replace("Finished chain.",
|
| 110 |
+
f":{self.colors[self.color_index]}[Finished chain.]")
|
| 111 |
+
|
| 112 |
+
self.buffer.append(cleaned_data)
|
| 113 |
+
if "\n" in data:
|
| 114 |
+
self.expander.markdown(''.join(self.buffer), unsafe_allow_html=True)
|
| 115 |
+
self.buffer = []
|
| 116 |
+
|
| 117 |
+
def flush(self):
|
| 118 |
+
"""Flush the buffer to the expander"""
|
| 119 |
+
if self.buffer:
|
| 120 |
+
self.expander.markdown(''.join(self.buffer), unsafe_allow_html=True)
|
| 121 |
+
self.buffer = []
|
| 122 |
+
|
| 123 |
+
def close(self):
|
| 124 |
+
"""Close the stream"""
|
| 125 |
+
self.flush()
|
app.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from crewai import Crew, LLM
|
| 2 |
+
from agents.trip_agents import TripAgents, StreamToExpander
|
| 3 |
+
from tasks.trip_tasks import TripTasks
|
| 4 |
+
import streamlit as st
|
| 5 |
+
import datetime
|
| 6 |
+
import sys
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
st.set_page_config(page_icon="✈️", layout="wide")
|
| 10 |
+
global gemini_api_key
|
| 11 |
+
global serper_api_key
|
| 12 |
+
global browserless_api_key
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def icon(emoji: str):
|
| 16 |
+
"""Shows an emoji as a Notion-style page icon."""
|
| 17 |
+
st.write(
|
| 18 |
+
f'<span style="font-size: 78px; line-height: 1">{emoji}</span>',
|
| 19 |
+
unsafe_allow_html=True,
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class TripCrew:
|
| 24 |
+
|
| 25 |
+
def __init__(self, origin, cities, date_range, interests):
|
| 26 |
+
self.cities = cities
|
| 27 |
+
self.origin = origin
|
| 28 |
+
self.interests = interests
|
| 29 |
+
# Convert date_range to string format for better handling
|
| 30 |
+
self.date_range = f"{date_range[0].strftime('%Y-%m-%d')} to {date_range[1].strftime('%Y-%m-%d')}"
|
| 31 |
+
self.output_placeholder = st.empty()
|
| 32 |
+
self.llm = LLM(model="gemini/gemini-2.0-flash")
|
| 33 |
+
|
| 34 |
+
def run(self):
|
| 35 |
+
try:
|
| 36 |
+
agents = TripAgents(llm=self.llm,
|
| 37 |
+
gemini_api_key=gemini_api_key,
|
| 38 |
+
serper_api_key=serper_api_key,
|
| 39 |
+
browserless_api_key=browserless_api_key)
|
| 40 |
+
tasks = TripTasks()
|
| 41 |
+
|
| 42 |
+
city_selector_agent = agents.city_selection_agent()
|
| 43 |
+
local_expert_agent = agents.local_expert()
|
| 44 |
+
travel_concierge_agent = agents.travel_concierge()
|
| 45 |
+
|
| 46 |
+
identify_task = tasks.identify_task(
|
| 47 |
+
city_selector_agent,
|
| 48 |
+
self.origin,
|
| 49 |
+
self.cities,
|
| 50 |
+
self.interests,
|
| 51 |
+
self.date_range
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
gather_task = tasks.gather_task(
|
| 55 |
+
local_expert_agent,
|
| 56 |
+
self.origin,
|
| 57 |
+
self.interests,
|
| 58 |
+
self.date_range
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
plan_task = tasks.plan_task(
|
| 62 |
+
travel_concierge_agent,
|
| 63 |
+
self.origin,
|
| 64 |
+
self.interests,
|
| 65 |
+
self.date_range
|
| 66 |
+
)
|
| 67 |
+
|
| 68 |
+
crew = Crew(
|
| 69 |
+
agents=[
|
| 70 |
+
city_selector_agent, local_expert_agent, travel_concierge_agent
|
| 71 |
+
],
|
| 72 |
+
tasks=[identify_task, gather_task, plan_task],
|
| 73 |
+
verbose=True
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
result = crew.kickoff()
|
| 77 |
+
self.output_placeholder.markdown(result)
|
| 78 |
+
return result
|
| 79 |
+
except Exception as e:
|
| 80 |
+
st.error(f"An error occurred: {str(e)}")
|
| 81 |
+
return None
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
if __name__ == "__main__":
|
| 85 |
+
icon("🏖️ Trekki")
|
| 86 |
+
|
| 87 |
+
st.subheader("Let AI agents plan your next vacation!",
|
| 88 |
+
divider="rainbow", anchor=False)
|
| 89 |
+
|
| 90 |
+
import datetime
|
| 91 |
+
|
| 92 |
+
today = datetime.datetime.now().date()
|
| 93 |
+
next_year = today.year + 1
|
| 94 |
+
jan_16_next_year = datetime.date(next_year, 1, 10)
|
| 95 |
+
|
| 96 |
+
with st.sidebar:
|
| 97 |
+
st.header("👇 Enter API Keys")
|
| 98 |
+
gemini_api_key = st.text_input("GEMINI_API_KEY", type="password")
|
| 99 |
+
serper_api_key = st.text_input("SERPER_API_KEY", type="password")
|
| 100 |
+
browserless_api_key = st.text_input("BROWSERLESS_API_KEY", type="password")
|
| 101 |
+
st.divider()
|
| 102 |
+
|
| 103 |
+
st.header("👇 Enter your trip details")
|
| 104 |
+
with st.form("my_form"):
|
| 105 |
+
location = st.text_input(
|
| 106 |
+
"Where are you currently located?", placeholder="San Mateo, CA")
|
| 107 |
+
cities = st.text_input(
|
| 108 |
+
"City and country are you interested in vacationing at?", placeholder="Bali, Indonesia")
|
| 109 |
+
date_range = st.date_input(
|
| 110 |
+
"Date range you are interested in traveling?",
|
| 111 |
+
min_value=today,
|
| 112 |
+
value=(today, jan_16_next_year + datetime.timedelta(days=6)),
|
| 113 |
+
format="MM/DD/YYYY",
|
| 114 |
+
)
|
| 115 |
+
interests = st.text_area("High level interests and hobbies or extra details about your trip?",
|
| 116 |
+
placeholder="2 adults who love swimming, dancing, hiking, and eating")
|
| 117 |
+
|
| 118 |
+
submitted = st.form_submit_button("Submit")
|
| 119 |
+
|
| 120 |
+
st.divider()
|
| 121 |
+
|
| 122 |
+
# Credits to joaomdmoura/CrewAI for the code: https://github.com/joaomdmoura/crewAI
|
| 123 |
+
st.sidebar.markdown(
|
| 124 |
+
"""
|
| 125 |
+
Credits to [**@joaomdmoura**](https://twitter.com/joaomdmoura)
|
| 126 |
+
for creating **crewAI** 🚀
|
| 127 |
+
""",
|
| 128 |
+
unsafe_allow_html=True
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
st.sidebar.info("Click the logo to visit GitHub repo", icon="👇")
|
| 132 |
+
st.sidebar.markdown(
|
| 133 |
+
"""
|
| 134 |
+
<a href="https://github.com/joaomdmoura/crewAI" target="_blank">
|
| 135 |
+
<img src="https://raw.githubusercontent.com/joaomdmoura/crewAI/main/docs/crewai_logo.png" alt="CrewAI Logo" style="width:100px;"/>
|
| 136 |
+
</a>
|
| 137 |
+
""",
|
| 138 |
+
unsafe_allow_html=True
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
if submitted:
|
| 143 |
+
with st.status("🤖 **Agents at work...**", state="running", expanded=True) as status:
|
| 144 |
+
with st.container(height=500, border=False):
|
| 145 |
+
sys.stdout = StreamToExpander(st)
|
| 146 |
+
trip_crew = TripCrew(location, cities, date_range, interests)
|
| 147 |
+
result = trip_crew.run()
|
| 148 |
+
status.update(label="✅ Trip Plan Ready!",
|
| 149 |
+
state="complete", expanded=False)
|
| 150 |
+
|
| 151 |
+
st.subheader("Here is your Trip Plan", anchor=False, divider="rainbow")
|
| 152 |
+
st.markdown(result)
|
images/trip_planner.jpg
ADDED
|
pyproject.toml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "trip-planner-using-crewai"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Add your description here"
|
| 5 |
+
readme = "README.md"
|
| 6 |
+
requires-python = ">=3.11"
|
| 7 |
+
dependencies = [
|
| 8 |
+
"crewai>=0.114.0",
|
| 9 |
+
"langchain>=0.3.24",
|
| 10 |
+
"langchain-community>=0.3.22",
|
| 11 |
+
"langchain-core>=0.3.56",
|
| 12 |
+
"langchain-groq>=0.3.2",
|
| 13 |
+
"requests>=2.32.3",
|
| 14 |
+
"streamlit>=1.44.1",
|
| 15 |
+
"tools>=0.1.9",
|
| 16 |
+
"unstructured>=0.17.2",
|
| 17 |
+
"uvicorn>=0.34.2",
|
| 18 |
+
]
|
tasks/__pycache__/trip_tasks.cpython-311.pyc
ADDED
|
Binary file (5.51 kB). View file
|
|
|
tasks/trip_tasks.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from crewai import Task
|
| 2 |
+
from textwrap import dedent
|
| 3 |
+
from datetime import date
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
class TripTasks():
|
| 7 |
+
def __validate_inputs(self, origin, cities, interests, date_range):
|
| 8 |
+
if not origin or not cities or not interests or not date_range:
|
| 9 |
+
raise ValueError("All input parameters must be provided")
|
| 10 |
+
return True
|
| 11 |
+
|
| 12 |
+
def identify_task(self, agent, origin, cities, interests, range):
|
| 13 |
+
self.__validate_inputs(origin, cities, interests, range)
|
| 14 |
+
return Task(description=dedent(f"""
|
| 15 |
+
Analyze and select the best city for the trip based
|
| 16 |
+
on specific criteria such as weather patterns, seasonal
|
| 17 |
+
events, and travel costs. This task involves comparing
|
| 18 |
+
multiple cities, considering factors like current weather
|
| 19 |
+
conditions, upcoming cultural or seasonal events, and
|
| 20 |
+
overall travel expenses.
|
| 21 |
+
|
| 22 |
+
Your final answer must be a detailed
|
| 23 |
+
report on the chosen city, and everything you found out
|
| 24 |
+
about it, including the actual flight costs, weather
|
| 25 |
+
forecast and attractions.
|
| 26 |
+
{self.__tip_section()}
|
| 27 |
+
|
| 28 |
+
Traveling from: {origin}
|
| 29 |
+
City Options: {cities}
|
| 30 |
+
Trip Date: {range}
|
| 31 |
+
Traveler Interests: {interests}
|
| 32 |
+
"""),
|
| 33 |
+
expected_output="A detailed report on the chosen city with flight costs, weather forecast, and attractions.",
|
| 34 |
+
agent=agent)
|
| 35 |
+
|
| 36 |
+
def gather_task(self, agent, origin, interests, range):
|
| 37 |
+
return Task(description=dedent(f"""
|
| 38 |
+
As a local expert on this city you must compile an
|
| 39 |
+
in-depth guide for someone traveling there and wanting
|
| 40 |
+
to have THE BEST trip ever!
|
| 41 |
+
Gather information about key attractions, local customs,
|
| 42 |
+
special events, and daily activity recommendations.
|
| 43 |
+
Find the best spots to go to, the kind of place only a
|
| 44 |
+
local would know.
|
| 45 |
+
This guide should provide a thorough overview of what
|
| 46 |
+
the city has to offer, including hidden gems, cultural
|
| 47 |
+
hotspots, must-visit landmarks, weather forecasts, and
|
| 48 |
+
high level costs.
|
| 49 |
+
|
| 50 |
+
The final answer must be a comprehensive city guide,
|
| 51 |
+
rich in cultural insights and practical tips,
|
| 52 |
+
tailored to enhance the travel experience.
|
| 53 |
+
{self.__tip_section()}
|
| 54 |
+
|
| 55 |
+
Trip Date: {range}
|
| 56 |
+
Traveling from: {origin}
|
| 57 |
+
Traveler Interests: {interests}
|
| 58 |
+
"""),
|
| 59 |
+
expected_output="A comprehensive city guide with cultural insights and practical tips.",
|
| 60 |
+
agent=agent)
|
| 61 |
+
|
| 62 |
+
def plan_task(self, agent, origin, interests, range):
|
| 63 |
+
return Task(description=dedent(f"""
|
| 64 |
+
Expand this guide into a full travel
|
| 65 |
+
itinerary for this time {range} with detailed per-day plans, including
|
| 66 |
+
weather forecasts, places to eat, packing suggestions,
|
| 67 |
+
and a budget breakdown.
|
| 68 |
+
|
| 69 |
+
You MUST suggest actual places to visit, actual hotels
|
| 70 |
+
to stay and actual restaurants to go to.
|
| 71 |
+
|
| 72 |
+
This itinerary should cover all aspects of the trip,
|
| 73 |
+
from arrival to departure, integrating the city guide
|
| 74 |
+
information with practical travel logistics.
|
| 75 |
+
|
| 76 |
+
Your final answer MUST be a complete expanded travel plan,
|
| 77 |
+
formatted as markdown, encompassing a daily schedule,
|
| 78 |
+
anticipated weather conditions, recommended clothing and
|
| 79 |
+
items to pack, and a detailed budget, ensuring THE BEST
|
| 80 |
+
TRIP EVER, Be specific and give it a reason why you picked
|
| 81 |
+
# up each place, what make them special! {self.__tip_section()}
|
| 82 |
+
|
| 83 |
+
Trip Date: {range}
|
| 84 |
+
Traveling from: {origin}
|
| 85 |
+
Traveler Interests: {interests}
|
| 86 |
+
"""),
|
| 87 |
+
expected_output="A complete 7-day travel plan, formatted as markdown, with a daily schedule and budget.",
|
| 88 |
+
agent=agent)
|
| 89 |
+
|
| 90 |
+
def __tip_section(self):
|
| 91 |
+
return "If you do your BEST WORK, I'll tip you $100 and grant you any wish you want!"
|
tools/__init__.py
ADDED
|
File without changes
|
tools/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (198 Bytes). View file
|
|
|
tools/__pycache__/browser_tools.cpython-311.pyc
ADDED
|
Binary file (4.62 kB). View file
|
|
|
tools/__pycache__/calculator_tools.cpython-311.pyc
ADDED
|
Binary file (1.84 kB). View file
|
|
|
tools/__pycache__/search_tools.cpython-311.pyc
ADDED
|
Binary file (3.42 kB). View file
|
|
|
tools/browser_tools.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import requests
|
| 3 |
+
import streamlit as st
|
| 4 |
+
from crewai.tools import BaseTool
|
| 5 |
+
from pydantic import BaseModel, Field
|
| 6 |
+
from unstructured.partition.html import partition_html
|
| 7 |
+
from crewai import Agent, Task
|
| 8 |
+
#from langchain_openai import ChatOpenAI
|
| 9 |
+
from langchain_groq import ChatGroq
|
| 10 |
+
from crewai import LLM
|
| 11 |
+
|
| 12 |
+
class WebsiteInput(BaseModel):
|
| 13 |
+
website: str = Field(..., description="The website URL to scrape")
|
| 14 |
+
|
| 15 |
+
class BrowserTools(BaseTool):
|
| 16 |
+
|
| 17 |
+
name: str = "Scrape website content"
|
| 18 |
+
description: str = "Useful to scrape and summarize a website content"
|
| 19 |
+
args_schema: type[BaseModel] = WebsiteInput
|
| 20 |
+
browserless_api_key : str = Field(..., description="Browserless API Key")
|
| 21 |
+
|
| 22 |
+
def _run(self, website: str) -> str:
|
| 23 |
+
try:
|
| 24 |
+
url = f"https://chrome.browserless.io/content?token={self.browserless_api_key}"
|
| 25 |
+
payload = json.dumps({"url": website})
|
| 26 |
+
headers = {'cache-control': 'no-cache', 'content-type': 'application/json'}
|
| 27 |
+
response = requests.request("POST", url, headers=headers, data=payload)
|
| 28 |
+
|
| 29 |
+
if response.status_code != 200:
|
| 30 |
+
return f"Error: Failed to fetch website content. Status code: {response.status_code}"
|
| 31 |
+
|
| 32 |
+
elements = partition_html(text=response.text)
|
| 33 |
+
content = "\n\n".join([str(el) for el in elements])
|
| 34 |
+
content = [content[i:i + 8000] for i in range(0, len(content), 8000)]
|
| 35 |
+
summaries = []
|
| 36 |
+
|
| 37 |
+
#llm = LLM(model="groq/deepseek-r1-distill-llama-70b")
|
| 38 |
+
llm = LLM(model="gemini/gemini-2.0-flash")
|
| 39 |
+
|
| 40 |
+
for chunk in content:
|
| 41 |
+
agent = Agent(
|
| 42 |
+
role='Principal Researcher',
|
| 43 |
+
goal='Do amazing researches and summaries based on the content you are working with',
|
| 44 |
+
backstory="You're a Principal Researcher at a big company and you need to do a research about a given topic.",
|
| 45 |
+
allow_delegation=False,
|
| 46 |
+
llm=llm
|
| 47 |
+
)
|
| 48 |
+
task = Task(
|
| 49 |
+
description=f'Analyze and summarize the content below, make sure to include the most relevant information in the summary, return only the summary nothing else.\n\nCONTENT\n----------\n{chunk}',
|
| 50 |
+
agent=agent
|
| 51 |
+
)
|
| 52 |
+
summary = task.execute()
|
| 53 |
+
summaries.append(summary)
|
| 54 |
+
return "\n\n".join(summaries)
|
| 55 |
+
except Exception as e:
|
| 56 |
+
return f"Error while processing website: {str(e)}"
|
| 57 |
+
|
| 58 |
+
async def _arun(self, website: str) -> str:
|
| 59 |
+
raise NotImplementedError("Async not implemented")
|
tools/calculator_tools.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from crewai.tools import BaseTool
|
| 2 |
+
from pydantic import BaseModel, Field
|
| 3 |
+
|
| 4 |
+
class CalculationInput(BaseModel):
|
| 5 |
+
operation: str = Field(..., description="The mathematical expression to evaluate")
|
| 6 |
+
|
| 7 |
+
class CalculatorTools(BaseTool):
|
| 8 |
+
name: str = "Make a calculation"
|
| 9 |
+
description: str = """Useful to perform any mathematical calculations,
|
| 10 |
+
like sum, minus, multiplication, division, etc.
|
| 11 |
+
The input should be a mathematical expression, e.g. '200*7' or '5000/2*10'"""
|
| 12 |
+
args_schema: type[BaseModel] = CalculationInput
|
| 13 |
+
|
| 14 |
+
def _run(self, operation: str) -> float:
|
| 15 |
+
return eval(operation)
|
| 16 |
+
|
| 17 |
+
async def _arun(self, operation: str) -> float:
|
| 18 |
+
raise NotImplementedError("Async not implemented")
|
tools/search_tools.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import requests
|
| 3 |
+
import streamlit as st
|
| 4 |
+
from crewai.tools import BaseTool
|
| 5 |
+
from pydantic import BaseModel, Field
|
| 6 |
+
|
| 7 |
+
class SearchQuery(BaseModel):
|
| 8 |
+
query: str = Field(..., description="The search query to look up")
|
| 9 |
+
|
| 10 |
+
class SearchTools(BaseTool):
|
| 11 |
+
|
| 12 |
+
name: str = "Search the internet"
|
| 13 |
+
description: str = "Useful to search the internet about a given topic and return relevant results"
|
| 14 |
+
args_schema: type[BaseModel] = SearchQuery
|
| 15 |
+
serper_api_key: str = Field(..., description="Serper API Key")
|
| 16 |
+
|
| 17 |
+
def _run(self, query: str) -> str:
|
| 18 |
+
try:
|
| 19 |
+
top_result_to_return = 4
|
| 20 |
+
url = "https://google.serper.dev/search"
|
| 21 |
+
payload = json.dumps({"q": query})
|
| 22 |
+
headers = {
|
| 23 |
+
'X-API-KEY': self.serper_api_key,
|
| 24 |
+
'content-type': 'application/json'
|
| 25 |
+
}
|
| 26 |
+
response = requests.request("POST", url, headers=headers, data=payload)
|
| 27 |
+
|
| 28 |
+
if response.status_code != 200:
|
| 29 |
+
return f"Error: Search API request failed. Status code: {response.status_code}"
|
| 30 |
+
|
| 31 |
+
data = response.json()
|
| 32 |
+
if 'organic' not in data:
|
| 33 |
+
return "No results found or API error occurred."
|
| 34 |
+
|
| 35 |
+
results = data['organic']
|
| 36 |
+
string = []
|
| 37 |
+
for result in results[:top_result_to_return]:
|
| 38 |
+
try:
|
| 39 |
+
string.append('\n'.join([
|
| 40 |
+
f"Title: {result['title']}",
|
| 41 |
+
f"Link: {result['link']}",
|
| 42 |
+
f"Snippet: {result['snippet']}",
|
| 43 |
+
"\n-----------------"
|
| 44 |
+
]))
|
| 45 |
+
except KeyError:
|
| 46 |
+
continue
|
| 47 |
+
return '\n'.join(string) if string else "No valid results found"
|
| 48 |
+
except Exception as e:
|
| 49 |
+
return f"Error during search: {str(e)}"
|
| 50 |
+
|
| 51 |
+
async def _arun(self, query: str) -> str:
|
| 52 |
+
raise NotImplementedError("Async not implemented")
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|