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.
- camel/__init__.py +3 -3
- camel/agents/__init__.py +2 -2
- camel/agents/_types.py +9 -4
- camel/agents/_utils.py +40 -2
- camel/agents/base.py +2 -2
- camel/agents/chat_agent.py +5012 -902
- camel/agents/critic_agent.py +2 -2
- camel/agents/deductive_reasoner_agent.py +56 -56
- camel/agents/embodied_agent.py +2 -2
- camel/agents/knowledge_graph_agent.py +20 -20
- camel/agents/mcp_agent.py +39 -36
- camel/agents/multi_hop_generator_agent.py +3 -3
- camel/agents/programmed_agent_instruction.py +2 -2
- camel/agents/repo_agent.py +4 -3
- camel/agents/role_assignment_agent.py +2 -2
- camel/agents/search_agent.py +2 -2
- camel/agents/task_agent.py +2 -2
- camel/agents/tool_agents/__init__.py +2 -2
- camel/agents/tool_agents/base.py +2 -2
- camel/agents/tool_agents/hugging_face_tool_agent.py +3 -3
- camel/benchmarks/__init__.py +2 -2
- camel/benchmarks/apibank.py +5 -5
- camel/benchmarks/apibench.py +2 -2
- camel/benchmarks/base.py +2 -2
- camel/benchmarks/browsecomp.py +44 -33
- camel/benchmarks/gaia.py +17 -13
- camel/benchmarks/mock_website/README.md +94 -0
- camel/benchmarks/mock_website/mock_web.py +299 -0
- camel/benchmarks/mock_website/requirements.txt +3 -0
- camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
- camel/benchmarks/mock_website/task.json +104 -0
- camel/benchmarks/nexus.py +3 -3
- camel/benchmarks/ragbench.py +2 -2
- camel/bots/__init__.py +2 -2
- camel/bots/discord/__init__.py +2 -2
- camel/bots/discord/discord_app.py +2 -2
- camel/bots/discord/discord_installation.py +2 -2
- camel/bots/discord/discord_store.py +3 -3
- camel/bots/slack/__init__.py +2 -2
- camel/bots/slack/models.py +4 -4
- camel/bots/slack/slack_app.py +2 -2
- camel/bots/telegram_bot.py +2 -2
- camel/configs/__init__.py +26 -2
- camel/configs/aihubmix_config.py +90 -0
- camel/configs/aiml_config.py +2 -2
- camel/configs/amd_config.py +70 -0
- camel/configs/anthropic_config.py +8 -7
- camel/configs/base_config.py +2 -2
- camel/configs/bedrock_config.py +5 -3
- camel/configs/cerebras_config.py +98 -0
- camel/configs/cohere_config.py +3 -3
- camel/configs/cometapi_config.py +106 -0
- camel/configs/crynux_config.py +94 -0
- camel/configs/deepseek_config.py +9 -8
- camel/configs/gemini_config.py +6 -4
- camel/configs/groq_config.py +6 -4
- camel/configs/internlm_config.py +6 -4
- camel/configs/litellm_config.py +2 -2
- camel/configs/lmstudio_config.py +6 -4
- camel/configs/minimax_config.py +95 -0
- camel/configs/mistral_config.py +3 -3
- camel/configs/modelscope_config.py +5 -3
- camel/configs/moonshot_config.py +2 -2
- camel/configs/nebius_config.py +105 -0
- camel/configs/netmind_config.py +2 -2
- camel/configs/novita_config.py +2 -2
- camel/configs/nvidia_config.py +2 -2
- camel/configs/ollama_config.py +2 -2
- camel/configs/openai_config.py +8 -3
- camel/configs/openrouter_config.py +6 -4
- camel/configs/ppio_config.py +2 -2
- camel/configs/qianfan_config.py +85 -0
- camel/configs/qwen_config.py +2 -2
- camel/configs/reka_config.py +3 -3
- camel/configs/samba_config.py +8 -6
- camel/configs/sglang_config.py +2 -2
- camel/configs/siliconflow_config.py +2 -2
- camel/configs/togetherai_config.py +2 -2
- camel/configs/vllm_config.py +4 -2
- camel/configs/watsonx_config.py +2 -2
- camel/configs/yi_config.py +6 -4
- camel/configs/zhipuai_config.py +6 -4
- camel/{data_collector → data_collectors}/__init__.py +2 -2
- camel/{data_collector → data_collectors}/alpaca_collector.py +19 -10
- camel/{data_collector → data_collectors}/base.py +2 -2
- camel/{data_collector → data_collectors}/sharegpt_collector.py +3 -3
- camel/datagen/__init__.py +2 -2
- camel/datagen/cot_datagen.py +32 -37
- camel/datagen/evol_instruct/__init__.py +2 -2
- camel/datagen/evol_instruct/evol_instruct.py +2 -2
- camel/datagen/evol_instruct/scorer.py +24 -25
- camel/datagen/evol_instruct/templates.py +48 -48
- camel/datagen/self_improving_cot.py +5 -5
- camel/datagen/self_instruct/__init__.py +2 -2
- camel/datagen/self_instruct/filter/__init__.py +2 -2
- camel/datagen/self_instruct/filter/filter_function.py +2 -2
- camel/datagen/self_instruct/filter/filter_registry.py +2 -2
- camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
- camel/datagen/self_instruct/self_instruct.py +2 -2
- camel/datagen/self_instruct/templates.py +47 -47
- camel/datagen/source2synth/__init__.py +2 -2
- camel/datagen/source2synth/data_processor.py +2 -2
- camel/datagen/source2synth/models.py +2 -2
- camel/datagen/source2synth/user_data_processor_config.py +2 -2
- camel/datahubs/__init__.py +2 -2
- camel/datahubs/base.py +2 -2
- camel/datahubs/huggingface.py +2 -2
- camel/datahubs/models.py +2 -2
- camel/datasets/__init__.py +2 -2
- camel/datasets/base_generator.py +41 -12
- camel/datasets/few_shot_generator.py +18 -18
- camel/datasets/models.py +3 -3
- camel/datasets/self_instruct_generator.py +2 -2
- camel/datasets/static_dataset.py +152 -2
- camel/embeddings/__init__.py +2 -2
- camel/embeddings/azure_embedding.py +2 -2
- camel/embeddings/base.py +2 -2
- camel/embeddings/gemini_embedding.py +2 -2
- camel/embeddings/jina_embedding.py +10 -3
- camel/embeddings/mistral_embedding.py +2 -2
- camel/embeddings/openai_compatible_embedding.py +2 -2
- camel/embeddings/openai_embedding.py +2 -2
- camel/embeddings/sentence_transformers_embeddings.py +4 -4
- camel/embeddings/together_embedding.py +2 -2
- camel/embeddings/vlm_embedding.py +11 -4
- camel/environments/__init__.py +14 -2
- camel/environments/models.py +2 -2
- camel/environments/multi_step.py +2 -2
- camel/environments/rlcards_env.py +860 -0
- camel/environments/single_step.py +30 -5
- camel/environments/tic_tac_toe.py +3 -3
- camel/extractors/__init__.py +2 -2
- camel/extractors/base.py +2 -2
- camel/extractors/python_strategies.py +2 -2
- camel/generators.py +2 -2
- camel/human.py +2 -2
- camel/interpreters/__init__.py +4 -2
- camel/interpreters/base.py +16 -3
- camel/interpreters/docker/Dockerfile +53 -7
- camel/interpreters/docker_interpreter.py +70 -11
- camel/interpreters/e2b_interpreter.py +59 -11
- camel/interpreters/internal_python_interpreter.py +81 -4
- camel/interpreters/interpreter_error.py +2 -2
- camel/interpreters/ipython_interpreter.py +23 -5
- camel/interpreters/microsandbox_interpreter.py +395 -0
- camel/interpreters/subprocess_interpreter.py +36 -4
- camel/loaders/__init__.py +17 -5
- camel/loaders/apify_reader.py +2 -2
- camel/loaders/base_io.py +2 -2
- camel/loaders/base_loader.py +85 -0
- camel/loaders/chunkr_reader.py +128 -93
- camel/loaders/crawl4ai_reader.py +2 -2
- camel/loaders/firecrawl_reader.py +6 -6
- camel/loaders/jina_url_reader.py +2 -2
- camel/loaders/markitdown.py +2 -2
- camel/loaders/mineru_extractor.py +2 -2
- camel/loaders/mistral_reader.py +148 -0
- camel/loaders/scrapegraph_reader.py +2 -2
- camel/loaders/unstructured_io.py +2 -2
- camel/logger.py +5 -5
- camel/memories/__init__.py +2 -2
- camel/memories/agent_memories.py +86 -3
- camel/memories/base.py +36 -2
- camel/memories/blocks/__init__.py +2 -2
- camel/memories/blocks/chat_history_block.py +126 -9
- camel/memories/blocks/vectordb_block.py +10 -3
- camel/memories/context_creators/__init__.py +2 -2
- camel/memories/context_creators/score_based.py +31 -239
- camel/memories/records.py +98 -13
- camel/messages/__init__.py +2 -2
- camel/messages/base.py +193 -46
- camel/messages/conversion/__init__.py +2 -2
- camel/messages/conversion/alpaca.py +2 -2
- camel/messages/conversion/conversation_models.py +2 -2
- camel/messages/conversion/sharegpt/__init__.py +2 -2
- camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
- camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
- camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
- camel/messages/func_message.py +54 -17
- camel/models/__init__.py +18 -2
- camel/models/_utils.py +3 -3
- camel/models/aihubmix_model.py +83 -0
- camel/models/aiml_model.py +11 -18
- camel/models/amd_model.py +101 -0
- camel/models/anthropic_model.py +127 -20
- camel/models/aws_bedrock_model.py +12 -35
- camel/models/azure_openai_model.py +263 -63
- camel/models/base_audio_model.py +5 -3
- camel/models/base_model.py +195 -26
- camel/models/cerebras_model.py +83 -0
- camel/models/cohere_model.py +81 -21
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +87 -0
- camel/models/deepseek_model.py +61 -59
- camel/models/fish_audio_model.py +8 -2
- camel/models/gemini_model.py +439 -30
- camel/models/groq_model.py +11 -19
- camel/models/internlm_model.py +11 -18
- camel/models/litellm_model.py +94 -34
- camel/models/lmstudio_model.py +17 -20
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +84 -19
- camel/models/model_factory.py +49 -6
- camel/models/model_manager.py +33 -11
- camel/models/modelscope_model.py +13 -193
- camel/models/moonshot_model.py +195 -21
- camel/models/nebius_model.py +83 -0
- camel/models/nemotron_model.py +19 -9
- camel/models/netmind_model.py +11 -18
- camel/models/novita_model.py +11 -18
- camel/models/nvidia_model.py +11 -18
- camel/models/ollama_model.py +14 -21
- camel/models/openai_audio_models.py +2 -2
- camel/models/openai_compatible_model.py +234 -27
- camel/models/openai_model.py +255 -39
- camel/models/openrouter_model.py +11 -19
- camel/models/ppio_model.py +11 -18
- camel/models/qianfan_model.py +89 -0
- camel/models/qwen_model.py +13 -193
- camel/models/reka_model.py +90 -21
- camel/models/reward/__init__.py +2 -2
- camel/models/reward/base_reward_model.py +2 -2
- camel/models/reward/evaluator.py +2 -2
- camel/models/reward/nemotron_model.py +2 -2
- camel/models/reward/skywork_model.py +2 -2
- camel/models/samba_model.py +117 -49
- camel/models/sglang_model.py +162 -42
- camel/models/siliconflow_model.py +12 -35
- camel/models/stub_model.py +10 -7
- camel/models/togetherai_model.py +11 -18
- camel/models/vllm_model.py +10 -18
- camel/models/volcano_model.py +16 -20
- camel/models/watsonx_model.py +69 -19
- camel/models/yi_model.py +11 -18
- camel/models/zhipuai_model.py +70 -18
- camel/parsers/__init__.py +18 -0
- camel/parsers/mcp_tool_call_parser.py +176 -0
- camel/personas/__init__.py +2 -2
- camel/personas/persona.py +2 -2
- camel/personas/persona_hub.py +2 -2
- camel/prompts/__init__.py +2 -2
- camel/prompts/ai_society.py +2 -2
- camel/prompts/base.py +2 -2
- camel/prompts/code.py +2 -2
- camel/prompts/evaluation.py +2 -2
- camel/prompts/generate_text_embedding_data.py +2 -2
- camel/prompts/image_craft.py +2 -2
- camel/prompts/misalignment.py +2 -2
- camel/prompts/multi_condition_image_craft.py +2 -2
- camel/prompts/object_recognition.py +2 -2
- camel/prompts/persona_hub.py +3 -3
- camel/prompts/prompt_templates.py +2 -2
- camel/prompts/role_description_prompt_template.py +2 -2
- camel/prompts/solution_extraction.py +8 -8
- camel/prompts/task_prompt_template.py +2 -2
- camel/prompts/translation.py +2 -2
- camel/prompts/video_description_prompt.py +3 -3
- camel/responses/__init__.py +2 -2
- camel/responses/agent_responses.py +2 -2
- camel/retrievers/__init__.py +2 -2
- camel/retrievers/auto_retriever.py +23 -3
- camel/retrievers/base.py +2 -2
- camel/retrievers/bm25_retriever.py +3 -4
- camel/retrievers/cohere_rerank_retriever.py +2 -2
- camel/retrievers/hybrid_retrival.py +4 -4
- camel/retrievers/vector_retriever.py +2 -2
- camel/runtimes/Dockerfile.multi-toolkit +90 -0
- camel/{runtime → runtimes}/__init__.py +2 -2
- camel/runtimes/api.py +153 -0
- camel/{runtime → runtimes}/base.py +2 -2
- camel/{runtime → runtimes}/configs.py +13 -13
- camel/{runtime → runtimes}/daytona_runtime.py +18 -19
- camel/{runtime → runtimes}/docker_runtime.py +13 -13
- camel/{runtime → runtimes}/llm_guard_runtime.py +28 -28
- camel/{runtime → runtimes}/remote_http_runtime.py +12 -12
- camel/{runtime → runtimes}/ubuntu_docker_runtime.py +3 -3
- camel/{runtime → runtimes}/utils/__init__.py +2 -2
- camel/{runtime → runtimes}/utils/function_risk_toolkit.py +2 -2
- camel/{runtime → runtimes}/utils/ignore_risk_toolkit.py +2 -2
- camel/schemas/__init__.py +2 -2
- camel/schemas/base.py +2 -2
- camel/schemas/openai_converter.py +3 -3
- camel/schemas/outlines_converter.py +2 -2
- camel/services/agent_openapi_server.py +380 -0
- camel/societies/__init__.py +4 -2
- camel/societies/babyagi_playing.py +2 -2
- camel/societies/role_playing.py +201 -80
- camel/societies/workforce/__init__.py +10 -3
- camel/societies/workforce/base.py +9 -5
- camel/societies/workforce/events.py +143 -0
- camel/societies/workforce/prompts.py +258 -33
- camel/societies/workforce/role_playing_worker.py +95 -30
- camel/societies/workforce/single_agent_worker.py +659 -30
- camel/societies/workforce/structured_output_handler.py +512 -0
- camel/societies/workforce/task_channel.py +182 -38
- camel/societies/workforce/utils.py +784 -18
- camel/societies/workforce/worker.py +96 -28
- camel/societies/workforce/workflow_memory_manager.py +1746 -0
- camel/societies/workforce/workforce.py +5730 -366
- camel/societies/workforce/workforce_callback.py +103 -0
- camel/societies/workforce/workforce_logger.py +647 -0
- camel/societies/workforce/workforce_metrics.py +33 -0
- camel/storages/__init__.py +10 -2
- camel/storages/graph_storages/__init__.py +2 -2
- camel/storages/graph_storages/base.py +2 -2
- camel/storages/graph_storages/graph_element.py +2 -2
- camel/storages/graph_storages/nebula_graph.py +4 -4
- camel/storages/graph_storages/neo4j_graph.py +7 -7
- camel/storages/key_value_storages/__init__.py +2 -2
- camel/storages/key_value_storages/base.py +2 -2
- camel/storages/key_value_storages/in_memory.py +2 -2
- camel/storages/key_value_storages/json.py +17 -4
- camel/storages/key_value_storages/mem0_cloud.py +50 -49
- camel/storages/key_value_storages/redis.py +2 -2
- camel/storages/object_storages/__init__.py +2 -2
- camel/storages/object_storages/amazon_s3.py +2 -2
- camel/storages/object_storages/azure_blob.py +2 -2
- camel/storages/object_storages/base.py +2 -2
- camel/storages/object_storages/google_cloud.py +3 -3
- camel/storages/vectordb_storages/__init__.py +12 -2
- camel/storages/vectordb_storages/base.py +2 -2
- camel/storages/vectordb_storages/chroma.py +731 -0
- camel/storages/vectordb_storages/faiss.py +712 -0
- camel/storages/vectordb_storages/milvus.py +2 -2
- camel/storages/vectordb_storages/oceanbase.py +16 -17
- camel/storages/vectordb_storages/pgvector.py +349 -0
- camel/storages/vectordb_storages/qdrant.py +6 -6
- camel/storages/vectordb_storages/surreal.py +372 -0
- camel/storages/vectordb_storages/tidb.py +11 -8
- camel/storages/vectordb_storages/weaviate.py +714 -0
- camel/tasks/__init__.py +2 -2
- camel/tasks/task.py +366 -27
- camel/tasks/task_prompt.py +3 -3
- camel/terminators/__init__.py +2 -2
- camel/terminators/base.py +2 -2
- camel/terminators/response_terminator.py +2 -2
- camel/terminators/token_limit_terminator.py +2 -2
- camel/toolkits/__init__.py +58 -10
- camel/toolkits/aci_toolkit.py +66 -21
- camel/toolkits/arxiv_toolkit.py +8 -8
- camel/toolkits/ask_news_toolkit.py +2 -2
- camel/toolkits/async_browser_toolkit.py +174 -575
- camel/toolkits/audio_analysis_toolkit.py +3 -3
- camel/toolkits/base.py +65 -7
- camel/toolkits/bohrium_toolkit.py +318 -0
- camel/toolkits/browser_toolkit.py +306 -566
- camel/toolkits/browser_toolkit_commons.py +568 -0
- camel/toolkits/code_execution.py +67 -11
- camel/toolkits/context_summarizer_toolkit.py +684 -0
- camel/toolkits/craw4ai_toolkit.py +93 -0
- camel/toolkits/dappier_toolkit.py +12 -8
- camel/toolkits/data_commons_toolkit.py +2 -2
- camel/toolkits/dingtalk.py +1135 -0
- camel/toolkits/earth_science_toolkit.py +5367 -0
- camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
- camel/toolkits/excel_toolkit.py +910 -70
- camel/toolkits/file_toolkit.py +1402 -0
- camel/toolkits/function_tool.py +128 -20
- camel/toolkits/github_toolkit.py +148 -43
- camel/toolkits/gmail_toolkit.py +1839 -0
- camel/toolkits/google_calendar_toolkit.py +40 -6
- camel/toolkits/google_drive_mcp_toolkit.py +54 -0
- camel/toolkits/google_maps_toolkit.py +2 -2
- camel/toolkits/google_scholar_toolkit.py +2 -2
- camel/toolkits/human_toolkit.py +36 -12
- camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
- camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
- camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
- camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
- camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1929 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
- camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
- camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
- camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
- camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
- camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
- camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
- camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
- camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
- camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
- camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
- camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
- camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
- camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
- camel/toolkits/image_analysis_toolkit.py +3 -3
- camel/toolkits/image_generation_toolkit.py +390 -0
- camel/toolkits/jina_reranker_toolkit.py +195 -79
- camel/toolkits/klavis_toolkit.py +7 -3
- camel/toolkits/linkedin_toolkit.py +2 -2
- camel/toolkits/markitdown_toolkit.py +104 -0
- camel/toolkits/math_toolkit.py +66 -12
- camel/toolkits/mcp_toolkit.py +841 -600
- camel/toolkits/memory_toolkit.py +7 -3
- camel/toolkits/meshy_toolkit.py +2 -2
- camel/toolkits/message_agent_toolkit.py +608 -0
- camel/toolkits/message_integration.py +724 -0
- camel/toolkits/mineru_toolkit.py +2 -2
- camel/toolkits/minimax_mcp_toolkit.py +195 -0
- camel/toolkits/networkx_toolkit.py +2 -2
- camel/toolkits/note_taking_toolkit.py +277 -0
- camel/toolkits/notion_mcp_toolkit.py +224 -0
- camel/toolkits/notion_toolkit.py +2 -2
- camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
- camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
- camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
- camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
- camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
- camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
- camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
- camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
- camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
- camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
- camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
- camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
- camel/toolkits/open_api_specs/security_config.py +2 -2
- camel/toolkits/open_api_specs/speak/__init__.py +2 -2
- camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
- camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
- camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
- camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
- camel/toolkits/open_api_toolkit.py +2 -2
- camel/toolkits/openbb_toolkit.py +7 -3
- camel/toolkits/origene_mcp_toolkit.py +56 -0
- camel/toolkits/page_script.js +86 -74
- camel/toolkits/playwright_mcp_toolkit.py +27 -32
- camel/toolkits/pptx_toolkit.py +790 -0
- camel/toolkits/pubmed_toolkit.py +2 -2
- camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
- camel/toolkits/pyautogui_toolkit.py +2 -2
- camel/toolkits/reddit_toolkit.py +2 -2
- camel/toolkits/resend_toolkit.py +168 -0
- camel/toolkits/retrieval_toolkit.py +2 -2
- camel/toolkits/screenshot_toolkit.py +213 -0
- camel/toolkits/search_toolkit.py +539 -146
- camel/toolkits/searxng_toolkit.py +2 -2
- camel/toolkits/semantic_scholar_toolkit.py +2 -2
- camel/toolkits/slack_toolkit.py +108 -58
- camel/toolkits/sql_toolkit.py +712 -0
- camel/toolkits/stripe_toolkit.py +2 -2
- camel/toolkits/sympy_toolkit.py +3 -3
- camel/toolkits/task_planning_toolkit.py +134 -0
- camel/toolkits/terminal_toolkit/__init__.py +18 -0
- camel/toolkits/terminal_toolkit/terminal_toolkit.py +1070 -0
- camel/toolkits/terminal_toolkit/utils.py +532 -0
- camel/toolkits/thinking_toolkit.py +3 -3
- camel/toolkits/twitter_toolkit.py +8 -3
- camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
- camel/toolkits/video_analysis_toolkit.py +112 -29
- camel/toolkits/video_download_toolkit.py +22 -16
- camel/toolkits/weather_toolkit.py +2 -2
- camel/toolkits/web_deploy_toolkit.py +1219 -0
- camel/toolkits/wechat_official_toolkit.py +483 -0
- camel/toolkits/whatsapp_toolkit.py +2 -2
- camel/toolkits/wolfram_alpha_toolkit.py +53 -25
- camel/toolkits/zapier_toolkit.py +7 -3
- camel/types/__init__.py +4 -4
- camel/types/agents/__init__.py +2 -2
- camel/types/agents/tool_calling_record.py +6 -3
- camel/types/enums.py +454 -35
- camel/types/mcp_registries.py +2 -2
- camel/types/openai_types.py +4 -4
- camel/types/unified_model_type.py +43 -6
- camel/utils/__init__.py +20 -2
- camel/utils/async_func.py +2 -2
- camel/utils/chunker/__init__.py +2 -2
- camel/utils/chunker/base.py +2 -2
- camel/utils/chunker/code_chunker.py +2 -2
- camel/utils/chunker/uio_chunker.py +2 -2
- camel/utils/commons.py +65 -7
- camel/utils/constants.py +5 -2
- camel/utils/context_utils.py +1134 -0
- camel/utils/deduplication.py +2 -2
- camel/utils/filename.py +2 -2
- camel/utils/langfuse.py +258 -0
- camel/utils/mcp.py +140 -6
- camel/utils/mcp_client.py +1056 -0
- camel/utils/message_summarizer.py +148 -0
- camel/utils/response_format.py +2 -2
- camel/utils/token_counting.py +45 -22
- camel/utils/tool_result.py +44 -0
- camel/verifiers/__init__.py +2 -2
- camel/verifiers/base.py +2 -2
- camel/verifiers/math_verifier.py +2 -2
- camel/verifiers/models.py +2 -2
- camel/verifiers/physics_verifier.py +2 -2
- camel/verifiers/python_verifier.py +2 -2
- {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/METADATA +349 -108
- camel_ai-0.2.82.dist-info/RECORD +507 -0
- {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
- {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
- camel/loaders/pandas_reader.py +0 -368
- camel/runtime/api.py +0 -97
- camel/toolkits/dalle_toolkit.py +0 -171
- camel/toolkits/file_write_toolkit.py +0 -395
- camel/toolkits/openai_agent_toolkit.py +0 -135
- camel/toolkits/terminal_toolkit.py +0 -1037
- 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
|