camel-ai 0.2.21__py3-none-any.whl → 0.2.23__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 (116) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +41 -0
  3. camel/agents/_utils.py +188 -0
  4. camel/agents/chat_agent.py +570 -965
  5. camel/agents/knowledge_graph_agent.py +7 -1
  6. camel/agents/multi_hop_generator_agent.py +1 -1
  7. camel/configs/base_config.py +10 -13
  8. camel/configs/deepseek_config.py +4 -30
  9. camel/configs/gemini_config.py +5 -31
  10. camel/configs/openai_config.py +14 -32
  11. camel/configs/qwen_config.py +36 -36
  12. camel/datagen/self_improving_cot.py +81 -3
  13. camel/datagen/self_instruct/filter/instruction_filter.py +19 -3
  14. camel/datagen/self_instruct/self_instruct.py +53 -4
  15. camel/datasets/__init__.py +28 -0
  16. camel/datasets/base.py +969 -0
  17. camel/embeddings/openai_embedding.py +10 -1
  18. camel/environments/__init__.py +16 -0
  19. camel/environments/base.py +503 -0
  20. camel/extractors/__init__.py +16 -0
  21. camel/extractors/base.py +263 -0
  22. camel/interpreters/docker/Dockerfile +12 -0
  23. camel/interpreters/docker_interpreter.py +19 -1
  24. camel/interpreters/subprocess_interpreter.py +42 -17
  25. camel/loaders/__init__.py +2 -0
  26. camel/loaders/mineru_extractor.py +250 -0
  27. camel/memories/agent_memories.py +16 -1
  28. camel/memories/blocks/chat_history_block.py +10 -2
  29. camel/memories/blocks/vectordb_block.py +1 -0
  30. camel/memories/context_creators/score_based.py +20 -3
  31. camel/memories/records.py +10 -0
  32. camel/messages/base.py +8 -8
  33. camel/models/__init__.py +2 -0
  34. camel/models/_utils.py +57 -0
  35. camel/models/aiml_model.py +48 -17
  36. camel/models/anthropic_model.py +41 -3
  37. camel/models/azure_openai_model.py +39 -3
  38. camel/models/base_audio_model.py +92 -0
  39. camel/models/base_model.py +132 -4
  40. camel/models/cohere_model.py +88 -11
  41. camel/models/deepseek_model.py +107 -63
  42. camel/models/fish_audio_model.py +18 -8
  43. camel/models/gemini_model.py +133 -15
  44. camel/models/groq_model.py +72 -10
  45. camel/models/internlm_model.py +14 -3
  46. camel/models/litellm_model.py +9 -2
  47. camel/models/mistral_model.py +42 -5
  48. camel/models/model_manager.py +57 -3
  49. camel/models/moonshot_model.py +33 -4
  50. camel/models/nemotron_model.py +32 -3
  51. camel/models/nvidia_model.py +43 -3
  52. camel/models/ollama_model.py +139 -17
  53. camel/models/openai_audio_models.py +87 -2
  54. camel/models/openai_compatible_model.py +37 -3
  55. camel/models/openai_model.py +158 -46
  56. camel/models/qwen_model.py +61 -4
  57. camel/models/reka_model.py +53 -3
  58. camel/models/samba_model.py +209 -4
  59. camel/models/sglang_model.py +153 -14
  60. camel/models/siliconflow_model.py +16 -3
  61. camel/models/stub_model.py +46 -4
  62. camel/models/togetherai_model.py +38 -3
  63. camel/models/vllm_model.py +37 -3
  64. camel/models/yi_model.py +36 -3
  65. camel/models/zhipuai_model.py +38 -3
  66. camel/retrievers/__init__.py +3 -0
  67. camel/retrievers/hybrid_retrival.py +237 -0
  68. camel/toolkits/__init__.py +20 -3
  69. camel/toolkits/arxiv_toolkit.py +2 -1
  70. camel/toolkits/ask_news_toolkit.py +4 -2
  71. camel/toolkits/audio_analysis_toolkit.py +238 -0
  72. camel/toolkits/base.py +22 -3
  73. camel/toolkits/code_execution.py +2 -0
  74. camel/toolkits/dappier_toolkit.py +2 -1
  75. camel/toolkits/data_commons_toolkit.py +38 -12
  76. camel/toolkits/excel_toolkit.py +172 -0
  77. camel/toolkits/function_tool.py +13 -0
  78. camel/toolkits/github_toolkit.py +5 -1
  79. camel/toolkits/google_maps_toolkit.py +2 -1
  80. camel/toolkits/google_scholar_toolkit.py +2 -0
  81. camel/toolkits/human_toolkit.py +0 -3
  82. camel/toolkits/image_analysis_toolkit.py +202 -0
  83. camel/toolkits/linkedin_toolkit.py +3 -2
  84. camel/toolkits/meshy_toolkit.py +3 -2
  85. camel/toolkits/mineru_toolkit.py +178 -0
  86. camel/toolkits/networkx_toolkit.py +240 -0
  87. camel/toolkits/notion_toolkit.py +2 -0
  88. camel/toolkits/openbb_toolkit.py +3 -2
  89. camel/toolkits/page_script.js +376 -0
  90. camel/toolkits/reddit_toolkit.py +11 -3
  91. camel/toolkits/retrieval_toolkit.py +6 -1
  92. camel/toolkits/semantic_scholar_toolkit.py +2 -1
  93. camel/toolkits/stripe_toolkit.py +8 -2
  94. camel/toolkits/sympy_toolkit.py +44 -1
  95. camel/toolkits/video_analysis_toolkit.py +407 -0
  96. camel/toolkits/{video_toolkit.py → video_download_toolkit.py} +21 -25
  97. camel/toolkits/web_toolkit.py +1307 -0
  98. camel/toolkits/whatsapp_toolkit.py +3 -2
  99. camel/toolkits/zapier_toolkit.py +191 -0
  100. camel/types/__init__.py +2 -2
  101. camel/types/agents/__init__.py +16 -0
  102. camel/types/agents/tool_calling_record.py +52 -0
  103. camel/types/enums.py +3 -0
  104. camel/types/openai_types.py +16 -14
  105. camel/utils/__init__.py +2 -1
  106. camel/utils/async_func.py +2 -2
  107. camel/utils/commons.py +114 -1
  108. camel/verifiers/__init__.py +23 -0
  109. camel/verifiers/base.py +340 -0
  110. camel/verifiers/models.py +82 -0
  111. camel/verifiers/python_verifier.py +202 -0
  112. camel_ai-0.2.23.dist-info/METADATA +671 -0
  113. {camel_ai-0.2.21.dist-info → camel_ai-0.2.23.dist-info}/RECORD +127 -99
  114. {camel_ai-0.2.21.dist-info → camel_ai-0.2.23.dist-info}/WHEEL +1 -1
  115. camel_ai-0.2.21.dist-info/METADATA +0 -528
  116. {camel_ai-0.2.21.dist-info → camel_ai-0.2.23.dist-info/licenses}/LICENSE +0 -0
