The same happened to me, and it seems like serper wants us to send the request via URL. So I wrote my own version of the tool that goes to google custom search instead of serper. Hope this helps. I’ve left the print statements in to check that it works. Its only compatible with the very latest package.
This how you import it.
my_serper_dev_tool_path = ‘D:/Code/agents/crewai/code/misc’
sys.path.insert(0, my_serper_dev_tool_path)
from my_serper_dev_tool import SerperDevTool
search_tool = SerperDevTool()
This is my class:
import os
import sys
import requests
from pydantic import BaseModel, Field
from typing import Any, Optional, Type
from crewai_tools import BaseTool
import datetime
def save_results_to_file(content: str) → None:
“”“Saves the search results to a file.”“”
filename = f"search_results{datetime.datetime.now().strftime(‘%Y-%m-%d_%H-%M-%S’)}.txt"
with open(filename, “w”) as file:
file.write(content)
print(f"Results saved to {filename}")
def _get_google_key() → str:
script_dir = os.path.dirname(os.path.abspath(file))
filename = os.path.join(script_dir, “google_key.txt”)
print(f’google_key filename={filename}')
try:
with open(filename, “r”) as file:
line = file.readline().strip()
key_name, API_KEY = line.split(“=”, 1)
except FileNotFoundError:
print(f"Error: {filename} file not found or could not be read")
exit()
return API_KEY
class SerperDevToolSchema(BaseModel):
search_query: str = Field(
…, description=“Mandatory search query you want to use to search the internet”
)
class SerperDevTool(BaseTool):
name: str = “Search the internet”
description: str = “A tool that can be used to search the internet with a search_query.”
args_schema = SerperDevToolSchema # Direct assignment, no Type hint here
search_url: str = “https://google.serper.dev/search”
country: Optional[str] = “”
location: Optional[str] = “”
locale: Optional[str] = “”
n_results: int = 10
save_file: bool = False
def _run(self, **kwargs: Any) -> Any:
search_query = kwargs.get("search_query") or kwargs.get("query")
save_file = kwargs.get("save_file", self.save_file)
n_results = kwargs.get("n_results", self.n_results)
api_key = _get_google_key()
cx = 'create your own site on google'
print(f'\n\nkwargs:\n{kwargs}')
if kwargs:
url = f"https://www.googleapis.com/customsearch/v1?q={search_query}&key={api_key}&cx={cx}&num={n_results}"
response = requests.get(url)
else:
response = "No search query provided and therefore no results returned."
data = response.json()
# Extract and format the output
formatted_results = []
for item in data.get("items", []):
result = {
"Title": item.get("title"),
"Link": item.get("link"),
"Snippet": item.get("snippet")
}
formatted_results.append(result)
# Print or save the formatted output in a readable way
for result in formatted_results:
print(f"Title: {result['Title']}")
print(f"Link: {result['Link']}")
print(f"Snippet: {result['Snippet']}")
print("---") # Separator for readability
if save_file:
_save_results_to_file(formatted_results)
return f"\nSearch results: {formatted_results}\n"
“”"
if “organic” in data:
results = data[“organic”][:self.n_results]
string =
for result in results:
try:
string.append(
“\n”.join(
[
f"Title: {result[‘title’]}“,
f"Link: {result[‘link’]}”,
f"Snippet: {result[‘snippet’]}",
“—”,
]
)
)
except KeyError:
continue
content = "\n".join(string)
if save_file:
_save_results_to_file(content)
return f"\nSearch results: {content}\n"
else:
return data
“”"