camel-ai 0.1.1__py3-none-any.whl → 0.1.3__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 (99) hide show
  1. camel/__init__.py +1 -11
  2. camel/agents/__init__.py +5 -5
  3. camel/agents/chat_agent.py +124 -63
  4. camel/agents/critic_agent.py +28 -17
  5. camel/agents/deductive_reasoner_agent.py +235 -0
  6. camel/agents/embodied_agent.py +92 -40
  7. camel/agents/role_assignment_agent.py +27 -17
  8. camel/agents/task_agent.py +60 -34
  9. camel/agents/tool_agents/base.py +0 -1
  10. camel/agents/tool_agents/hugging_face_tool_agent.py +7 -4
  11. camel/configs.py +119 -7
  12. camel/embeddings/__init__.py +2 -0
  13. camel/embeddings/base.py +3 -2
  14. camel/embeddings/openai_embedding.py +3 -3
  15. camel/embeddings/sentence_transformers_embeddings.py +65 -0
  16. camel/functions/__init__.py +13 -3
  17. camel/functions/google_maps_function.py +335 -0
  18. camel/functions/math_functions.py +7 -7
  19. camel/functions/openai_function.py +344 -42
  20. camel/functions/search_functions.py +100 -35
  21. camel/functions/twitter_function.py +484 -0
  22. camel/functions/weather_functions.py +36 -23
  23. camel/generators.py +65 -46
  24. camel/human.py +17 -11
  25. camel/interpreters/__init__.py +25 -0
  26. camel/interpreters/base.py +49 -0
  27. camel/{utils/python_interpreter.py → interpreters/internal_python_interpreter.py} +129 -48
  28. camel/interpreters/interpreter_error.py +19 -0
  29. camel/interpreters/subprocess_interpreter.py +190 -0
  30. camel/loaders/__init__.py +22 -0
  31. camel/{functions/base_io_functions.py → loaders/base_io.py} +38 -35
  32. camel/{functions/unstructured_io_fuctions.py → loaders/unstructured_io.py} +199 -110
  33. camel/memories/__init__.py +17 -7
  34. camel/memories/agent_memories.py +156 -0
  35. camel/memories/base.py +97 -32
  36. camel/memories/blocks/__init__.py +21 -0
  37. camel/memories/{chat_history_memory.py → blocks/chat_history_block.py} +34 -34
  38. camel/memories/blocks/vectordb_block.py +101 -0
  39. camel/memories/context_creators/__init__.py +3 -2
  40. camel/memories/context_creators/score_based.py +32 -20
  41. camel/memories/records.py +6 -5
  42. camel/messages/__init__.py +2 -2
  43. camel/messages/base.py +99 -16
  44. camel/messages/func_message.py +7 -4
  45. camel/models/__init__.py +4 -2
  46. camel/models/anthropic_model.py +132 -0
  47. camel/models/base_model.py +3 -2
  48. camel/models/model_factory.py +10 -8
  49. camel/models/open_source_model.py +25 -13
  50. camel/models/openai_model.py +9 -10
  51. camel/models/stub_model.py +6 -5
  52. camel/prompts/__init__.py +7 -5
  53. camel/prompts/ai_society.py +21 -14
  54. camel/prompts/base.py +54 -47
  55. camel/prompts/code.py +22 -14
  56. camel/prompts/evaluation.py +8 -5
  57. camel/prompts/misalignment.py +26 -19
  58. camel/prompts/object_recognition.py +35 -0
  59. camel/prompts/prompt_templates.py +14 -8
  60. camel/prompts/role_description_prompt_template.py +16 -10
  61. camel/prompts/solution_extraction.py +9 -5
  62. camel/prompts/task_prompt_template.py +24 -21
  63. camel/prompts/translation.py +9 -5
  64. camel/responses/agent_responses.py +5 -2
  65. camel/retrievers/__init__.py +24 -0
  66. camel/retrievers/auto_retriever.py +319 -0
  67. camel/retrievers/base.py +64 -0
  68. camel/retrievers/bm25_retriever.py +149 -0
  69. camel/retrievers/vector_retriever.py +166 -0
  70. camel/societies/__init__.py +1 -1
  71. camel/societies/babyagi_playing.py +56 -32
  72. camel/societies/role_playing.py +188 -133
  73. camel/storages/__init__.py +18 -0
  74. camel/storages/graph_storages/__init__.py +23 -0
  75. camel/storages/graph_storages/base.py +82 -0
  76. camel/storages/graph_storages/graph_element.py +74 -0
  77. camel/storages/graph_storages/neo4j_graph.py +582 -0
  78. camel/storages/key_value_storages/base.py +1 -2
  79. camel/storages/key_value_storages/in_memory.py +1 -2
  80. camel/storages/key_value_storages/json.py +8 -13
  81. camel/storages/vectordb_storages/__init__.py +33 -0
  82. camel/storages/vectordb_storages/base.py +202 -0
  83. camel/storages/vectordb_storages/milvus.py +396 -0
  84. camel/storages/vectordb_storages/qdrant.py +371 -0
  85. camel/terminators/__init__.py +1 -1
  86. camel/terminators/base.py +2 -3
  87. camel/terminators/response_terminator.py +21 -12
  88. camel/terminators/token_limit_terminator.py +5 -3
  89. camel/types/__init__.py +12 -6
  90. camel/types/enums.py +86 -13
  91. camel/types/openai_types.py +10 -5
  92. camel/utils/__init__.py +18 -13
  93. camel/utils/commons.py +242 -81
  94. camel/utils/token_counting.py +135 -15
  95. {camel_ai-0.1.1.dist-info → camel_ai-0.1.3.dist-info}/METADATA +116 -74
  96. camel_ai-0.1.3.dist-info/RECORD +101 -0
  97. {camel_ai-0.1.1.dist-info → camel_ai-0.1.3.dist-info}/WHEEL +1 -1
  98. camel/memories/context_creators/base.py +0 -72
  99. camel_ai-0.1.1.dist-info/RECORD +0 -75
