camel-ai 0.2.59__py3-none-any.whl → 0.2.82__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 (506) 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 +5012 -902
  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 +39 -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 +94 -0
  28. camel/benchmarks/mock_website/mock_web.py +299 -0
  29. camel/benchmarks/mock_website/requirements.txt +3 -0
  30. camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
  31. camel/benchmarks/mock_website/task.json +104 -0
  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 +26 -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 +8 -7
  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 +3 -3
  52. camel/configs/cometapi_config.py +106 -0
  53. camel/configs/crynux_config.py +94 -0
  54. camel/configs/deepseek_config.py +9 -8
  55. camel/configs/gemini_config.py +6 -4
  56. camel/configs/groq_config.py +6 -4
  57. camel/configs/internlm_config.py +6 -4
  58. camel/configs/litellm_config.py +2 -2
  59. camel/configs/lmstudio_config.py +6 -4
  60. camel/configs/minimax_config.py +95 -0
  61. camel/configs/mistral_config.py +3 -3
  62. camel/configs/modelscope_config.py +5 -3
  63. camel/configs/moonshot_config.py +2 -2
  64. camel/configs/nebius_config.py +105 -0
  65. camel/configs/netmind_config.py +2 -2
  66. camel/configs/novita_config.py +2 -2
  67. camel/configs/nvidia_config.py +2 -2
  68. camel/configs/ollama_config.py +2 -2
  69. camel/configs/openai_config.py +8 -3
  70. camel/configs/openrouter_config.py +6 -4
  71. camel/configs/ppio_config.py +2 -2
  72. camel/configs/qianfan_config.py +85 -0
  73. camel/configs/qwen_config.py +2 -2
  74. camel/configs/reka_config.py +3 -3
  75. camel/configs/samba_config.py +8 -6
  76. camel/configs/sglang_config.py +2 -2
  77. camel/configs/siliconflow_config.py +2 -2
  78. camel/configs/togetherai_config.py +2 -2
  79. camel/configs/vllm_config.py +4 -2
  80. camel/configs/watsonx_config.py +2 -2
  81. camel/configs/yi_config.py +6 -4
  82. camel/configs/zhipuai_config.py +6 -4
  83. camel/{data_collector → data_collectors}/__init__.py +2 -2
  84. camel/{data_collector → data_collectors}/alpaca_collector.py +19 -10
  85. camel/{data_collector → data_collectors}/base.py +2 -2
  86. camel/{data_collector → data_collectors}/sharegpt_collector.py +3 -3
  87. camel/datagen/__init__.py +2 -2
  88. camel/datagen/cot_datagen.py +32 -37
  89. camel/datagen/evol_instruct/__init__.py +2 -2
  90. camel/datagen/evol_instruct/evol_instruct.py +2 -2
  91. camel/datagen/evol_instruct/scorer.py +24 -25
  92. camel/datagen/evol_instruct/templates.py +48 -48
  93. camel/datagen/self_improving_cot.py +5 -5
  94. camel/datagen/self_instruct/__init__.py +2 -2
  95. camel/datagen/self_instruct/filter/__init__.py +2 -2
  96. camel/datagen/self_instruct/filter/filter_function.py +2 -2
  97. camel/datagen/self_instruct/filter/filter_registry.py +2 -2
  98. camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
  99. camel/datagen/self_instruct/self_instruct.py +2 -2
  100. camel/datagen/self_instruct/templates.py +47 -47
  101. camel/datagen/source2synth/__init__.py +2 -2
  102. camel/datagen/source2synth/data_processor.py +2 -2
  103. camel/datagen/source2synth/models.py +2 -2
  104. camel/datagen/source2synth/user_data_processor_config.py +2 -2
  105. camel/datahubs/__init__.py +2 -2
  106. camel/datahubs/base.py +2 -2
  107. camel/datahubs/huggingface.py +2 -2
  108. camel/datahubs/models.py +2 -2
  109. camel/datasets/__init__.py +2 -2
  110. camel/datasets/base_generator.py +41 -12
  111. camel/datasets/few_shot_generator.py +18 -18
  112. camel/datasets/models.py +3 -3
  113. camel/datasets/self_instruct_generator.py +2 -2
  114. camel/datasets/static_dataset.py +152 -2
  115. camel/embeddings/__init__.py +2 -2
  116. camel/embeddings/azure_embedding.py +2 -2
  117. camel/embeddings/base.py +2 -2
  118. camel/embeddings/gemini_embedding.py +2 -2
  119. camel/embeddings/jina_embedding.py +10 -3
  120. camel/embeddings/mistral_embedding.py +2 -2
  121. camel/embeddings/openai_compatible_embedding.py +2 -2
  122. camel/embeddings/openai_embedding.py +2 -2
  123. camel/embeddings/sentence_transformers_embeddings.py +4 -4
  124. camel/embeddings/together_embedding.py +2 -2
  125. camel/embeddings/vlm_embedding.py +11 -4
  126. camel/environments/__init__.py +14 -2
  127. camel/environments/models.py +2 -2
  128. camel/environments/multi_step.py +2 -2
  129. camel/environments/rlcards_env.py +860 -0
  130. camel/environments/single_step.py +30 -5
  131. camel/environments/tic_tac_toe.py +3 -3
  132. camel/extractors/__init__.py +2 -2
  133. camel/extractors/base.py +2 -2
  134. camel/extractors/python_strategies.py +2 -2
  135. camel/generators.py +2 -2
  136. camel/human.py +2 -2
  137. camel/interpreters/__init__.py +4 -2
  138. camel/interpreters/base.py +16 -3
  139. camel/interpreters/docker/Dockerfile +53 -7
  140. camel/interpreters/docker_interpreter.py +70 -11
  141. camel/interpreters/e2b_interpreter.py +59 -11
  142. camel/interpreters/internal_python_interpreter.py +81 -4
  143. camel/interpreters/interpreter_error.py +2 -2
  144. camel/interpreters/ipython_interpreter.py +23 -5
  145. camel/interpreters/microsandbox_interpreter.py +395 -0
  146. camel/interpreters/subprocess_interpreter.py +36 -4
  147. camel/loaders/__init__.py +17 -5
  148. camel/loaders/apify_reader.py +2 -2
  149. camel/loaders/base_io.py +2 -2
  150. camel/loaders/base_loader.py +85 -0
  151. camel/loaders/chunkr_reader.py +128 -93
  152. camel/loaders/crawl4ai_reader.py +2 -2
  153. camel/loaders/firecrawl_reader.py +6 -6
  154. camel/loaders/jina_url_reader.py +2 -2
  155. camel/loaders/markitdown.py +2 -2
  156. camel/loaders/mineru_extractor.py +2 -2
  157. camel/loaders/mistral_reader.py +148 -0
  158. camel/loaders/scrapegraph_reader.py +2 -2
  159. camel/loaders/unstructured_io.py +2 -2
  160. camel/logger.py +5 -5
  161. camel/memories/__init__.py +2 -2
  162. camel/memories/agent_memories.py +86 -3
  163. camel/memories/base.py +36 -2
  164. camel/memories/blocks/__init__.py +2 -2
  165. camel/memories/blocks/chat_history_block.py +126 -9
  166. camel/memories/blocks/vectordb_block.py +10 -3
  167. camel/memories/context_creators/__init__.py +2 -2
  168. camel/memories/context_creators/score_based.py +31 -239
  169. camel/memories/records.py +98 -13
  170. camel/messages/__init__.py +2 -2
  171. camel/messages/base.py +193 -46
  172. camel/messages/conversion/__init__.py +2 -2
  173. camel/messages/conversion/alpaca.py +2 -2
  174. camel/messages/conversion/conversation_models.py +2 -2
  175. camel/messages/conversion/sharegpt/__init__.py +2 -2
  176. camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
  177. camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
  178. camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
  179. camel/messages/func_message.py +54 -17
  180. camel/models/__init__.py +18 -2
  181. camel/models/_utils.py +3 -3
  182. camel/models/aihubmix_model.py +83 -0
  183. camel/models/aiml_model.py +11 -18
  184. camel/models/amd_model.py +101 -0
  185. camel/models/anthropic_model.py +127 -20
  186. camel/models/aws_bedrock_model.py +12 -35
  187. camel/models/azure_openai_model.py +263 -63
  188. camel/models/base_audio_model.py +5 -3
  189. camel/models/base_model.py +195 -26
  190. camel/models/cerebras_model.py +83 -0
  191. camel/models/cohere_model.py +81 -21
  192. camel/models/cometapi_model.py +83 -0
  193. camel/models/crynux_model.py +87 -0
  194. camel/models/deepseek_model.py +61 -59
  195. camel/models/fish_audio_model.py +8 -2
  196. camel/models/gemini_model.py +439 -30
  197. camel/models/groq_model.py +11 -19
  198. camel/models/internlm_model.py +11 -18
  199. camel/models/litellm_model.py +94 -34
  200. camel/models/lmstudio_model.py +17 -20
  201. camel/models/minimax_model.py +83 -0
  202. camel/models/mistral_model.py +84 -19
  203. camel/models/model_factory.py +49 -6
  204. camel/models/model_manager.py +33 -11
  205. camel/models/modelscope_model.py +13 -193
  206. camel/models/moonshot_model.py +195 -21
  207. camel/models/nebius_model.py +83 -0
  208. camel/models/nemotron_model.py +19 -9
  209. camel/models/netmind_model.py +11 -18
  210. camel/models/novita_model.py +11 -18
  211. camel/models/nvidia_model.py +11 -18
  212. camel/models/ollama_model.py +14 -21
  213. camel/models/openai_audio_models.py +2 -2
  214. camel/models/openai_compatible_model.py +234 -27
  215. camel/models/openai_model.py +255 -39
  216. camel/models/openrouter_model.py +11 -19
  217. camel/models/ppio_model.py +11 -18
  218. camel/models/qianfan_model.py +89 -0
  219. camel/models/qwen_model.py +13 -193
  220. camel/models/reka_model.py +90 -21
  221. camel/models/reward/__init__.py +2 -2
  222. camel/models/reward/base_reward_model.py +2 -2
  223. camel/models/reward/evaluator.py +2 -2
  224. camel/models/reward/nemotron_model.py +2 -2
  225. camel/models/reward/skywork_model.py +2 -2
  226. camel/models/samba_model.py +117 -49
  227. camel/models/sglang_model.py +162 -42
  228. camel/models/siliconflow_model.py +12 -35
  229. camel/models/stub_model.py +10 -7
  230. camel/models/togetherai_model.py +11 -18
  231. camel/models/vllm_model.py +10 -18
  232. camel/models/volcano_model.py +16 -20
  233. camel/models/watsonx_model.py +69 -19
  234. camel/models/yi_model.py +11 -18
  235. camel/models/zhipuai_model.py +70 -18
  236. camel/parsers/__init__.py +18 -0
  237. camel/parsers/mcp_tool_call_parser.py +176 -0
  238. camel/personas/__init__.py +2 -2
  239. camel/personas/persona.py +2 -2
  240. camel/personas/persona_hub.py +2 -2
  241. camel/prompts/__init__.py +2 -2
  242. camel/prompts/ai_society.py +2 -2
  243. camel/prompts/base.py +2 -2
  244. camel/prompts/code.py +2 -2
  245. camel/prompts/evaluation.py +2 -2
  246. camel/prompts/generate_text_embedding_data.py +2 -2
  247. camel/prompts/image_craft.py +2 -2
  248. camel/prompts/misalignment.py +2 -2
  249. camel/prompts/multi_condition_image_craft.py +2 -2
  250. camel/prompts/object_recognition.py +2 -2
  251. camel/prompts/persona_hub.py +3 -3
  252. camel/prompts/prompt_templates.py +2 -2
  253. camel/prompts/role_description_prompt_template.py +2 -2
  254. camel/prompts/solution_extraction.py +8 -8
  255. camel/prompts/task_prompt_template.py +2 -2
  256. camel/prompts/translation.py +2 -2
  257. camel/prompts/video_description_prompt.py +3 -3
  258. camel/responses/__init__.py +2 -2
  259. camel/responses/agent_responses.py +2 -2
  260. camel/retrievers/__init__.py +2 -2
  261. camel/retrievers/auto_retriever.py +23 -3
  262. camel/retrievers/base.py +2 -2
  263. camel/retrievers/bm25_retriever.py +3 -4
  264. camel/retrievers/cohere_rerank_retriever.py +2 -2
  265. camel/retrievers/hybrid_retrival.py +4 -4
  266. camel/retrievers/vector_retriever.py +2 -2
  267. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  268. camel/{runtime → runtimes}/__init__.py +2 -2
  269. camel/runtimes/api.py +153 -0
  270. camel/{runtime → runtimes}/base.py +2 -2
  271. camel/{runtime → runtimes}/configs.py +13 -13
  272. camel/{runtime → runtimes}/daytona_runtime.py +18 -19
  273. camel/{runtime → runtimes}/docker_runtime.py +13 -13
  274. camel/{runtime → runtimes}/llm_guard_runtime.py +28 -28
  275. camel/{runtime → runtimes}/remote_http_runtime.py +12 -12
  276. camel/{runtime → runtimes}/ubuntu_docker_runtime.py +3 -3
  277. camel/{runtime → runtimes}/utils/__init__.py +2 -2
  278. camel/{runtime → runtimes}/utils/function_risk_toolkit.py +2 -2
  279. camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +2 -2
  280. camel/schemas/__init__.py +2 -2
  281. camel/schemas/base.py +2 -2
  282. camel/schemas/openai_converter.py +3 -3
  283. camel/schemas/outlines_converter.py +2 -2
  284. camel/services/agent_openapi_server.py +380 -0
  285. camel/societies/__init__.py +4 -2
  286. camel/societies/babyagi_playing.py +2 -2
  287. camel/societies/role_playing.py +201 -80
  288. camel/societies/workforce/__init__.py +10 -3
  289. camel/societies/workforce/base.py +9 -5
  290. camel/societies/workforce/events.py +143 -0
  291. camel/societies/workforce/prompts.py +258 -33
  292. camel/societies/workforce/role_playing_worker.py +95 -30
  293. camel/societies/workforce/single_agent_worker.py +659 -30
  294. camel/societies/workforce/structured_output_handler.py +512 -0
  295. camel/societies/workforce/task_channel.py +182 -38
  296. camel/societies/workforce/utils.py +784 -18
  297. camel/societies/workforce/worker.py +96 -28
  298. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  299. camel/societies/workforce/workforce.py +5730 -366
  300. camel/societies/workforce/workforce_callback.py +103 -0
  301. camel/societies/workforce/workforce_logger.py +647 -0
  302. camel/societies/workforce/workforce_metrics.py +33 -0
  303. camel/storages/__init__.py +10 -2
  304. camel/storages/graph_storages/__init__.py +2 -2
  305. camel/storages/graph_storages/base.py +2 -2
  306. camel/storages/graph_storages/graph_element.py +2 -2
  307. camel/storages/graph_storages/nebula_graph.py +4 -4
  308. camel/storages/graph_storages/neo4j_graph.py +7 -7
  309. camel/storages/key_value_storages/__init__.py +2 -2
  310. camel/storages/key_value_storages/base.py +2 -2
  311. camel/storages/key_value_storages/in_memory.py +2 -2
  312. camel/storages/key_value_storages/json.py +17 -4
  313. camel/storages/key_value_storages/mem0_cloud.py +50 -49
  314. camel/storages/key_value_storages/redis.py +2 -2
  315. camel/storages/object_storages/__init__.py +2 -2
  316. camel/storages/object_storages/amazon_s3.py +2 -2
  317. camel/storages/object_storages/azure_blob.py +2 -2
  318. camel/storages/object_storages/base.py +2 -2
  319. camel/storages/object_storages/google_cloud.py +3 -3
  320. camel/storages/vectordb_storages/__init__.py +12 -2
  321. camel/storages/vectordb_storages/base.py +2 -2
  322. camel/storages/vectordb_storages/chroma.py +731 -0
  323. camel/storages/vectordb_storages/faiss.py +712 -0
  324. camel/storages/vectordb_storages/milvus.py +2 -2
  325. camel/storages/vectordb_storages/oceanbase.py +16 -17
  326. camel/storages/vectordb_storages/pgvector.py +349 -0
  327. camel/storages/vectordb_storages/qdrant.py +6 -6
  328. camel/storages/vectordb_storages/surreal.py +372 -0
  329. camel/storages/vectordb_storages/tidb.py +11 -8
  330. camel/storages/vectordb_storages/weaviate.py +714 -0
  331. camel/tasks/__init__.py +2 -2
  332. camel/tasks/task.py +366 -27
  333. camel/tasks/task_prompt.py +3 -3
  334. camel/terminators/__init__.py +2 -2
  335. camel/terminators/base.py +2 -2
  336. camel/terminators/response_terminator.py +2 -2
  337. camel/terminators/token_limit_terminator.py +2 -2
  338. camel/toolkits/__init__.py +58 -10
  339. camel/toolkits/aci_toolkit.py +66 -21
  340. camel/toolkits/arxiv_toolkit.py +8 -8
  341. camel/toolkits/ask_news_toolkit.py +2 -2
  342. camel/toolkits/async_browser_toolkit.py +174 -575
  343. camel/toolkits/audio_analysis_toolkit.py +3 -3
  344. camel/toolkits/base.py +65 -7
  345. camel/toolkits/bohrium_toolkit.py +318 -0
  346. camel/toolkits/browser_toolkit.py +306 -566
  347. camel/toolkits/browser_toolkit_commons.py +568 -0
  348. camel/toolkits/code_execution.py +67 -11
  349. camel/toolkits/context_summarizer_toolkit.py +684 -0
  350. camel/toolkits/craw4ai_toolkit.py +93 -0
  351. camel/toolkits/dappier_toolkit.py +12 -8
  352. camel/toolkits/data_commons_toolkit.py +2 -2
  353. camel/toolkits/dingtalk.py +1135 -0
  354. camel/toolkits/earth_science_toolkit.py +5367 -0
  355. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  356. camel/toolkits/excel_toolkit.py +910 -70
  357. camel/toolkits/file_toolkit.py +1402 -0
  358. camel/toolkits/function_tool.py +128 -20
  359. camel/toolkits/github_toolkit.py +148 -43
  360. camel/toolkits/gmail_toolkit.py +1839 -0
  361. camel/toolkits/google_calendar_toolkit.py +40 -6
  362. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  363. camel/toolkits/google_maps_toolkit.py +2 -2
  364. camel/toolkits/google_scholar_toolkit.py +2 -2
  365. camel/toolkits/human_toolkit.py +36 -12
  366. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  367. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  368. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  369. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  370. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  371. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
  372. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  373. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  374. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1929 -0
  375. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  376. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
  377. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  378. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  379. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  380. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  381. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
  382. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
  383. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  384. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
  385. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  386. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  387. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  388. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  389. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  390. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  391. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  392. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  393. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  394. camel/toolkits/image_analysis_toolkit.py +3 -3
  395. camel/toolkits/image_generation_toolkit.py +390 -0
  396. camel/toolkits/jina_reranker_toolkit.py +195 -79
  397. camel/toolkits/klavis_toolkit.py +7 -3
  398. camel/toolkits/linkedin_toolkit.py +2 -2
  399. camel/toolkits/markitdown_toolkit.py +104 -0
  400. camel/toolkits/math_toolkit.py +66 -12
  401. camel/toolkits/mcp_toolkit.py +841 -600
  402. camel/toolkits/memory_toolkit.py +7 -3
  403. camel/toolkits/meshy_toolkit.py +2 -2
  404. camel/toolkits/message_agent_toolkit.py +608 -0
  405. camel/toolkits/message_integration.py +724 -0
  406. camel/toolkits/mineru_toolkit.py +2 -2
  407. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  408. camel/toolkits/networkx_toolkit.py +2 -2
  409. camel/toolkits/note_taking_toolkit.py +277 -0
  410. camel/toolkits/notion_mcp_toolkit.py +224 -0
  411. camel/toolkits/notion_toolkit.py +2 -2
  412. camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
  413. camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
  414. camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
  415. camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
  416. camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
  417. camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
  418. camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
  419. camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
  420. camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
  421. camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
  422. camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
  423. camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
  424. camel/toolkits/open_api_specs/security_config.py +2 -2
  425. camel/toolkits/open_api_specs/speak/__init__.py +2 -2
  426. camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
  427. camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
  428. camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
  429. camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
  430. camel/toolkits/open_api_toolkit.py +2 -2
  431. camel/toolkits/openbb_toolkit.py +7 -3
  432. camel/toolkits/origene_mcp_toolkit.py +56 -0
  433. camel/toolkits/page_script.js +86 -74
  434. camel/toolkits/playwright_mcp_toolkit.py +27 -32
  435. camel/toolkits/pptx_toolkit.py +790 -0
  436. camel/toolkits/pubmed_toolkit.py +2 -2
  437. camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
  438. camel/toolkits/pyautogui_toolkit.py +2 -2
  439. camel/toolkits/reddit_toolkit.py +2 -2
  440. camel/toolkits/resend_toolkit.py +168 -0
  441. camel/toolkits/retrieval_toolkit.py +2 -2
  442. camel/toolkits/screenshot_toolkit.py +213 -0
  443. camel/toolkits/search_toolkit.py +539 -146
  444. camel/toolkits/searxng_toolkit.py +2 -2
  445. camel/toolkits/semantic_scholar_toolkit.py +2 -2
  446. camel/toolkits/slack_toolkit.py +108 -58
  447. camel/toolkits/sql_toolkit.py +712 -0
  448. camel/toolkits/stripe_toolkit.py +2 -2
  449. camel/toolkits/sympy_toolkit.py +3 -3
  450. camel/toolkits/task_planning_toolkit.py +134 -0
  451. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  452. camel/toolkits/terminal_toolkit/terminal_toolkit.py +1070 -0
  453. camel/toolkits/terminal_toolkit/utils.py +532 -0
  454. camel/toolkits/thinking_toolkit.py +3 -3
  455. camel/toolkits/twitter_toolkit.py +8 -3
  456. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  457. camel/toolkits/video_analysis_toolkit.py +112 -29
  458. camel/toolkits/video_download_toolkit.py +22 -16
  459. camel/toolkits/weather_toolkit.py +2 -2
  460. camel/toolkits/web_deploy_toolkit.py +1219 -0
  461. camel/toolkits/wechat_official_toolkit.py +483 -0
  462. camel/toolkits/whatsapp_toolkit.py +2 -2
  463. camel/toolkits/wolfram_alpha_toolkit.py +53 -25
  464. camel/toolkits/zapier_toolkit.py +7 -3
  465. camel/types/__init__.py +4 -4
  466. camel/types/agents/__init__.py +2 -2
  467. camel/types/agents/tool_calling_record.py +6 -3
  468. camel/types/enums.py +454 -35
  469. camel/types/mcp_registries.py +2 -2
  470. camel/types/openai_types.py +4 -4
  471. camel/types/unified_model_type.py +43 -6
  472. camel/utils/__init__.py +20 -2
  473. camel/utils/async_func.py +2 -2
  474. camel/utils/chunker/__init__.py +2 -2
  475. camel/utils/chunker/base.py +2 -2
  476. camel/utils/chunker/code_chunker.py +2 -2
  477. camel/utils/chunker/uio_chunker.py +2 -2
  478. camel/utils/commons.py +65 -7
  479. camel/utils/constants.py +5 -2
  480. camel/utils/context_utils.py +1134 -0
  481. camel/utils/deduplication.py +2 -2
  482. camel/utils/filename.py +2 -2
  483. camel/utils/langfuse.py +258 -0
  484. camel/utils/mcp.py +140 -6
  485. camel/utils/mcp_client.py +1056 -0
  486. camel/utils/message_summarizer.py +148 -0
  487. camel/utils/response_format.py +2 -2
  488. camel/utils/token_counting.py +45 -22
  489. camel/utils/tool_result.py +44 -0
  490. camel/verifiers/__init__.py +2 -2
  491. camel/verifiers/base.py +2 -2
  492. camel/verifiers/math_verifier.py +2 -2
  493. camel/verifiers/models.py +2 -2
  494. camel/verifiers/physics_verifier.py +2 -2
  495. camel/verifiers/python_verifier.py +2 -2
  496. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/METADATA +349 -108
  497. camel_ai-0.2.82.dist-info/RECORD +507 -0
  498. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
  499. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
  500. camel/loaders/pandas_reader.py +0 -368
  501. camel/runtime/api.py +0 -97
  502. camel/toolkits/dalle_toolkit.py +0 -171
  503. camel/toolkits/file_write_toolkit.py +0 -395
  504. camel/toolkits/openai_agent_toolkit.py +0 -135
  505. camel/toolkits/terminal_toolkit.py +0 -1037
  506. camel_ai-0.2.59.dist-info/RECORD +0 -410
