camel-ai 0.2.65__py3-none-any.whl → 0.2.83a6__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 (509) hide show
  1. camel/__init__.py +3 -3
  2. camel/agents/__init__.py +2 -2
  3. camel/agents/_types.py +9 -4
  4. camel/agents/_utils.py +40 -2
  5. camel/agents/base.py +2 -2
  6. camel/agents/chat_agent.py +5107 -995
  7. camel/agents/critic_agent.py +2 -2
  8. camel/agents/deductive_reasoner_agent.py +56 -56
  9. camel/agents/embodied_agent.py +2 -2
  10. camel/agents/knowledge_graph_agent.py +20 -20
  11. camel/agents/mcp_agent.py +35 -36
  12. camel/agents/multi_hop_generator_agent.py +3 -3
  13. camel/agents/programmed_agent_instruction.py +2 -2
  14. camel/agents/repo_agent.py +4 -3
  15. camel/agents/role_assignment_agent.py +2 -2
  16. camel/agents/search_agent.py +2 -2
  17. camel/agents/task_agent.py +2 -2
  18. camel/agents/tool_agents/__init__.py +2 -2
  19. camel/agents/tool_agents/base.py +2 -2
  20. camel/agents/tool_agents/hugging_face_tool_agent.py +3 -3
  21. camel/benchmarks/__init__.py +2 -2
  22. camel/benchmarks/apibank.py +5 -5
  23. camel/benchmarks/apibench.py +2 -2
  24. camel/benchmarks/base.py +2 -2
  25. camel/benchmarks/browsecomp.py +44 -33
  26. camel/benchmarks/gaia.py +17 -13
  27. camel/benchmarks/mock_website/README.md +1 -3
  28. camel/benchmarks/mock_website/mock_web.py +2 -2
  29. camel/benchmarks/mock_website/requirements.txt +1 -1
  30. camel/benchmarks/mock_website/shopping_mall/app.py +2 -2
  31. camel/benchmarks/mock_website/task.json +1 -1
  32. camel/benchmarks/nexus.py +3 -3
  33. camel/benchmarks/ragbench.py +2 -2
  34. camel/bots/__init__.py +2 -2
  35. camel/bots/discord/__init__.py +2 -2
  36. camel/bots/discord/discord_app.py +2 -2
  37. camel/bots/discord/discord_installation.py +2 -2
  38. camel/bots/discord/discord_store.py +3 -3
  39. camel/bots/slack/__init__.py +2 -2
  40. camel/bots/slack/models.py +4 -4
  41. camel/bots/slack/slack_app.py +2 -2
  42. camel/bots/telegram_bot.py +2 -2
  43. camel/configs/__init__.py +29 -2
  44. camel/configs/aihubmix_config.py +90 -0
  45. camel/configs/aiml_config.py +2 -2
  46. camel/configs/amd_config.py +70 -0
  47. camel/configs/anthropic_config.py +2 -2
  48. camel/configs/base_config.py +2 -2
  49. camel/configs/bedrock_config.py +5 -3
  50. camel/configs/cerebras_config.py +98 -0
  51. camel/configs/cohere_config.py +2 -2
  52. camel/configs/cometapi_config.py +106 -0
  53. camel/configs/crynux_config.py +2 -2
  54. camel/configs/deepseek_config.py +9 -8
  55. camel/configs/function_gemma_config.py +59 -0
  56. camel/configs/gemini_config.py +6 -4
  57. camel/configs/groq_config.py +6 -4
  58. camel/configs/internlm_config.py +6 -4
  59. camel/configs/litellm_config.py +2 -2
  60. camel/configs/lmstudio_config.py +6 -4
  61. camel/configs/minimax_config.py +95 -0
  62. camel/configs/mistral_config.py +2 -2
  63. camel/configs/modelscope_config.py +5 -3
  64. camel/configs/moonshot_config.py +2 -2
  65. camel/configs/nebius_config.py +105 -0
  66. camel/configs/netmind_config.py +2 -2
  67. camel/configs/novita_config.py +2 -2
  68. camel/configs/nvidia_config.py +2 -2
  69. camel/configs/ollama_config.py +2 -2
  70. camel/configs/openai_config.py +5 -3
  71. camel/configs/openrouter_config.py +6 -4
  72. camel/configs/ppio_config.py +2 -2
  73. camel/configs/qianfan_config.py +85 -0
  74. camel/configs/qwen_config.py +2 -2
  75. camel/configs/reka_config.py +2 -2
  76. camel/configs/samba_config.py +6 -4
  77. camel/configs/sglang_config.py +2 -2
  78. camel/configs/siliconflow_config.py +2 -2
  79. camel/configs/togetherai_config.py +2 -2
  80. camel/configs/vllm_config.py +4 -2
  81. camel/configs/watsonx_config.py +2 -2
  82. camel/configs/yi_config.py +6 -4
  83. camel/configs/zhipuai_config.py +6 -4
  84. camel/data_collectors/__init__.py +2 -2
  85. camel/data_collectors/alpaca_collector.py +18 -9
  86. camel/data_collectors/base.py +2 -2
  87. camel/data_collectors/sharegpt_collector.py +2 -2
  88. camel/datagen/__init__.py +2 -2
  89. camel/datagen/cot_datagen.py +3 -3
  90. camel/datagen/evol_instruct/__init__.py +2 -2
  91. camel/datagen/evol_instruct/evol_instruct.py +2 -2
  92. camel/datagen/evol_instruct/scorer.py +12 -12
  93. camel/datagen/evol_instruct/templates.py +16 -16
  94. camel/datagen/self_improving_cot.py +5 -5
  95. camel/datagen/self_instruct/__init__.py +2 -2
  96. camel/datagen/self_instruct/filter/__init__.py +2 -2
  97. camel/datagen/self_instruct/filter/filter_function.py +2 -2
  98. camel/datagen/self_instruct/filter/filter_registry.py +2 -2
  99. camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
  100. camel/datagen/self_instruct/self_instruct.py +2 -2
  101. camel/datagen/self_instruct/templates.py +47 -47
  102. camel/datagen/source2synth/__init__.py +2 -2
  103. camel/datagen/source2synth/data_processor.py +2 -2
  104. camel/datagen/source2synth/models.py +2 -2
  105. camel/datagen/source2synth/user_data_processor_config.py +2 -2
  106. camel/datahubs/__init__.py +2 -2
  107. camel/datahubs/base.py +2 -2
  108. camel/datahubs/huggingface.py +2 -2
  109. camel/datahubs/models.py +2 -2
  110. camel/datasets/__init__.py +2 -2
  111. camel/datasets/base_generator.py +41 -12
  112. camel/datasets/few_shot_generator.py +18 -18
  113. camel/datasets/models.py +2 -2
  114. camel/datasets/self_instruct_generator.py +2 -2
  115. camel/datasets/static_dataset.py +2 -2
  116. camel/embeddings/__init__.py +2 -2
  117. camel/embeddings/azure_embedding.py +2 -2
  118. camel/embeddings/base.py +2 -2
  119. camel/embeddings/gemini_embedding.py +2 -2
  120. camel/embeddings/jina_embedding.py +2 -2
  121. camel/embeddings/mistral_embedding.py +2 -2
  122. camel/embeddings/openai_compatible_embedding.py +2 -2
  123. camel/embeddings/openai_embedding.py +2 -2
  124. camel/embeddings/sentence_transformers_embeddings.py +2 -2
  125. camel/embeddings/together_embedding.py +2 -2
  126. camel/embeddings/vlm_embedding.py +2 -2
  127. camel/environments/__init__.py +14 -2
  128. camel/environments/models.py +2 -2
  129. camel/environments/multi_step.py +2 -2
  130. camel/environments/rlcards_env.py +860 -0
  131. camel/environments/single_step.py +30 -5
  132. camel/environments/tic_tac_toe.py +3 -3
  133. camel/extractors/__init__.py +2 -2
  134. camel/extractors/base.py +2 -2
  135. camel/extractors/python_strategies.py +2 -2
  136. camel/generators.py +2 -2
  137. camel/human.py +2 -2
  138. camel/interpreters/__init__.py +4 -2
  139. camel/interpreters/base.py +2 -2
  140. camel/interpreters/docker/Dockerfile +14 -24
  141. camel/interpreters/docker_interpreter.py +5 -4
  142. camel/interpreters/e2b_interpreter.py +36 -3
  143. camel/interpreters/internal_python_interpreter.py +53 -4
  144. camel/interpreters/interpreter_error.py +2 -2
  145. camel/interpreters/ipython_interpreter.py +2 -2
  146. camel/interpreters/microsandbox_interpreter.py +395 -0
  147. camel/interpreters/subprocess_interpreter.py +2 -2
  148. camel/loaders/__init__.py +13 -4
  149. camel/loaders/apify_reader.py +2 -2
  150. camel/loaders/base_io.py +2 -2
  151. camel/loaders/base_loader.py +85 -0
  152. camel/loaders/chunkr_reader.py +11 -2
  153. camel/loaders/crawl4ai_reader.py +2 -2
  154. camel/loaders/firecrawl_reader.py +6 -6
  155. camel/loaders/jina_url_reader.py +2 -2
  156. camel/loaders/markitdown.py +2 -2
  157. camel/loaders/mineru_extractor.py +2 -2
  158. camel/loaders/mistral_reader.py +2 -2
  159. camel/loaders/scrapegraph_reader.py +2 -2
  160. camel/loaders/unstructured_io.py +2 -2
  161. camel/logger.py +5 -5
  162. camel/memories/__init__.py +2 -2
  163. camel/memories/agent_memories.py +86 -3
  164. camel/memories/base.py +36 -2
  165. camel/memories/blocks/__init__.py +2 -2
  166. camel/memories/blocks/chat_history_block.py +125 -7
  167. camel/memories/blocks/vectordb_block.py +10 -3
  168. camel/memories/context_creators/__init__.py +2 -2
  169. camel/memories/context_creators/score_based.py +109 -230
  170. camel/memories/records.py +90 -10
  171. camel/messages/__init__.py +2 -2
  172. camel/messages/base.py +178 -43
  173. camel/messages/conversion/__init__.py +2 -2
  174. camel/messages/conversion/alpaca.py +2 -2
  175. camel/messages/conversion/conversation_models.py +2 -2
  176. camel/messages/conversion/sharegpt/__init__.py +2 -2
  177. camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
  178. camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
  179. camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
  180. camel/messages/func_message.py +54 -17
  181. camel/models/__init__.py +18 -2
  182. camel/models/_utils.py +3 -3
  183. camel/models/aihubmix_model.py +83 -0
  184. camel/models/aiml_model.py +11 -18
  185. camel/models/amd_model.py +101 -0
  186. camel/models/anthropic_model.py +127 -20
  187. camel/models/aws_bedrock_model.py +12 -35
  188. camel/models/azure_openai_model.py +214 -115
  189. camel/models/base_audio_model.py +5 -3
  190. camel/models/base_model.py +378 -31
  191. camel/models/cerebras_model.py +83 -0
  192. camel/models/cohere_model.py +18 -49
  193. camel/models/cometapi_model.py +83 -0
  194. camel/models/crynux_model.py +11 -18
  195. camel/models/deepseek_model.py +20 -84
  196. camel/models/fish_audio_model.py +8 -2
  197. camel/models/function_gemma_model.py +889 -0
  198. camel/models/gemini_model.py +391 -52
  199. camel/models/groq_model.py +11 -19
  200. camel/models/internlm_model.py +11 -18
  201. camel/models/litellm_model.py +57 -49
  202. camel/models/lmstudio_model.py +17 -20
  203. camel/models/minimax_model.py +83 -0
  204. camel/models/mistral_model.py +20 -47
  205. camel/models/model_factory.py +39 -3
  206. camel/models/model_manager.py +26 -8
  207. camel/models/modelscope_model.py +13 -193
  208. camel/models/moonshot_model.py +183 -21
  209. camel/models/nebius_model.py +83 -0
  210. camel/models/nemotron_model.py +19 -9
  211. camel/models/netmind_model.py +11 -18
  212. camel/models/novita_model.py +11 -18
  213. camel/models/nvidia_model.py +11 -18
  214. camel/models/ollama_model.py +14 -21
  215. camel/models/openai_audio_models.py +2 -2
  216. camel/models/openai_compatible_model.py +190 -71
  217. camel/models/openai_model.py +192 -86
  218. camel/models/openrouter_model.py +11 -19
  219. camel/models/ppio_model.py +11 -18
  220. camel/models/qianfan_model.py +89 -0
  221. camel/models/qwen_model.py +13 -193
  222. camel/models/reka_model.py +23 -49
  223. camel/models/reward/__init__.py +2 -2
  224. camel/models/reward/base_reward_model.py +2 -2
  225. camel/models/reward/evaluator.py +2 -2
  226. camel/models/reward/nemotron_model.py +2 -2
  227. camel/models/reward/skywork_model.py +2 -2
  228. camel/models/samba_model.py +50 -75
  229. camel/models/sglang_model.py +90 -68
  230. camel/models/siliconflow_model.py +12 -35
  231. camel/models/stub_model.py +10 -7
  232. camel/models/togetherai_model.py +11 -18
  233. camel/models/vllm_model.py +10 -18
  234. camel/models/volcano_model.py +158 -19
  235. camel/models/watsonx_model.py +9 -47
  236. camel/models/yi_model.py +11 -18
  237. camel/models/zhipuai_model.py +70 -18
  238. camel/parsers/__init__.py +18 -0
  239. camel/parsers/mcp_tool_call_parser.py +176 -0
  240. camel/personas/__init__.py +2 -2
  241. camel/personas/persona.py +2 -2
  242. camel/personas/persona_hub.py +2 -2
  243. camel/prompts/__init__.py +2 -2
  244. camel/prompts/ai_society.py +2 -2
  245. camel/prompts/base.py +2 -2
  246. camel/prompts/code.py +2 -2
  247. camel/prompts/evaluation.py +2 -2
  248. camel/prompts/generate_text_embedding_data.py +2 -2
  249. camel/prompts/image_craft.py +2 -2
  250. camel/prompts/misalignment.py +2 -2
  251. camel/prompts/multi_condition_image_craft.py +2 -2
  252. camel/prompts/object_recognition.py +2 -2
  253. camel/prompts/persona_hub.py +3 -3
  254. camel/prompts/prompt_templates.py +2 -2
  255. camel/prompts/role_description_prompt_template.py +2 -2
  256. camel/prompts/solution_extraction.py +8 -8
  257. camel/prompts/task_prompt_template.py +2 -2
  258. camel/prompts/translation.py +2 -2
  259. camel/prompts/video_description_prompt.py +3 -3
  260. camel/responses/__init__.py +2 -2
  261. camel/responses/agent_responses.py +2 -2
  262. camel/retrievers/__init__.py +2 -2
  263. camel/retrievers/auto_retriever.py +3 -2
  264. camel/retrievers/base.py +2 -2
  265. camel/retrievers/bm25_retriever.py +2 -2
  266. camel/retrievers/cohere_rerank_retriever.py +2 -2
  267. camel/retrievers/hybrid_retrival.py +2 -2
  268. camel/retrievers/vector_retriever.py +2 -2
  269. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  270. camel/runtimes/__init__.py +2 -2
  271. camel/runtimes/api.py +79 -23
  272. camel/runtimes/base.py +2 -2
  273. camel/runtimes/configs.py +13 -13
  274. camel/runtimes/daytona_runtime.py +17 -18
  275. camel/runtimes/docker_runtime.py +12 -12
  276. camel/runtimes/llm_guard_runtime.py +26 -26
  277. camel/runtimes/remote_http_runtime.py +11 -11
  278. camel/runtimes/ubuntu_docker_runtime.py +2 -2
  279. camel/runtimes/utils/__init__.py +2 -2
  280. camel/runtimes/utils/function_risk_toolkit.py +2 -2
  281. camel/runtimes/utils/ignore_risk_toolkit.py +2 -2
  282. camel/schemas/__init__.py +2 -2
  283. camel/schemas/base.py +2 -2
  284. camel/schemas/openai_converter.py +3 -3
  285. camel/schemas/outlines_converter.py +2 -2
  286. camel/services/agent_openapi_server.py +380 -0
  287. camel/societies/__init__.py +4 -2
  288. camel/societies/babyagi_playing.py +2 -2
  289. camel/societies/role_playing.py +201 -80
  290. camel/societies/workforce/__init__.py +10 -3
  291. camel/societies/workforce/base.py +2 -2
  292. camel/societies/workforce/events.py +145 -0
  293. camel/societies/workforce/prompts.py +259 -33
  294. camel/societies/workforce/role_playing_worker.py +88 -31
  295. camel/societies/workforce/single_agent_worker.py +638 -40
  296. camel/societies/workforce/structured_output_handler.py +512 -0
  297. camel/societies/workforce/task_channel.py +182 -38
  298. camel/societies/workforce/utils.py +780 -65
  299. camel/societies/workforce/worker.py +92 -26
  300. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  301. camel/societies/workforce/workforce.py +5354 -372
  302. camel/societies/workforce/workforce_callback.py +103 -0
  303. camel/societies/workforce/workforce_logger.py +647 -0
  304. camel/societies/workforce/workforce_metrics.py +33 -0
  305. camel/storages/__init__.py +6 -2
  306. camel/storages/graph_storages/__init__.py +2 -2
  307. camel/storages/graph_storages/base.py +2 -2
  308. camel/storages/graph_storages/graph_element.py +2 -2
  309. camel/storages/graph_storages/nebula_graph.py +4 -4
  310. camel/storages/graph_storages/neo4j_graph.py +7 -7
  311. camel/storages/key_value_storages/__init__.py +2 -2
  312. camel/storages/key_value_storages/base.py +2 -2
  313. camel/storages/key_value_storages/in_memory.py +2 -2
  314. camel/storages/key_value_storages/json.py +17 -4
  315. camel/storages/key_value_storages/mem0_cloud.py +50 -49
  316. camel/storages/key_value_storages/redis.py +2 -2
  317. camel/storages/object_storages/__init__.py +2 -2
  318. camel/storages/object_storages/amazon_s3.py +2 -2
  319. camel/storages/object_storages/azure_blob.py +2 -2
  320. camel/storages/object_storages/base.py +2 -2
  321. camel/storages/object_storages/google_cloud.py +3 -3
  322. camel/storages/vectordb_storages/__init__.py +8 -2
  323. camel/storages/vectordb_storages/base.py +2 -2
  324. camel/storages/vectordb_storages/chroma.py +731 -0
  325. camel/storages/vectordb_storages/faiss.py +2 -2
  326. camel/storages/vectordb_storages/milvus.py +2 -2
  327. camel/storages/vectordb_storages/oceanbase.py +15 -15
  328. camel/storages/vectordb_storages/pgvector.py +349 -0
  329. camel/storages/vectordb_storages/qdrant.py +6 -6
  330. camel/storages/vectordb_storages/surreal.py +372 -0
  331. camel/storages/vectordb_storages/tidb.py +11 -8
  332. camel/storages/vectordb_storages/weaviate.py +2 -2
  333. camel/tasks/__init__.py +2 -2
  334. camel/tasks/task.py +348 -26
  335. camel/tasks/task_prompt.py +3 -3
  336. camel/terminators/__init__.py +2 -2
  337. camel/terminators/base.py +2 -2
  338. camel/terminators/response_terminator.py +2 -2
  339. camel/terminators/token_limit_terminator.py +2 -2
  340. camel/toolkits/__init__.py +57 -10
  341. camel/toolkits/aci_toolkit.py +66 -21
  342. camel/toolkits/arxiv_toolkit.py +8 -8
  343. camel/toolkits/ask_news_toolkit.py +2 -2
  344. camel/toolkits/async_browser_toolkit.py +4 -4
  345. camel/toolkits/audio_analysis_toolkit.py +3 -3
  346. camel/toolkits/base.py +106 -6
  347. camel/toolkits/bohrium_toolkit.py +2 -2
  348. camel/toolkits/browser_toolkit.py +34 -21
  349. camel/toolkits/browser_toolkit_commons.py +4 -4
  350. camel/toolkits/code_execution.py +31 -4
  351. camel/toolkits/context_summarizer_toolkit.py +684 -0
  352. camel/toolkits/craw4ai_toolkit.py +93 -0
  353. camel/toolkits/dappier_toolkit.py +12 -8
  354. camel/toolkits/data_commons_toolkit.py +2 -2
  355. camel/toolkits/dingtalk.py +1135 -0
  356. camel/toolkits/earth_science_toolkit.py +5367 -0
  357. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  358. camel/toolkits/excel_toolkit.py +905 -71
  359. camel/toolkits/file_toolkit.py +1402 -0
  360. camel/toolkits/function_tool.py +205 -27
  361. camel/toolkits/github_toolkit.py +109 -22
  362. camel/toolkits/gmail_toolkit.py +1839 -0
  363. camel/toolkits/google_calendar_toolkit.py +40 -6
  364. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  365. camel/toolkits/google_maps_toolkit.py +2 -2
  366. camel/toolkits/google_scholar_toolkit.py +2 -2
  367. camel/toolkits/human_toolkit.py +36 -12
  368. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  369. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  370. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  371. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1958 -0
  372. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  373. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
  374. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  375. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  376. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1940 -0
  377. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  378. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
  379. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  380. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  381. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  382. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  383. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
  384. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
  385. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +325 -0
  386. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
  387. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  388. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  389. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  390. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  391. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  392. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  393. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  394. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  395. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  396. camel/toolkits/image_analysis_toolkit.py +3 -6
  397. camel/toolkits/image_generation_toolkit.py +390 -0
  398. camel/toolkits/jina_reranker_toolkit.py +5 -6
  399. camel/toolkits/klavis_toolkit.py +7 -3
  400. camel/toolkits/linkedin_toolkit.py +2 -2
  401. camel/toolkits/markitdown_toolkit.py +104 -0
  402. camel/toolkits/math_toolkit.py +66 -12
  403. camel/toolkits/mcp_toolkit.py +412 -36
  404. camel/toolkits/memory_toolkit.py +7 -3
  405. camel/toolkits/meshy_toolkit.py +2 -2
  406. camel/toolkits/message_agent_toolkit.py +608 -0
  407. camel/toolkits/message_integration.py +728 -0
  408. camel/toolkits/microsoft_outlook_mail_toolkit.py +1885 -0
  409. camel/toolkits/mineru_toolkit.py +2 -2
  410. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  411. camel/toolkits/networkx_toolkit.py +2 -2
  412. camel/toolkits/note_taking_toolkit.py +277 -0
  413. camel/toolkits/notion_mcp_toolkit.py +224 -0
  414. camel/toolkits/notion_toolkit.py +2 -2
  415. camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
  416. camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
  417. camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
  418. camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
  419. camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
  420. camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
  421. camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
  422. camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
  423. camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
  424. camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
  425. camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
  426. camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
  427. camel/toolkits/open_api_specs/security_config.py +2 -2
  428. camel/toolkits/open_api_specs/speak/__init__.py +2 -2
  429. camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
  430. camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
  431. camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
  432. camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
  433. camel/toolkits/open_api_toolkit.py +2 -2
  434. camel/toolkits/openbb_toolkit.py +7 -3
  435. camel/toolkits/origene_mcp_toolkit.py +56 -0
  436. camel/toolkits/page_script.js +53 -53
  437. camel/toolkits/playwright_mcp_toolkit.py +13 -31
  438. camel/toolkits/pptx_toolkit.py +36 -23
  439. camel/toolkits/pubmed_toolkit.py +2 -2
  440. camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
  441. camel/toolkits/pyautogui_toolkit.py +2 -2
  442. camel/toolkits/reddit_toolkit.py +2 -2
  443. camel/toolkits/resend_toolkit.py +168 -0
  444. camel/toolkits/retrieval_toolkit.py +2 -2
  445. camel/toolkits/screenshot_toolkit.py +213 -0
  446. camel/toolkits/search_toolkit.py +606 -156
  447. camel/toolkits/searxng_toolkit.py +2 -2
  448. camel/toolkits/semantic_scholar_toolkit.py +2 -2
  449. camel/toolkits/slack_toolkit.py +108 -58
  450. camel/toolkits/sql_toolkit.py +712 -0
  451. camel/toolkits/stripe_toolkit.py +2 -2
  452. camel/toolkits/sympy_toolkit.py +3 -3
  453. camel/toolkits/task_planning_toolkit.py +5 -5
  454. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  455. camel/toolkits/terminal_toolkit/terminal_toolkit.py +1281 -0
  456. camel/toolkits/terminal_toolkit/utils.py +659 -0
  457. camel/toolkits/thinking_toolkit.py +3 -3
  458. camel/toolkits/twitter_toolkit.py +2 -2
  459. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  460. camel/toolkits/video_analysis_toolkit.py +109 -29
  461. camel/toolkits/video_download_toolkit.py +19 -16
  462. camel/toolkits/weather_toolkit.py +2 -2
  463. camel/toolkits/web_deploy_toolkit.py +1219 -0
  464. camel/toolkits/wechat_official_toolkit.py +483 -0
  465. camel/toolkits/whatsapp_toolkit.py +2 -2
  466. camel/toolkits/wolfram_alpha_toolkit.py +2 -2
  467. camel/toolkits/zapier_toolkit.py +7 -3
  468. camel/types/__init__.py +4 -4
  469. camel/types/agents/__init__.py +2 -2
  470. camel/types/agents/tool_calling_record.py +6 -3
  471. camel/types/enums.py +381 -41
  472. camel/types/mcp_registries.py +2 -2
  473. camel/types/openai_types.py +4 -4
  474. camel/types/unified_model_type.py +46 -10
  475. camel/utils/__init__.py +5 -2
  476. camel/utils/agent_context.py +41 -0
  477. camel/utils/async_func.py +2 -2
  478. camel/utils/chunker/__init__.py +2 -2
  479. camel/utils/chunker/base.py +2 -2
  480. camel/utils/chunker/code_chunker.py +2 -2
  481. camel/utils/chunker/uio_chunker.py +2 -2
  482. camel/utils/commons.py +38 -7
  483. camel/utils/constants.py +5 -2
  484. camel/utils/context_utils.py +1134 -0
  485. camel/utils/deduplication.py +2 -2
  486. camel/utils/filename.py +2 -2
  487. camel/utils/langfuse.py +18 -10
  488. camel/utils/mcp.py +140 -6
  489. camel/utils/mcp_client.py +48 -38
  490. camel/utils/message_summarizer.py +148 -0
  491. camel/utils/response_format.py +2 -2
  492. camel/utils/token_counting.py +45 -22
  493. camel/utils/tool_result.py +44 -0
  494. camel/verifiers/__init__.py +2 -2
  495. camel/verifiers/base.py +2 -2
  496. camel/verifiers/math_verifier.py +2 -2
  497. camel/verifiers/models.py +2 -2
  498. camel/verifiers/physics_verifier.py +2 -2
  499. camel/verifiers/python_verifier.py +2 -2
  500. {camel_ai-0.2.65.dist-info → camel_ai-0.2.83a6.dist-info}/METADATA +355 -117
  501. camel_ai-0.2.83a6.dist-info/RECORD +511 -0
  502. {camel_ai-0.2.65.dist-info → camel_ai-0.2.83a6.dist-info}/WHEEL +1 -1
  503. {camel_ai-0.2.65.dist-info → camel_ai-0.2.83a6.dist-info}/licenses/LICENSE +1 -1
  504. camel/loaders/pandas_reader.py +0 -368
  505. camel/toolkits/dalle_toolkit.py +0 -175
  506. camel/toolkits/file_write_toolkit.py +0 -444
  507. camel/toolkits/openai_agent_toolkit.py +0 -135
  508. camel/toolkits/terminal_toolkit.py +0 -1037
  509. camel_ai-0.2.65.dist-info/RECORD +0 -426
