camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__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.
Files changed (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
camel/tasks/task.py CHANGED
@@ -19,16 +19,21 @@ from typing import (
19
19
  Any,
20
20
  Callable,
21
21
  Dict,
22
+ Generator,
22
23
  List,
23
24
  Literal,
24
25
  Optional,
25
26
  Union,
26
27
  )
27
28
 
28
- from pydantic import BaseModel
29
+ from PIL import Image
30
+ from pydantic import BaseModel, ConfigDict, Field
29
31
 
30
32
  if TYPE_CHECKING:
31
33
  from camel.agents import ChatAgent
34
+ from camel.agents.chat_agent import StreamingChatAgentResponse
35
+ import uuid
36
+
32
37
  from camel.logger import get_logger
33
38
  from camel.messages import BaseMessage
34
39
  from camel.prompts import TextPrompt
@@ -43,19 +48,35 @@ from .task_prompt import (
43
48
  logger = get_logger(__name__)
44
49
 
45
50
 
51
+ class TaskValidationMode(Enum):
52
+ r"""Validation modes for different use cases."""
53
+
54
+ INPUT = "input" # For validating task content before processing
55
+ OUTPUT = "output" # For validating task results after completion
56
+
57
+
46
58
  def validate_task_content(
47
- content: str, task_id: str = "unknown", min_length: int = 10
59
+ content: str,
60
+ task_id: str = "unknown",
61
+ min_length: int = 1,
62
+ mode: TaskValidationMode = TaskValidationMode.INPUT,
63
+ check_failure_patterns: bool = True,
48
64
  ) -> bool:
49
- r"""Validates task result content to avoid silent failures.
50
- It performs basic checks to ensure the content meets minimum
51
- quality standards.
65
+ r"""Unified validation for task content and results to avoid silent
66
+ failures. Performs comprehensive checks to ensure content meets quality
67
+ standards.
52
68
 
53
69
  Args:
54
- content (str): The task result content to validate.
70
+ content (str): The task content or result to validate.
55
71
  task_id (str): Task ID for logging purposes.
56
72
  (default: :obj:`"unknown"`)
57
73
  min_length (int): Minimum content length after stripping whitespace.
58
- (default: :obj:`10`)
74
+ (default: :obj:`1`)
75
+ mode (TaskValidationMode): Validation mode - INPUT for task content,
76
+ OUTPUT for task results. (default: :obj:`TaskValidationMode.INPUT`)
77
+ check_failure_patterns (bool): Whether to check for failure indicators
78
+ in the content. Only effective in OUTPUT mode.
79
+ (default: :obj:`True`)
59
80
 
60
81
  Returns:
61
82
  bool: True if content passes validation, False otherwise.
@@ -78,18 +99,74 @@ def validate_task_content(
78
99
  logger.warning(
79
100
  f"Task {task_id}: Content too short ({len(stripped_content)} "
80
101
  f"chars < {min_length} minimum). Content preview: "
81
- f"'{stripped_content[:50]}...'"
102
+ f"'{stripped_content}'"
82
103
  )
83
104
  return False
84
105
 
106
+ # 4: For OUTPUT mode, check for failure patterns if enabled
107
+ if mode == TaskValidationMode.OUTPUT and check_failure_patterns:
108
+ content_lower = stripped_content.lower()
109
+
110
+ # Check for explicit failure indicators
111
+ failure_indicators = [
112
+ "i cannot complete",
113
+ "i cannot do",
114
+ "task failed",
115
+ "unable to complete",
116
+ "cannot be completed",
117
+ "failed to complete",
118
+ "i cannot",
119
+ "not possible",
120
+ "impossible to",
121
+ "cannot perform",
122
+ ]
123
+
124
+ if any(indicator in content_lower for indicator in failure_indicators):
125
+ logger.warning(
126
+ f"Task {task_id}: Failure indicator detected in result. "
127
+ f"Content preview: '{stripped_content}'"
128
+ )
129
+ return False
130
+
131
+ # Check for responses that are just error messages or refusals
132
+ if content_lower.startswith(("error", "failed", "cannot", "unable")):
133
+ logger.warning(
134
+ f"Task {task_id}: Error/refusal pattern detected at start. "
135
+ f"Content preview: '{stripped_content}'"
136
+ )
137
+ return False
138
+
85
139
  # All validation checks passed
86
140
  logger.debug(
87
- f"Task {task_id}: Content validation passed "
141
+ f"Task {task_id}: {mode.value} validation passed "
88
142
  f"({len(stripped_content)} chars)"
89
143
  )
90
144
  return True
91
145
 
92
146
 
147
+ def is_task_result_insufficient(task: "Task") -> bool:
148
+ r"""Check if a task result is insufficient and should be treated as failed.
149
+
150
+ This is a convenience wrapper around validate_task_content for backward
151
+ compatibility and semantic clarity when checking task results.
152
+
153
+ Args:
154
+ task (Task): The task to check.
155
+
156
+ Returns:
157
+ bool: True if the result is insufficient, False otherwise.
158
+ """
159
+ if not hasattr(task, 'result') or task.result is None:
160
+ return True
161
+
162
+ return not validate_task_content(
163
+ content=task.result,
164
+ task_id=task.id,
165
+ mode=TaskValidationMode.OUTPUT,
166
+ check_failure_patterns=True,
167
+ )
168
+
169
+
93
170
  def parse_response(
94
171
  response: str, task_id: Optional[str] = None
95
172
  ) -> List["Task"]:
@@ -109,7 +186,7 @@ def parse_response(
109
186
  tasks = []
110
187
  if task_id is None:
111
188
  task_id = "0"
112
- for i, content in enumerate(tasks_content):
189
+ for i, content in enumerate(tasks_content, 1):
113
190
  stripped_content = content.strip()
114
191
  # validate subtask content before creating the task
115
192
  if validate_task_content(stripped_content, f"{task_id}.{i}"):
@@ -118,7 +195,7 @@ def parse_response(
118
195
  logger.warning(
119
196
  f"Skipping invalid subtask {task_id}.{i} "
120
197
  f"during decomposition: "
121
- f"Content '{stripped_content[:50]}...' failed validation"
198
+ f"Content '{stripped_content}' failed validation"
122
199
  )
123
200
  return tasks
124
201
 
@@ -142,27 +219,42 @@ class Task(BaseModel):
142
219
  content (str): string content for task.
143
220
  id (str): An unique string identifier for the task. This should
144
221
  ideally be provided by the provider/model which created the task.
145
- (default: :obj: `""`)
222
+ (default: :obj:`uuid.uuid4()`)
146
223
  state (TaskState): The state which should be OPEN, RUNNING, DONE or
147
- DELETED. (default: :obj: `TaskState.OPEN`)
148
- type (Optional[str]): task type. (default: :obj: `None`)
224
+ DELETED. (default: :obj:`TaskState.FAILED`)
225
+ type (Optional[str]): task type. (default: :obj:`None`)
149
226
  parent (Optional[Task]): The parent task, None for root task.
150
- (default: :obj: `None`)
227
+ (default: :obj:`None`)
151
228
  subtasks (List[Task]): The childrent sub-tasks for the task.
152
- (default: :obj: `[]`)
229
+ (default: :obj:`[]`)
153
230
  result (Optional[str]): The answer for the task.
154
- (default: :obj: `""`)
231
+ (default: :obj:`""`)
155
232
  failure_count (int): The failure count for the task.
156
- (default: :obj: `0`)
233
+ (default: :obj:`0`)
234
+ assigned_worker_id (Optional[str]): The ID of the worker assigned to
235
+ this task. (default: :obj:`None`)
236
+ dependencies (List[Task]): The dependencies for the task.
237
+ (default: :obj:`[]`)
157
238
  additional_info (Optional[Dict[str, Any]]): Additional information for
158
- the task. (default: :obj: `None`)
239
+ the task. (default: :obj:`None`)
240
+ image_list (Optional[List[Union[Image.Image, str]]]): Optional list
241
+ of PIL Image objects or image URLs (strings) associated with the
242
+ task. (default: :obj:`None`)
243
+ image_detail (Literal["auto", "low", "high"]): Detail level of the
244
+ images associated with the task. (default: :obj:`auto`)
245
+ video_bytes (Optional[bytes]): Optional bytes of a video associated
246
+ with the task. (default: :obj:`None`)
247
+ video_detail (Literal["auto", "low", "high"]): Detail level of the
248
+ videos associated with the task. (default: :obj:`auto`)
159
249
  """
160
250
 
161
251
  content: str
162
252
 
163
- id: str = ""
253
+ id: str = Field(default_factory=lambda: str(uuid.uuid4()))
164
254
 
165
- state: TaskState = TaskState.OPEN
255
+ state: TaskState = (
256
+ TaskState.FAILED
257
+ ) # TODO: Add logic for OPEN in workforce.py
166
258
 
167
259
  type: Optional[str] = None
168
260
 
@@ -174,8 +266,22 @@ class Task(BaseModel):
174
266
 
175
267
  failure_count: int = 0
176
268
 
269
+ assigned_worker_id: Optional[str] = None
270
+
271
+ dependencies: List["Task"] = []
272
+
177
273
  additional_info: Optional[Dict[str, Any]] = None
178
274
 
275
+ image_list: Optional[List[Union[Image.Image, str]]] = None
276
+
277
+ image_detail: Literal["auto", "low", "high"] = "auto"
278
+
279
+ video_bytes: Optional[bytes] = None
280
+
281
+ video_detail: Literal["auto", "low", "high"] = "auto"
282
+
283
+ model_config = ConfigDict(arbitrary_types_allowed=True)
284
+
179
285
  def __repr__(self) -> str:
180
286
  r"""Return a string representation of the task."""
181
287
  content_preview = self.content
@@ -204,7 +310,9 @@ class Task(BaseModel):
204
310
 
205
311
  def reset(self):
206
312
  r"""Reset Task to initial state."""
207
- self.state = TaskState.OPEN
313
+ self.state = (
314
+ TaskState.FAILED
315
+ ) # TODO: Add logic for OPEN in workforce.py
208
316
  self.result = ""
209
317
 
210
318
  def update_result(self, result: str):
@@ -301,9 +409,9 @@ class Task(BaseModel):
301
409
  agent: "ChatAgent",
302
410
  prompt: Optional[str] = None,
303
411
  task_parser: Callable[[str, str], List["Task"]] = parse_response,
304
- ) -> List["Task"]:
305
- r"""Decompose a task to a list of sub-tasks. It can be used for data
306
- generation and planner of agent.
412
+ ) -> Union[List["Task"], Generator[List["Task"], None, None]]:
413
+ r"""Decompose a task to a list of sub-tasks. Automatically detects
414
+ streaming or non-streaming based on agent configuration.
307
415
 
308
416
  Args:
309
417
  agent (ChatAgent): An agent that used to decompose the task.
@@ -314,7 +422,10 @@ class Task(BaseModel):
314
422
  the default parse_response will be used.
315
423
 
316
424
  Returns:
317
- List[Task]: A list of tasks which are :obj:`Task` instances.
425
+ Union[List[Task], Generator[List[Task], None, None]]: If agent is
426
+ configured for streaming, returns a generator that yields lists
427
+ of new tasks as they are parsed. Otherwise returns a list of
428
+ all tasks.
318
429
  """
319
430
 
320
431
  role_name = agent.role_name
@@ -326,9 +437,106 @@ class Task(BaseModel):
326
437
  role_name=role_name, content=content
327
438
  )
328
439
  response = agent.step(msg)
440
+
441
+ # Auto-detect streaming based on response type
442
+ from camel.agents.chat_agent import StreamingChatAgentResponse
443
+
444
+ if isinstance(response, StreamingChatAgentResponse):
445
+ return self._decompose_streaming(response, task_parser)
446
+ else:
447
+ return self._decompose_non_streaming(response, task_parser)
448
+
449
+ def _decompose_streaming(
450
+ self,
451
+ response: "StreamingChatAgentResponse",
452
+ task_parser: Callable[[str, str], List["Task"]],
453
+ ) -> Generator[List["Task"], None, None]:
454
+ r"""Handle streaming response for task decomposition.
455
+
456
+ Args:
457
+ response: Streaming response from agent
458
+ task_parser: Function to parse tasks from response
459
+
460
+ Yields:
461
+ List[Task]: New tasks as they are parsed from streaming response
462
+ """
463
+ accumulated_content = ""
464
+ yielded_count = 0
465
+
466
+ # Process streaming response
467
+ for chunk in response:
468
+ accumulated_content = chunk.msg.content
469
+
470
+ # Try to parse partial tasks from accumulated content
471
+ try:
472
+ current_tasks = self._parse_partial_tasks(accumulated_content)
473
+
474
+ # Yield new tasks if we have more than previously yielded
475
+ if len(current_tasks) > yielded_count:
476
+ new_tasks = current_tasks[yielded_count:]
477
+ for task in new_tasks:
478
+ task.additional_info = self.additional_info
479
+ task.parent = self
480
+ yield new_tasks
481
+ yielded_count = len(current_tasks)
482
+
483
+ except Exception:
484
+ # If parsing fails, continue accumulating
485
+ continue
486
+
487
+ # Final complete parsing
488
+ final_tasks = task_parser(accumulated_content, self.id)
489
+ for task in final_tasks:
490
+ task.additional_info = self.additional_info
491
+ task.parent = self
492
+ self.subtasks = final_tasks
493
+
494
+ def _decompose_non_streaming(
495
+ self, response, task_parser: Callable[[str, str], List["Task"]]
496
+ ) -> List["Task"]:
497
+ r"""Handle non-streaming response for task decomposition.
498
+
499
+ Args:
500
+ response: Regular response from agent
501
+ task_parser: Function to parse tasks from response
502
+
503
+ Returns:
504
+ List[Task]: All parsed tasks
505
+ """
329
506
  tasks = task_parser(response.msg.content, self.id)
330
507
  for task in tasks:
331
508
  task.additional_info = self.additional_info
509
+ task.parent = self
510
+ self.subtasks = tasks
511
+ return tasks
512
+
513
+ def _parse_partial_tasks(self, response: str) -> List["Task"]:
514
+ r"""Parse tasks from potentially incomplete response.
515
+
516
+ Args:
517
+ response: Partial response content
518
+
519
+ Returns:
520
+ List[Task]: Tasks parsed from complete <task></task> blocks
521
+ """
522
+ pattern = r"<task>(.*?)</task>"
523
+ tasks_content = re.findall(pattern, response, re.DOTALL)
524
+
525
+ tasks = []
526
+ task_id = self.id or "0"
527
+
528
+ for i, content in enumerate(tasks_content, 1):
529
+ stripped_content = content.strip()
530
+ if validate_task_content(stripped_content, f"{task_id}.{i}"):
531
+ tasks.append(
532
+ Task(content=stripped_content, id=f"{task_id}.{i}")
533
+ )
534
+ else:
535
+ logger.warning(
536
+ f"Skipping invalid subtask {task_id}.{i} "
537
+ f"during streaming decomposition: "
538
+ f"Content '{stripped_content}' failed validation"
539
+ )
332
540
  return tasks
333
541
 
334
542
  def compose(
@@ -357,6 +565,10 @@ class Task(BaseModel):
357
565
  role_name=role_name,
358
566
  content=self.content,
359
567
  additional_info=self.additional_info,
568
+ image_list=self.image_list,
569
+ image_detail=self.image_detail,
570
+ video_bytes=self.video_bytes,
571
+ video_detail=self.video_detail,
360
572
  other_results=sub_tasks_result,
361
573
  )
362
574
  msg = BaseMessage.make_user_message(
@@ -507,7 +719,12 @@ class TaskManager:
507
719
  role_name = agent.role_name
508
720
  content = template.format(role_name=role_name, content=task.content)
509
721
  msg = BaseMessage.make_user_message(
510
- role_name=role_name, content=content
722
+ role_name=role_name,
723
+ content=content,
724
+ image_list=task.image_list,
725
+ image_detail=task.image_detail,
726
+ video_bytes=task.video_bytes,
727
+ video_detail=task.video_detail,
511
728
  )
512
729
  response = agent.step(msg)
513
730
  if task_parser is None:
@@ -23,7 +23,7 @@ from .open_api_specs.security_config import openapi_security_config
23
23
  from .math_toolkit import MathToolkit
24
24
  from .search_toolkit import SearchToolkit
25
25
  from .weather_toolkit import WeatherToolkit
26
- from .dalle_toolkit import DalleToolkit
26
+ from .image_generation_toolkit import ImageGenToolkit, OpenAIImageToolkit
27
27
  from .ask_news_toolkit import AskNewsToolkit, AsyncAskNewsToolkit
28
28
  from .linkedin_toolkit import LinkedInToolkit
29
29
  from .reddit_toolkit import RedditToolkit
@@ -31,15 +31,18 @@ from .meshy_toolkit import MeshyToolkit
31
31
  from .openbb_toolkit import OpenBBToolkit
32
32
  from .bohrium_toolkit import BohriumToolkit
33
33
 
34
- from .base import BaseToolkit
34
+ from .base import BaseToolkit, RegisteredAgentToolkit
35
35
  from .google_maps_toolkit import GoogleMapsToolkit
36
36
  from .code_execution import CodeExecutionToolkit
37
37
  from .github_toolkit import GithubToolkit
38
38
  from .google_scholar_toolkit import GoogleScholarToolkit
39
39
  from .google_calendar_toolkit import GoogleCalendarToolkit
40
+ from .gmail_toolkit import GmailToolkit
40
41
  from .arxiv_toolkit import ArxivToolkit
41
42
  from .slack_toolkit import SlackToolkit
42
43
  from .whatsapp_toolkit import WhatsAppToolkit
44
+ from .wechat_official_toolkit import WeChatOfficialToolkit
45
+ from .dingtalk import DingtalkToolkit
43
46
  from .twitter_toolkit import TwitterToolkit
44
47
  from .open_api_toolkit import OpenAPIToolkit
45
48
  from .retrieval_toolkit import RetrievalToolkit
@@ -61,23 +64,37 @@ from .image_analysis_toolkit import ImageAnalysisToolkit
61
64
  from .mcp_toolkit import MCPToolkit
62
65
  from .browser_toolkit import BrowserToolkit
63
66
  from .async_browser_toolkit import AsyncBrowserToolkit
64
- from .file_write_toolkit import FileWriteToolkit
67
+ from .file_toolkit import FileToolkit, FileWriteToolkit
65
68
  from .pptx_toolkit import PPTXToolkit
66
69
  from .terminal_toolkit import TerminalToolkit
67
70
  from .pubmed_toolkit import PubMedToolkit
68
71
  from .data_commons_toolkit import DataCommonsToolkit
69
72
  from .thinking_toolkit import ThinkingToolkit
70
73
  from .pyautogui_toolkit import PyAutoGUIToolkit
71
- from .openai_agent_toolkit import OpenAIAgentToolkit
72
74
  from .searxng_toolkit import SearxNGToolkit
73
75
  from .jina_reranker_toolkit import JinaRerankerToolkit
74
76
  from .pulse_mcp_search_toolkit import PulseMCPSearchToolkit
75
77
  from .klavis_toolkit import KlavisToolkit
76
78
  from .aci_toolkit import ACIToolkit
79
+ from .origene_mcp_toolkit import OrigeneToolkit
77
80
  from .playwright_mcp_toolkit import PlaywrightMCPToolkit
81
+ from .resend_toolkit import ResendToolkit
78
82
  from .wolfram_alpha_toolkit import WolframAlphaToolkit
79
83
  from .task_planning_toolkit import TaskPlanningToolkit
80
-
84
+ from .hybrid_browser_toolkit import HybridBrowserToolkit
85
+ from .edgeone_pages_mcp_toolkit import EdgeOnePagesMCPToolkit
86
+ from .google_drive_mcp_toolkit import GoogleDriveMCPToolkit
87
+ from .craw4ai_toolkit import Crawl4AIToolkit
88
+ from .markitdown_toolkit import MarkItDownToolkit
89
+ from .note_taking_toolkit import NoteTakingToolkit
90
+ from .message_agent_toolkit import AgentCommunicationToolkit
91
+ from .web_deploy_toolkit import WebDeployToolkit
92
+ from .screenshot_toolkit import ScreenshotToolkit
93
+ from .message_integration import ToolkitMessageIntegration
94
+ from .context_summarizer_toolkit import ContextSummarizerToolkit
95
+ from .notion_mcp_toolkit import NotionMCPToolkit
96
+ from .vertex_ai_veo_toolkit import VertexAIVeoToolkit
97
+ from .minimax_mcp_toolkit import MinimaxMCPToolkit
81
98
 
82
99
  __all__ = [
83
100
  'BaseToolkit',
@@ -92,7 +109,9 @@ __all__ = [
92
109
  'SearchToolkit',
93
110
  'SlackToolkit',
94
111
  'WhatsAppToolkit',
95
- 'DalleToolkit',
112
+ 'WeChatOfficialToolkit',
113
+ 'DingtalkToolkit',
114
+ 'ImageGenToolkit',
96
115
  'TwitterToolkit',
97
116
  'WeatherToolkit',
98
117
  'RetrievalToolkit',
@@ -104,6 +123,7 @@ __all__ = [
104
123
  'AsyncAskNewsToolkit',
105
124
  'GoogleScholarToolkit',
106
125
  'GoogleCalendarToolkit',
126
+ 'GmailToolkit',
107
127
  'NotionToolkit',
108
128
  'ArxivToolkit',
109
129
  'HumanToolkit',
@@ -125,21 +145,39 @@ __all__ = [
125
145
  'ImageAnalysisToolkit',
126
146
  'BrowserToolkit',
127
147
  'AsyncBrowserToolkit',
128
- 'FileWriteToolkit',
148
+ 'FileToolkit',
149
+ 'FileWriteToolkit', # Deprecated, use FileToolkit instead
129
150
  'PPTXToolkit',
130
151
  'TerminalToolkit',
131
152
  'PubMedToolkit',
132
153
  'DataCommonsToolkit',
133
154
  'ThinkingToolkit',
134
155
  'PyAutoGUIToolkit',
135
- 'OpenAIAgentToolkit',
136
156
  'SearxNGToolkit',
137
157
  'JinaRerankerToolkit',
158
+ 'OrigeneToolkit',
138
159
  'PulseMCPSearchToolkit',
139
160
  'KlavisToolkit',
140
161
  'ACIToolkit',
141
162
  'PlaywrightMCPToolkit',
163
+ 'ResendToolkit',
142
164
  'WolframAlphaToolkit',
143
165
  'BohriumToolkit',
166
+ 'OpenAIImageToolkit', # Backward compatibility
144
167
  'TaskPlanningToolkit',
168
+ 'HybridBrowserToolkit',
169
+ 'EdgeOnePagesMCPToolkit',
170
+ 'GoogleDriveMCPToolkit',
171
+ 'Crawl4AIToolkit',
172
+ 'MarkItDownToolkit',
173
+ 'NoteTakingToolkit',
174
+ 'AgentCommunicationToolkit',
175
+ 'WebDeployToolkit',
176
+ 'ScreenshotToolkit',
177
+ 'RegisteredAgentToolkit',
178
+ 'ToolkitMessageIntegration',
179
+ 'ContextSummarizerToolkit',
180
+ 'NotionMCPToolkit',
181
+ 'VertexAIVeoToolkit',
182
+ 'MinimaxMCPToolkit',
145
183
  ]
@@ -12,6 +12,7 @@
12
12
  # limitations under the License.
13
13
  # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
+ import asyncio
15
16
  import os
16
17
  from typing import TYPE_CHECKING, Dict, List, Optional, Union
17
18
 
@@ -48,14 +49,14 @@ class ACIToolkit(BaseToolkit):
48
49
 
49
50
  Args:
50
51
  api_key (Optional[str]): The API key for authentication.
51
- (default: :obj: `None`)
52
+ (default: :obj:`None`)
52
53
  base_url (Optional[str]): The base URL for the ACI API.
53
- (default: :obj: `None`)
54
+ (default: :obj:`None`)
54
55
  linked_account_owner_id (Optional[str]): ID of the owner of the
55
56
  linked account, e.g., "johndoe"
56
- (default: :obj: `None`)
57
+ (default: :obj:`None`)
57
58
  timeout (Optional[float]): Request timeout.
58
- (default: :obj: `None`)
59
+ (default: :obj:`None`)
59
60
  """
60
61
  from aci import ACI
61
62
 
@@ -80,20 +81,20 @@ class ACIToolkit(BaseToolkit):
80
81
  Args:
81
82
  intent (Optional[str]): Search results will be sorted by relevance
82
83
  to this intent.
83
- (default: :obj: `None`)
84
+ (default: :obj:`None`)
84
85
  allowed_app_only (bool): If true, only return apps that
85
86
  are allowed by the agent/accessor, identified by the api key.
86
- (default: :obj: `True`)
87
+ (default: :obj:`True`)
87
88
  include_functions (bool): If true, include functions
88
89
  (name and description) in the search results.
89
- (default: :obj: `False`)
90
+ (default: :obj:`False`)
90
91
  categories (Optional[List[str]]): List of categories to filter the
91
92
  search results. Defaults to an empty list.
92
- (default: :obj: `None`)
93
+ (default: :obj:`None`)
93
94
  limit (Optional[int]): Maximum number of results to return.
94
- (default: :obj: `10`)
95
+ (default: :obj:`10`)
95
96
  offset (Optional[int]): Offset for pagination.
96
- (default: :obj: `0`)
97
+ (default: :obj:`0`)
97
98
 
98
99
  Returns:
99
100
  Optional[List[AppBasic]]: List of matching apps if successful,
@@ -123,10 +124,10 @@ class ACIToolkit(BaseToolkit):
123
124
 
124
125
  Args:
125
126
  app_names (Optional[List[str]]): List of app names to filter the
126
- results. (default: :obj: `None`)
127
+ results. (default: :obj:`None`)
127
128
  limit (Optional[int]): Maximum number of results to return.
128
- (default: :obj: `10`)
129
- offset (Optional[int]): Offset for pagination. (default: :obj: `0`)
129
+ (default: :obj:`10`)
130
+ offset (Optional[int]): Offset for pagination. (default: :obj:`0`)
130
131
 
131
132
  Returns:
132
133
  Union[List[AppConfiguration], str]: List of configured apps if
@@ -356,15 +357,15 @@ class ACIToolkit(BaseToolkit):
356
357
 
357
358
  Args:
358
359
  app_names (Optional[List[str]]): List of app names to filter the
359
- search results. (default: :obj: `None`)
360
+ search results. (default: :obj:`None`)
360
361
  intent (Optional[str]): The search query/intent.
361
- (default: :obj: `None`)
362
+ (default: :obj:`None`)
362
363
  allowed_apps_only (bool): If true, only return
363
- functions from allowed apps. (default: :obj: `True`)
364
+ functions from allowed apps. (default: :obj:`True`)
364
365
  limit (Optional[int]): Maximum number of results to return.
365
- (default: :obj: `10`)
366
+ (default: :obj:`10`)
366
367
  offset (Optional[int]): Offset for pagination.
367
- (default: :obj: `0`)
368
+ (default: :obj:`0`)
368
369
 
369
370
  Returns:
370
371
  List[Dict]: List of matching functions
@@ -395,7 +396,7 @@ class ACIToolkit(BaseToolkit):
395
396
  owner id in the ACI dashboard (https://platform.aci.dev).
396
397
  allowed_apps_only (bool): If true, only returns functions/apps
397
398
  that are allowed to be used by the agent/accessor, identified
398
- by the api key. (default: :obj: `False`)
399
+ by the api key. (default: :obj:`False`)
399
400
 
400
401
  Returns:
401
402
  Dict: Result of the function execution
@@ -408,6 +409,38 @@ class ACIToolkit(BaseToolkit):
408
409
  )
409
410
  return result
410
411
 
412
+ async def aexecute_function(
413
+ self,
414
+ function_name: str,
415
+ function_arguments: Dict,
416
+ linked_account_owner_id: str,
417
+ allowed_apps_only: bool = False,
418
+ ) -> Dict:
419
+ r"""Execute a function call asynchronously.
420
+
421
+ Args:
422
+ function_name (str): Name of the function to execute.
423
+ function_arguments (Dict): Arguments to pass to the function.
424
+ linked_account_owner_id (str): To specify the end-user (account
425
+ owner) on behalf of whom you want to execute functions
426
+ You need to first link corresponding account with the same
427
+ owner id in the ACI dashboard (https://platform.aci.dev).
428
+ allowed_apps_only (bool): If true, only returns functions/apps
429
+ that are allowed to be used by the agent/accessor, identified
430
+ by the api key. (default: :obj:`False`)
431
+
432
+ Returns:
433
+ Dict: Result of the function execution
434
+ """
435
+ result = await asyncio.to_thread(
436
+ self.client.handle_function_call,
437
+ function_name,
438
+ function_arguments,
439
+ linked_account_owner_id,
440
+ allowed_apps_only,
441
+ )
442
+ return result
443
+
411
444
  def get_tools(self) -> List[FunctionTool]:
412
445
  r"""Get a list of tools (functions) available in the configured apps.
413
446
 
@@ -434,6 +467,8 @@ class ACIToolkit(BaseToolkit):
434
467
  FunctionTool(self.delete_linked_account),
435
468
  FunctionTool(self.function_definition),
436
469
  FunctionTool(self.search_function),
470
+ FunctionTool(self.execute_function),
471
+ FunctionTool(self.aexecute_function),
437
472
  ]
438
473
 
439
474
  for function in _all_function:
@@ -448,6 +483,16 @@ class ACIToolkit(BaseToolkit):
448
483
  linked_account_owner_id=self.linked_account_owner_id,
449
484
  )
450
485
 
486
+ async def async_dummy_func(*, schema=schema, **kwargs):
487
+ return await self.aexecute_function(
488
+ function_name=schema['function']['name'],
489
+ function_arguments=kwargs,
490
+ linked_account_owner_id=self.linked_account_owner_id,
491
+ )
492
+
493
+ # Add async_call method to the sync function for compatibility
494
+ dummy_func.async_call = async_dummy_func # type: ignore[attr-defined]
495
+
451
496
  tool = FunctionTool(
452
497
  func=dummy_func,
453
498
  openai_tool_schema=schema,