@@ -0,0 +1,647 @@
1
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ import json
15
+ from datetime import datetime, timezone
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ from camel.logger import get_logger
19
+ from camel.societies.workforce.events import (
20
+ AllTasksCompletedEvent,
21
+ LogEvent,
22
+ QueueStatusEvent,
23
+ TaskAssignedEvent,
24
+ TaskCompletedEvent,
25
+ TaskCreatedEvent,
26
+ TaskDecomposedEvent,
27
+ TaskFailedEvent,
28
+ TaskStartedEvent,
29
+ WorkerCreatedEvent,
30
+ WorkerDeletedEvent,
31
+ )
32
+ from camel.societies.workforce.workforce_callback import WorkforceCallback
33
+ from camel.societies.workforce.workforce_metrics import WorkforceMetrics
34
+ from camel.types.agents import ToolCallingRecord
35
+
36
+ logger = get_logger(__name__)
37
+
38
+
39
+ class WorkforceLogger(WorkforceCallback, WorkforceMetrics):
40
+ r"""Logs events and metrics for a Workforce instance."""
41
+
42
+ def __init__(self, workforce_id: str):
43
+ """Initializes the WorkforceLogger.
44
+
45
+ Args:
46
+ workforce_id (str): The unique identifier for the workforce.
47
+ """
48
+ self.workforce_id: str = workforce_id
49
+ self.log_entries: List[Dict[str, Any]] = []
50
+ self._task_hierarchy: Dict[str, Dict[str, Any]] = {}
51
+ self._worker_information: Dict[str, Dict[str, Any]] = {}
52
+ self._initial_worker_logs: List[Dict[str, Any]] = []
53
+
54
+ def log_message(self, event: LogEvent) -> None:
55
+ r"""Logs a message to the console with color."""
56
+ colored_message = self._get_color_message(event)
57
+ print(colored_message)
58
+
59
+ def _log_event(self, event_type: str, **kwargs: Any) -> None:
60
+ r"""Internal method to create and store a log entry.
61
+
62
+ Args:
63
+ event_type (str): The type of event being logged.
64
+ **kwargs: Additional data associated with the event.
65
+ """
66
+ log_entry = {
67
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
68
+ 'workforce_id': self.workforce_id,
69
+ 'event_type': event_type,
70
+ **kwargs,
71
+ }
72
+ self.log_entries.append(log_entry)
73
+ if event_type == 'worker_created':
74
+ self._initial_worker_logs.append(log_entry)
75
+
76
+ def log_task_created(
77
+ self,
78
+ event: TaskCreatedEvent,
79
+ ) -> None:
80
+ r"""Logs the creation of a new task."""
81
+ self._log_event(
82
+ event_type=event.event_type,
83
+ task_id=event.task_id,
84
+ description=event.description,
85
+ parent_task_id=event.parent_task_id,
86
+ task_type=event.task_type,
87
+ metadata=event.metadata or {},
88
+ )
89
+ self._task_hierarchy[event.task_id] = {
90
+ 'parent': event.parent_task_id,
91
+ 'children': [],
92
+ 'status': 'created',
93
+ 'description': event.description,
94
+ 'assigned_to': None,
95
+ **(event.metadata or {}),
96
+ }
97
+ if (
98
+ event.parent_task_id
99
+ and event.parent_task_id in self._task_hierarchy
100
+ ):
101
+ self._task_hierarchy[event.parent_task_id]['children'].append(
102
+ event.task_id
103
+ )
104
+
105
+ def log_task_decomposed(
106
+ self,
107
+ event: TaskDecomposedEvent,
108
+ ) -> None:
109
+ r"""Logs the decomposition of a task into subtasks."""
110
+ self._log_event(
111
+ event_type=event.event_type,
112
+ parent_task_id=event.parent_task_id,
113
+ subtask_ids=event.subtask_ids,
114
+ metadata=event.metadata or {},
115
+ )
116
+ if event.parent_task_id in self._task_hierarchy:
117
+ self._task_hierarchy[event.parent_task_id]['status'] = "decomposed"
118
+
119
+ def log_task_assigned(
120
+ self,
121
+ event: TaskAssignedEvent,
122
+ ) -> None:
123
+ r"""Logs the assignment of a task to a worker."""
124
+ self._log_event(
125
+ event_type=event.event_type,
126
+ task_id=event.task_id,
127
+ worker_id=event.worker_id,
128
+ queue_time_seconds=event.queue_time_seconds,
129
+ dependencies=event.dependencies or [],
130
+ metadata=event.metadata or {},
131
+ )
132
+ if event.task_id in self._task_hierarchy:
133
+ self._task_hierarchy[event.task_id]['status'] = 'assigned'
134
+ self._task_hierarchy[event.task_id]['assigned_to'] = (
135
+ event.worker_id
136
+ )
137
+ self._task_hierarchy[event.task_id]['dependencies'] = (
138
+ event.dependencies or []
139
+ )
140
+ if event.worker_id in self._worker_information:
141
+ self._worker_information[event.worker_id]['current_task_id'] = (
142
+ event.task_id
143
+ )
144
+ self._worker_information[event.worker_id]['status'] = 'busy'
145
+
146
+ def log_task_started(
147
+ self,
148
+ event: TaskStartedEvent,
149
+ ) -> None:
150
+ r"""Logs when a worker starts processing a task."""
151
+ self._log_event(
152
+ event_type=event.event_type,
153
+ task_id=event.task_id,
154
+ worker_id=event.worker_id,
155
+ metadata=event.metadata or {},
156
+ )
157
+ if event.task_id in self._task_hierarchy:
158
+ self._task_hierarchy[event.task_id]['status'] = 'processing'
159
+
160
+ def log_task_completed(self, event: TaskCompletedEvent) -> None:
161
+ r"""Logs the successful completion of a task."""
162
+ self._log_event(
163
+ event_type=event.event_type,
164
+ task_id=event.task_id,
165
+ worker_id=event.worker_id,
166
+ result_summary=event.result_summary,
167
+ processing_time_seconds=event.processing_time_seconds,
168
+ token_usage=event.token_usage or {},
169
+ metadata=event.metadata or {},
170
+ )
171
+ if event.task_id in self._task_hierarchy:
172
+ self._task_hierarchy[event.task_id]['status'] = 'completed'
173
+ self._task_hierarchy[event.task_id]['assigned_to'] = None
174
+ # Store processing time in task hierarchy for display in tree
175
+ if event.processing_time_seconds is not None:
176
+ self._task_hierarchy[event.task_id][
177
+ 'completion_time_seconds'
178
+ ] = event.processing_time_seconds
179
+ # Store token usage in task hierarchy for display in tree
180
+ if event.token_usage is not None:
181
+ self._task_hierarchy[event.task_id]['token_usage'] = (
182
+ event.token_usage
183
+ )
184
+ if event.worker_id in self._worker_information:
185
+ self._worker_information[event.worker_id]['current_task_id'] = None
186
+ self._worker_information[event.worker_id]['status'] = 'idle'
187
+ self._worker_information[event.worker_id]['tasks_completed'] = (
188
+ self._worker_information[event.worker_id].get(
189
+ 'tasks_completed', 0
190
+ )
191
+ + 1
192
+ )
193
+
194
+ def log_task_failed(
195
+ self,
196
+ event: TaskFailedEvent,
197
+ ) -> None:
198
+ r"""Logs the failure of a task."""
199
+ self._log_event(
200
+ event_type=event.event_type,
201
+ task_id=event.task_id,
202
+ worker_id=event.worker_id,
203
+ error_message=event.error_message,
204
+ metadata=event.metadata or {},
205
+ )
206
+ if event.task_id in self._task_hierarchy:
207
+ self._task_hierarchy[event.task_id]['status'] = 'failed'
208
+ self._task_hierarchy[event.task_id]['error'] = event.error_message
209
+ self._task_hierarchy[event.task_id]['assigned_to'] = None
210
+ if event.worker_id and event.worker_id in self._worker_information:
211
+ self._worker_information[event.worker_id]['current_task_id'] = None
212
+ self._worker_information[event.worker_id]['status'] = 'idle'
213
+ self._worker_information[event.worker_id]['tasks_failed'] = (
214
+ self._worker_information[event.worker_id].get(
215
+ 'tasks_failed', 0
216
+ )
217
+ + 1
218
+ )
219
+
220
+ def log_worker_created(
221
+ self,
222
+ event: WorkerCreatedEvent,
223
+ ) -> None:
224
+ r"""Logs the creation of a new worker."""
225
+ self._log_event(
226
+ event_type=event.event_type,
227
+ worker_id=event.worker_id,
228
+ worker_type=event.worker_type,
229
+ role=event.role,
230
+ metadata=event.metadata or {},
231
+ )
232
+ self._worker_information[event.worker_id] = {
233
+ 'type': event.worker_type,
234
+ 'role': event.role,
235
+ 'status': 'idle',
236
+ 'current_task_id': None,
237
+ 'tasks_completed': 0,
238
+ 'tasks_failed': 0,
239
+ **(event.metadata or {}),
240
+ }
241
+
242
+ def log_worker_deleted(
243
+ self,
244
+ event: WorkerDeletedEvent,
245
+ ) -> None:
246
+ r"""Logs the deletion of a worker."""
247
+ self._log_event(
248
+ event_type=event.event_type,
249
+ worker_id=event.worker_id,
250
+ reason=event.reason,
251
+ metadata=event.metadata or {},
252
+ )
253
+ if event.worker_id in self._worker_information:
254
+ self._worker_information[event.worker_id]['status'] = 'deleted'
255
+ # Or del self._worker_information[worker_id]
256
+
257
+ def log_queue_status(
258
+ self,
259
+ event: QueueStatusEvent,
260
+ ) -> None:
261
+ r"""Logs the status of a task queue."""
262
+ self._log_event(
263
+ event_type=event.event_type,
264
+ queue_name=event.queue_name,
265
+ length=event.length,
266
+ pending_task_ids=event.pending_task_ids or [],
267
+ metadata=event.metadata or {},
268
+ )
269
+
270
+ def log_all_tasks_completed(self, event: AllTasksCompletedEvent) -> None:
271
+ pass
272
+
273
+ def reset_task_data(self) -> None:
274
+ r"""Resets logs and data related to tasks, preserving worker
275
+ information.
276
+ """
277
+ # Restore log entries from the initial worker logs
278
+ self.log_entries = list(self._initial_worker_logs) # Make a copy
279
+
280
+ self._task_hierarchy.clear()
281
+ for worker_id in self._worker_information:
282
+ if (
283
+ self._worker_information[worker_id].get('status') != 'deleted'
284
+ ): # Don't revive deleted workers
285
+ self._worker_information[worker_id]['current_task_id'] = None
286
+ self._worker_information[worker_id]['status'] = 'idle'
287
+ logger.info(
288
+ f"WorkforceLogger: Task data reset for workforce "
289
+ f"{self.workforce_id}"
290
+ )
291
+
292
+ def dump_to_json(self, file_path: str) -> None:
293
+ r"""Dumps all log entries to a JSON file.
294
+
295
+ Args:
296
+ file_path (str): The path to the JSON file.
297
+ """
298
+
299
+ def json_serializer_default(o: Any) -> Any:
300
+ if isinstance(o, ToolCallingRecord):
301
+ return o.as_dict()
302
+ # Let the default encoder raise the TypeError for other types
303
+ raise TypeError(
304
+ f"Object of type {o.__class__.__name__} is not "
305
+ f"JSON serializable"
306
+ )
307
+
308
+ try:
309
+ with open(file_path, 'w') as f:
310
+ json.dump(
311
+ self.log_entries,
312
+ f,
313
+ indent=4,
314
+ default=json_serializer_default,
315
+ )
316
+ except IOError as e:
317
+ # Consider using camel.logger for this kind of internal error
318
+ logger.error(f"Error dumping logs to JSON: {e}")
319
+
320
+ def _get_all_tasks_in_hierarchy(
321
+ self, task_id: str
322
+ ) -> Dict[str, Dict[str, Any]]:
323
+ r"""Recursively collect all tasks in the hierarchy starting from
324
+ task_id.
325
+ """
326
+ result: Dict[str, Dict[str, Any]] = {}
327
+ if task_id not in self._task_hierarchy:
328
+ return result
329
+
330
+ # Add the current task
331
+ result[task_id] = self._task_hierarchy[task_id]
332
+
333
+ # Add all children recursively
334
+ children = self._task_hierarchy[task_id].get('children', [])
335
+ for child_id in children:
336
+ result.update(self._get_all_tasks_in_hierarchy(child_id))
337
+
338
+ return result
339
+
340
+ def _get_task_tree_string(
341
+ self, task_id: str, prefix: str = "", is_last: bool = True
342
+ ) -> str:
343
+ r"""Generate a string representation of the task tree."""
344
+ if task_id not in self._task_hierarchy:
345
+ return ""
346
+
347
+ task_info = self._task_hierarchy[task_id]
348
+ description = task_info.get('description', '')
349
+ status = task_info.get('status', 'unknown')
350
+ assignee = task_info.get('assigned_to')
351
+ assignee_str = f" [assigned to: {assignee}]" if assignee else ""
352
+ dependencies = task_info.get('dependencies', [])
353
+ dependencies_list = [
354
+ dep for dep in dependencies if dep in self._task_hierarchy
355
+ ]
356
+ dependencies_str = (
357
+ f" (dependencies: {', '.join(dependencies_list)})"
358
+ if dependencies_list
359
+ else ""
360
+ )
361
+ error_str = (
362
+ f" [ERROR: {task_info.get('error', '')}]"
363
+ if status == 'failed'
364
+ else ""
365
+ )
366
+
367
+ # Add completion time and token usage for completed tasks
368
+ completion_time_str = ""
369
+ token_usage_str = ""
370
+
371
+ if status == 'completed':
372
+ # For the root task (typically task_id = '0'), calculate total
373
+ # tokens and time
374
+ if task_id == '0':
375
+ # Calculate total tokens from all child tasks
376
+ total_tokens = 0
377
+ total_time = 0.0
378
+
379
+ # Recursively get all tasks in the hierarchy
380
+ all_tasks = self._get_all_tasks_in_hierarchy(task_id)
381
+
382
+ # Sum up tokens and time from all tasks
383
+ for child_id, child_info in all_tasks.items():
384
+ if (
385
+ child_id != task_id
386
+ ): # Skip the root task itself to avoid double counting
387
+ # Add tokens
388
+ if (
389
+ 'token_usage' in child_info
390
+ and child_info['token_usage'] is not None
391
+ ):
392
+ child_tokens = child_info['token_usage']
393
+ if (
394
+ isinstance(child_tokens, dict)
395
+ and 'total_tokens' in child_tokens
396
+ ):
397
+ total_tokens += child_tokens['total_tokens']
398
+ elif isinstance(child_tokens, int):
399
+ total_tokens += child_tokens
400
+
401
+ # Add completion time
402
+ if (
403
+ 'completion_time_seconds' in child_info
404
+ and child_info['completion_time_seconds']
405
+ is not None
406
+ ):
407
+ total_time += child_info['completion_time_seconds']
408
+
409
+ # Format the strings for the root task
410
+ completion_time_str = (
411
+ f" (completed in {total_time:.2f} seconds total)"
412
+ )
413
+ token_usage_str = f" [total tokens: {total_tokens}]"
414
+ else:
415
+ # Regular task (not root) - show its own completion time and
416
+ # tokens
417
+ if (
418
+ 'completion_time_seconds' in task_info
419
+ and task_info['completion_time_seconds'] is not None
420
+ ):
421
+ completion_time = task_info['completion_time_seconds']
422
+ completion_time_str = (
423
+ f" (completed in {completion_time:.2f} seconds)"
424
+ )
425
+ else:
426
+ # Add a default message when completion time is not
427
+ # available
428
+ completion_time_str = " (completed)"
429
+
430
+ # Add token usage if available
431
+ if (
432
+ 'token_usage' in task_info
433
+ and task_info['token_usage'] is not None
434
+ ):
435
+ token_usage = task_info['token_usage']
436
+ if (
437
+ isinstance(token_usage, dict)
438
+ and 'total_tokens' in token_usage
439
+ ):
440
+ token_usage_str = (
441
+ f" [tokens: {token_usage['total_tokens']}]"
442
+ )
443
+ elif isinstance(token_usage, int):
444
+ token_usage_str = f" [tokens: {token_usage}]"
445
+
446
+ tree_str = f"{prefix}{'`-- ' if is_last else '|-- '}[{task_id}] {description} [{status}]{completion_time_str}{token_usage_str}{assignee_str}{dependencies_str}{error_str}\n" # noqa: E501
447
+
448
+ children = task_info.get('children', [])
449
+ for i, child_id in enumerate(children):
450
+ new_prefix = prefix + (" " if is_last else "| ")
451
+ tree_str += self._get_task_tree_string(
452
+ child_id, new_prefix, i == len(children) - 1
453
+ )
454
+ return tree_str
455
+
456
+ def get_ascii_tree_representation(self) -> str:
457
+ r"""Generates an ASCII tree representation of the current task
458
+ hierarchy and worker status.
459
+ """
460
+ output_str = "=== Task Hierarchy ===\n"
461
+ root_tasks = [
462
+ task_id
463
+ for task_id, info in self._task_hierarchy.items()
464
+ if info.get('parent') is None
465
+ ]
466
+ if not root_tasks:
467
+ output_str += "No tasks recorded.\n"
468
+ else:
469
+ for i, task_id in enumerate(root_tasks):
470
+ output_str += self._get_task_tree_string(
471
+ task_id, "", i == len(root_tasks) - 1
472
+ )
473
+
474
+ output_str += "\n=== Worker Information ===\n"
475
+ if not self._worker_information:
476
+ output_str += "No workers recorded.\n"
477
+ else:
478
+ for worker_id, info in self._worker_information.items():
479
+ role = info.get('role', 'N/A')
480
+ completed = info.get('tasks_completed', 0)
481
+ failed = info.get('tasks_failed', 0)
482
+ output_str += (
483
+ f"- Worker ID: {worker_id} (Role: {role})\n"
484
+ f" Tasks Completed: {completed}, Tasks "
485
+ f"Failed: {failed}\n"
486
+ )
487
+ return output_str
488
+
489
+ def get_kpis(self) -> Dict[str, Any]:
490
+ r"""Calculates and returns key performance indicators from the logs."""
491
+ kpis: Dict[str, Any] = {
492
+ 'total_tasks_created': 0,
493
+ 'total_tasks_completed': 0,
494
+ 'total_tasks_failed': 0,
495
+ 'worker_utilization': {},
496
+ 'current_pending_tasks': 0,
497
+ 'total_workforce_running_time_seconds': 0.0,
498
+ }
499
+
500
+ task_start_times: Dict[str, float] = {}
501
+ task_creation_timestamps: Dict[str, datetime] = {}
502
+ task_assignment_timestamps: Dict[str, datetime] = {}
503
+ first_timestamp: Optional[datetime] = None
504
+ last_timestamp: Optional[datetime] = None
505
+
506
+ tasks_handled_by_worker: Dict[str, int] = {}
507
+
508
+ # Track unique task final states to avoid double-counting
509
+ task_final_states: Dict[
510
+ str, str
511
+ ] = {} # task_id -> 'completed' or 'failed'
512
+
513
+ # Helper function to check if a task is the main task (has no parent)
514
+ def is_main_task(task_id: str) -> bool:
515
+ return (
516
+ task_id in self._task_hierarchy
517
+ and self._task_hierarchy[task_id].get('parent') is None
518
+ )
519
+
520
+ for entry in self.log_entries:
521
+ event_type = entry['event_type']
522
+ timestamp = datetime.fromisoformat(entry['timestamp'])
523
+ task_id = entry.get('task_id', '')
524
+
525
+ if first_timestamp is None or timestamp < first_timestamp:
526
+ first_timestamp = timestamp
527
+ if last_timestamp is None or timestamp > last_timestamp:
528
+ last_timestamp = timestamp
529
+
530
+ if event_type == 'task_created':
531
+ # Exclude main task from total count
532
+ if not is_main_task(task_id):
533
+ kpis['total_tasks_created'] += 1
534
+ task_creation_timestamps[task_id] = timestamp
535
+ elif event_type == 'task_assigned':
536
+ task_assignment_timestamps[task_id] = timestamp
537
+ # Queue time tracking has been removed
538
+
539
+ elif event_type == 'task_started':
540
+ # Store start time for processing time calculation
541
+ task_start_times[task_id] = timestamp.timestamp()
542
+
543
+ elif event_type == 'task_completed':
544
+ # Exclude main task from total count
545
+ if not is_main_task(task_id):
546
+ # Track final state - a completed task overwrites any
547
+ # previous failed state
548
+ task_final_states[task_id] = 'completed'
549
+ # Count tasks handled by worker (only for non-main tasks)
550
+ if 'worker_id' in entry and entry['worker_id'] is not None:
551
+ worker_id = entry['worker_id']
552
+ tasks_handled_by_worker[worker_id] = (
553
+ tasks_handled_by_worker.get(worker_id, 0) + 1
554
+ )
555
+
556
+ if task_id in task_assignment_timestamps:
557
+ completion_time = (
558
+ timestamp - task_assignment_timestamps[task_id]
559
+ ).total_seconds()
560
+ # Store completion time in task hierarchy instead of KPIs
561
+ # array
562
+ if task_id in self._task_hierarchy:
563
+ self._task_hierarchy[task_id][
564
+ 'completion_time_seconds'
565
+ ] = completion_time
566
+
567
+ elif event_type == 'task_failed':
568
+ # Exclude main task from total count
569
+ if not is_main_task(task_id):
570
+ # Only track as failed if not already completed
571
+ # (in case of retries, the final completion overwrites
572
+ # failed state)
573
+ if task_final_states.get(task_id) != 'completed':
574
+ task_final_states[task_id] = 'failed'
575
+ # Count tasks handled by worker (only for non-main tasks)
576
+ if 'worker_id' in entry and entry['worker_id'] is not None:
577
+ worker_id = entry['worker_id']
578
+ tasks_handled_by_worker[worker_id] = (
579
+ tasks_handled_by_worker.get(worker_id, 0) + 1
580
+ )
581
+ elif event_type == 'queue_status':
582
+ pass # Placeholder for now
583
+
584
+ # Calculate total workforce running time
585
+ if first_timestamp and last_timestamp and self.log_entries:
586
+ kpis['total_workforce_running_time_seconds'] = (
587
+ last_timestamp - first_timestamp
588
+ ).total_seconds()
589
+
590
+ # Count unique tasks by final state
591
+ for _task_id, state in task_final_states.items():
592
+ if state == 'completed':
593
+ kpis['total_tasks_completed'] += 1
594
+ elif state == 'failed':
595
+ kpis['total_tasks_failed'] += 1
596
+
597
+ # Calculate worker utilization based on proportion of tasks handled
598
+ total_tasks_processed_for_utilization = (
599
+ kpis['total_tasks_completed'] + kpis['total_tasks_failed']
600
+ )
601
+ if total_tasks_processed_for_utilization > 0:
602
+ for (
603
+ worker_id_key,
604
+ num_tasks_handled,
605
+ ) in tasks_handled_by_worker.items():
606
+ percentage = (
607
+ num_tasks_handled / total_tasks_processed_for_utilization
608
+ ) * 100
609
+ kpis['worker_utilization'][worker_id_key] = (
610
+ f"{percentage:.2f}%"
611
+ )
612
+ else:
613
+ for worker_id_key in (
614
+ tasks_handled_by_worker
615
+ ): # Ensure all workers who handled tasks are listed, even if 0%
616
+ kpis['worker_utilization'][worker_id_key] = "0.00%"
617
+ # If no tasks were processed, but workers exist (e.g. from
618
+ # _initial_worker_logs), list them with 0%
619
+ for worker_id_key in self._worker_information:
620
+ if worker_id_key not in kpis['worker_utilization']:
621
+ kpis['worker_utilization'][worker_id_key] = "0.00%"
622
+
623
+ # Task throughput (completed tasks per minute, for example)
624
+ if self.log_entries:
625
+ first_log_time = datetime.fromisoformat(
626
+ self.log_entries[0]['timestamp']
627
+ )
628
+ last_log_time = datetime.fromisoformat(
629
+ self.log_entries[-1]['timestamp']
630
+ )
631
+ duration_seconds = (last_log_time - first_log_time).total_seconds()
632
+ if duration_seconds > 0:
633
+ kpis['task_throughput_per_second'] = (
634
+ kpis['total_tasks_completed'] / duration_seconds
635
+ )
636
+ kpis['task_throughput_per_minute'] = (
637
+ kpis['task_throughput_per_second'] * 60
638
+ )
639
+
640
+ kpis['total_workers_created'] = len(self._worker_information)
641
+
642
+ # Current pending tasks - tasks created but not yet completed or failed
643
+ kpis['current_pending_tasks'] = kpis['total_tasks_created'] - len(
644
+ task_final_states
645
+ )
646
+
647
+ return kpis
@@ -0,0 +1,33 @@
1
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
14
+ from abc import ABC, abstractmethod
15
+ from typing import Any, Dict
16
+
17
+
18
+ class WorkforceMetrics(ABC):
19
+ @abstractmethod
20
+ def reset_task_data(self) -> None:
21
+ pass
22
+
23
+ @abstractmethod
24
+ def dump_to_json(self, file_path: str) -> None:
25
+ pass
26
+
27
+ @abstractmethod
28
+ def get_ascii_tree_representation(self) -> str:
29
+ pass
30
+
31
+ @abstractmethod
32
+ def get_kpis(self) -> Dict[str, Any]:
33
+ pass