@@ -14,6 +14,7 @@
14
14
  import logging
15
15
  from typing import Any, Dict, List, Optional, Union
16
16
 
17
+ from camel.toolkits import FunctionTool
17
18
  from camel.toolkits.base import BaseToolkit
18
19
 
19
20
  logger = logging.getLogger(__name__)
@@ -35,8 +36,18 @@ class DataCommonsToolkit(BaseToolkit):
35
36
  Refer to https://datacommons.org/browser/ for more details.
36
37
  """
37
38
 
38
- @staticmethod
39
+ def __init__(self, timeout: Optional[float] = None):
40
+ r"""Initialize the DataCommonsToolkit.
41
+
42
+ Args:
43
+ timeout (Optional[float], optional): Maximum time in seconds to
44
+ wait for API calls to complete. If None, will wait indefinitely.
45
+ (default: :obj:`None`)
46
+ """
47
+ super().__init__(timeout=timeout)
48
+
39
49
  def query_data_commons(
50
+ self,
40
51
  query_string: str,
41
52
  ) -> Optional[List[Dict[str, Any]]]:
42
53
  r"""Query the Data Commons knowledge graph using SPARQL.
@@ -76,9 +87,8 @@ class DataCommonsToolkit(BaseToolkit):
76
87
  )
77
88
  return None