@@ -11,44 +11,356 @@
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
13
  # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
- from typing import Any, Callable, Dict, Optional
14
+ from inspect import Parameter, signature
15
+ from typing import Any, Callable, Dict, Mapping, Optional, Tuple
15
16
 
17
+ from docstring_parser import parse
18
+ from jsonschema.exceptions import SchemaError
16
19
  from jsonschema.validators import Draft202012Validator as JSONValidator
20
+ from pydantic import create_model
21
+ from pydantic.fields import FieldInfo
17
22
 
18
- from camel.utils import parse_doc
23
+ from camel.utils import PYDANTIC_V2, to_pascal
24
+
25
+
26
+ def _remove_a_key(d: Dict, remove_key: Any) -> None:
27
+ r"""Remove a key from a dictionary recursively."""
28
+ if isinstance(d, dict):
29
+ for key in list(d.keys()):
30
+ if key == remove_key:
31
+ del d[key]
32
+ else:
33
+ _remove_a_key(d[key], remove_key)
34
+
35
+
36
+ def get_openai_function_schema(func: Callable) -> Dict[str, Any]:
37
+ r"""Generates a schema dict for an OpenAI function based on its signature.
38
+
39
+ This function is deprecated and will be replaced by
40
+ :obj:`get_openai_tool_schema()` in future versions. It parses the
41
+ function's parameters and docstring to construct a JSON schema-like
42
+ dictionary.
43
+
44
+ Args:
45
+ func (Callable): The OpenAI function to generate the schema for.
46
+
47
+ Returns:
48
+ Dict[str, Any]: A dictionary representing the JSON schema of the
49
+ function, including its name, description, and parameter
50
+ specifications.
51
+ """
52
+ openai_function_schema = get_openai_tool_schema(func)["function"]
53
+ return openai_function_schema
54
+
55
+
56
+ def get_openai_tool_schema(func: Callable) -> Dict[str, Any]:
57
+ r"""Generates an OpenAI JSON schema from a given Python function.
58
+
59
+ This function creates a schema compatible with OpenAI's API specifications,
60
+ based on the provided Python function. It processes the function's
61
+ parameters, types, and docstrings, and constructs a schema accordingly.
62
+
63
+ Note:
64
+ - Each parameter in `func` must have a type annotation; otherwise, it's
65
+ treated as 'Any'.
66
+ - Variable arguments (*args) and keyword arguments (**kwargs) are not
67
+ supported and will be ignored.
68
+ - A functional description including a brief and detailed explanation
69
+ should be provided in the docstring of `func`.
70
+ - All parameters of `func` must be described in its docstring.
71
+ - Supported docstring styles: ReST, Google, Numpydoc, and Epydoc.
72
+
73
+ Args:
74
+ func (Callable): The Python function to be converted into an OpenAI
75
+ JSON schema.
76
+
77
+ Returns:
78
+ Dict[str, Any]: A dictionary representing the OpenAI JSON schema of
79
+ the provided function.
80
+
81
+ See Also:
82
+ `OpenAI API Reference
83
+ <https://platform.openai.com/docs/api-reference/assistants/object>`_
84
+ """
85
+ params: Mapping[str, Parameter] = signature(func).parameters
86
+ fields: Dict[str, Tuple[type, FieldInfo]] = {}
87
+ for param_name, p in params.items():
88
+ param_type = p.annotation
89
+ param_default = p.default
90
+ param_kind = p.kind
91
+ param_annotation = p.annotation
92
+ # Variable parameters are not supported
93
+ if (
94
+ param_kind == Parameter.VAR_POSITIONAL
95
+ or param_kind == Parameter.VAR_KEYWORD
96
+ ):
97
+ continue
98
+ # If the parameter type is not specified, it defaults to typing.Any
99
+ if param_annotation is Parameter.empty:
100
+ param_type = Any
101
+ # Check if the parameter has a default value
102
+ if param_default is Parameter.empty:
103
+ fields[param_name] = (param_type, FieldInfo())
104
+ else:
105
+ fields[param_name] = (param_type, FieldInfo(default=param_default))
106
+
107
+ # Applying `create_model()` directly will result in a mypy error,
108
+ # create an alias to avoid this.
109
+ def _create_mol(name, field):
110
+ return create_model(name, **field)
111
+
112
+ model = _create_mol(to_pascal(func.__name__), fields)
113
+ # NOTE: Method `.schema()` is deprecated in pydantic v2.
114
+ # the result would be identical to `.model_json_schema()` in v2
115
+ if PYDANTIC_V2:
116
+ parameters_dict = model.model_json_schema()
117
+ else:
118
+ parameters_dict = model.schema()
119
+ # The `"title"` is generated by `model.model_json_schema()`
120
+ # but is useless for openai json schema
121
+ _remove_a_key(parameters_dict, "title")
122
+
123
+ docstring = parse(func.__doc__ or "")
124
+ for param in docstring.params:
125
+ if (name := param.arg_name) in parameters_dict["properties"] and (
126
+ description := param.description
127
+ ):
128
+ parameters_dict["properties"][name]["description"] = description
129
+
130
+ short_description = docstring.short_description or ""
131
+ long_description = docstring.long_description or ""
132
+ if long_description:
133
+ func_description = f"{short_description}\n{long_description}"
134
+ else:
135
+ func_description = short_description
136
+
137
+ openai_function_schema = {
138
+ "name": func.__name__,
139
+ "description": func_description,
140
+ "parameters": parameters_dict,
141
+ }
142
+
143
+ openai_tool_schema = {
144
+ "type": "function",
145
+ "function": openai_function_schema,
146
+ }
147
+ return openai_tool_schema
19
148
 