@@ -1,4 +1,4 @@
1
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
1
+ # ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========
2
2
  # Licensed under the Apache License, Version 2.0 (the "License");
3
3
  # you may not use this file except in compliance with the License.
4
4
  # You may obtain a copy of the License at
@@ -10,15 +10,105 @@
10
10
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
13
+ # ========= Copyright 2023-2026 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ from enum import Enum
14
15
  from functools import wraps
15
- from typing import Callable
16
+ from typing import Callable, List, Optional
16
17
 
17
- from pydantic import BaseModel, Field
18
+ from pydantic import BaseModel, Field, field_validator
18
19
 
19
- from camel.logger import get_logger
20
+ # generic role names that should trigger fallback in role identification
21
+ # used for workflow organization to avoid using generic names as folder names
22
+ GENERIC_ROLE_NAMES = frozenset(
23
+ {'assistant', 'agent', 'user', 'system', 'worker', 'helper'}
24
+ )
20
25
 
21
- logger = get_logger(__name__)
26
+
27
+ def is_generic_role_name(role_name: str) -> bool:
28
+ r"""Check if a role name is generic and should trigger fallback logic.
29
+
30
+ Generic role names are common, non-specific identifiers that don't
31
+ provide meaningful information about an agent's actual purpose.
32
+ When a role name is generic, fallback logic should be used to find
33
+ a more specific identifier (e.g., from LLM-generated agent_title
34
+ or description).
35
+
36
+ Args:
37
+ role_name (str): The role name to check (will be converted to
38
+ lowercase for case-insensitive comparison).
39
+
40
+ Returns:
41
+ bool: True if the role name is generic, False otherwise.
42
+
43
+ Example:
44
+ >>> is_generic_role_name("assistant")
45
+ True
46
+ >>> is_generic_role_name("data_analyst")
47
+ False
48
+ >>> is_generic_role_name("AGENT")
49
+ True
50
+ """
51
+ return role_name.lower() in GENERIC_ROLE_NAMES
52
+
53
+
54
+ class WorkflowMetadata(BaseModel):
55
+ r"""Pydantic model for workflow metadata tracking.
56
+
57
+ This model defines the formal schema for workflow metadata that tracks
58
+ versioning, timestamps, and contextual information about saved workflows.
59
+ Used to maintain workflow history and enable proper version management.
60
+ """
61
+
62
+ session_id: str = Field(
63
+ description="Session identifier for the workflow execution"
64
+ )
65
+ working_directory: str = Field(
66
+ description="Directory path where the workflow is stored"
67
+ )
68
+ created_at: str = Field(
69
+ description="ISO timestamp when workflow was first created"
70
+ )
71
+ updated_at: str = Field(
72
+ description="ISO timestamp of last modification to the workflow"
73
+ )
74
+ workflow_version: int = Field(
75
+ default=1, description="Version number, increments on updates"
76
+ )
77
+ agent_id: str = Field(
78
+ description="UUID of the agent that created/updated the workflow"
79
+ )
80
+ message_count: int = Field(
81
+ description="Number of messages in the workflow conversation"
82
+ )
83
+
84
+
85
+ class WorkflowConfig(BaseModel):
86
+ r"""Configuration for workflow memory management.
87
+
88
+ Centralizes all workflow-related configuration options to avoid scattered
89
+ settings across multiple files and methods.
90
+ """
91
+
92
+ max_workflows_per_role: int = Field(
93
+ default=100,
94
+ description="Maximum number of workflows to keep per role folder",
95
+ )
96
+ workflow_filename_suffix: str = Field(
97
+ default="_workflow",
98
+ description="Suffix appended to workflow filenames",
99
+ )
100
+ workflow_folder_name: str = Field(
101
+ default="workforce_workflows",
102
+ description="Base folder name for storing workflows",
103
+ )
104
+ enable_versioning: bool = Field(
105
+ default=True,
106
+ description="Whether to track workflow versions",
107
+ )
108
+ default_max_files_to_load: int = Field(
109
+ default=3,
110
+ description="Default maximum number of workflow files to load",
111
+ )
22
112
 
