Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions camel/toolkits/search_toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,57 @@ def __init__(
"""
super().__init__(timeout=timeout)
self.exclude_domains = exclude_domains

@api_keys_required(
[
(None, "SERPER_API_KEY"),
]
)
def search_serper(
self,
query: str,
page: int = 1,
location: str = "United States",
) -> Dict[str, Any]:
r"""Use Serper.dev API to perform Google search.

Args:
query (str): The search query.
page (int): The page number of results to retrieve. (default: :obj:`1`)
location (str): The location for the search results.
(default: :obj:`"United States"`)

Returns:
Dict[str, Any]: The search result dictionary containing 'organic',
'peopleAlsoAsk', etc.
"""
import json

import requests

SERPER_API_KEY = os.getenv("SERPER_API_KEY")

url = "https://google.serper.dev/search"

payload = json.dumps(
{
"q": query,
"location": location,
"page": page,
}
)

headers = {
"X-API-KEY": SERPER_API_KEY,
"Content-Type": "application/json",
}

try:
response = requests.post(url, headers=headers, data=payload)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise RuntimeError(f"Error making request to Serper: {e}")

@dependencies_required("wikipedia")
def search_wiki(self, entity: str) -> str:
Expand Down Expand Up @@ -1353,6 +1404,7 @@ def get_tools(self) -> List[FunctionTool]:
representing the functions in the toolkit.
"""
return [
FunctionTool(self.search_serper),
FunctionTool(self.search_wiki),
FunctionTool(self.search_linkup),
FunctionTool(self.search_google),
Expand Down
25 changes: 25 additions & 0 deletions docs/mintlify/reference/camel.toolkits.search_toolkit.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ Initializes the SearchToolkit.
- **timeout** (float): Timeout for API requests in seconds. (default: :obj:`None`)
- **exclude_domains** (Optional[List[str]]): List of domains to exclude from search results. Currently only supported by the `search_google` function. (default: :obj:`None`)

<a id="camel.toolkits.search_toolkit.SearchToolkit.search_serper"></a>

### search_serper

```python
def search_serper(
self,
query: str,
page: int = 1,
location: str = 'United States'
):
```

Use Serper.dev API to perform Google search.

**Parameters:**

- **query** (str): The search query.
- **page** (int): The page number of results to retrieve. (default: :obj:`1`)
- **location** (str): The location for the search results. (default: :obj:`"United States"`)

**Returns:**

Dict[str, Any]: The search result dictionary containing 'organic', 'peopleAlsoAsk', etc.

<a id="camel.toolkits.search_toolkit.SearchToolkit.search_wiki"></a>

### search_wiki
Expand Down
25 changes: 25 additions & 0 deletions docs/reference/camel.toolkits.search_toolkit.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ Initializes the SearchToolkit.
- **timeout** (float): Timeout for API requests in seconds. (default: :obj:`None`)
- **exclude_domains** (Optional[List[str]]): List of domains to exclude from search results. Currently only supported by the `search_google` function. (default: :obj:`None`)

<a id="camel.toolkits.search_toolkit.SearchToolkit.search_serper"></a>

### search_serper

```python
def search_serper(
self,
query: str,
page: int = 1,
location: str = 'United States'
):
```

Use Serper.dev API to perform Google search.

**Parameters:**

- **query** (str): The search query.
- **page** (int): The page number of results to retrieve. (default: :obj:`1`)
- **location** (str): The location for the search results. (default: :obj:`"United States"`)

**Returns:**

Dict[str, Any]: The search result dictionary containing 'organic', 'peopleAlsoAsk', etc.

<a id="camel.toolkits.search_toolkit.SearchToolkit.search_wiki"></a>

### search_wiki
Expand Down
52 changes: 52 additions & 0 deletions examples/toolkits/search_toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,3 +417,55 @@ class PersonInfo(BaseModel):
Similarity: 0.9584", 'position': 10, 'date': '2025年05月23日'}], 'total': 38}
===============================================================================
""" # noqa: E501



# Example using Serper search
serper_response = SearchToolkit().search_serper(
query="Apple Inc",
page=1,
)
print(serper_response)
"""
===============================================================================
{'searchParameters': {'q': 'Apple Inc', 'type': 'search', 'page': 1, 'location':
'United States', 'engine': 'google', 'gl': 'us'}, 'organic': [{'title': 'Apple',
'link': 'https://www.apple.com/', 'snippet': 'Discover the innovative world of
Apple and shop everything iPhone, iPad, Apple Watch, Mac, and Apple TV, plus
explore accessories, entertainment, ...', 'position': 1}, {'title': 'Apple Inc.',
'link': 'https://en.wikipedia.org/wiki/Apple_Inc.', 'snippet': 'Apple Inc. is an
American multinational technology company headquartered in Cupertino, California,
in Silicon Valley, best known for its consumer electronics, ...', 'position': 2},
{'title': 'AAPL: Apple Inc Stock Price Quote - NASDAQ GS', 'link':
'https://www.bloomberg.com/quote/AAPL:US', 'snippet': 'Apple Inc. designs,
manufactures, and markets smartphones, personal computers, tablets, wearables and
accessories, and sells a variety of related accessories.', 'position': 3}, {'title':
'Apple Inc. | History, Products, Headquarters, & Facts', 'link':
'https://www.britannica.com/money/Apple-Inc', 'snippet': 'Apple Inc. is an American
multinational technology company that revolutionized the technology sector through
its innovation of computer software, personal ...', 'date': '3 days ago', 'position':
4}, {'title': 'Apple Inc. (AAPL)', 'link': 'https://finance.yahoo.com/quote/AAPL/',
'snippet': 'Apple Inc. designs, manufactures, and markets smartphones, personal
computers, tablets, wearables, and accessories worldwide.', 'position': 5}, {'title':
'What the heck is going on at Apple?', 'link':
'https://www.cnn.com/2025/12/06/tech/apple-tim-cook-leadership-changes', 'snippet':
'Now the company known for its steadiness is going through a shakeup at the top, as
both Apple and the tech industry at large are at a crossroads ...', 'date': '1 day ago',
'position': 6}, {'title': 'Apple Inc. (AAPL) Stock Price Today - WSJ', 'link':
'https://www.wsj.com/market-data/quotes/AAPL?gaa_at=eafs&gaa_n=AWEtsqeZrQcR92j11_
hQeDtRlWcl9tKefoYwgR1oId6oHIJbV4gU_v-Mpi48&gaa_ts=6935bd94&gaa_sig=3tbJ_
7ZRrguPYjDxogTwd2ytCG7b70pNDaNogjgUg14icaShrFItmMWpypVli_jwYm1WncqFLHFta52UOD1ngQ%3D%3D',
'snippet': 'Key Stock Data · P/E Ratio (TTM). 37.37(12/05/25) · EPS (TTM). .46 ·
Market Cap. .12 T · Shares Outstanding. 14.78 B · Public Float. 14.76 B · Yield. 0.37%
( ...', 'position': 7}, {'title': 'Apple', 'link': 'https://www.linkedin.com/company/apple',
'snippet': 'Company size: 10,001+ employees. Headquarters: Cupertino, California.
Type: Public Company. Founded: 1976. Specialties: Innovative Product ...', 'position': 8},
{'title': 'Apple | AAPL Stock Price, Company Overview & News', 'link':
'https://www.forbes.com/companies/apple/', 'snippet': 'Apple Inc. engages in the design,
manufacture, and sale of smartphones, personal computers, tablets, wearables and accessories,
and other variety of related ...', 'position': 9}, {'title': 'iCloud', 'link':
'https://www.icloud.com/', 'snippet': 'Log in to iCloud to access your photos, mail,
notes, documents and more. Sign in with your Apple Account or create a new account to
start using Apple services ...', 'position': 10}], 'credits': 1}
===============================================================================
"""
59 changes: 59 additions & 0 deletions test/toolkits/test_search_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -899,3 +899,62 @@ def test_search_metaso_invalid_json(mock_https_connection, search_toolkit):
# Verify connection was attempted
mock_https_connection.assert_called_once_with("metaso.cn")
mock_conn.request.assert_called_once()

@patch('requests.post')
def test_search_serper_success(mock_post, search_toolkit):
"""Test successful Serper search."""
import json

mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"searchParameters": {
"q": "apple inc",
"gl": "us",
"hl": "en",
"num": 10,
"type": "search"
},
"organic": [
{
"title": "Apple",
"link": "https://www.apple.com/",
"snippet": "Discover the innovative world of Apple...",
"position": 1
}
]
}
mock_post.return_value = mock_response

with patch.dict(os.environ, {'SERPER_API_KEY': 'test_key'}):
result = search_toolkit.search_serper(query="apple inc")

assert result == mock_response.json.return_value

# Verify request
mock_post.assert_called_once()
args, kwargs = mock_post.call_args
assert args[0] == "https://google.serper.dev/search"
assert kwargs['headers'] == {
"X-API-KEY": "test_key",
"Content-Type": "application/json",
}
# Verify payload structure matches exactly what user requested
# Note: requests.post(json=payload) sets data to json dump
assert json.loads(kwargs['data']) == {
"q": "apple inc",
"location": "United States",
"page": 1
}


@patch('requests.post')
def test_search_serper_error(mock_post, search_toolkit):
"""Test error handling in Serper search."""
mock_post.side_effect = requests.exceptions.RequestException("API Error")

with patch.dict(os.environ, {'SERPER_API_KEY': 'test_key'}):
with pytest.raises(RuntimeError) as excinfo:
search_toolkit.search_serper(query="test")

assert "Error making request to Serper" in str(excinfo.value)
Loading