20
149
 
21
150
  class OpenAIFunction:
22
151
  r"""An abstraction of a function that OpenAI chat models can call. See
23
- https://platform.openai.com/docs/guides/gpt/function-calling. If
24
- :obj:`description` and :obj:`parameters` are both :obj:`None`, try to use
25
- document parser to generate them.
152
+ https://platform.openai.com/docs/api-reference/chat/create.
153
+
154
+ By default, the tool schema will be parsed from the func, or you can
155
+ provide a user-defined tool schema to override.
26
156
 
27
- # flake8: noqa :E501
28
157
  Args:
29
- func (Callable): The function to call.
30
- name (str, optional): The name of the function to be called. Must be
31
- a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum
32
- length of 64. If :obj:`None`, use the name of :obj:`func`.
158
+ func (Callable): The function to call.The tool schema is parsed from
159
+ the signature and docstring by default.
160
+ openai_tool_schema (Optional[Dict[str, Any]], optional): A user-defined
161
+ openai tool schema to override the default result.
33
162
  (default: :obj:`None`)
34
- description (str, optional): The description of what the
35
- function does. (default: :obj:`None`)
36
- parameters (dict, optional): The parameters the
37
- functions accepts, described as a JSON Schema object. See the
38
- `Function calling guide <https://platform.openai.com/docs/guides/gpt/function-calling>`_
39
- for examples, and the `JSON Schema reference <https://json-schema.org/understanding-json-schema/>`_
40
- for documentation about the format.
41
163
  """
