msaifee commited on
Commit
2d39dcc
·
1 Parent(s): c7a1bbb

Trip Planner Agent using CrewAI

Browse files
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
+ ![CrewAI](https://miro.medium.com/v2/resize:fit:1400/0*-7HC-GJCxjn-Dm7i.png)
2
+
3
+ # 🏖️ Trip Planner: Streamlit with CrewAI
4
+
5
+ ![Streamlit App](images/trip_planner.jpg)
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