23
113
 
24
114
  class WorkerConf(BaseModel):
@@ -41,84 +131,709 @@ class TaskResult(BaseModel):
41
131
 
42
132
  content: str = Field(description="The result of the task.")
43
133
  failed: bool = Field(
44
- description="Flag indicating whether the task processing failed."
134
+ default=False,
135
+ description="Flag indicating whether the task processing failed.",
45
136
  )
46
137
 
47
138
 
48
- class TaskAssignResult(BaseModel):
49
- r"""The result of task assignment."""
139
+ class QualityEvaluation(BaseModel):
140
+ r"""Quality evaluation result for a completed task.
141
+
142
+ .. deprecated::
143
+ Use :class:`TaskAnalysisResult` instead. This class is kept for
144
+ backward compatibility.
145
+ """
50
146
 
147
+ quality_sufficient: bool = Field(
148
+ description="Whether the task result meets quality standards."
149
+ )
150
+ quality_score: int = Field(
151
+ description="Quality score from 0 to 100.", ge=0, le=100
152
+ )
153
+ issues: List[str] = Field(
154
+ default_factory=list,
155
+ description="List of quality issues found in the result.",
156
+ )
157
+ recovery_strategy: Optional[str] = Field(
158
+ default=None,
159
+ description="Recommended recovery strategy if quality is "
160
+ "insufficient: "
161
+ "'retry', 'reassign', 'replan', or 'decompose'.",
162
+ )
163
+ modified_task_content: Optional[str] = Field(
164
+ default=None,
165
+ description="Modified task content for replan strategy.",
166
+ )
167
+
168
+
169
+ class TaskAssignment(BaseModel):
170
+ r"""An individual task assignment within a batch."""
171
+
172
+ task_id: str = Field(description="The ID of the task to be assigned.")
51
173
  assignee_id: str = Field(
52
- description="The ID of the workforce that is assigned to the task."
174
+ description="The ID of the worker/workforce to assign the task to."
175
+ )
176
+ dependencies: List[str] = Field(
177
+ default_factory=list,
178
+ description="List of task IDs that must complete before this task. "
179
+ "This is critical for the task decomposition and "
180
+ "execution.",
53
181
  )