42
164
 
43
- def __init__(self, func: Callable, name: Optional[str] = None,
44
- description: Optional[str] = None,
45
- parameters: Optional[Dict[str, Any]] = None):
165
+ def __init__(
166
+ self,
167
+ func: Callable,
168
+ openai_tool_schema: Optional[Dict[str, Any]] = None,
169
+ ) -> None:
46
170
  self.func = func
47
- self.name = name or func.__name__
171
+ self.openai_tool_schema = openai_tool_schema or get_openai_tool_schema(
172
+ func
173
+ )
174
+ self.properties = self.openai_tool_schema
175
+
176
+ @staticmethod
177
+ def validate_openai_tool_schema(openai_tool_schema: Dict[str, Any]) -> None:
178
+ r"""Validates the OpenAI tool schema against
179
+ :obj:`ToolAssistantToolsFunction`.
180
+ This function checks if the provided :obj:`openai_tool_schema` adheres
181
+ to the specifications required by OpenAI's
182
+ :obj:`ToolAssistantToolsFunction`. It ensures that the function
183
+ description and parameters are correctly formatted according to JSON
184
+ Schema specifications.
185
+ Args:
186
+ openai_tool_schema (Dict[str, Any]): The OpenAI tool schema to
187
+ validate.
188
+ Raises:
189
+ ValidationError: If the schema does not comply with the
190
+ specifications.
191
+ ValueError: If the function description or parameter descriptions
192
+ are missing in the schema.
193
+ SchemaError: If the parameters do not meet JSON Schema reference
194
+ specifications.
195
+ """
196
+ # Check the function description
197
+ if not openai_tool_schema["function"]["description"]:
198
+ raise ValueError("miss function description")
199
+
200
+ # Validate whether parameters
201
+ # meet the JSON Schema reference specifications.
202
+ # See https://platform.openai.com/docs/guides/gpt/function-calling
203
+ # for examples, and the
204
+ # https://json-schema.org/understanding-json-schema/ for
205
+ # documentation about the format.
206
+ parameters = openai_tool_schema["function"]["parameters"]
207
+ try:
208
+ JSONValidator.check_schema(parameters)
209
+ except SchemaError as e:
210
+ raise e
211
+ # Check the parameter description
212
+ properties: Dict[str, Any] = parameters["properties"]
213
+ for param_name in properties.keys():
214
+ param_dict = properties[param_name]
215
+ if "description" not in param_dict:
216
+ raise ValueError(
217
+ f'miss description of parameter "{param_name}"'
218
+ )
219
+
220
+ def get_openai_tool_schema(self) -> Dict[str, Any]:
221
+ r"""Gets the OpenAI tool schema for this function.
222
+
223
+ This method returns the OpenAI tool schema associated with this
224
+ function, after validating it to ensure it meets OpenAI's
225
+ specifications.
226
+
227
+ Returns:
228
+ Dict[str, Any]: The OpenAI tool schema for this function.
229
+ """
230
+ self.validate_openai_tool_schema(self.openai_tool_schema)
231
+ return self.openai_tool_schema
232
+
233
+ def set_openai_tool_schema(self, schema: Dict[str, Any]) -> None:
234
+ r"""Sets the OpenAI tool schema for this function.
235
+
236
+ Allows setting a custom OpenAI tool schema for this function.
237
+
238
+ Args:
239
+ schema (Dict[str, Any]): The OpenAI tool schema to set.
240
+ """
241
+ self.openai_tool_schema = schema
242
+
243
+ def get_openai_function_schema(self) -> Dict[str, Any]:
244
+ r"""Gets the schema of the function from the OpenAI tool schema.
245
+
246
+ This method extracts and returns the function-specific part of the
247
+ OpenAI tool schema associated with this function.
248
+
249
+ Returns:
250
+ Dict[str, Any]: The schema of the function within the OpenAI tool
251
+ schema.
252
+ """
253
+ self.validate_openai_tool_schema(self.openai_tool_schema)
254
+ return self.openai_tool_schema["function"]
48
255
 
