camel-ai 0.2.65__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 +4835 -947
- 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 +23 -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/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 +31 -239
- 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 +16 -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 +212 -89
- 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 +16 -21
- camel/models/cometapi_model.py +83 -0
- camel/models/crynux_model.py +11 -18
- camel/models/deepseek_model.py +18 -58
- camel/models/fish_audio_model.py +8 -2
- camel/models/gemini_model.py +389 -26
- camel/models/groq_model.py +11 -19
- camel/models/internlm_model.py +11 -18
- camel/models/litellm_model.py +56 -34
- camel/models/lmstudio_model.py +17 -20
- camel/models/minimax_model.py +83 -0
- camel/models/mistral_model.py +18 -19
- camel/models/model_factory.py +37 -3
- camel/models/model_manager.py +26 -8
- 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 +188 -45
- camel/models/openai_model.py +216 -71
- 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 +21 -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 +48 -47
- camel/models/sglang_model.py +88 -40
- 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 +7 -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 +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 +143 -0
- camel/societies/workforce/prompts.py +258 -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 +5276 -355
- 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 +54 -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 +65 -7
- 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 +126 -18
- 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 +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 -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 +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 +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 +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 +5 -5
- 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 +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 +378 -39
- camel/types/mcp_registries.py +2 -2
- camel/types/openai_types.py +4 -4
- camel/types/unified_model_type.py +38 -6
- camel/utils/__init__.py +2 -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 +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 +2 -2
- 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.82.dist-info}/METADATA +327 -94
- camel_ai-0.2.82.dist-info/RECORD +507 -0
- {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
- {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.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
|
@@ -0,0 +1,724 @@
|
|
|
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 inspect
|
|
15
|
+
from functools import wraps
|
|
16
|
+
from typing import Callable, List, Optional, Union
|
|
17
|
+
|
|
18
|
+
from camel.logger import get_logger
|
|
19
|
+
from camel.toolkits import BaseToolkit, FunctionTool
|
|
20
|
+
|
|
21
|
+
logger = get_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ToolkitMessageIntegration:
|
|
25
|
+
r"""Integrates user messaging capabilities into CAMEL toolkits and
|
|
26
|
+
functions.
|
|
27
|
+
|
|
28
|
+
This class allows agents to send status updates to users while executing
|
|
29
|
+
toolkit functions in a single step, improving communication and reducing
|
|
30
|
+
the number of tool calls needed.
|
|
31
|
+
|
|
32
|
+
Supports both built-in and custom message handlers with flexible parameter
|
|
33
|
+
names. Can update both toolkit methods and standalone functions.
|
|
34
|
+
|
|
35
|
+
Example:
|
|
36
|
+
>>> # Using default message handler with toolkit
|
|
37
|
+
>>> message_integration = ToolkitMessageIntegration()
|
|
38
|
+
>>> search_with_messaging = message_integration.
|
|
39
|
+
register_toolkits(
|
|
40
|
+
... SearchToolkit()
|
|
41
|
+
... )
|
|
42
|
+
|
|
43
|
+
>>> # Using with standalone functions
|
|
44
|
+
>>> def search_web(query: str) -> list:
|
|
45
|
+
... return ["result1", "result2"]
|
|
46
|
+
...
|
|
47
|
+
>>> enhanced_tools = message_integration.register_functions
|
|
48
|
+
([search_web])
|
|
49
|
+
|
|
50
|
+
>>> # Using custom message handler with different parameters
|
|
51
|
+
>>> def notify_user(severity: str, action: str, details: str = "") ->
|
|
52
|
+
str:
|
|
53
|
+
... '''Send notification to user.
|
|
54
|
+
...
|
|
55
|
+
... Args:
|
|
56
|
+
... severity: Notification level (info/warning/error)
|
|
57
|
+
... action: What action is being performed
|
|
58
|
+
... details: Additional details
|
|
59
|
+
... '''
|
|
60
|
+
... print(f"[{severity}] {action}: {details}")
|
|
61
|
+
... return "Notified"
|
|
62
|
+
...
|
|
63
|
+
>>> message_integration = ToolkitMessageIntegration(
|
|
64
|
+
... message_handler=notify_user,
|
|
65
|
+
... extract_params_callback=lambda kwargs: (
|
|
66
|
+
... kwargs.pop('severity', 'info'),
|
|
67
|
+
... kwargs.pop('action', 'executing'),
|
|
68
|
+
... kwargs.pop('details', '')
|
|
69
|
+
... )
|
|
70
|
+
... )
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def __init__(
|
|
74
|
+
self,
|
|
75
|
+
message_handler: Optional[Callable] = None,
|
|
76
|
+
extract_params_callback: Optional[Callable[[dict], tuple]] = None,
|
|
77
|
+
):
|
|
78
|
+
r"""Initialize the toolkit message integration.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
message_handler (Optional[Callable]): Custom message handler
|
|
82
|
+
function. If not provided, uses the built-in
|
|
83
|
+
send_message_to_user. (default: :obj:`None`)
|
|
84
|
+
extract_params_callback (Optional[Callable]): Function to extract
|
|
85
|
+
parameters from kwargs for the custom message handler. Should
|
|
86
|
+
return a tuple of arguments to pass to the message handler. If
|
|
87
|
+
not provided, uses default extraction for built-in handler.
|
|
88
|
+
(default: :obj:`None`)
|
|
89
|
+
"""
|
|
90
|
+
self.message_handler = message_handler or self.send_message_to_user
|
|
91
|
+
self.extract_params_callback = (
|
|
92
|
+
extract_params_callback or self._default_extract_params
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# If custom handler is provided, we'll use its signature and docstring
|
|
96
|
+
self.use_custom_handler = message_handler is not None
|
|
97
|
+
|
|
98
|
+
def _default_extract_params(self, kwargs: dict) -> tuple:
|
|
99
|
+
r"""Default parameter extraction for built-in message handler."""
|
|
100
|
+
return (
|
|
101
|
+
kwargs.pop('message_title', ''),
|
|
102
|
+
kwargs.pop('message_description', ''),
|
|
103
|
+
kwargs.pop('message_attachment', ''),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def send_message_to_user(
|
|
107
|
+
self,
|
|
108
|
+
message_title: str,
|
|
109
|
+
message_description: str,
|
|
110
|
+
message_attachment: str = "",
|
|
111
|
+
) -> str:
|
|
112
|
+
r"""Built-in message handler that sends tidy messages to the user.
|
|
113
|
+
|
|
114
|
+
This one-way tool keeps the user informed about agent progress,
|
|
115
|
+
decisions, or actions. It does not require a response.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
message_title (str): The title of the message.
|
|
119
|
+
message_description (str): The short description message.
|
|
120
|
+
message_attachment (str): The additional attachment of the message,
|
|
121
|
+
which can be a file path or a URL.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
str: Confirmation that the message was successfully sent.
|
|
125
|
+
"""
|
|
126
|
+
print(f"\nAgent Message:\n{message_title}\n{message_description}\n")
|
|
127
|
+
if message_attachment:
|
|
128
|
+
print(message_attachment)
|
|
129
|
+
|
|
130
|
+
logger.info(
|
|
131
|
+
f"\nAgent Message:\n{message_title} "
|
|
132
|
+
f"{message_description} {message_attachment}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
f"Message successfully sent to user: '{message_title} "
|
|
137
|
+
f"{message_description} {message_attachment}'"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
def get_message_tool(self) -> FunctionTool:
|
|
141
|
+
r"""Get the send_message_to_user as a standalone FunctionTool.
|
|
142
|
+
|
|
143
|
+
This can be used when you want to provide the messaging capability
|
|
144
|
+
as a separate tool rather than integrating it into other tools.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
FunctionTool: The message sending tool.
|
|
148
|
+
"""
|
|
149
|
+
return FunctionTool(self.send_message_to_user)
|
|
150
|
+
|
|
151
|
+
def register_toolkits(self, toolkit: BaseToolkit) -> BaseToolkit:
|
|
152
|
+
r"""Add messaging capabilities to all toolkit methods.
|
|
153
|
+
|
|
154
|
+
This method modifies a toolkit so that all its tools can send
|
|
155
|
+
status messages to users while executing their primary function.
|
|
156
|
+
The tools will accept optional messaging parameters:
|
|
157
|
+
- message_title: Title of the status message
|
|
158
|
+
- message_description: Description of what the tool is doing
|
|
159
|
+
- message_attachment: Optional file path or URL
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
toolkit: The toolkit to add messaging capabilities to
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The same toolkit instance with messaging capabilities added to
|
|
166
|
+
all methods.
|
|
167
|
+
"""
|
|
168
|
+
original_tools = toolkit.get_tools()
|
|
169
|
+
enhanced_methods = {}
|
|
170
|
+
for tool in original_tools:
|
|
171
|
+
method_name = tool.func.__name__
|
|
172
|
+
enhanced_func = self._add_messaging_to_tool(tool.func)
|
|
173
|
+
enhanced_methods[method_name] = enhanced_func
|
|
174
|
+
setattr(toolkit, method_name, enhanced_func)
|
|
175
|
+
original_get_tools_method = toolkit.get_tools
|
|
176
|
+
|
|
177
|
+
def enhanced_get_tools() -> List[FunctionTool]:
|
|
178
|
+
tools = []
|
|
179
|
+
for _, enhanced_method in enhanced_methods.items():
|
|
180
|
+
tools.append(FunctionTool(enhanced_method))
|
|
181
|
+
original_tools_list = original_get_tools_method()
|
|
182
|
+
for tool in original_tools_list:
|
|
183
|
+
if tool.func.__name__ not in enhanced_methods:
|
|
184
|
+
tools.append(tool)
|
|
185
|
+
|
|
186
|
+
return tools
|
|
187
|
+
|
|
188
|
+
toolkit.get_tools = enhanced_get_tools # type: ignore[method-assign]
|
|
189
|
+
|
|
190
|
+
# Also handle clone_for_new_session
|
|
191
|
+
# if it exists to ensure cloned toolkits
|
|
192
|
+
# also have message integration
|
|
193
|
+
if hasattr(toolkit, 'clone_for_new_session'):
|
|
194
|
+
original_clone_method = toolkit.clone_for_new_session
|
|
195
|
+
message_integration_instance = self
|
|
196
|
+
|
|
197
|
+
def enhanced_clone_for_new_session(new_session_id=None):
|
|
198
|
+
cloned_toolkit = original_clone_method(new_session_id)
|
|
199
|
+
return message_integration_instance.register_toolkits(
|
|
200
|
+
cloned_toolkit
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
toolkit.clone_for_new_session = enhanced_clone_for_new_session
|
|
204
|
+
|
|
205
|
+
return toolkit
|
|
206
|
+
|
|
207
|
+
def _create_bound_method_wrapper(
|
|
208
|
+
self, enhanced_func: Callable, toolkit_instance
|
|
209
|
+
) -> Callable:
|
|
210
|
+
r"""Create a wrapper that mimics a bound method for _clone_tools.
|
|
211
|
+
|
|
212
|
+
This wrapper preserves the toolkit instance reference while maintaining
|
|
213
|
+
the enhanced messaging functionality.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
# Create a wrapper that appears as a bound method to _clone_tools
|
|
217
|
+
@wraps(enhanced_func)
|
|
218
|
+
def bound_method_wrapper(*args, **kwargs):
|
|
219
|
+
return enhanced_func(*args, **kwargs)
|
|
220
|
+
|
|
221
|
+
# Make it appear as a bound method by setting __self__
|
|
222
|
+
bound_method_wrapper.__self__ = toolkit_instance # type: ignore[attr-defined]
|
|
223
|
+
|
|
224
|
+
# Preserve other important attributes
|
|
225
|
+
if hasattr(enhanced_func, '__signature__'):
|
|
226
|
+
bound_method_wrapper.__signature__ = enhanced_func.__signature__ # type: ignore[attr-defined]
|
|
227
|
+
if hasattr(enhanced_func, '__doc__'):
|
|
228
|
+
bound_method_wrapper.__doc__ = enhanced_func.__doc__
|
|
229
|
+
|
|
230
|
+
return bound_method_wrapper
|
|
231
|
+
|
|
232
|
+
def register_functions(
|
|
233
|
+
self,
|
|
234
|
+
functions: Union[List[FunctionTool], List[Callable]],
|
|
235
|
+
function_names: Optional[List[str]] = None,
|
|
236
|
+
) -> List[FunctionTool]:
|
|
237
|
+
r"""Add messaging capabilities to a list of functions or FunctionTools.
|
|
238
|
+
|
|
239
|
+
This method enhances functions so they can send status messages to
|
|
240
|
+
users while executing. The enhanced functions will accept optional
|
|
241
|
+
messaging parameters that trigger status updates.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
functions (Union[List[FunctionTool], List[Callable]]): List of
|
|
245
|
+
FunctionTool objects or callable functions to enhance.
|
|
246
|
+
function_names (Optional[List[str]]): List of specific function
|
|
247
|
+
names to modify. If None, messaging is added to all functions.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
List[FunctionTool]: List of enhanced FunctionTool objects
|
|
251
|
+
|
|
252
|
+
Example:
|
|
253
|
+
>>> # With FunctionTools
|
|
254
|
+
>>> tools = [FunctionTool(search_func), FunctionTool(analyze_func)]
|
|
255
|
+
>>> enhanced_tools = message_integration.register_functions
|
|
256
|
+
(tools)
|
|
257
|
+
|
|
258
|
+
>>> # With callable functions
|
|
259
|
+
>>> funcs = [search_web, analyze_data, generate_report]
|
|
260
|
+
>>> enhanced_tools = message_integration.register_functions
|
|
261
|
+
(
|
|
262
|
+
... funcs,
|
|
263
|
+
... function_names=['search_web', 'analyze_data']
|
|
264
|
+
... )
|
|
265
|
+
"""
|
|
266
|
+
enhanced_tools = []
|
|
267
|
+
|
|
268
|
+
for item in functions:
|
|
269
|
+
# Extract the function based on input type
|
|
270
|
+
if isinstance(item, FunctionTool):
|
|
271
|
+
func = item.func
|
|
272
|
+
elif callable(item):
|
|
273
|
+
func = item
|
|
274
|
+
else:
|
|
275
|
+
raise ValueError(
|
|
276
|
+
f"Invalid item type: {type(item)}. Expected "
|
|
277
|
+
f"FunctionTool or callable."
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# Check if this function should be enhanced
|
|
281
|
+
if function_names is None or func.__name__ in function_names:
|
|
282
|
+
enhanced_func = self._add_messaging_to_tool(func)
|
|
283
|
+
enhanced_tools.append(FunctionTool(enhanced_func))
|
|
284
|
+
else:
|
|
285
|
+
# Return as FunctionTool regardless of input type
|
|
286
|
+
if isinstance(item, FunctionTool):
|
|
287
|
+
enhanced_tools.append(item)
|
|
288
|
+
else:
|
|
289
|
+
enhanced_tools.append(FunctionTool(func))
|
|
290
|
+
|
|
291
|
+
return enhanced_tools
|
|
292
|
+
|
|
293
|
+
def _add_messaging_to_tool(self, func: Callable) -> Callable:
|
|
294
|
+
r"""Add messaging parameters to a tool function.
|
|
295
|
+
|
|
296
|
+
This internal method modifies the function signature and docstring
|
|
297
|
+
to include optional messaging parameters that trigger status updates.
|
|
298
|
+
"""
|
|
299
|
+
if getattr(func, "__message_integration_enhanced__", False):
|
|
300
|
+
logger.debug(
|
|
301
|
+
f"Function {func.__name__} already enhanced, skipping"
|
|
302
|
+
)
|
|
303
|
+
return func
|
|
304
|
+
|
|
305
|
+
# Get the original signature
|
|
306
|
+
original_sig = inspect.signature(func)
|
|
307
|
+
|
|
308
|
+
# Check if the function is async
|
|
309
|
+
is_async = inspect.iscoroutinefunction(func)
|
|
310
|
+
|
|
311
|
+
# Create new parameters for the enhanced function
|
|
312
|
+
new_params = list(original_sig.parameters.values())
|
|
313
|
+
|
|
314
|
+
# Determine which parameters to add based on handler type
|
|
315
|
+
if self.use_custom_handler:
|
|
316
|
+
# Use the custom handler's signature
|
|
317
|
+
handler_sig = inspect.signature(self.message_handler)
|
|
318
|
+
message_params = []
|
|
319
|
+
|
|
320
|
+
# Add parameters from the custom handler (excluding self if it's a
|
|
321
|
+
# method)
|
|
322
|
+
for param_name, param in handler_sig.parameters.items():
|
|
323
|
+
if param_name != 'self':
|
|
324
|
+
# Create a keyword-only parameter with the same annotation
|
|
325
|
+
# and default
|
|
326
|
+
new_param = inspect.Parameter(
|
|
327
|
+
param_name,
|
|
328
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
329
|
+
default=param.default
|
|
330
|
+
if param.default != inspect.Parameter.empty
|
|
331
|
+
else None,
|
|
332
|
+
annotation=param.annotation
|
|
333
|
+
if param.annotation != inspect.Parameter.empty
|
|
334
|
+
else inspect.Parameter.empty,
|
|
335
|
+
)
|
|
336
|
+
message_params.append(new_param)
|
|
337
|
+
else:
|
|
338
|
+
# Use default parameters for built-in handler
|
|
339
|
+
message_params = [
|
|
340
|
+
inspect.Parameter(
|
|
341
|
+
'message_title',
|
|
342
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
343
|
+
default="",
|
|
344
|
+
annotation=str,
|
|
345
|
+
),
|
|
346
|
+
inspect.Parameter(
|
|
347
|
+
'message_description',
|
|
348
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
349
|
+
default="",
|
|
350
|
+
annotation=str,
|
|
351
|
+
),
|
|
352
|
+
inspect.Parameter(
|
|
353
|
+
'message_attachment',
|
|
354
|
+
inspect.Parameter.KEYWORD_ONLY,
|
|
355
|
+
default="",
|
|
356
|
+
annotation=str,
|
|
357
|
+
),
|
|
358
|
+
]
|
|
359
|
+
|
|
360
|
+
# Find where to insert the new parameters (before **kwargs if it
|
|
361
|
+
# exists)
|
|
362
|
+
insert_index = len(new_params)
|
|
363
|
+
for i, param in enumerate(new_params):
|
|
364
|
+
if param.kind == inspect.Parameter.VAR_KEYWORD:
|
|
365
|
+
insert_index = i
|
|
366
|
+
break
|
|
367
|
+
|
|
368
|
+
# Insert the message parameters
|
|
369
|
+
for param in reversed(message_params):
|
|
370
|
+
new_params.insert(insert_index, param)
|
|
371
|
+
|
|
372
|
+
# Create the new signature
|
|
373
|
+
new_sig = original_sig.replace(parameters=new_params)
|
|
374
|
+
|
|
375
|
+
if is_async:
|
|
376
|
+
|
|
377
|
+
@wraps(func)
|
|
378
|
+
async def wrapper(*args, **kwargs):
|
|
379
|
+
try:
|
|
380
|
+
params = self.extract_params_callback(kwargs)
|
|
381
|
+
except KeyError:
|
|
382
|
+
return await func(*args, **kwargs)
|
|
383
|
+
|
|
384
|
+
# Check if we should send a message
|
|
385
|
+
should_send = False
|
|
386
|
+
if self.use_custom_handler:
|
|
387
|
+
should_send = any(
|
|
388
|
+
p is not None and p != '' for p in params
|
|
389
|
+
)
|
|
390
|
+
else:
|
|
391
|
+
# For default handler, params
|
|
392
|
+
# (title, description, attachment)
|
|
393
|
+
should_send = bool(params[0]) or bool(params[1])
|
|
394
|
+
|
|
395
|
+
# Send message if needed (handle async properly)
|
|
396
|
+
if should_send:
|
|
397
|
+
try:
|
|
398
|
+
if self.use_custom_handler:
|
|
399
|
+
# Check if message handler is async
|
|
400
|
+
if inspect.iscoroutinefunction(
|
|
401
|
+
self.message_handler
|
|
402
|
+
):
|
|
403
|
+
await self.message_handler(*params)
|
|
404
|
+
else:
|
|
405
|
+
self.message_handler(*params)
|
|
406
|
+
else:
|
|
407
|
+
# For built-in handler, provide defaults
|
|
408
|
+
title, desc, attach = params
|
|
409
|
+
self.message_handler(
|
|
410
|
+
title or "Executing Tool",
|
|
411
|
+
desc or f"Running {func.__name__}",
|
|
412
|
+
attach or '',
|
|
413
|
+
)
|
|
414
|
+
except Exception as msg_error:
|
|
415
|
+
# Don't let message handler
|
|
416
|
+
# errors break the main function
|
|
417
|
+
logger.warning(f"Message handler error: {msg_error}")
|
|
418
|
+
|
|
419
|
+
# Execute the original function
|
|
420
|
+
# (kwargs have been modified to remove message params)
|
|
421
|
+
result = await func(*args, **kwargs)
|
|
422
|
+
|
|
423
|
+
return result
|
|
424
|
+
else:
|
|
425
|
+
|
|
426
|
+
@wraps(func)
|
|
427
|
+
def wrapper(*args, **kwargs):
|
|
428
|
+
# Extract parameters using the callback
|
|
429
|
+
# (this will modify kwargs by removing message params)
|
|
430
|
+
try:
|
|
431
|
+
params = self.extract_params_callback(kwargs)
|
|
432
|
+
except KeyError:
|
|
433
|
+
# If parameters are missing,
|
|
434
|
+
# just execute the original function
|
|
435
|
+
return func(*args, **kwargs)
|
|
436
|
+
|
|
437
|
+
# Check if we should send a message
|
|
438
|
+
should_send = False
|
|
439
|
+
if self.use_custom_handler:
|
|
440
|
+
should_send = any(
|
|
441
|
+
p is not None and p != '' for p in params
|
|
442
|
+
)
|
|
443
|
+
else:
|
|
444
|
+
should_send = bool(params[0]) or bool(params[1])
|
|
445
|
+
|
|
446
|
+
# Send message if needed
|
|
447
|
+
if should_send:
|
|
448
|
+
try:
|
|
449
|
+
if self.use_custom_handler:
|
|
450
|
+
self.message_handler(*params)
|
|
451
|
+
else:
|
|
452
|
+
# For built-in handler, provide defaults
|
|
453
|
+
title, desc, attach = params
|
|
454
|
+
self.message_handler(
|
|
455
|
+
title or "Executing Tool",
|
|
456
|
+
desc or f"Running {func.__name__}",
|
|
457
|
+
attach or '',
|
|
458
|
+
)
|
|
459
|
+
except Exception as msg_error:
|
|
460
|
+
logger.warning(f"Message handler error: {msg_error}")
|
|
461
|
+
|
|
462
|
+
result = func(*args, **kwargs)
|
|
463
|
+
|
|
464
|
+
return result
|
|
465
|
+
|
|
466
|
+
# Apply the new signature to the wrapper
|
|
467
|
+
wrapper.__signature__ = new_sig # type: ignore[attr-defined]
|
|
468
|
+
|
|
469
|
+
# Mark this function as enhanced by message integration
|
|
470
|
+
wrapper.__message_integration_enhanced__ = True # type: ignore[attr-defined]
|
|
471
|
+
|
|
472
|
+
# Create a hybrid approach:
|
|
473
|
+
# store toolkit instance info but preserve calling behavior
|
|
474
|
+
# We'll use a property-like
|
|
475
|
+
# approach to make __self__ available when needed
|
|
476
|
+
if hasattr(func, '__self__'):
|
|
477
|
+
toolkit_instance = func.__self__
|
|
478
|
+
|
|
479
|
+
# Store the toolkit instance as an attribute
|
|
480
|
+
# Use setattr to avoid MyPy type checking issues
|
|
481
|
+
wrapper.__toolkit_instance__ = toolkit_instance # type: ignore[attr-defined]
|
|
482
|
+
|
|
483
|
+
# Create a dynamic __self__ property
|
|
484
|
+
# that only appears during introspection
|
|
485
|
+
# but doesn't interfere with normal function calls
|
|
486
|
+
def get_self():
|
|
487
|
+
return toolkit_instance
|
|
488
|
+
|
|
489
|
+
# Only set __self__
|
|
490
|
+
# if we're being called in an introspection context
|
|
491
|
+
# (like from _clone_tools)
|
|
492
|
+
# Use setattr to avoid MyPy type checking issues
|
|
493
|
+
wrapper.__self__ = toolkit_instance # type: ignore[attr-defined]
|
|
494
|
+
|
|
495
|
+
# Enhance the docstring
|
|
496
|
+
if func.__doc__:
|
|
497
|
+
enhanced_doc = func.__doc__.rstrip()
|
|
498
|
+
lines = enhanced_doc.split('\n')
|
|
499
|
+
|
|
500
|
+
# Find where to insert parameters
|
|
501
|
+
insert_idx = self._find_docstring_insert_point(lines)
|
|
502
|
+
|
|
503
|
+
# Check if we need to create an Args section
|
|
504
|
+
has_args_section = any(
|
|
505
|
+
'Args:' in line
|
|
506
|
+
or 'Arguments:' in line
|
|
507
|
+
or 'Parameters:' in line
|
|
508
|
+
for line in lines
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
if not has_args_section and insert_idx is not None:
|
|
512
|
+
# Need to create Args section
|
|
513
|
+
base_indent = self._get_base_indent(lines)
|
|
514
|
+
|
|
515
|
+
# Add blank line before Args section if needed
|
|
516
|
+
if insert_idx > 0 and lines[insert_idx - 1].strip():
|
|
517
|
+
lines.insert(insert_idx, "")
|
|
518
|
+
insert_idx += 1
|
|
519
|
+
|
|
520
|
+
# Add Args section header
|
|
521
|
+
lines.insert(insert_idx, f"{base_indent}Args:")
|
|
522
|
+
insert_idx += 1
|
|
523
|
+
|
|
524
|
+
# Get parameter documentation
|
|
525
|
+
if self.use_custom_handler and self.message_handler.__doc__:
|
|
526
|
+
# Extract parameter docs from custom handler's docstring
|
|
527
|
+
param_docs = self._extract_param_docs_from_handler()
|
|
528
|
+
else:
|
|
529
|
+
# Use default parameter docs
|
|
530
|
+
param_docs = [
|
|
531
|
+
"message_title (str, optional): Title for status "
|
|
532
|
+
"message to user.",
|
|
533
|
+
"message_description (str, optional): Description for "
|
|
534
|
+
"status message.",
|
|
535
|
+
"message_attachment (str, optional): File path or URL to "
|
|
536
|
+
"attach to message.",
|
|
537
|
+
]
|
|
538
|
+
|
|
539
|
+
# Insert the parameter documentation
|
|
540
|
+
if insert_idx is not None and param_docs:
|
|
541
|
+
# Get proper indentation for parameters
|
|
542
|
+
indent = self._get_docstring_indent(lines, insert_idx)
|
|
543
|
+
|
|
544
|
+
# Insert each parameter doc with proper indentation
|
|
545
|
+
for doc in param_docs:
|
|
546
|
+
lines.insert(insert_idx, f"{indent}{doc}")
|
|
547
|
+
insert_idx += 1
|
|
548
|
+
|
|
549
|
+
wrapper.__doc__ = '\n'.join(lines)
|
|
550
|
+
|
|
551
|
+
return wrapper
|
|
552
|
+
|
|
553
|
+
def _find_docstring_insert_point(self, lines: List[str]) -> Optional[int]:
|
|
554
|
+
r"""Find where to insert parameters in a docstring."""
|
|
555
|
+
current_indent = ""
|
|
556
|
+
|
|
557
|
+
# First, look for existing Args section
|
|
558
|
+
for i, line in enumerate(lines):
|
|
559
|
+
if (
|
|
560
|
+
'Args:' in line
|
|
561
|
+
or 'Arguments:' in line
|
|
562
|
+
or 'Parameters:' in line
|
|
563
|
+
):
|
|
564
|
+
current_indent = line[: len(line) - len(line.lstrip())]
|
|
565
|
+
# Find where Args section ends by looking for the last
|
|
566
|
+
# parameter
|
|
567
|
+
last_param_idx = None
|
|
568
|
+
for j in range(i + 1, len(lines)):
|
|
569
|
+
stripped = lines[j].strip()
|
|
570
|
+
# If it's an empty line, skip it
|
|
571
|
+
if not stripped:
|
|
572
|
+
continue
|
|
573
|
+
# If it's a parameter line (has proper indentation and
|
|
574
|
+
# content)
|
|
575
|
+
if lines[j].startswith(current_indent + ' '):
|
|
576
|
+
last_param_idx = j
|
|
577
|
+
else:
|
|
578
|
+
# Hit a line with different indentation or a new
|
|
579
|
+
# section
|
|
580
|
+
if last_param_idx is not None:
|
|
581
|
+
return last_param_idx + 1
|
|
582
|
+
else:
|
|
583
|
+
# No parameters found, insert right after Args:
|
|
584
|
+
return i + 1
|
|
585
|
+
# Args is the last section, return after last parameter
|
|
586
|
+
if last_param_idx is not None:
|
|
587
|
+
return last_param_idx + 1
|
|
588
|
+
else:
|
|
589
|
+
# No parameters found, insert right after Args:
|
|
590
|
+
return i + 1
|
|
591
|
+
|
|
592
|
+
# No Args section, need to create one
|
|
593
|
+
# Try to insert before Returns/Yields/Raises/Examples sections
|
|
594
|
+
for i, line in enumerate(lines):
|
|
595
|
+
stripped = line.strip()
|
|
596
|
+
if any(
|
|
597
|
+
section in line
|
|
598
|
+
for section in [
|
|
599
|
+
'Returns:',
|
|
600
|
+
'Return:',
|
|
601
|
+
'Yields:',
|
|
602
|
+
'Raises:',
|
|
603
|
+
'Examples:',
|
|
604
|
+
'Example:',
|
|
605
|
+
'Note:',
|
|
606
|
+
'Notes:',
|
|
607
|
+
]
|
|
608
|
+
):
|
|
609
|
+
return i
|
|
610
|
+
|
|
611
|
+
# No special sections, add at the end
|
|
612
|
+
return len(lines)
|
|
613
|
+
|
|
614
|
+
def _get_docstring_indent(self, lines: List[str], insert_idx: int) -> str:
|
|
615
|
+
r"""Get the proper indentation for docstring parameters."""
|
|
616
|
+
# Look for Args: or similar section to match indentation
|
|
617
|
+
for i, line in enumerate(lines):
|
|
618
|
+
if (
|
|
619
|
+
'Args:' in line
|
|
620
|
+
or 'Arguments:' in line
|
|
621
|
+
or 'Parameters:' in line
|
|
622
|
+
):
|
|
623
|
+
base_indent = line[: len(line) - len(line.lstrip())]
|
|
624
|
+
# Look at the next line to see parameter indentation
|
|
625
|
+
if i + 1 < len(lines):
|
|
626
|
+
if lines[i + 1].strip():
|
|
627
|
+
next_indent = lines[i + 1][
|
|
628
|
+
: len(lines[i + 1]) - len(lines[i + 1].lstrip())
|
|
629
|
+
]
|
|
630
|
+
if len(next_indent) > len(base_indent):
|
|
631
|
+
return next_indent
|
|
632
|
+
return base_indent + ' '
|
|
633
|
+
|
|
634
|
+
# No Args section, use base indent + 4 spaces
|
|
635
|
+
base_indent = self._get_base_indent(lines)
|
|
636
|
+
return base_indent + ' '
|
|
637
|
+
|
|
638
|
+
def _get_base_indent(self, lines: List[str]) -> str:
|
|
639
|
+
r"""Get the base indentation level of the docstring."""
|
|
640
|
+
# Find first non-empty line to determine base indentation
|
|
641
|
+
for line in lines:
|
|
642
|
+
if line.strip() and not line.strip().startswith('"""'):
|
|
643
|
+
return line[: len(line) - len(line.lstrip())]
|
|
644
|
+
return ' ' # Default indentation
|
|
645
|
+
|
|
646
|
+
def _extract_param_docs_from_handler(self) -> List[str]:
|
|
647
|
+
r"""Extract parameter documentation from the custom handler's
|
|
648
|
+
docstring.
|
|
649
|
+
"""
|
|
650
|
+
if not self.message_handler.__doc__:
|
|
651
|
+
return []
|
|
652
|
+
|
|
653
|
+
docs = []
|
|
654
|
+
handler_sig = inspect.signature(self.message_handler)
|
|
655
|
+
|
|
656
|
+
# Parse the handler's docstring to find parameter descriptions
|
|
657
|
+
lines = self.message_handler.__doc__.split('\n')
|
|
658
|
+
in_args = False
|
|
659
|
+
param_docs = {}
|
|
660
|
+
|
|
661
|
+
for line in lines:
|
|
662
|
+
if (
|
|
663
|
+
'Args:' in line
|
|
664
|
+
or 'Arguments:' in line
|
|
665
|
+
or 'Parameters:' in line
|
|
666
|
+
):
|
|
667
|
+
in_args = True
|
|
668
|
+
continue
|
|
669
|
+
elif in_args and line.strip():
|
|
670
|
+
# Check if we've reached a new section
|
|
671
|
+
stripped_line = line.lstrip()
|
|
672
|
+
if any(
|
|
673
|
+
section in stripped_line
|
|
674
|
+
for section in [
|
|
675
|
+
'Returns:',
|
|
676
|
+
'Return:',
|
|
677
|
+
'Yields:',
|
|
678
|
+
'Raises:',
|
|
679
|
+
'Examples:',
|
|
680
|
+
'Example:',
|
|
681
|
+
'Note:',
|
|
682
|
+
'Notes:',
|
|
683
|
+
]
|
|
684
|
+
):
|
|
685
|
+
# End of Args section
|
|
686
|
+
break
|
|
687
|
+
elif in_args and ':' in line:
|
|
688
|
+
# Parse parameter documentation
|
|
689
|
+
parts = line.strip().split(':', 1)
|
|
690
|
+
if len(parts) == 2:
|
|
691
|
+
param_name = parts[0].strip()
|
|
692
|
+
param_desc = parts[1].strip()
|
|
693
|
+
# Extract just the parameter name (before any type
|
|
694
|
+
# annotation)
|
|
695
|
+
param_name = param_name.split()[0].strip()
|
|
696
|
+
param_docs[param_name] = param_desc
|
|
697
|
+
|
|
698
|
+
# Build documentation for each parameter
|
|
699
|
+
for param_name, param in handler_sig.parameters.items():
|
|
700
|
+
if param_name != 'self':
|
|
701
|
+
# Get type annotation
|
|
702
|
+
annotation = ''
|
|
703
|
+
if param.annotation != inspect.Parameter.empty:
|
|
704
|
+
if hasattr(param.annotation, '__name__'):
|
|
705
|
+
annotation = f" ({param.annotation.__name__})"
|
|
706
|
+
else:
|
|
707
|
+
annotation = f" ({param.annotation!s})"
|
|
708
|
+
|
|
709
|
+
# Check if optional
|
|
710
|
+
optional = (
|
|
711
|
+
', optional'
|
|
712
|
+
if param.default != inspect.Parameter.empty
|
|
713
|
+
else ''
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
# Get description from parsed docs
|
|
717
|
+
desc = param_docs.get(
|
|
718
|
+
param_name,
|
|
719
|
+
f"Parameter for {self.message_handler.__name__}",
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
docs.append(f"{param_name}{annotation}{optional}: {desc}")
|
|
723
|
+
|
|
724
|
+
return docs
|