camel-ai 0.2.11__py3-none-any.whl → 0.2.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of camel-ai might be problematic. Click here for more details.

Files changed (81) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/chat_agent.py +14 -2
  3. camel/benchmarks/__init__.py +18 -0
  4. camel/benchmarks/base.py +152 -0
  5. camel/benchmarks/gaia.py +478 -0
  6. camel/configs/__init__.py +3 -0
  7. camel/configs/ollama_config.py +4 -2
  8. camel/configs/sglang_config.py +71 -0
  9. camel/data_collector/__init__.py +19 -0
  10. camel/data_collector/alpaca_collector.py +127 -0
  11. camel/data_collector/base.py +211 -0
  12. camel/data_collector/sharegpt_collector.py +205 -0
  13. camel/datahubs/__init__.py +23 -0
  14. camel/datahubs/base.py +136 -0
  15. camel/datahubs/huggingface.py +433 -0
  16. camel/datahubs/models.py +22 -0
  17. camel/embeddings/openai_compatible_embedding.py +1 -1
  18. camel/embeddings/openai_embedding.py +1 -1
  19. camel/interpreters/__init__.py +2 -0
  20. camel/interpreters/e2b_interpreter.py +136 -0
  21. camel/loaders/__init__.py +3 -1
  22. camel/loaders/base_io.py +41 -41
  23. camel/messages/__init__.py +2 -0
  24. camel/messages/base.py +5 -5
  25. camel/models/__init__.py +4 -0
  26. camel/models/anthropic_model.py +15 -5
  27. camel/models/azure_openai_model.py +1 -1
  28. camel/models/base_model.py +28 -0
  29. camel/models/deepseek_model.py +1 -1
  30. camel/models/fish_audio_model.py +146 -0
  31. camel/models/gemini_model.py +1 -1
  32. camel/models/groq_model.py +2 -2
  33. camel/models/model_factory.py +3 -0
  34. camel/models/nemotron_model.py +1 -1
  35. camel/models/nvidia_model.py +1 -1
  36. camel/models/ollama_model.py +13 -1
  37. camel/models/openai_compatible_model.py +1 -1
  38. camel/models/openai_model.py +1 -27
  39. camel/models/qwen_model.py +1 -1
  40. camel/models/reward/__init__.py +22 -0
  41. camel/models/reward/base_reward_model.py +58 -0
  42. camel/models/reward/evaluator.py +63 -0
  43. camel/models/reward/nemotron_model.py +112 -0
  44. camel/models/samba_model.py +1 -1
  45. camel/models/sglang_model.py +225 -0
  46. camel/models/togetherai_model.py +1 -1
  47. camel/models/vllm_model.py +2 -2
  48. camel/models/yi_model.py +1 -1
  49. camel/models/zhipuai_model.py +1 -1
  50. camel/personas/persona_hub.py +2 -2
  51. camel/runtime/configs.py +12 -12
  52. camel/runtime/docker_runtime.py +7 -7
  53. camel/runtime/llm_guard_runtime.py +3 -3
  54. camel/runtime/remote_http_runtime.py +5 -5
  55. camel/runtime/utils/function_risk_toolkit.py +1 -1
  56. camel/runtime/utils/ignore_risk_toolkit.py +2 -2
  57. camel/schemas/openai_converter.py +2 -2
  58. camel/societies/workforce/role_playing_worker.py +2 -2
  59. camel/societies/workforce/single_agent_worker.py +2 -2
  60. camel/societies/workforce/workforce.py +3 -3
  61. camel/storages/object_storages/amazon_s3.py +2 -2
  62. camel/storages/object_storages/azure_blob.py +2 -2
  63. camel/storages/object_storages/google_cloud.py +2 -2
  64. camel/toolkits/__init__.py +2 -0
  65. camel/toolkits/arxiv_toolkit.py +6 -6
  66. camel/toolkits/ask_news_toolkit.py +2 -2
  67. camel/toolkits/code_execution.py +5 -1
  68. camel/toolkits/function_tool.py +41 -0
  69. camel/toolkits/github_toolkit.py +3 -3
  70. camel/toolkits/google_scholar_toolkit.py +16 -2
  71. camel/toolkits/math_toolkit.py +47 -16
  72. camel/toolkits/meshy_toolkit.py +2 -2
  73. camel/toolkits/search_toolkit.py +155 -3
  74. camel/toolkits/stripe_toolkit.py +273 -0
  75. camel/types/__init__.py +2 -0
  76. camel/types/enums.py +27 -2
  77. camel/utils/token_counting.py +31 -12
  78. {camel_ai-0.2.11.dist-info → camel_ai-0.2.13.dist-info}/METADATA +24 -14
  79. {camel_ai-0.2.11.dist-info → camel_ai-0.2.13.dist-info}/RECORD +81 -61
  80. {camel_ai-0.2.11.dist-info → camel_ai-0.2.13.dist-info}/LICENSE +0 -0
  81. {camel_ai-0.2.11.dist-info → camel_ai-0.2.13.dist-info}/WHEEL +0 -0
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from __future__ import annotations
15
15
 