49
- info = parse_doc(self.func)
50
- self.description = description or info["description"]
51
- self.parameters = parameters or info["parameters"]
256
+ def set_openai_function_schema(
257
+ self,
258
+ openai_function_schema: Dict[str, Any],
259
+ ) -> None:
260
+ r"""Sets the schema of the function within the OpenAI tool schema.
261
+
262
+ Args:
263
+ openai_function_schema (Dict[str, Any]): The function schema to set
264
+ within the OpenAI tool schema.
265
+ """
266
+ self.openai_tool_schema["function"] = openai_function_schema
267
+
268
+ def get_function_name(self) -> str:
269
+ r"""Gets the name of the function from the OpenAI tool schema.
270
+
271
+ Returns:
272
+ str: The name of the function.
273
+ """
274
+ self.validate_openai_tool_schema(self.openai_tool_schema)
275
+ return self.openai_tool_schema["function"]["name"]
276
+
277
+ def set_function_name(self, name: str) -> None:
278
+ r"""Sets the name of the function in the OpenAI tool schema.
279
+
280
+ Args:
281
+ name (str): The name of the function to set.
282
+ """
283
+ self.openai_tool_schema["function"]["name"] = name
284
+
285
+ def get_function_description(self) -> str:
286
+ r"""Gets the description of the function from the OpenAI tool
287
+ schema.
288
+
289
+ Returns:
290
+ str: The description of the function.
291
+ """
292
+ self.validate_openai_tool_schema(self.openai_tool_schema)
293
+ return self.openai_tool_schema["function"]["description"]
294
+
295
+ def set_function_description(self, description: str) -> None:
296
+ r"""Sets the description of the function in the OpenAI tool schema.
297
+
298
+ Args:
299
+ description (str): The description for the function.
300
+ """
301
+ self.openai_tool_schema["function"]["description"] = description
302
+
303
+ def get_paramter_description(self, param_name: str) -> str:
304
+ r"""Gets the description of a specific parameter from the function
305
+ schema.
306
+
307
+ Args:
308
+ param_name (str): The name of the parameter to get the
309
+ description.
310
+
311
+ Returns:
312
+ str: The description of the specified parameter.
313
+ """
314
+ self.validate_openai_tool_schema(self.openai_tool_schema)
315
+ return self.openai_tool_schema["function"]["parameters"]["properties"][
316
+ param_name
317
+ ]["description"]
318
+
319
+ def set_paramter_description(
320
+ self,
321
+ param_name: str,
322
+ description: str,
323
+ ) -> None:
324
+ r"""Sets the description for a specific parameter in the function
325
+ schema.
326
+
327
+ Args:
328
+ param_name (str): The name of the parameter to set the description
329
+ for.
330
+ description (str): The description for the parameter.
331
+ """
332
+ self.openai_tool_schema["function"]["parameters"]["properties"][
333
+ param_name
334
+ ]["description"] = description
335
+
336
+ def get_parameter(self, param_name: str) -> Dict[str, Any]:
337
+ r"""Gets the schema for a specific parameter from the function schema.
338
+
339
+ Args:
340
+ param_name (str): The name of the parameter to get the schema.
341
+
342
+ Returns:
343
+ Dict[str, Any]: The schema of the specified parameter.
344
+ """
345
+ self.validate_openai_tool_schema(self.openai_tool_schema)
346
+ return self.openai_tool_schema["function"]["parameters"]["properties"][
347
+ param_name
348
+ ]
349
+
350
+ def set_parameter(self, param_name: str, value: Dict[str, Any]):
351
+ r"""Sets the schema for a specific parameter in the function schema.
352
+
353
+ Args:
354
+ param_name (str): The name of the parameter to set the schema for.
355
+ value (Dict[str, Any]): The schema to set for the parameter.
356
+ """
357
+ try:
358
+ JSONValidator.check_schema(value)
359
+ except SchemaError as e:
360
+ raise e
361
+ self.openai_tool_schema["function"]["parameters"]["properties"][
362
+ param_name
363
+ ] = value
52
364
 
53
365
  @property
54
366
  def parameters(self) -> Dict[str, Any]:
@@ -58,10 +370,11 @@ class OpenAIFunction:
58
370
  Dict[str, Any]: the dictionary containing information of
59
371
  parameters of this function.
60
372
  """
61
- return self._parameters
373
+ self.validate_openai_tool_schema(self.openai_tool_schema)
374
+ return self.openai_tool_schema["function"]["parameters"]["properties"]
62
375
 
63
376
  @parameters.setter
64
- def parameters(self, value: Dict[str, Any]):
377
+ def parameters(self, value: Dict[str, Any]) -> None:
65
378
  r"""Setter method for the property :obj:`parameters`. It will