54
182
 
183
+ # Allow LLMs to output dependencies as a comma-separated string or empty
184
+ # string. This validator converts such cases into a list[str] so that
185
+ # downstream logic does not break with validation errors.
186
+ @staticmethod
187
+ def _split_and_strip(dep_str: str) -> List[str]:
188
+ r"""Utility to split a comma separated string and strip
189
+ whitespace."""
190
+ return [d.strip() for d in dep_str.split(',') if d.strip()]
55
191
 
56
- def check_if_running(running: bool) -> Callable:
57
- r"""Check if the workforce is (not) running, specified the boolean value.
58
- If the workforce is not in the expected status, raise an exception.
192
+ @field_validator("dependencies", mode="before")
193
+ def validate_dependencies(cls, v) -> List[str]:
194
+ if v is None:
195
+ return []
196
+ # Handle empty string or comma-separated string from LLM
197
+ if isinstance(v, str):
198
+ return TaskAssignment._split_and_strip(v)
199
+ return v
59
200
 
60
- Raises:
61
- RuntimeError: If the workforce is not in the expected status.
201
+
202
+ class TaskAssignResult(BaseModel):
203
+ r"""The result of task assignment for both single and batch
204
+ assignments."""
205
+
206
+ assignments: List[TaskAssignment] = Field(
207
+ description="List of task assignments."
208
+ )
209
+
210
+
211
+ class RecoveryStrategy(str, Enum):
212
+ r"""Strategies for handling failed tasks."""
213
+
214
+ RETRY = "retry"
215
+ REPLAN = "replan"
216
+ DECOMPOSE = "decompose"
217
+ CREATE_WORKER = "create_worker"
218
+ REASSIGN = "reassign"
219
+
220
+ def __str__(self):
221
+ return self.value
222
+
223
+ def __repr__(self):
224
+ return f"RecoveryStrategy.{self.name}"
225
+
226
+
227
+ class FailureHandlingConfig(BaseModel):
228
+ r"""Configuration for failure handling behavior in Workforce.
229
+
230
+ This configuration allows users to customize how the Workforce handles
231
+ task failures. This config allows users to disable reassignment or other
232
+ recovery strategies as needed.
233
+
234
+ Args:
235
+ max_retries (int): Maximum number of retry attempts before giving up
236
+ on a task. (default: :obj:`3`)
237
+ enabled_strategies (Optional[List[RecoveryStrategy]]): List of recovery
238
+ strategies that are allowed to be used. Can be specified as
239
+ RecoveryStrategy enums or strings (e.g., ["retry", "replan"]).
240
+ If None, all strategies are enabled (with LLM analysis).
241
+ If an empty list, no recovery strategies are applied and failed
242
+ tasks are marked as failed immediately. If only ["retry"] is
243
+ specified, simple retry is used without LLM analysis.
244
+ (default: :obj:`None` - all strategies enabled)
245
+ halt_on_max_retries (bool): Whether to halt the entire workforce
246
+ when a task exceeds max retries. If False, the task is marked
247
+ as failed and the workflow continues (similar to PIPELINE mode
248
+ behavior). (default: :obj:`True` for AUTO_DECOMPOSE mode behavior)
249
+
250
+ Example:
251
+ >>> # Using string list (simple)
252
+ >>> config = FailureHandlingConfig(
253
+ ... enabled_strategies=["retry", "replan", "decompose"],
254
+ ... )
255
+ >>>
256
+ >>> # Using enum list
257
+ >>> config = FailureHandlingConfig(
258
+ ... enabled_strategies=[
259
+ ... RecoveryStrategy.RETRY,
260
+ ... RecoveryStrategy.REPLAN,
261
+ ... ]
262
+ ... )
263
+ >>>
264
+ >>> # Simple retry only
265
+ >>> config = FailureHandlingConfig(
266
+ ... enabled_strategies=["retry"],
267
+ ... max_retries=2,
268
+ ... )
269
+ >>>
270
+ >>> # No recovery - failed tasks are immediately marked as failed
271
+ >>> config = FailureHandlingConfig(
272
+ ... enabled_strategies=[],
273
+ ... )
274
+ >>>
275
+ >>> # Allow failures without halting
276
+ >>> config = FailureHandlingConfig(
277
+ ... halt_on_max_retries=False,
278
+ ... )
62
279
  """
63
280
 
64
- def decorator(func):
65
- @wraps(func)
66
- def wrapper(self, *args, **kwargs):
67
- if self._running != running:
68
- status = "not running" if running else "running"
69
- raise RuntimeError(
70
- f"The workforce is {status}. Cannot perform the "
71
- f"operation {func.__name__}."
281
+ max_retries: int = Field(
282
+ default=3,
283
+ ge=1,
284
+ description="Maximum retry attempts before giving up on a task",
285
+ )
286
+
287
+ enabled_strategies: Optional[List[RecoveryStrategy]] = Field(
288
+ default=None,
289
+ description="List of enabled recovery strategies. None means all "
290
+ "enabled. Empty list means no recovery (immediate failure). "
291
+ "Can be strings like ['retry', 'replan'] or RecoveryStrategy enums.",
292
+ )
293
+
294
+ halt_on_max_retries: bool = Field(
295
+ default=True,
296
+ description="Whether to halt workforce when max retries exceeded",
297
+ )
298
+
299
+ @field_validator("enabled_strategies", mode="before")
300
+ @classmethod
301
+ def validate_enabled_strategies(
302
+ cls, v
303
+ ) -> Optional[List[RecoveryStrategy]]:
304
+ r"""Convert string list to RecoveryStrategy enum list."""
305
+ if v is None:
306
+ return None
307
+ if not isinstance(v, list):
308
+ raise ValueError("enabled_strategies must be a list or None")
309
+
310
+ result = []
311
+ for item in v:
312
+ if isinstance(item, RecoveryStrategy):
313
+ result.append(item)
314
+ elif isinstance(item, str):
315
+ try:
316
+ result.append(RecoveryStrategy(item.lower()))
317
+ except ValueError:
318
+ valid = [s.value for s in RecoveryStrategy]
319
+ raise ValueError(
320
+ f"Invalid strategy '{item}'. "
321
+ f"Valid options: {valid}"
322
+ )
323
+ else:
324
+ raise ValueError(
325
+ f"Strategy must be string or RecoveryStrategy, "
326
+ f"got {type(item).__name__}"
72
327
  )
73
- return func(self, *args, **kwargs)
328
+ return result
74
329
 
75
- return wrapper
76
330
 
77
- return decorator
331
+ class FailureContext(BaseModel):
332
+ r"""Context information about a task failure."""
78
333
 
334
+ task_id: str = Field(description="ID of the failed task")
335
+ task_content: str = Field(description="Content of the failed task")
336
+ failure_count: int = Field(
337
+ description="Number of times this task has failed"
338
+ )
339
+ error_message: str = Field(description="Detailed error message")
340
+ worker_id: Optional[str] = Field(
341
+ default=None, description="ID of the worker that failed"
342
+ )
343
+ task_depth: int = Field(
344
+ description="Depth of the task in the decomposition hierarchy"
345
+ )
346
+ additional_info: Optional[str] = Field(
347
+ default=None, description="Additional context about the task"
348
+ )
79
349
 
80
- def validate_task_content(
81
- content: str, task_id: str = "unknown", min_length: int = 10
82
- ) -> bool:
83
- r"""Validates task result content to avoid silent failures.
84
- It performs basic checks to ensure the content meets minimum
85
- quality standards.
86
350
 