16
- import ast
16
+ import json
17
17
  from typing import Any, List
18
18
 
19
19
  from colorama import Fore
@@ -87,7 +87,7 @@ class SingleAgentWorker(Worker):
87
87
 
88
88
  print(f"======\n{Fore.GREEN}Reply from {self}:{Fore.RESET}")
89
89
 
90
- result_dict = ast.literal_eval(response.msg.content)
90
+ result_dict = json.loads(response.msg.content)
91
91
  task_result = TaskResult(**result_dict)
92
92
 
93
93
  color = Fore.RED if task_result.failed else Fore.GREEN
@@ -13,8 +13,8 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  from __future__ import annotations
15
15
 
16
- import ast
17
16
  import asyncio
17
+ import json
18
18
  import logging
19
19
  from collections import deque
20
20
  from typing import Deque, Dict, List, Optional
@@ -289,7 +289,7 @@ class Workforce(BaseNode):
289
289
  response = self.coordinator_agent.step(
290
290
  req, response_format=TaskAssignResult
291
291
  )
292
- result_dict = ast.literal_eval(response.msg.content)
292
+ result_dict = json.loads(response.msg.content)
293
293
  task_assign_result = TaskAssignResult(**result_dict)
294
294
  return task_assign_result.assignee_id
295
295
 
@@ -320,7 +320,7 @@ class Workforce(BaseNode):
320
320
  content=prompt,
321
321
  )
322
322
  response = self.coordinator_agent.step(req, response_format=WorkerConf)
323
- result_dict = ast.literal_eval(response.msg.content)
323
+ result_dict = json.loads(response.msg.content)
324
324
  new_node_conf = WorkerConf(**result_dict)
325
325
 
326
326
  new_agent = self._create_new_agent(
@@ -17,7 +17,7 @@ from pathlib import Path, PurePath
17
17
  from typing import Optional, Tuple
18
18
  from warnings import warn
19
19
 
20
- from camel.loaders import File
20
+ from camel.loaders import File, create_file_from_raw_bytes
21
21
  from camel.storages.object_storages.base import BaseObjectStorage
22
22
 
23
23
 
@@ -156,7 +156,7 @@ class AmazonS3Storage(BaseObjectStorage):
156
156
  Bucket=self._bucket_name, Key=file_key
157
157
  )
158
158
  raw_bytes = response["Body"].read()
159
- return File.create_file_from_raw_bytes(raw_bytes, filename)
159
+ return create_file_from_raw_bytes(raw_bytes, filename)
160
160
 