66
379
  firstly check if the input parameters schema is valid. If invalid,
67
380
  the method will raise :obj:`jsonschema.exceptions.SchemaError`.
@@ -70,19 +383,8 @@ class OpenAIFunction:
70
383
  value (Dict[str, Any]): the new dictionary value for the
71
384
  function's parameters.
72
385
  """
73
- JSONValidator.check_schema(value)
74
- self._parameters = value
75
-
76
- def as_dict(self) -> Dict[str, Any]:
77
- r"""Method to represent the information of this function into
78
- a dictionary object.
79
-
80
- Returns:
81
- Dict[str, Any]: The dictionary object containing information
82
- of this function's name, description and parameters.
83
- """
84
- return {
85
- attr: getattr(self, attr)
86
- for attr in ["name", "description", "parameters"]
87
- if getattr(self, attr) is not None
88
- }
386
+ try:
387
+ JSONValidator.check_schema(value)
388
+ except SchemaError as e:
389
+ raise e
390
+ self.openai_tool_schema["function"]["parameters"]["properties"] = value
@@ -14,8 +14,8 @@
14
14
  import os
15
15
  from typing import Any, Dict, List
16
16
 
17
- import camel.agents
18
- from camel.functions import OpenAIFunction
17
+ from camel.agents import ChatAgent
18
+ from camel.functions.openai_function import OpenAIFunction
19
19
  from camel.messages import BaseMessage
20
20
  from camel.prompts import TextPrompt
21
21
 
@@ -25,10 +25,10 @@ def search_wiki(entity: str) -> str:
25
25
  required page, containing factual information about the given entity.
26
26
 
27
27
  Args:
28
- entity (string): The entity to be searched.
28
+ entity (str): The entity to be searched.
29
29
 
30
30
  Returns:
31
- string: The search result. If the page corresponding to the entity
31
+ str: The search result. If the page corresponding to the entity
32
32
  exists, return the summary of this entity in a string.
33
33
  """
34
34
  try:
@@ -36,19 +36,23 @@ def search_wiki(entity: str) -> str:
36
36
  except ImportError:
37
37
  raise ImportError(
38
38
  "Please install `wikipedia` first. You can install it by running "
39
- "`pip install wikipedia`.")
39
+ "`pip install wikipedia`."
40
+ )
40
41
 
41
42
  result: str
42
43
 
43
44
  try:
44
45
  result = wikipedia.summary(entity, sentences=5, auto_suggest=False)
45
46
  except wikipedia.exceptions.DisambiguationError as e:
46
- result = wikipedia.summary(e.options[0], sentences=5,
47
- auto_suggest=False)
47
+ result = wikipedia.summary(
48
+ e.options[0], sentences=5, auto_suggest=False
49
+ )
48
50
  except wikipedia.exceptions.PageError:
49
- result = ("There is no page in Wikipedia corresponding to entity "
50
- f"{entity}, please specify another word to describe the"
51
- " entity to be searched.")
51
+ result = (
52
+ "There is no page in Wikipedia corresponding to entity "
53
+ f"{entity}, please specify another word to describe the"
54
+ " entity to be searched."
55
+ )
52
56
  except wikipedia.exceptions.WikipediaException as e:
53
57
  result = f"An exception occurred during the search: {e}"
54
58
 
@@ -59,7 +63,7 @@ def search_google(query: str) -> List[Dict[str, Any]]:
59
63
  r"""Use Google search engine to search information for the given query.
60
64
 
61
65
  Args:
62
- query (string): The query to be searched.
66
+ query (str): The query to be searched.
63
67
 
64
68
  Returns:
65
69
  List[Dict[str, Any]]: A list of dictionaries where each dictionary
@@ -83,7 +87,7 @@ def search_google(query: str) -> List[Dict[str, Any]]:
83
87
  as a whole',
84
88
  'url': 'https://www.openai.com'
85
89
  }
86
- title, descrption, url of a website.
90
+ title, description, url of a website.
87
91
  """
88
92
  import requests
89
93
 
@@ -100,9 +104,11 @@ def search_google(query: str) -> List[Dict[str, Any]]:
100
104
  num_result_pages = 10
101
105
  # Constructing the URL