87
- Args:
88
- content (str): The task result content to validate.
89
- task_id (str): Task ID for logging purposes.
90
- (default: :obj:`"unknown"`)
91
- min_length (int): Minimum content length after stripping whitespace.
92
- (default: :obj:`10`)
351
+ class TaskAnalysisResult(BaseModel):
352
+ r"""Unified result for task failure analysis and quality evaluation.
93
353
 
94
- Returns:
95
- bool: True if content passes validation, False otherwise.
354
+ This model combines both failure recovery decisions and quality evaluation
355
+ results into a single structure. For failure analysis, only the recovery
356
+ strategy and reasoning fields are populated. For quality evaluation, all
357
+ fields including quality_score and issues are populated.
96
358
  """
97
- # 1: Content must not be None
98
- if content is None:
99
- logger.warning(f"Task {task_id}: None content rejected")
100
- return False
101
-
102
- # 2: Content must not be empty after stripping whitespace
103
- stripped_content = content.strip()
104
- if not stripped_content:
105
- logger.warning(
106
- f"Task {task_id}: Empty or whitespace-only content rejected."
359
+
360
+ # Common fields - always populated
361
+ reasoning: str = Field(
362
+ description="Explanation for the analysis result or recovery decision"
363
+ )
364
+
365
+ recovery_strategy: Optional[RecoveryStrategy] = Field(
366
+ default=None,
367
+ description="Recommended recovery strategy: 'retry', 'replan', "
368
+ "'decompose', 'create_worker', or 'reassign'. None indicates no "
369
+ "recovery needed (quality sufficient).",
370
+ )
371
+
372
+ modified_task_content: Optional[str] = Field(
373
+ default=None,
374
+ description="Modified task content if strategy requires replan",
375
+ )
376
+
377
+ # Quality-specific fields - populated only for quality evaluation
378
+ quality_score: Optional[int] = Field(
379
+ default=None,
380
+ description="Quality score from 0 to 100 (only for quality "
381
+ "evaluation). "
382
+ "None indicates this is a failure analysis, "
383
+ "not quality evaluation.",
384
+ ge=0,
385
+ le=100,
386
+ )
387
+
388
+ issues: List[str] = Field(
389
+ default_factory=list,
390
+ description="List of issues found. For failures: error details. "
391
+ "For quality evaluation: quality issues.",
392
+ )
393
+
394
+ @property
395
+ def is_quality_evaluation(self) -> bool:
396
+ r"""Check if this is a quality evaluation result.
397
+
398
+ Returns:
399
+ bool: True if this is a quality evaluation (has quality_score),
400
+ False if this is a failure analysis.
401
+ """
402
+ return self.quality_score is not None
403
+
404
+ @property
405
+ def quality_sufficient(self) -> bool:
406
+ r"""For quality evaluations, check if quality meets standards.
407
+
408
+ Returns:
409
+ bool: True if quality is sufficient (score >= 70 and no recovery
410
+ strategy recommended), False otherwise. Always False for
411
+ failure analysis results.
412
+ """
413
+ return (
414
+ self.quality_score is not None
415
+ and self.quality_score >= 70
416
+ and self.recovery_strategy is None
107
417
  )
108
- return False
109
-
110
- # 3: Content must meet minimum meaningful length
111
- if len(stripped_content) < min_length:
112
- logger.warning(
113
- f"Task {task_id}: Content too short ({len(stripped_content)} "
114
- f"chars < {min_length} minimum). Content preview: "
115
- f"'{stripped_content[:50]}...'"
418
+
419
+
420
+ class PipelineTaskBuilder:
421
+ r"""Helper class for building pipeline tasks with dependencies."""
422
+
423
+ def __init__(self):
424
+ """Initialize an empty pipeline task builder."""
425
+ from camel.tasks import Task
426
+
427
+ self._TaskClass = Task
428
+ self.task_list = []
429
+ self.task_counter = 0
430
+ self._task_registry = {} # task_id -> Task mapping for fast lookup
431
+ self._last_task_id = (
432
+ None # Track the last added task for chain inference
116
433
  )
117
- return False
434
+ # Track the last added parallel tasks for sync
435
+ self._last_parallel_tasks: List[str] = []
118
436
 
119
- # All validation checks passed
120
- logger.debug(
121
- f"Task {task_id}: Content validation passed "
122
- f"({len(stripped_content)} chars)"
123
- )
124
- return True
437
+ def add(
438
+ self,
439
+ content: str,
440
+ task_id: Optional[str] = None,
441
+ dependencies: Optional[List[str]] = None,
442
+ additional_info: Optional[dict] = None,
443
+ auto_depend: bool = True,
444
+ ) -> 'PipelineTaskBuilder':
445
+ """Add a task to the pipeline with support for chaining.
446
+
447
+ Args:
448
+ content (str): The content/description of the task.
449
+ task_id (str, optional): Unique identifier for the task. If None,
450
+ a unique ID will be generated. (default: :obj:`None`)
451
+ dependencies (List[str], optional): List of task IDs that this
452
+ task depends on. If None and auto_depend=True, will depend on
453
+ the last added task. (default: :obj:`None`)
454
+ additional_info (dict, optional): Additional information
455
+ for the task. (default: :obj:`None`)
456
+ auto_depend (bool, optional): If True and dependencies is None,
457
+ automatically depend on the last added task.
458
+ (default: :obj:`True`)
459
+
460
+ Returns:
461
+ PipelineTaskBuilder: Self for method chaining.
462
+
463
+ Raises:
464
+ ValueError: If task_id already exists or if any dependency is
465
+ not found.
466
+
467
+ Example:
468
+ >>> builder.add("Step 1").add("Step 2").add("Step 3")
469
+ # Step 2 depends on Step 1, Step 3 depends on Step 2
470
+ """
471
+ # Generate or validate task_id
472
+ task_id = task_id or f"pipeline_task_{self.task_counter}"
473
+
474
+ # Check ID uniqueness
475
+ if task_id in self._task_registry:
476
+ raise ValueError(f"Task ID '{task_id}' already exists")
477
+
478
+ # Auto-infer dependencies if not specified
479
+ if (
480
+ dependencies is None
481
+ and auto_depend
482
+ and self._last_task_id is not None
483
+ ):
484
+ dependencies = [self._last_task_id]
485
+
486
+ # Validate dependencies exist
487
+ dep_tasks = []
488
+ if dependencies:
489
+ missing_deps = [
490
+ dep for dep in dependencies if dep not in self._task_registry
491
+ ]
492
+ if missing_deps:
493
+ raise ValueError(f"Dependencies not found: {missing_deps}")
494
+ dep_tasks = [self._task_registry[dep] for dep in dependencies]
495
+
496
+ # Create task
497
+ task = self._TaskClass(
498
+ content=content,
499
+ id=task_id,
500
+ dependencies=dep_tasks,
501
+ additional_info=additional_info,
502
+ )
503
+
504
+ self.task_list.append(task)
505
+ self._task_registry[task_id] = task
506
+ self._last_task_id = task_id # Update last task for chaining
507
+ self.task_counter += 1
508
+ return self
509
+
510
+ def add_parallel_tasks(
511
+ self,
512
+ task_contents: List[str],
513
+ dependencies: Optional[List[str]] = None,
514
+ task_id_prefix: str = "parallel",
515
+ auto_depend: bool = True,
516
+ ) -> 'PipelineTaskBuilder':
517
+ """Add multiple parallel tasks that can execute simultaneously.
518
+
519
+ Args:
520
+ task_contents (List[str]): List of task content strings.
521
+ dependencies (List[str], optional): Common dependencies for all
522
+ parallel tasks. If None and auto_depend=True, will depend on
523
+ the last added task. (default: :obj:`None`)
524
+ task_id_prefix (str, optional): Prefix for generated task IDs.
525
+ (default: :obj:`"parallel"`)
526
+ auto_depend (bool, optional): If True and dependencies is None,
527
+ automatically depend on the last added task.
528
+ (default: :obj:`True`)
529
+
530
+ Returns:
531
+ PipelineTaskBuilder: Self for method chaining.
532
+
533
+ Raises:
534
+ ValueError: If any task_id already exists or if any dependency
535
+ is not found.
536
+
537
+ Example:
538
+ >>> builder.add("Collect Data").add_parallel_tasks([
539
+ ... "Technical Analysis", "Fundamental Analysis"
540
+ ... ]).add_sync_task("Generate Report")
541
+ """
542
+ if not task_contents:
543
+ raise ValueError("task_contents cannot be empty")
544
+
545
+ # Auto-infer dependencies if not specified
546
+ if (
547
+ dependencies is None
548
+ and auto_depend
549
+ and self._last_task_id is not None
550
+ ):
551
+ dependencies = [self._last_task_id]
552
+
553
+ parallel_task_ids = []
554
+ base_counter = (
555
+ self.task_counter
556
+ ) # Save current counter for consistent naming
557
+
558
+ for i, content in enumerate(task_contents):
559
+ task_id = f"{task_id_prefix}_{i}_{base_counter}"
560
+ # Use auto_depend=False since we're manually managing dependencies
561
+ self.add(content, task_id, dependencies, auto_depend=False)
562
+ parallel_task_ids.append(task_id)
563
+
564
+ # Set the last task to None since we have multiple parallel endings
565
+ # The next task will need to explicitly specify dependencies
566
+ self._last_task_id = None
567
+ # Store parallel task IDs for potential sync operations
568
+ self._last_parallel_tasks = parallel_task_ids
569
+
570
+ return self
571
+
572
+ def add_sync_task(
573
+ self,
574
+ content: str,
575
+ wait_for: Optional[List[str]] = None,
576
+ task_id: Optional[str] = None,
577
+ ) -> 'PipelineTaskBuilder':
578
+ """Add a synchronization task that waits for multiple tasks.
579
+
580
+ Args:
581
+ content (str): Content of the synchronization task.
582
+ wait_for (List[str], optional): List of task IDs to wait for.
583
+ If None, will automatically wait for the last parallel tasks.
584
+ (default: :obj:`None`)
585
+ task_id (str, optional): ID for the sync task. If None, a unique
586
+ ID will be generated. (default: :obj:`None`)
587
+
588
+ Returns:
589
+ PipelineTaskBuilder: Self for method chaining.
590
+
591
+ Raises:
592
+ ValueError: If task_id already exists or if any dependency is
593
+ not found.
594
+
595
+ Example:
596
+ >>> builder.add_parallel_tasks(
597
+ ... ["Task A", "Task B"]
598
+ ... ).add_sync_task("Merge Results")
599
+ # Automatically waits for both parallel tasks
600
+ """
601
+ # Auto-infer wait_for from last parallel tasks
602
+ if wait_for is None:
603
+ if self._last_parallel_tasks:
604
+ wait_for = self._last_parallel_tasks
605
+ # Clear the parallel tasks after using them
606
+ self._last_parallel_tasks = []
607
+ else:
608
+ raise ValueError(
609
+ "wait_for cannot be empty for sync task and no "
610
+ "parallel tasks found"
611
+ )
612
+
613
+ if not wait_for:
614
+ raise ValueError("wait_for cannot be empty for sync task")
615
+
616
+ return self.add(
617
+ content, task_id, dependencies=wait_for, auto_depend=False
618
+ )
619
+
620
+ def build(self) -> List:
621
+ """Build and return the complete task list with dependencies.
622
+
623
+ Returns:
624
+ List[Task]: List of tasks with proper dependency relationships.
625
+
626
+ Raises:
627
+ ValueError: If there are circular dependencies or other
628
+ validation errors.
629
+ """
630
+ if not self.task_list:
631
+ raise ValueError("No tasks defined in pipeline")
632
+
633
+ # Validate no circular dependencies
634
+ self._validate_dependencies()
635
+
636
+ return self.task_list.copy()
637
+
638
+ def clear(self) -> None:
639
+ """Clear all tasks from the builder."""
640
+ self.task_list.clear()
641
+ self._task_registry.clear()
642
+ self.task_counter = 0
643
+ self._last_task_id = None
644
+ self._last_parallel_tasks = []
645
+
646
+ def fork(self, task_contents: List[str]) -> 'PipelineTaskBuilder':
647
+ """Create parallel branches from the current task (alias for
648
+ add_parallel_tasks).
649
+
650
+ Args:
651
+ task_contents (List[str]): List of task content strings for
652
+ parallel execution.
653
+
654
+ Returns:
655
+ PipelineTaskBuilder: Self for method chaining.
656
+
657
+ Example:
658
+ >>> builder.add("Collect Data").fork([
659
+ ... "Technical Analysis", "Fundamental Analysis"
660
+ ... ]).join("Generate Report")
661
+ """
662
+ return self.add_parallel_tasks(task_contents)
663
+
664
+ def join(
665
+ self, content: str, task_id: Optional[str] = None
666
+ ) -> 'PipelineTaskBuilder':
667
+ """Join parallel branches with a synchronization task (alias for
668
+ add_sync_task).
669
+
670
+ Args:
671
+ content (str): Content of the join/sync task.
672
+ task_id (str, optional): ID for the sync task.
673
+
674
+ Returns:
675
+ PipelineTaskBuilder: Self for method chaining.
676
+
677
+ Example:
678
+ >>> builder.fork(["Task A", "Task B"]).join("Merge Results")
679
+ """
680
+ return self.add_sync_task(content, task_id=task_id)
681
+
682
+ def _validate_dependencies(self) -> None:
683
+ """Validate that there are no circular dependencies.
684
+
685
+ Raises:
686
+ ValueError: If circular dependencies are detected.
687
+ """
688
+ # Use DFS to detect cycles
689
+ visited = set()
690
+ rec_stack = set()
691
+
692
+ def has_cycle(task_id: str) -> bool:
693
+ visited.add(task_id)
694
+ rec_stack.add(task_id)
695
+
696
+ task = self._task_registry[task_id]
697
+ for dep in task.dependencies:
698
+ if dep.id not in visited:
699
+ if has_cycle(dep.id):
700
+ return True
701
+ elif dep.id in rec_stack:
702
+ return True
703
+
704
+ rec_stack.remove(task_id)
705
+ return False
706
+
707
+ for task_id in self._task_registry:
708
+ if task_id not in visited:
709
+ if has_cycle(task_id):
710
+ raise ValueError(
711
+ f"Circular dependency detected involving task: "
712
+ f"{task_id}"
713
+ )
714
+
715
+ def get_task_info(self) -> dict:
716
+ """Get information about all tasks in the pipeline.
717
+
718
+ Returns:
719
+ dict: Dictionary containing task count and task details.
720
+ """
721
+ return {
722
+ "task_count": len(self.task_list),
723
+ "tasks": [
724
+ {
725
+ "id": task.id,
726
+ "content": task.content,
727
+ "dependencies": [dep.id for dep in task.dependencies],
728
+ }
729
+ for task in self.task_list
730
+ ],
731
+ }
732
+
733
+
734
+ def check_if_running(
735
+ running: bool,
736
+ max_retries: int = 3,
737
+ retry_delay: float = 1.0,
738
+ handle_exceptions: bool = False,
739
+ ) -> Callable:
740
+ r"""Check if the workforce is (not) running, specified by the boolean
741
+ value. Provides fault tolerance through automatic retries and exception
742
+ handling.
743
+
744
+ Args:
745
+ running (bool): Expected running state (True or False).
746
+ max_retries (int, optional): Maximum number of retry attempts if the
747
+ operation fails. Set to 0 to disable retries. (default: :obj:`3`)
748
+ retry_delay (float, optional): Delay in seconds between retry attempts.
749
+ (default: :obj:`1.0`)
750
+ handle_exceptions (bool, optional): If True, catch and log exceptions
751
+ instead of propagating them. (default: :obj:`False`)
752
+
753
+ Raises:
754
+ RuntimeError: If the workforce is not in the expected status and
755
+ retries are exhausted or disabled.
756
+ Exception: Any exception raised by the decorated function if
757
+ handle_exceptions is False and retries are exhausted.
758
+ """
759
+ import logging
760
+ import time
761
+
762
+ logger = logging.getLogger(__name__)
763
+
764
+ def decorator(func):
765
+ @wraps(func)
766
+ def wrapper(self, *args, **kwargs):
767
+ retries = 0
768
+ last_exception = None
769
+
770
+ while retries <= max_retries:
771
+ try:
772
+ # Check running state
773
+ if self._running != running:
774
+ status = "not running" if running else "running"
775
+ error_msg = (
776
+ f"The workforce is {status}. Cannot perform the "
777
+ f"operation {func.__name__}."
778
+ )
779
+
780
+ # If we have retries left, wait and try again
781
+ if retries < max_retries:
782
+ logger.warning(
783
+ f"{error_msg} Retrying in {retry_delay}s... "
784
+ f"(Attempt {retries + 1}/{max_retries})"
785
+ )
786
+ time.sleep(retry_delay)
787
+ retries += 1
788
+ continue
789
+ else:
790
+ raise RuntimeError(error_msg)
791
+
792
+ return func(self, *args, **kwargs)
793
+
794
+ except Exception as e:
795
+ last_exception = e
796
+
797
+ if isinstance(e, RuntimeError) and "workforce is" in str(
798
+ e
799
+ ):
800
+ raise
801
+
802
+ if retries < max_retries:
803
+ logger.warning(
804
+ f"Exception in {func.__name__}: {e}. "
805
+ f"Retrying in {retry_delay}s... "
806
+ f"(Attempt {retries + 1}/{max_retries})"
807
+ )
808
+ time.sleep(retry_delay)
809
+ retries += 1
810
+ else:
811
+ if handle_exceptions:
812
+ logger.error(
813
+ f"Failed to execute {func.__name__} after "
814
+ f"{max_retries} retries: {e}"
815
+ )
816
+ return None
817
+ else:
818
+ # Re-raise the exception
819
+ raise
820
+
821
+ # This should not be reached, but just in case
822
+ if handle_exceptions:
823
+ logger.error(
824
+ f"Unexpected failure in {func.__name__}: {last_exception}"
825
+ )
826
+ return None
827
+ else:
828
+ raise (
829
+ last_exception
830
+ if last_exception
831
+ else RuntimeError(
832
+ f"Unexpected failure in {func.__name__} "
833
+ "with no exception captured."
834
+ )
835
+ )
836
+
837
+ return wrapper
838
+
839
+ return decorator