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