161
161
  def _upload_file(
162
162
  self, local_file_path: Path, remote_file_key: str
@@ -16,7 +16,7 @@ from pathlib import Path, PurePath
16
16
  from typing import Optional, Tuple
17
17
  from warnings import warn
18
18
 
19
- from camel.loaders import File
19
+ from camel.loaders import File, create_file_from_raw_bytes
20
20
  from camel.storages.object_storages.base import BaseObjectStorage
21
21
 
22
22
 
@@ -123,7 +123,7 @@ class AzureBlobStorage(BaseObjectStorage):
123
123
  File: The object from the container.
124
124
  """
125
125
  raw_bytes = self._client.download_blob(file_key).readall()
126
- file = File.create_file_from_raw_bytes(raw_bytes, filename)
126
+ file = create_file_from_raw_bytes(raw_bytes, filename)
127
127
  return file
128
128
 
129
129
  def _upload_file(
@@ -15,7 +15,7 @@ from pathlib import Path, PurePath
15
15
  from typing import Tuple
16
16
  from warnings import warn
17
17
 
18
- from camel.loaders import File
18
+ from camel.loaders import File, create_file_from_raw_bytes
19
19
  from camel.storages.object_storages.base import BaseObjectStorage
20
20
 
21
21
 
@@ -111,7 +111,7 @@ class GoogleCloudStorage(BaseObjectStorage):
111
111
  File: The object from the S3 bucket.
112
112
  """
113
113
  raw_bytes = self._client.get_blob(file_key).download_as_bytes()
114
- return File.create_file_from_raw_bytes(raw_bytes, filename)
114
+ return create_file_from_raw_bytes(raw_bytes, filename)
115
115
 
116
116
  def _upload_file(
117
117
  self, local_file_path: Path, remote_file_key: str
@@ -41,6 +41,7 @@ from .open_api_toolkit import OpenAPIToolkit
41
41
  from .retrieval_toolkit import RetrievalToolkit
42
42
  from .notion_toolkit import NotionToolkit
43
43
  from .human_toolkit import HumanToolkit
44
+ from .stripe_toolkit import StripeToolkit
44
45
  from .video_toolkit import VideoDownloaderToolkit
45
46
 
46
47
  __all__ = [
@@ -70,5 +71,6 @@ __all__ = [
70
71
  'ArxivToolkit',
71
72
  'HumanToolkit',
72
73
  'VideoDownloaderToolkit',
74
+ 'StripeToolkit',
73
75
  'MeshyToolkit',
74
76
  ]
@@ -44,9 +44,9 @@ class ArxivToolkit(BaseToolkit):
44
44
  query (str): The search query string used to search for papers on
45
45
  arXiv.
46
46
  paper_ids (List[str], optional): A list of specific arXiv paper
47
- IDs to search for. (default::obj: `None`)
47
+ IDs to search for. (default: :obj: `None`)
48
48
  max_results (int, optional): The maximum number of search results
49
- to retrieve. (default::obj: `5`)
49
+ to retrieve. (default: :obj: `5`)
50
50
 
51
51
  Returns:
52
52
  Generator: A generator that yields results from the arXiv search
@@ -75,9 +75,9 @@ class ArxivToolkit(BaseToolkit):
75
75
  Args:
76
76
  query (str): The search query string.
77
77
  paper_ids (List[str], optional): A list of specific arXiv paper
78
- IDs to search for. (default::obj: `None`)
78
+ IDs to search for. (default: :obj: `None`)
79
79
  max_results (int, optional): The maximum number of search results
80
- to return. (default::obj: `5`)
80
+ to return. (default: :obj: `5`)
81
81
 
82
82
  Returns:
83
83
  List[Dict[str, str]]: A list of dictionaries, each containing
@@ -119,9 +119,9 @@ class ArxivToolkit(BaseToolkit):
119
119
  Args:
120
120
  query (str): The search query string.
121
121
  paper_ids (List[str], optional): A list of specific arXiv paper
122
- IDs to download. (default::obj: `None`)
122
+ IDs to download. (default: :obj: `None`)
123
123
  max_results (int, optional): The maximum number of search results
124
- to download. (default::obj: `5`)
124
+ to download. (default: :obj: `5`)
125
125
  output_dir (str, optional): The directory to save the downloaded
126
126
  PDFs. Defaults to the current directory.
127
127
 
@@ -228,7 +228,7 @@ class AskNewsToolkit(BaseToolkit):
228
228
  return value. (default: :obj:`"string"`)
229
229
  method (Literal["nl", "kw"]): The search method, either "nl" for
230
230
  natural language or "kw" for keyword search.
231
- (default::obj:`"kw"`)
231
+ (default: :obj:`"kw"`)
232
232
 
233
233
  Returns:
234
234
  Union[str, dict, Tuple[str, dict]]: The Reddit search
@@ -523,7 +523,7 @@ class AsyncAskNewsToolkit(BaseToolkit):
523
523
  return value. (default: :obj:"string")
524
524
  method (Literal["nl", "kw"]): The search method, either "nl" for
525
525
  natural language or "kw" for keyword search.
526
- (default::obj:"kw")
526
+ (default: :obj:"kw")
527
527
 
528
528
  Returns:
529
529
  Union[str, dict, Tuple[str, dict]]: The Reddit search
@@ -15,6 +15,7 @@ from typing import List, Literal, Optional, Union
15
15
 
16
16
  from camel.interpreters import (
17
17
  DockerInterpreter,
18
+ E2BInterpreter,
18
19
  InternalPythonInterpreter,
19
20
  JupyterKernelInterpreter,
20
21
  SubprocessInterpreter,
@@ -41,7 +42,7 @@ class CodeExecutionToolkit(BaseToolkit):
41
42
  def __init__(
42
43
  self,
43
44
  sandbox: Literal[
44
- "internal_python", "jupyter", "docker", "subprocess"
45
+ "internal_python", "jupyter", "docker", "subprocess", "e2b"
45
46
  ] = "internal_python",
46
47
  verbose: bool = False,
47
48
  unsafe_mode: bool = False,
@@ -58,6 +59,7 @@ class CodeExecutionToolkit(BaseToolkit):
58
59
  JupyterKernelInterpreter,
59
60
  DockerInterpreter,
60
61
  SubprocessInterpreter,
62
+ E2BInterpreter,
61
63
  ]
62
64
 
63
65
  if sandbox == "internal_python":
@@ -83,6 +85,8 @@ class CodeExecutionToolkit(BaseToolkit):
83
85
  print_stdout=self.verbose,
84
86
  print_stderr=self.verbose,
85
87
  )
88
+ elif sandbox == "e2b":
89
+ self.interpreter = E2BInterpreter(require_confirm=require_confirm)
86
90
  else:
87
91
  raise RuntimeError(
88
92
  f"The sandbox type `{sandbox}` is not supported."
@@ -165,9 +165,15 @@ def get_openai_tool_schema(func: Callable) -> Dict[str, Any]:
165
165
  else:
166
166
  func_description = short_description
167
167
 
168
+ # OpenAI client.beta.chat.completions.parse for structured output has
169
+ # additional requirements for the schema, refer:
170
+ # https://platform.openai.com/docs/guides/structured-outputs/some-type-specific-keywords-are-not-yet-supported#supported-schemas
171
+ parameters_dict["additionalProperties"] = False
172
+
168
173
  openai_function_schema = {
169
174
  "name": func.__name__,
170
175
  "description": func_description,
176
+ "strict": True,
171
177
  "parameters": parameters_dict,
172
178
  }
173
179
 
@@ -175,9 +181,44 @@ def get_openai_tool_schema(func: Callable) -> Dict[str, Any]:
175
181
  "type": "function",
176
182
  "function": openai_function_schema,
177
183
  }
184
+
185
+ openai_tool_schema = sanitize_and_enforce_required(openai_tool_schema)
178
186
  return openai_tool_schema
179
187
 
180
188
 
189
+ def sanitize_and_enforce_required(parameters_dict):
190
+ r"""Cleans and updates the function schema to conform with OpenAI's
191
+ requirements:
192
+ - Removes invalid 'default' fields from the parameters schema.
193
+ - Ensures all fields or function parameters are marked as required.
194
+
195
+ Args:
196
+ parameters_dict (dict): The dictionary representing the function
197
+ schema.
198
+
199
+ Returns:
200
+ dict: The updated dictionary with invalid defaults removed and all
201
+ fields set as required.
202
+ """
203
+ # Check if 'function' and 'parameters' exist
204
+ if (
205
+ 'function' in parameters_dict
206
+ and 'parameters' in parameters_dict['function']
207
+ ):
208
+ # Access the 'parameters' section
209
+ parameters = parameters_dict['function']['parameters']
210
+ properties = parameters.get('properties', {})
211
+
212
+ # Remove 'default' key from each property
213
+ for field in properties.values():
214
+ field.pop('default', None)
215
+
216
+ # Mark all keys in 'properties' as required
217
+ parameters['required'] = list(properties.keys())
218
+
219
+ return parameters_dict
220
+
221
+
181
222
  def generate_docstring(
182
223
  code: str,
183
224
  model: Optional[BaseModelBackend] = None,
@@ -139,7 +139,7 @@ class GithubToolkit(BaseToolkit):
139
139
 
140
140
  Args:
141
141
  state (Literal["open", "closed", "all"]): The state of pull
142
- requests to retrieve. (default::obj: `all`)
142
+ requests to retrieve. (default: :obj: `all`)
143
143
  Options are:
144
144
  - "open": Retrieve only open pull requests.
145
145
  - "closed": Retrieve only closed pull requests.
@@ -179,7 +179,7 @@ class GithubToolkit(BaseToolkit):
179
179
 
180
180
  Args:
181
181
  state (Literal["open", "closed", "all"]): The state of pull
182
- requests to retrieve. (default::obj: `all`)
182
+ requests to retrieve. (default: :obj: `all`)
183
183
  Options are:
184
184
  - "open": Retrieve only open pull requests.
185
185
  - "closed": Retrieve only closed pull requests.
@@ -254,7 +254,7 @@ class GithubToolkit(BaseToolkit):
254
254
  Args:
255
255
  path (str): The repository path to start the traversal from.
256
256
  empty string means starts from the root directory.
257
- (default::obj: `""`)
257
+ (default: :obj: `""`)
258
258
 
259
259
  Returns:
260
260
  List[str]: A list of file paths within the specified directory
@@ -33,7 +33,11 @@ class GoogleScholarToolkit(BaseToolkit):
33
33
  """
34
34
 
35
35
  def __init__(
36
- self, author_identifier: str, is_author_name: bool = False
36
+ self,
37
+ author_identifier: str,
38
+ is_author_name: bool = False,
39
+ proxy_http: Optional[str] = None,
40
+ proxy_https: Optional[str] = None,
37
41
  ) -> None:
38
42
  r"""Initializes the GoogleScholarToolkit with the author's identifier.
39
43
 
@@ -42,8 +46,18 @@ class GoogleScholarToolkit(BaseToolkit):
42
46
  of the author to search for.
43
47
  is_author_name (bool): Flag to indicate if the identifier is a
44
48
  name. (default: :obj:`False`)
49
+ proxy_http ( Optional[str]): Proxy http address pass to pg.
50
+ SingleProxy. (default: :obj:`None`)
51
+ proxy_https ( Optional[str]): Proxy https address pass to pg.
52
+ SingleProxy. (default: :obj:`None`)
45
53
  """
46
- from scholarly import scholarly
54
+ from scholarly import ProxyGenerator, scholarly
55
+
56
+ # Set Proxy is HTTP or HTTPS provided
57
+ if proxy_http or proxy_https:
58
+ pg = ProxyGenerator()
59
+ pg.SingleProxy(http=proxy_http, https=proxy_https)
60
+ scholarly.use_proxy(pg)
47
61
 
48
62
  self.scholarly = scholarly
49
63
  self.author_identifier = author_identifier
@@ -22,44 +22,73 @@ class MathToolkit(BaseToolkit):
22
22
  r"""A class representing a toolkit for mathematical operations.
23
23
 
24
24
  This class provides methods for basic mathematical operations such as
25
- addition, subtraction, and multiplication.
25
+ addition, subtraction, multiplication, division, and rounding.
26
26
  """
27
27
 
28
- def add(self, a: int, b: int) -> int:
28
+ def add(self, a: float, b: float) -> float:
29
29
  r"""Adds two numbers.
30
30
 
31
31
  Args:
32
- a (int): The first number to be added.
33
- b (int): The second number to be added.
32
+ a (float): The first number to be added.
33
+ b (float): The second number to be added.
34
34
 
35
35
  Returns:
36
- integer: The sum of the two numbers.
36
+ float: The sum of the two numbers.
37
37
  """
38
38
  return a + b
39
39
 
40
- def sub(self, a: int, b: int) -> int:
40
+ def sub(self, a: float, b: float) -> float:
41
41
  r"""Do subtraction between two numbers.
42
42
 
43
43
  Args:
44
- a (int): The minuend in subtraction.
45
- b (int): The subtrahend in subtraction.
44
+ a (float): The minuend in subtraction.
45
+ b (float): The subtrahend in subtraction.
46
46
 
47
47
  Returns:
48
- integer: The result of subtracting :obj:`b` from :obj:`a`.
48
+ float: The result of subtracting :obj:`b` from :obj:`a`.
49
49
  """
50
50
  return a - b
51
51
 
52
- def mul(self, a: int, b: int) -> int:
53
- r"""Multiplies two integers.
52
+ def multiply(self, a: float, b: float, decimal_places: int = 2) -> float:
53
+ r"""Multiplies two numbers.
54
54
 
55
55
  Args:
56
- a (int): The multiplier in the multiplication.
57
- b (int): The multiplicand in the multiplication.
56
+ a (float): The multiplier in the multiplication.
57
+ b (float): The multiplicand in the multiplication.
58
+ decimal_places (int, optional): The number of decimal
59
+ places to round to. Defaults to 2.
58
60
 
59
61
  Returns:
60
- integer: The product of the two numbers.
62
+ float: The product of the two numbers.
61
63
  """
62
- return a * b
64
+ return round(a * b, decimal_places)
65
+
66
+ def divide(self, a: float, b: float, decimal_places: int = 2) -> float:
67
+ r"""Divides two numbers.
68
+
69
+ Args:
70
+ a (float): The dividend in the division.
71
+ b (float): The divisor in the division.
72
+ decimal_places (int, optional): The number of
73
+ decimal places to round to. Defaults to 2.
74
+
75
+ Returns:
76
+ float: The result of dividing :obj:`a` by :obj:`b`.
77
+ """
78
+ return round(a / b, decimal_places)
79
+
80
+ def round(self, a: float, decimal_places: int = 0) -> float:
81
+ r"""Rounds a number to a specified number of decimal places.
82
+
83
+ Args:
84
+ a (float): The number to be rounded.
85
+ decimal_places (int, optional): The number of decimal places
86
+ to round to. Defaults to 0.
87
+
88
+ Returns:
89
+ float: The rounded number.
90
+ """
91
+ return round(a, decimal_places)
63
92
 
64
93
  def get_tools(self) -> List[FunctionTool]:
65
94
  r"""Returns a list of FunctionTool objects representing the
@@ -72,5 +101,7 @@ class MathToolkit(BaseToolkit):
72
101
  return [
73
102
  FunctionTool(self.add),
74
103
  FunctionTool(self.sub),
75
- FunctionTool(self.mul),
104
+ FunctionTool(self.multiply),
105
+ FunctionTool(self.divide),
106
+ FunctionTool(self.round),
76
107
  ]
@@ -117,9 +117,9 @@ class MeshyToolkit(BaseToolkit):
117
117
  Args:
118
118
  task_id (str): The ID of the task to monitor.
119
119
  polling_interval (int): Seconds to wait between status checks.
120
- (default::obj:`10`)
120
+ (default: :obj:`10`)
121
121
  timeout (int): Maximum seconds to wait before timing out.
122
- (default::obj:`3600`)
122
+ (default: :obj:`3600`)
123
123
 
124
124
  Returns:
125
125
  Dict[str, Any]: Final response from the API when task completes.
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
  import os
15
15
  import xml.etree.ElementTree as ET
16
- from typing import Any, Dict, List, Union
16
+ from typing import Any, Dict, List, Optional, TypeAlias, Union
17
17
 
18
18
  import requests
19
19
 
@@ -26,7 +26,7 @@ class SearchToolkit(BaseToolkit):
26
26
  r"""A class representing a toolkit for web search.
27
27
 
28
28
  This class provides methods for searching information on the web using
29
- search engines like Google, DuckDuckGo, Wikipedia and Wolfram Alpha.
29
+ search engines like Google, DuckDuckGo, Wikipedia and Wolfram Alpha, Brave.
30
30
  """
31
31
 
32
32
  @dependencies_required("wikipedia")
@@ -151,6 +151,152 @@ class SearchToolkit(BaseToolkit):
151
151
  # If no answer found, return an empty list
152
152
  return responses
153
153
 
154
+ @api_keys_required("BRAVE_API_KEY")
155
+ def search_brave(
156
+ self,
157
+ q: str,
158
+ country: str = "US",
159
+ search_lang: str = "en",
160
+ ui_lang: str = "en-US",
161
+ count: int = 20,
162
+ offset: int = 0,
163
+ safesearch: str = "moderate",
164
+ freshness: Optional[str] = None,
165
+ text_decorations: bool = True,
166
+ spellcheck: bool = True,
167
+ result_filter: Optional[str] = None,
168
+ goggles_id: Optional[str] = None,
169
+ units: Optional[str] = None,
170
+ extra_snippets: Optional[bool] = None,
171
+ summary: Optional[bool] = None,
172
+ ) -> Dict[str, Any]:
173
+ r"""This function queries the Brave search engine API and returns a
174
+ dictionary, representing a search result.
175
+ See https://api.search.brave.com/app/documentation/web-search/query
176
+ for more details.
177
+
178
+ Args:
179
+ q (str): The user's search query term. Query cannot be empty.
180
+ Maximum of 400 characters and 50 words in the query.
181
+ country (str): The search query country where results come from.
182
+ The country string is limited to 2 character country codes of
183
+ supported countries. For a list of supported values, see
184
+ Country Codes. (default: :obj:`US `)
185
+ search_lang (str): The search language preference. The 2 or more
186
+ character language code for which search results are provided.
187
+ For a list of possible values, see Language Codes.
188
+ ui_lang (str): User interface language preferred in response.
189
+ Usually of the format '<language_code>-<country_code>'. For
190
+ more, see RFC 9110. For a list of supported values, see UI
191
+ Language Codes.
192
+ count (int): The number of search results returned in response.
193
+ The maximum is 20. The actual number delivered may be less than
194
+ requested. Combine this parameter with offset to paginate
195
+ search results.
196
+ offset (int): The zero based offset that indicates number of search
197
+ results per page (count) to skip before returning the result.
198
+ The maximum is 9. The actual number delivered may be less than
199
+ requested based on the query. In order to paginate results use
200
+ this parameter together with count. For example, if your user
201
+ interface displays 20 search results per page, set count to 20
202
+ and offset to 0 to show the first page of results. To get
203
+ subsequent pages, increment offset by 1 (e.g. 0, 1, 2). The
204
+ results may overlap across multiple pages.
205
+ safesearch (str): Filters search results for adult content.
206
+ The following values are supported:
207
+ - 'off': No filtering is done.
208
+ - 'moderate': Filters explicit content, like images and videos,
209
+ but allows adult domains in the search results.
210
+ - 'strict': Drops all adult content from search results.
211
+ freshness (Optional[str]): Filters search results by when they were
212
+ discovered:
213
+ - 'pd': Discovered within the last 24 hours.
214
+ - 'pw': Discovered within the last 7 Days.
215
+ - 'pm': Discovered within the last 31 Days.
216
+ - 'py': Discovered within the last 365 Days.
217
+ - 'YYYY-MM-DDtoYYYY-MM-DD': Timeframe is also supported by
218
+ specifying the date range e.g. '2022-04-01to2022-07-30'.
219
+ text_decorations (bool): Whether display strings (e.g. result
220
+ snippets) should include decoration markers (e.g. highlighting
221
+ characters).
222
+ spellcheck (bool): Whether to spellcheck provided query. If the
223
+ spellchecker is enabled, the modified query is always used for
224
+ search. The modified query can be found in altered key from the
225
+ query response model.
226
+ result_filter (Optional[str]): A comma delimited string of result
227
+ types to include in the search response. Not specifying this
228
+ parameter will return back all result types in search response
229
+ where data is available and a plan with the corresponding
230
+ option is subscribed. The response always includes query and
231
+ type to identify any query modifications and response type
232
+ respectively. Available result filter values are:
233
+ - 'discussions'
234
+ - 'faq'
235
+ - 'infobox'
236
+ - 'news'
237
+ - 'query'
238
+ - 'summarizer'
239
+ - 'videos'
240
+ - 'web'
241
+ - 'locations'
242
+ goggles_id (Optional[str]): Goggles act as a custom re-ranking on
243
+ top of Brave's search index. For more details, refer to the
244
+ Goggles repository.
245
+ units (Optional[str]): The measurement units. If not provided,
246
+ units are derived from search country. Possible values are:
247
+ - 'metric': The standardized measurement system
248
+ - 'imperial': The British Imperial system of units.
249
+ extra_snippets (Optional[bool]): A snippet is an excerpt from a
250
+ page you get as a result of the query, and extra_snippets
251
+ allow you to get up to 5 additional, alternative excerpts. Only
252
+ available under Free AI, Base AI, Pro AI, Base Data, Pro Data
253
+ and Custom plans.
254
+ summary (Optional[bool]): This parameter enables summary key
255
+ generation in web search results. This is required for
256
+ summarizer to be enabled.
257
+
258
+ Returns:
259
+ Dict[str, Any]: A dictionary representing a search result.
260
+ """
261
+
262
+ import requests
263
+
264
+ BRAVE_API_KEY = os.getenv("BRAVE_API_KEY")
265
+
266
+ url = "https://api.search.brave.com/res/v1/web/search"
267
+ headers = {
268
+ "Content-Type": "application/json",
269
+ "X-BCP-APIV": "1.0",
270
+ "X-Subscription-Token": BRAVE_API_KEY,
271
+ }
272
+
273
+ ParamsType: TypeAlias = Dict[
274
+ str,
275
+ Union[str, int, float, List[Union[str, int, float]], None],
276
+ ]
277
+
278
+ params: ParamsType = {
279
+ "q": q,
280
+ "country": country,
281
+ "search_lang": search_lang,
282
+ "ui_lang": ui_lang,
283
+ "count": count,
284
+ "offset": offset,
285
+ "safesearch": safesearch,
286
+ "freshness": freshness,
287
+ "text_decorations": text_decorations,
288
+ "spellcheck": spellcheck,
289
+ "result_filter": result_filter,
290
+ "goggles_id": goggles_id,
291
+ "units": units,
292
+ "extra_snippets": extra_snippets,
293
+ "summary": summary,
294
+ }
295
+
296
+ response = requests.get(url, headers=headers, params=params)
297
+ data = response.json()["web"]
298
+ return data
299
+
154
300
  @api_keys_required("GOOGLE_API_KEY", "SEARCH_ENGINE_ID")
155
301
  def search_google(
156
302
  self, query: str, num_result_pages: int = 5
@@ -219,6 +365,11 @@ class SearchToolkit(BaseToolkit):
219
365
 
220
366
  # Iterate over 10 results found
221
367
  for i, search_item in enumerate(search_items, start=1):
368
+ # Check metatags are present
369
+ if "pagemap" not in search_item:
370
+ continue
371
+ if "metatags" not in search_item["pagemap"]:
372
+ continue
222
373
  if (
223
374
  "og:description"
224
375
  in search_item["pagemap"]["metatags"][0]
@@ -265,7 +416,7 @@ class SearchToolkit(BaseToolkit):
265
416
  query (str): The query to send to Wolfram Alpha.
266
417
  is_detailed (bool): Whether to include additional details
267
418
  including step by step information in the result.
268
- (default::obj:`False`)
419
+ (default: :obj:`False`)
269
420
 
270
421
  Returns:
271
422
  Union[str, Dict[str, Any]]: The result from Wolfram Alpha.
@@ -471,4 +622,5 @@ class SearchToolkit(BaseToolkit):
471
622
  FunctionTool(self.search_duckduckgo),
472
623
  FunctionTool(self.query_wolfram_alpha),
473
624
  FunctionTool(self.tavily_search),
625
+ FunctionTool(self.search_brave),
474
626
  ]