78
89
 
79
- @staticmethod
80
90
  def get_triples(
81
- dcids: Union[str, List[str]], limit: int = 500
91
+ self, dcids: Union[str, List[str]], limit: int = 500
82
92
  ) -> Optional[Dict[str, List[tuple]]]:
83
93
  r"""Retrieve triples associated with nodes.
84
94
 
@@ -117,8 +127,8 @@ class DataCommonsToolkit(BaseToolkit):
117
127
  logger.error(f"An error occurred: {e!s}")
118
128
  return None
119
129
 
120
- @staticmethod
121
130
  def get_stat_time_series(
131
+ self,
122
132
  place: str,
123
133
  stat_var: str,
124
134
  measurement_method: Optional[str] = None,
@@ -166,9 +176,8 @@ class DataCommonsToolkit(BaseToolkit):
166
176
  )
167
177
  return None
168
178
 
169
- @staticmethod
170
179
  def get_property_labels(
171
- dcids: Union[str, List[str]], out: bool = True
180
+ self, dcids: Union[str, List[str]], out: bool = True
172
181
  ) -> Optional[Dict[str, List[str]]]:
173
182
  r"""Retrieves and analyzes property labels for given DCIDs.
174
183
 
@@ -195,8 +204,8 @@ class DataCommonsToolkit(BaseToolkit):
195
204
  )
196
205
  return None
197
206
 
198
- @staticmethod
199
207
  def get_property_values(
208
+ self,
200
209
  dcids: Union[str, List[str]],
201
210
  prop: str,
202
211
  out: Optional[bool] = True,
@@ -239,9 +248,8 @@ class DataCommonsToolkit(BaseToolkit):
239
248
  )
240
249
  return None
241
250
 
242
- @staticmethod
243
251
  def get_places_in(
244
- dcids: list, place_type: str
252
+ self, dcids: list, place_type: str
245
253
  ) -> Optional[Dict[str, Any]]:
246
254
  r"""Retrieves places within a given place type.
247
255
 
@@ -269,8 +277,8 @@ class DataCommonsToolkit(BaseToolkit):
269
277
  )
270
278
  return None
271
279
 
272
- @staticmethod
273
280
  def get_stat_value(
281
+ self,
274
282
  place: str,
275
283
  stat_var: str,
276
284
  date: Optional[str] = None,
@@ -326,8 +334,7 @@ class DataCommonsToolkit(BaseToolkit):
326
334
  )
327
335
  return None
328
336
 
329
- @staticmethod
330
- def get_stat_all(places: str, stat_vars: str) -> Optional[dict]:
337
+ def get_stat_all(self, places: str, stat_vars: str) -> Optional[dict]:
331
338
  r"""Retrieves the value of a statistical variable for a given place
332
339
  and date.
333
340
 
@@ -358,3 +365,22 @@ class DataCommonsToolkit(BaseToolkit):
358
365
  f"statistical variable: {e!s}"
359
366
  )
360
367
  return None