102
106
  # Doc: https://developers.google.com/custom-search/v1/using_rest
103
- url = f"https://www.googleapis.com/customsearch/v1?" \
104
- f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start=" \
105
- f"{start_page_idx}&lr={search_language}&num={num_result_pages}"
107
+ url = (
108
+ f"https://www.googleapis.com/customsearch/v1?"
109
+ f"key={GOOGLE_API_KEY}&cx={SEARCH_ENGINE_ID}&q={query}&start="
110
+ f"{start_page_idx}&lr={search_language}&num={num_result_pages}"
111
+ )
106
112
 
107
113
  responses = []
108
114
  # Fetch the results given the URL
@@ -118,8 +124,9 @@ def search_google(query: str) -> List[Dict[str, Any]]:
118
124
  # Iterate over 10 results found
119
125
  for i, search_item in enumerate(search_items, start=1):
120
126
  if "og:description" in search_item["pagemap"]["metatags"][0]:
121
- long_description = \
122
- search_item["pagemap"]["metatags"][0]["og:description"]
127
+ long_description = search_item["pagemap"]["metatags"][0][
128
+ "og:description"
129
+ ]
123
130
  else:
124
131
  long_description = "N/A"
125
132
  # Get the page title
@@ -134,7 +141,7 @@ def search_google(query: str) -> List[Dict[str, Any]]:
134
141
  "title": title,
135
142
  "description": snippet,
136
143
  "long_description": long_description,
137
- "url": link
144
+ "url": link,
138
145
  }
139
146
  responses.append(response)
140
147
  else:
@@ -150,10 +157,10 @@ def text_extract_from_web(url: str) -> str:
150
157
  r"""Get the text information from given url.
151
158
 
152
159
  Args:
153
- url (string): The web site you want to search.
160
+ url (str): The website you want to search.
154
161
 
155
162
  Returns:
156
- string: All texts extract from the web.
163
+ str: All texts extract from the web.
157
164
  """
158
165
  import requests
159
166
  from bs4 import BeautifulSoup
@@ -171,8 +178,9 @@ def text_extract_from_web(url: str) -> str:
171
178
  text = soup.get_text()
172
179
  # Strip text
173
180
  lines = (line.strip() for line in text.splitlines())
174
- chunks = (phrase.strip() for line in lines
175
- for phrase in line.split(" "))
181
+ chunks = (
182
+ phrase.strip() for line in lines for phrase in line.split(" ")
183
+ )
176
184
  text = ".".join(chunk for chunk in chunks if chunk)
177
185
 
178
186
  except requests.RequestException:
@@ -186,11 +194,11 @@ def create_chunks(text: str, n: int) -> List[str]:
186
194
  r"""Returns successive n-sized chunks from provided text."
187
195
 
188
196
  Args:
189
- text (string): The text to be split.
197
+ text (str): The text to be split.
190
198
  n (int): The max length of a single chunk.
191
199
 
192
200
  Returns:
193
- List[str]: A list of splited texts.
201
+ List[str]: A list of split texts.
194
202
  """
195
203
 
196
204
  chunks = []
@@ -220,7 +228,7 @@ def prompt_single_step_agent(prompt: str) -> str:
220
228
  role_name="Assistant",
221
229
  content="You are a helpful assistant.",
222
230
  )
223
- agent = camel.agents.ChatAgent(assistant_sys_msg)
231
+ agent = ChatAgent(assistant_sys_msg)
224
232
  agent.reset()
225
233
 