368
+
369
+ def get_tools(self) -> List[FunctionTool]:
370
+ r"""Returns a list of FunctionTool objects representing the functions
371
+ in the toolkit.
372
+
373
+ Returns:
374
+ List[FunctionTool]: A list of FunctionTool objects representing
375
+ the functions in the toolkit.
376
+ """
377
+ return [
378
+ FunctionTool(self.query_data_commons),
379
+ FunctionTool(self.get_triples),
380
+ FunctionTool(self.get_stat_time_series),
381
+ FunctionTool(self.get_property_labels),
382
+ FunctionTool(self.get_property_values),
383
+ FunctionTool(self.get_places_in),
384
+ FunctionTool(self.get_stat_value),
385
+ FunctionTool(self.get_stat_all),
386
+ ]
@@ -0,0 +1,172 @@
1
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ from typing import List
16
+
17
+ import pandas as pd
18
+
19
+ from camel.logger import get_logger
20
+ from camel.toolkits.base import BaseToolkit
21
+ from camel.toolkits.function_tool import FunctionTool
22
+
23
+ logger = get_logger(__name__)
24
+
25
+
26
+ class ExcelToolkit(BaseToolkit):
27
+ r"""A class representing a toolkit for extract detailed cell information
28
+ from an Excel file.
29
+
30
+ This class provides method for processing docx, pdf, pptx, etc. It cannot
31
+ process excel files.
32
+ """
33
+
34
+ def _convert_to_markdown(self, df: pd.DataFrame) -> str:
35
+ r"""Convert DataFrame to Markdown format table.
36
+
37
+ Args:
38
+ df (pd.DataFrame): DataFrame containing the Excel data.
39
+
40
+ Returns:
41
+ str: Markdown formatted table.
42
+ """
43
+ from tabulate import tabulate
44
+
45
+ md_table = tabulate(df, headers='keys', tablefmt='pipe')
46
+ return str(md_table)
47
+
48
+ def extract_excel_content(self, document_path: str) -> str:
49
+ r"""Extract detailed cell information from an Excel file, including
50
+ multiple sheets.
51
+
52
+ Args:
53
+ document_path (str): The path of the Excel file.
54
+
55
+ Returns:
56
+ str: Extracted excel information, including details of each sheet.
57
+ """
58
+ from openpyxl import load_workbook
59
+ from xls2xlsx import XLS2XLSX
60
+
61
+ logger.debug(
62
+ f"Calling extract_excel_content with document_path"
63
+ f": {document_path}"
64
+ )
65
+
66
+ if not (
67
+ document_path.endswith("xls")
68
+ or document_path.endswith("xlsx")
69
+ or document_path.endswith("csv")
70
+ ):
71
+ logger.error("Only xls, xlsx, csv files are supported.")
72
+ return (
73
+ f"Failed to process file {document_path}: "
74
+ f"It is not excel format. Please try other ways."
75
+ )
76
+
77
+ if document_path.endswith("csv"):
78
+ try:
79
+ df = pd.read_csv(document_path)
80
+ md_table = self._convert_to_markdown(df)
81
+ return f"CSV File Processed:\n{md_table}"
82
+ except Exception as e:
83
+ logger.error(f"Failed to process file {document_path}: {e}")
84
+ return f"Failed to process file {document_path}: {e}"
85
+
86
+ if document_path.endswith("xls"):
87
+ output_path = document_path.replace(".xls", ".xlsx")
88
+ x2x = XLS2XLSX(document_path)
89
+ x2x.to_xlsx(output_path)
90
+ document_path = output_path
91
+
92
+ # Load the Excel workbook
93
+ wb = load_workbook(document_path, data_only=True)
94
+ sheet_info_list = []
95
+
96
+ # Iterate through all sheets
97
+ for sheet in wb.sheetnames:
98
+ ws = wb[sheet]
99
+ cell_info_list = []
100
+
101
+ for row in ws.iter_rows():
102
+ for cell in row:
103
+ row_num = cell.row
104
+ col_letter = cell.column_letter
105
+
106
+ cell_value = cell.value
107
+
108
+ font_color = None
109
+ if (
110
+ cell.font
111
+ and cell.font.color
112
+ and "rgb=None" not in str(cell.font.color)
113
+ ): # Handle font color
114
+ font_color = cell.font.color.rgb
115
+
116
+ fill_color = None
117
+ if (
118
+ cell.fill
119
+ and cell.fill.fgColor
120
+ and "rgb=None" not in str(cell.fill.fgColor)
121
+ ): # Handle fill color
122
+ fill_color = cell.fill.fgColor.rgb
123
+
124
+ cell_info_list.append(
125
+ {
126
+ "index": f"{row_num}{col_letter}",
127
+ "value": cell_value,
128
+ "font_color": font_color,
129
+ "fill_color": fill_color,
130
+ }
131
+ )
132
+
133
+ # Convert the sheet to a DataFrame and then to markdown
134
+ sheet_df = pd.read_excel(
135
+ document_path, sheet_name=sheet, engine='openpyxl'
136
+ )
137
+ markdown_content = self._convert_to_markdown(sheet_df)
138
+
139
+ # Collect all information for the sheet
140
+ sheet_info = {
141
+ "sheet_name": sheet,
142
+ "cell_info_list": cell_info_list,
143
+ "markdown_content": markdown_content,
144
+ }
145
+ sheet_info_list.append(sheet_info)
146
+
147
+ result_str = ""
148
+ for sheet_info in sheet_info_list:
149
+ result_str += f"""
150
+ Sheet Name: {sheet_info['sheet_name']}
151
+ Cell information list:
152
+ {sheet_info['cell_info_list']}
153
+
154
+ Markdown View of the content:
155
+ {sheet_info['markdown_content']}
156
+
157
+ {'-'*40}
158
+ """
159
+
160
+ return result_str
161
+
162
+ def get_tools(self) -> List[FunctionTool]:
163
+ r"""Returns a list of FunctionTool objects representing the functions
164
+ in the toolkit.
165
+
166
+ Returns:
167
+ List[FunctionTool]: A list of FunctionTool objects representing
168
+ the functions in the toolkit.
169
+ """
170
+ return [
171
+ FunctionTool(self.extract_excel_content),
172
+ ]
@@ -398,6 +398,19 @@ class FunctionTool:
398
398
  f"Error: {e}"
399
399
  )
400
400
 
401
+ async def async_call(self, *args: Any, **kwargs: Any) -> Any:
402
+ if self.synthesize_output:
403
+ result = self.synthesize_execution_output(args, kwargs)
404
+ return result
405
+ if self.is_async:
406
+ return await self.func(*args, **kwargs)
407
+ else:
408
+ return self.func(*args, **kwargs)
409
+
410
+ @property
411
+ def is_async(self) -> bool:
412
+ return inspect.iscoroutinefunction(self.func)
413
+
401
414
  @staticmethod
402
415
  def validate_openai_tool_schema(
403
416
  openai_tool_schema: Dict[str, Any],
@@ -39,7 +39,10 @@ class GithubToolkit(BaseToolkit):
39
39
 
40
40
  @dependencies_required('github')
41
41
  def __init__(
42
- self, repo_name: str, access_token: Optional[str] = None
42
+ self,
43
+ repo_name: str,
44
+ access_token: Optional[str] = None,
45
+ timeout: Optional[float] = None,
43
46
  ) -> None:
44
47
  r"""Initializes a new instance of the GitHubToolkit class.
45
48
 
@@ -49,6 +52,7 @@ class GithubToolkit(BaseToolkit):
49
52
  with GitHub. If not provided, it will be obtained using the
50
53
  `get_github_access_token` method.
51
54
  """
55
+ super().__init__(timeout=timeout)
52
56
  from github import Auth, Github
53
57
 
54
58
  if access_token is None:
@@ -101,7 +101,8 @@ class GoogleMapsToolkit(BaseToolkit):
101
101
  """
102
102
 
103
103
  @dependencies_required('googlemaps')
104
- def __init__(self) -> None:
104
+ def __init__(self, timeout: Optional[float] = None) -> None:
105
+ super().__init__(timeout=timeout)
105
106
  import googlemaps
106
107
 
107
108
  api_key = os.environ.get('GOOGLE_API_KEY')
@@ -39,6 +39,7 @@ class GoogleScholarToolkit(BaseToolkit):
39
39
  use_free_proxies: bool = False,
40
40
  proxy_http: Optional[str] = None,
41
41
  proxy_https: Optional[str] = None,
42
+ timeout: Optional[float] = None,
42
43
  ) -> None:
43
44
  r"""Initializes the GoogleScholarToolkit with the author's identifier.
44
45
 
@@ -54,6 +55,7 @@ class GoogleScholarToolkit(BaseToolkit):
54
55
  proxy_https ( Optional[str]): Proxy https address pass to pg.
55
56
  SingleProxy. (default: :obj:`None`)
56
57
  """
58
+ super().__init__(timeout=timeout)
57
59
  from scholarly import ProxyGenerator, scholarly
58
60
 
59
61
  # Set Free Proxies is needed
@@ -24,9 +24,6 @@ logger = logging.getLogger(__name__)
24
24
  class HumanToolkit(BaseToolkit):
25
25
  r"""A class representing a toolkit for human interaction."""
26
26
 
27
- def __init__(self):
28
- pass
29
-
30
27
  def ask_human_via_console(self, question: str) -> str:
31
28
  r"""Ask a question to the human via the console.
32
29
 
@@ -0,0 +1,202 @@
1
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
+
15
+ from io import BytesIO
16
+ from typing import List, Optional
17
+ from urllib.parse import urlparse
18
+
19
+ import requests
20
+ from PIL import Image
21
+
22
+ from camel.logger import get_logger
23
+ from camel.messages import BaseMessage
24
+ from camel.models import BaseModelBackend, ModelFactory
25
+ from camel.toolkits import FunctionTool
26
+ from camel.toolkits.base import BaseToolkit
27
+ from camel.types import ModelPlatformType, ModelType
28
+
29
+ logger = get_logger(__name__)
30
+
31
+
32
+ class ImageAnalysisToolkit(BaseToolkit):
33
+ r"""A toolkit for comprehensive image analysis and understanding.
34
+ The toolkit uses vision-capable language models to perform these tasks.
35
+ """
36
+
37
+ def __init__(self, model: Optional[BaseModelBackend] = None):
38
+ r"""Initialize the ImageAnalysisToolkit.
39
+
40
+ Args:
41
+ model (Optional[BaseModelBackend]): The model backend to use for
42
+ image analysis tasks. This model should support processing
43
+ images for tasks like image description and visual question
44
+ answering. If None, a default model will be created using
45
+ ModelFactory. (default: :obj:`None`)
46
+ """
47
+ if model:
48
+ self.model = model
49
+ else:
50
+ self.model = ModelFactory.create(
51
+ model_platform=ModelPlatformType.DEFAULT,
52
+ model_type=ModelType.DEFAULT,
53
+ )
54
+
55
+ def image_to_text(
56
+ self, image_path: str, sys_prompt: Optional[str] = None
57
+ ) -> str:
58
+ r"""Generates textual description of an image with optional custom
59
+ prompt.
60
+
61
+ Args:
62
+ image_path (str): Local path or URL to an image file.
63
+ sys_prompt (Optional[str]): Custom system prompt for the analysis.
64
+ (default: :obj:`None`)
65
+
66
+ Returns:
67
+ str: Natural language description of the image.
68
+ """
69
+ default_content = '''You are an image analysis expert. Provide a
70
+ detailed description including text if present.'''
71
+
72
+ system_msg = BaseMessage.make_assistant_message(
73
+ role_name="Senior Computer Vision Analyst",
74
+ content=sys_prompt if sys_prompt else default_content,
75
+ )
76
+
77
+ return self._analyze_image(
78
+ image_path=image_path,
79
+ prompt="Please describe the contents of this image.",
80
+ system_message=system_msg,
81
+ )
82
+
83
+ def ask_question_about_image(
84
+ self, image_path: str, question: str, sys_prompt: Optional[str] = None
85
+ ) -> str:
86
+ r"""Answers image questions with optional custom instructions.
87
+
88
+ Args:
89
+ image_path (str): Local path or URL to an image file.
90
+ question (str): Query about the image content.
91
+ sys_prompt (Optional[str]): Custom system prompt for the analysis.
92
+ (default: :obj:`None`)
93
+
94
+ Returns:
95
+ str: Detailed answer based on visual understanding
96
+ """
97
+ default_content = """Answer questions about images by:
98
+ 1. Careful visual inspection
99
+ 2. Contextual reasoning
100
+ 3. Text transcription where relevant
101
+ 4. Logical deduction from visual evidence"""
102
+
103
+ system_msg = BaseMessage.make_assistant_message(
104
+ role_name="Visual QA Specialist",
105
+ content=sys_prompt if sys_prompt else default_content,
106
+ )
107
+
108
+ return self._analyze_image(
109
+ image_path=image_path,
110
+ prompt=question,
111
+ system_message=system_msg,
112
+ )
113
+
114
+ def _load_image(self, image_path: str) -> Image.Image:
115
+ r"""Loads an image from either local path or URL.
116
+
117
+ Args:
118
+ image_path (str): Local path or URL to image.
119
+
120
+ Returns:
121
+ Image.Image: Loaded PIL Image object.
122
+
123
+ Raises:
124
+ ValueError: For invalid paths/URLs or unreadable images.
125
+ requests.exceptions.RequestException: For URL fetch failures.
126
+ """
127
+ parsed = urlparse(image_path)
128
+
129
+ if parsed.scheme in ("http", "https"):
130
+ logger.debug(f"Fetching image from URL: {image_path}")
131
+ try:
132
+ response = requests.get(image_path, timeout=15)
133
+ response.raise_for_status()
134
+ return Image.open(BytesIO(response.content))
135
+ except requests.exceptions.RequestException as e:
136
+ logger.error(f"URL fetch failed: {e}")
137
+ raise
138
+ else:
139
+ logger.debug(f"Loading local image: {image_path}")
140
+ try:
141
+ return Image.open(image_path)
142
+ except Exception as e:
143
+ logger.error(f"Image loading failed: {e}")
144
+ raise ValueError(f"Invalid image file: {e}")
145
+
146
+ def _analyze_image(
147
+ self,
148
+ image_path: str,
149
+ prompt: str,
150
+ system_message: BaseMessage,
151
+ ) -> str:
152
+ r"""Core analysis method handling image loading and processing.
153
+
154
+ Args:
155
+ image_path (str): Image location.
156
+ prompt (str): Analysis query/instructions.
157
+ system_message (BaseMessage): Custom system prompt for the
158
+ analysis.
159
+
160
+ Returns:
161
+ str: Analysis result or error message.
162
+ """
163
+ try:
164
+ image = self._load_image(image_path)
165
+ logger.info(f"Analyzing image: {image_path}")
166
+
167
+ from camel.agents.chat_agent import ChatAgent
168
+
169
+ agent = ChatAgent(
170
+ system_message=system_message,
171
+ model=self.model,
172
+ )
173
+
174
+ user_msg = BaseMessage.make_user_message(
175
+ role_name="User",
176
+ content=prompt,
177
+ image_list=[image],
178
+ )
179
+
180
+ response = agent.step(user_msg)
181
+ agent.reset()
182
+ return response.msgs[0].content
183
+
184
+ except (ValueError, requests.exceptions.RequestException) as e:
185
+ logger.error(f"Image handling error: {e}")
186
+ return f"Image error: {e!s}"
187
+ except Exception as e:
188
+ logger.error(f"Unexpected error: {e}")
189
+ return f"Analysis failed: {e!s}"
190
+
191
+ def get_tools(self) -> List[FunctionTool]:
192
+ r"""Returns a list of FunctionTool objects representing the functions
193
+ in the toolkit.
194
+
195
+ Returns:
196
+ List[FunctionTool]: A list of FunctionTool objects representing the
197
+ functions in the toolkit.
198
+ """
199
+ return [
200
+ FunctionTool(self.image_to_text),
201
+ FunctionTool(self.ask_question_about_image),
202
+ ]
@@ -15,7 +15,7 @@
15
15
  import json
16
16
  import os
17
17
  from http import HTTPStatus
18
- from typing import List
18
+ from typing import List, Optional
19
19
 
20
20
  import requests
21
21
 
@@ -33,7 +33,8 @@ class LinkedInToolkit(BaseToolkit):
33
33
  retrieving the authenticated user's profile information.
34
34
  """
35
35
 
36
- def __init__(self):
36
+ def __init__(self, timeout: Optional[float] = None):
37
+ super().__init__(timeout=timeout)
37
38
  self._access_token = self._get_access_token()
38
39
 
39
40
  def create_post(self, text: str) -> dict:
@@ -13,7 +13,7 @@
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
15
  import os
16
- from typing import Any, Dict
16
+ from typing import Any, Dict, Optional
17
17
 
18
18
  import requests
19
19
 
@@ -38,10 +38,11 @@ class MeshyToolkit(BaseToolkit):
38
38
  (None, 'MESHY_API_KEY'),
39
39
  ]
40
40
  )
41
- def __init__(self):
41
+ def __init__(self, timeout: Optional[float] = None):
42
42
  r"""Initializes the MeshyToolkit with the API key from the
43
43
  environment.
44
44
  """
45
+ super().__init__(timeout=timeout)
45
46
  self.api_key = os.getenv('MESHY_API_KEY')
46
47
 
47
48
  def generate_3d_preview(