226
234
  user_msg = BaseMessage.make_user_message(
@@ -238,15 +246,16 @@ def summarize_text(text: str, query: str) -> str:
238
246
  given.
239
247
 
240
248
  Args:
241
- text (string): Text to summarise.
242
- query (string): What information you want.
249
+ text (str): Text to summarize.
250
+ query (str): What information you want.
243
251
 
244
252
  Returns:
245
- string: Strings with information.
253
+ str: Strings with information.
246
254
  """
247
255
  summary_prompt = TextPrompt(
248
256
  '''Gather information from this text that relative to the question, but
249
- do not directly answer the question.\nquestion: {query}\ntext ''')
257
+ do not directly answer the question.\nquestion: {query}\ntext '''
258
+ )
250
259
  summary_prompt = summary_prompt.format(query=query)
251
260
  # Max length of each chunk
252
261
  max_len = 3000
@@ -261,7 +270,8 @@ def summarize_text(text: str, query: str) -> str:
261
270
  # Final summarise
262
271
  final_prompt = TextPrompt(
263
272
  '''Here are some summarized texts which split from one text, Using the
264
- information to answer the question: {query}.\n\nText: ''')
273
+ information to answer the question: {query}.\n\nText: '''
274
+ )
265
275
  final_prompt = final_prompt.format(query=query)
266
276
  prompt = final_prompt + results
267
277
 
@@ -276,10 +286,10 @@ def search_google_and_summarize(query: str) -> str:
276
286
  internet, and then return a summarized answer.
277
287
 
278
288
  Args:
279
- query (string): Question you want to be answered.
289
+ query (str): Question you want to be answered.
280
290
 
281
291
  Returns:
282
- string: Summarized information from webs.
292
+ str: Summarized information from webs.
283
293
  """
284
294
  # Google search will return a list of urls
285
295
  responses = search_google(query)
@@ -294,7 +304,8 @@ def search_google_and_summarize(query: str) -> str:
294
304
  # Let chatgpt decide whether to continue search or not
295
305
  prompt = TextPrompt(
296
306
  '''Do you think the answer: {answer} can answer the query:
297
- {query}. Use only 'yes' or 'no' to answer.''')
307
+ {query}. Use only 'yes' or 'no' to answer.'''
308
+ )
298
309
  prompt = prompt.format(answer=answer, query=query)
299
310
  reply = prompt_single_step_agent(prompt)
300
311
  if "yes" in str(reply).lower():
@@ -303,7 +314,61 @@ def search_google_and_summarize(query: str) -> str:
303
314
  return "Failed to find the answer from google search."
304
315
 
305
316
 
317
+ def query_wolfram_alpha(query: str, is_detailed: bool) -> str:
318
+ r"""Queries Wolfram|Alpha and returns the result. Wolfram|Alpha is an
319
+ answer engine developed by Wolfram Research. It is offered as an online
320
+ service that answers factual queries by computing answers from externally
321
+ sourced data.
322
+
323
+ Args:
324
+ query (str): The query to send to Wolfram Alpha.
325
+ is_detailed (bool): Whether to include additional details in the
326
+ result.
327
+
328
+ Returns:
329
+ str: The result from Wolfram Alpha, formatted as a string.
330
+ """
331
+ try:
332
+ import wolframalpha
333
+ except ImportError:
334
+ raise ImportError(
335
+ "Please install `wolframalpha` first. You can install it by running `pip install wolframalpha`."
336
+ )
337
+
338
+ WOLFRAMALPHA_APP_ID = os.environ.get('WOLFRAMALPHA_APP_ID')
339
+ if not WOLFRAMALPHA_APP_ID:
340
+ raise ValueError(
341
+ "`WOLFRAMALPHA_APP_ID` not found in environment "
342
+ "variables. Get `WOLFRAMALPHA_APP_ID` here: "
343
+ "`https://products.wolframalpha.com/api/`."
344
+ )
345
+
346
+ try:
347
+ client = wolframalpha.Client(WOLFRAMALPHA_APP_ID)
348
+ res = client.query(query)
349
+ assumption = next(res.pods).text or "No assumption made."
350
+ answer = next(res.results).text or "No answer found."
351
+ except Exception as e:
352
+ if isinstance(e, StopIteration):
353
+ return "Wolfram Alpha wasn't able to answer it"
354
+ else:
355
+ error_message = f"Wolfram Alpha wasn't able to answer it" f"{e!s}."
356
+ return error_message
357
+
358
+ result = f"Assumption:\n{assumption}\n\nAnswer:\n{answer}"
359
+
360
+ # Add additional details in the result
361
+ if is_detailed:
362
+ result += '\n'
363
+ for pod in res.pods:
364
+ result += '\n' + pod['@title'] + ':\n'
365
+ for sub in pod.subpods:
366
+ result += (sub.plaintext or "None") + '\n'
367
+
368
+ return result.rstrip() # Remove trailing whitespace
369
+
370
+
306
371
  SEARCH_FUNCS: List[OpenAIFunction] = [
307
- OpenAIFunction(func)
308
- for func in [search_wiki, search_google_and_summarize]
372
+ OpenAIFunction(func) # type: ignore[arg-type]
373
+ for func in [search_wiki, search_google_and_summarize, query_wolfram_alpha]
309
374
  ]