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
|
@@ -0,0 +1,1135 @@
|
|
|
1
|
+
# ========= Copyright 2023-2026 @ 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-2026 @ CAMEL-AI.org. All Rights Reserved. =========
|
|
14
|
+
|
|
15
|
+
import base64
|
|
16
|
+
import hashlib
|
|
17
|
+
import hmac
|
|
18
|
+
import os
|
|
19
|
+
import re
|
|
20
|
+
import time
|
|
21
|
+
import urllib.parse
|
|
22
|
+
from typing import Any, Dict, List, Literal, Optional
|
|
23
|
+
|
|
24
|
+
import requests
|
|
25
|
+
|
|
26
|
+
from camel.logger import get_logger
|
|
27
|
+
from camel.toolkits import FunctionTool
|
|
28
|
+
from camel.toolkits.base import BaseToolkit
|
|
29
|
+
from camel.utils import MCPServer, api_keys_required, retry_on_error
|
|
30
|
+
|
|
31
|
+
logger = get_logger(__name__)
|
|
32
|
+
|
|
33
|
+
# Global variables for caching access token
|
|
34
|
+
_dingtalk_access_token = None
|
|
35
|
+
_dingtalk_access_token_expires_at = 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@retry_on_error()
|
|
39
|
+
def _get_dingtalk_access_token() -> str:
|
|
40
|
+
r"""Gets access token for Dingtalk API.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
str: Access token for API requests.
|
|
44
|
+
"""
|
|
45
|
+
global _dingtalk_access_token, _dingtalk_access_token_expires_at
|
|
46
|
+
|
|
47
|
+
if (
|
|
48
|
+
_dingtalk_access_token
|
|
49
|
+
and _dingtalk_access_token_expires_at > time.time()
|
|
50
|
+
):
|
|
51
|
+
return _dingtalk_access_token
|
|
52
|
+
|
|
53
|
+
app_key = os.environ.get("DINGTALK_APP_KEY", "")
|
|
54
|
+
app_secret = os.environ.get("DINGTALK_APP_SECRET", "")
|
|
55
|
+
|
|
56
|
+
url = "https://oapi.dingtalk.com/gettoken"
|
|
57
|
+
params = {"appkey": app_key, "appsecret": app_secret}
|
|
58
|
+
|
|
59
|
+
response = requests.get(url, params=params, timeout=30)
|
|
60
|
+
response.raise_for_status()
|
|
61
|
+
data = response.json()
|
|
62
|
+
|
|
63
|
+
if data.get("errcode") == 0 and "access_token" in data:
|
|
64
|
+
_dingtalk_access_token = data["access_token"]
|
|
65
|
+
_dingtalk_access_token_expires_at = (
|
|
66
|
+
time.time() + data.get("expires_in", 7200) - 60
|
|
67
|
+
)
|
|
68
|
+
logger.info("Dingtalk access token refreshed.")
|
|
69
|
+
return _dingtalk_access_token
|
|
70
|
+
else:
|
|
71
|
+
errcode = data.get("errcode")
|
|
72
|
+
errmsg = data.get("errmsg", "Unknown error")
|
|
73
|
+
raise ValueError(f"Failed to get access token {errcode}: {errmsg}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _make_dingtalk_request(
|
|
77
|
+
method: Literal["GET", "POST"], endpoint: str, **kwargs
|
|
78
|
+
) -> Dict[str, Any]:
|
|
79
|
+
r"""Makes authenticated request to Dingtalk API.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
method (Literal["GET", "POST"]): HTTP method to use.
|
|
83
|
+
endpoint (str): API endpoint path.
|
|
84
|
+
**kwargs: Additional arguments passed to requests.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Dict[str, Any]: API response data.
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
Exception: If API request fails or returns error.
|
|
91
|
+
"""
|
|
92
|
+
global _dingtalk_access_token, _dingtalk_access_token_expires_at
|
|
93
|
+
access_token = _get_dingtalk_access_token()
|
|
94
|
+
|
|
95
|
+
# Handle URL parameter concatenation
|
|
96
|
+
separator = "&" if "?" in endpoint else "?"
|
|
97
|
+
url = f"https://oapi.dingtalk.com{endpoint}{separator}access_token={access_token}"
|
|
98
|
+
|
|
99
|
+
if method.upper() == "GET":
|
|
100
|
+
response = requests.get(url, **kwargs)
|
|
101
|
+
else:
|
|
102
|
+
response = requests.post(url, **kwargs)
|
|
103
|
+
|
|
104
|
+
response.raise_for_status()
|
|
105
|
+
data = response.json()
|
|
106
|
+
|
|
107
|
+
if data.get("errcode") and data.get("errcode") != 0:
|
|
108
|
+
errcode = data.get("errcode")
|
|
109
|
+
errmsg = data.get("errmsg", "Unknown error")
|
|
110
|
+
raise ValueError(f"Dingtalk API error {errcode}: {errmsg}")
|
|
111
|
+
|
|
112
|
+
return data
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _generate_signature(secret: str, timestamp: str) -> str:
|
|
116
|
+
r"""Generates signature for Dingtalk webhook.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
secret (str): Webhook secret.
|
|
120
|
+
timestamp (str): Current timestamp.
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
str: Generated signature.
|
|
124
|
+
"""
|
|
125
|
+
string_to_sign = f"{timestamp}\n{secret}"
|
|
126
|
+
hmac_code = hmac.new(
|
|
127
|
+
secret.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha256
|
|
128
|
+
).digest()
|
|
129
|
+
return base64.b64encode(hmac_code).decode('utf-8')
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@MCPServer()
|
|
133
|
+
class DingtalkToolkit(BaseToolkit):
|
|
134
|
+
r"""A toolkit for Dingtalk operations.
|
|
135
|
+
|
|
136
|
+
This toolkit provides methods to interact with the Dingtalk API,
|
|
137
|
+
allowing users to send messages, manage users, departments, and handle
|
|
138
|
+
webhook operations.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
def __init__(self, timeout: Optional[float] = None):
|
|
142
|
+
r"""Initializes the DingtalkToolkit.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
timeout (Optional[float]): Timeout for API requests in seconds.
|
|
146
|
+
"""
|
|
147
|
+
super().__init__(timeout=timeout)
|
|
148
|
+
self.base_url = "https://oapi.dingtalk.com"
|
|
149
|
+
|
|
150
|
+
# Validate credentials
|
|
151
|
+
app_key = os.environ.get("DINGTALK_APP_KEY", "")
|
|
152
|
+
app_secret = os.environ.get("DINGTALK_APP_SECRET", "")
|
|
153
|
+
|
|
154
|
+
if not all([app_key, app_secret]):
|
|
155
|
+
raise ValueError(
|
|
156
|
+
"Dingtalk credentials missing. Set DINGTALK_APP_KEY and"
|
|
157
|
+
" DINGTALK_APP_SECRET."
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Initialize access token for enterprise reliability
|
|
161
|
+
self._initialize_token_safely()
|
|
162
|
+
|
|
163
|
+
def _initialize_token_safely(self):
|
|
164
|
+
r"""Safely initializes access token during toolkit setup.
|
|
165
|
+
|
|
166
|
+
This method attempts to get an access token during initialization
|
|
167
|
+
but doesn't raise exceptions if it fails, allowing the toolkit
|
|
168
|
+
to be instantiated even if credentials are temporarily invalid.
|
|
169
|
+
"""
|
|
170
|
+
try:
|
|
171
|
+
_get_dingtalk_access_token()
|
|
172
|
+
logger.info("Dingtalk toolkit initialized successfully.")
|
|
173
|
+
except Exception as e:
|
|
174
|
+
logger.warning(f"Failed to initialize access token: {e}")
|
|
175
|
+
logger.warning(
|
|
176
|
+
"Toolkit created but may fail on actual API calls. "
|
|
177
|
+
"Check your credentials."
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
@api_keys_required(
|
|
181
|
+
[
|
|
182
|
+
(None, "DINGTALK_APP_KEY"),
|
|
183
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
184
|
+
]
|
|
185
|
+
)
|
|
186
|
+
def dingtalk_send_text_message(
|
|
187
|
+
self,
|
|
188
|
+
userid: str,
|
|
189
|
+
content: str,
|
|
190
|
+
) -> str:
|
|
191
|
+
r"""Sends a text message to a Dingtalk user.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
userid (str): The user's userid.
|
|
195
|
+
content (str): Message content.
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
str: Success or error message.
|
|
199
|
+
|
|
200
|
+
References:
|
|
201
|
+
https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
|
|
202
|
+
"""
|
|
203
|
+
payload = {
|
|
204
|
+
"msg": {"msgtype": "text", "text": {"content": content}},
|
|
205
|
+
"userid": userid,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
_make_dingtalk_request(
|
|
210
|
+
"POST",
|
|
211
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
212
|
+
headers={"Content-Type": "application/json"},
|
|
213
|
+
json=payload,
|
|
214
|
+
)
|
|
215
|
+
return f"Message sent successfully to user {userid}."
|
|
216
|
+
except Exception as e:
|
|
217
|
+
return f"Failed to send message: {e!s}"
|
|
218
|
+
|
|
219
|
+
@api_keys_required(
|
|
220
|
+
[
|
|
221
|
+
(None, "DINGTALK_APP_KEY"),
|
|
222
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
223
|
+
]
|
|
224
|
+
)
|
|
225
|
+
def dingtalk_send_markdown_message(
|
|
226
|
+
self,
|
|
227
|
+
userid: str,
|
|
228
|
+
title: str,
|
|
229
|
+
markdown_content: str,
|
|
230
|
+
) -> str:
|
|
231
|
+
r"""Sends a markdown message to a Dingtalk user.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
userid (str): The user's userid.
|
|
235
|
+
title (str): Message title.
|
|
236
|
+
markdown_content (str): Markdown formatted content.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
str: Success or error message.
|
|
240
|
+
|
|
241
|
+
References:
|
|
242
|
+
https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
|
|
243
|
+
"""
|
|
244
|
+
payload = {
|
|
245
|
+
"msg": {
|
|
246
|
+
"msgtype": "markdown",
|
|
247
|
+
"markdown": {"title": title, "text": markdown_content},
|
|
248
|
+
},
|
|
249
|
+
"userid": userid,
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
_make_dingtalk_request(
|
|
254
|
+
"POST",
|
|
255
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
256
|
+
headers={"Content-Type": "application/json"},
|
|
257
|
+
json=payload,
|
|
258
|
+
)
|
|
259
|
+
return f"Markdown message sent successfully to user {userid}."
|
|
260
|
+
except Exception as e:
|
|
261
|
+
return f"Failed to send markdown message: {e!s}"
|
|
262
|
+
|
|
263
|
+
@api_keys_required(
|
|
264
|
+
[
|
|
265
|
+
(None, "DINGTALK_APP_KEY"),
|
|
266
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
267
|
+
]
|
|
268
|
+
)
|
|
269
|
+
def dingtalk_get_user_info(
|
|
270
|
+
self,
|
|
271
|
+
userid: str,
|
|
272
|
+
) -> Dict[str, Any]:
|
|
273
|
+
r"""Retrieves Dingtalk user information.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
userid (str): The user's userid.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Dict[str, Any]: User information or error information.
|
|
280
|
+
|
|
281
|
+
References:
|
|
282
|
+
https://open.dingtalk.com/document/orgapp-server/query-user-details
|
|
283
|
+
"""
|
|
284
|
+
payload = {"userid": userid}
|
|
285
|
+
|
|
286
|
+
try:
|
|
287
|
+
data = _make_dingtalk_request(
|
|
288
|
+
"POST",
|
|
289
|
+
"/topapi/v2/user/get",
|
|
290
|
+
headers={"Content-Type": "application/json"},
|
|
291
|
+
json=payload,
|
|
292
|
+
)
|
|
293
|
+
return data
|
|
294
|
+
except Exception as e:
|
|
295
|
+
return {"error": f"Failed to get user info: {e!s}"}
|
|
296
|
+
|
|
297
|
+
@api_keys_required(
|
|
298
|
+
[
|
|
299
|
+
(None, "DINGTALK_APP_KEY"),
|
|
300
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
301
|
+
]
|
|
302
|
+
)
|
|
303
|
+
def dingtalk_get_department_list(
|
|
304
|
+
self,
|
|
305
|
+
dept_id: Optional[int] = None,
|
|
306
|
+
) -> Dict[str, Any]:
|
|
307
|
+
r"""Retrieves list of departments.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
dept_id (Optional[int]): Department ID. If None, gets root
|
|
311
|
+
departments.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
Dict[str, Any]: Department list or error information.
|
|
315
|
+
|
|
316
|
+
References:
|
|
317
|
+
https://open.dingtalk.com/document/orgapp-server/obtain-the-department-list-v2
|
|
318
|
+
"""
|
|
319
|
+
payload = {}
|
|
320
|
+
if dept_id is not None:
|
|
321
|
+
payload["dept_id"] = dept_id
|
|
322
|
+
|
|
323
|
+
try:
|
|
324
|
+
data = _make_dingtalk_request(
|
|
325
|
+
"POST",
|
|
326
|
+
"/topapi/v2/department/listsub",
|
|
327
|
+
headers={"Content-Type": "application/json"},
|
|
328
|
+
json=payload,
|
|
329
|
+
)
|
|
330
|
+
return data
|
|
331
|
+
except Exception as e:
|
|
332
|
+
return {"error": f"Failed to get department list: {e!s}"}
|
|
333
|
+
|
|
334
|
+
@api_keys_required(
|
|
335
|
+
[
|
|
336
|
+
(None, "DINGTALK_APP_KEY"),
|
|
337
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
338
|
+
]
|
|
339
|
+
)
|
|
340
|
+
def dingtalk_get_department_users(
|
|
341
|
+
self,
|
|
342
|
+
dept_id: int,
|
|
343
|
+
offset: int = 0,
|
|
344
|
+
size: int = 100,
|
|
345
|
+
) -> Dict[str, Any]:
|
|
346
|
+
r"""Retrieves users in a department.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
dept_id (int): Department ID.
|
|
350
|
+
offset (int): Offset for pagination (default: 0).
|
|
351
|
+
size (int): Number of users to retrieve (default: 100, max: 100).
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Dict[str, Any]: Users list or error information.
|
|
355
|
+
|
|
356
|
+
References:
|
|
357
|
+
https://open.dingtalk.com/document/orgapp-server/queries-the-complete-information-of-a-department-user
|
|
358
|
+
"""
|
|
359
|
+
payload = {"dept_id": dept_id, "offset": offset, "size": size}
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
data = _make_dingtalk_request(
|
|
363
|
+
"POST",
|
|
364
|
+
"/topapi/v2/user/list",
|
|
365
|
+
headers={"Content-Type": "application/json"},
|
|
366
|
+
json=payload,
|
|
367
|
+
)
|
|
368
|
+
return data
|
|
369
|
+
except Exception as e:
|
|
370
|
+
return {"error": f"Failed to get department users: {e!s}"}
|
|
371
|
+
|
|
372
|
+
@api_keys_required(
|
|
373
|
+
[
|
|
374
|
+
(None, "DINGTALK_APP_KEY"),
|
|
375
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
376
|
+
]
|
|
377
|
+
)
|
|
378
|
+
def dingtalk_search_users_by_name(
|
|
379
|
+
self,
|
|
380
|
+
name: str,
|
|
381
|
+
) -> Dict[str, Any]:
|
|
382
|
+
r"""Searches for users by name.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
name (str): User name to search for.
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
Dict[str, Any]: Search results or error information.
|
|
389
|
+
|
|
390
|
+
References:
|
|
391
|
+
https://open.dingtalk.com/document/orgapp-server/query-users
|
|
392
|
+
"""
|
|
393
|
+
payload = {"name": name}
|
|
394
|
+
|
|
395
|
+
try:
|
|
396
|
+
data = _make_dingtalk_request(
|
|
397
|
+
"POST",
|
|
398
|
+
"/topapi/user/search",
|
|
399
|
+
headers={"Content-Type": "application/json"},
|
|
400
|
+
json=payload,
|
|
401
|
+
)
|
|
402
|
+
return data
|
|
403
|
+
except Exception as e:
|
|
404
|
+
return {"error": f"Failed to search users: {e!s}"}
|
|
405
|
+
|
|
406
|
+
# Include more webhook methods if useful. Double check link implementation
|
|
407
|
+
# is done correctly here.
|
|
408
|
+
def dingtalk_send_webhook_message(
|
|
409
|
+
self,
|
|
410
|
+
content: str,
|
|
411
|
+
msgtype: Literal["text", "markdown", "link", "actionCard"] = "text",
|
|
412
|
+
title: Optional[str] = None,
|
|
413
|
+
webhook_url: Optional[str] = None,
|
|
414
|
+
webhook_secret: Optional[str] = None,
|
|
415
|
+
) -> str:
|
|
416
|
+
r"""Sends a message via Dingtalk webhook.
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
content (str): Message content.
|
|
420
|
+
msgtype (Literal): Message type (text, markdown, link, actionCard).
|
|
421
|
+
title (Optional[str]): Message title (required for markdown).
|
|
422
|
+
webhook_url (Optional[str]): Webhook URL. If None, uses env var.
|
|
423
|
+
webhook_secret (Optional[str]): Webhook secret. If None, uses env
|
|
424
|
+
var.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
str: Success or error message.
|
|
428
|
+
|
|
429
|
+
References:
|
|
430
|
+
https://open.dingtalk.com/document/robots/custom-robot-access
|
|
431
|
+
"""
|
|
432
|
+
# Get webhook configuration
|
|
433
|
+
url = webhook_url or os.environ.get("DINGTALK_WEBHOOK_URL", "")
|
|
434
|
+
secret = webhook_secret or os.environ.get(
|
|
435
|
+
"DINGTALK_WEBHOOK_SECRET", ""
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
if not url:
|
|
439
|
+
return "Error: Webhook URL not provided or set in environment"
|
|
440
|
+
|
|
441
|
+
# Prepare message payload
|
|
442
|
+
payload: Dict[str, Any] = {"msgtype": msgtype}
|
|
443
|
+
|
|
444
|
+
if msgtype == "text":
|
|
445
|
+
payload["text"] = {"content": content}
|
|
446
|
+
elif msgtype == "markdown":
|
|
447
|
+
if not title:
|
|
448
|
+
return "Error: Title is required for markdown messages"
|
|
449
|
+
payload["markdown"] = {"title": title, "text": content}
|
|
450
|
+
elif msgtype == "link":
|
|
451
|
+
# For link messages, content should be structured differently
|
|
452
|
+
# This is a simplified implementation
|
|
453
|
+
payload["link"] = {
|
|
454
|
+
"text": content,
|
|
455
|
+
"title": title or "Link Message",
|
|
456
|
+
"messageUrl": "https://www.dingtalk.com/",
|
|
457
|
+
}
|
|
458
|
+
elif msgtype == "actionCard":
|
|
459
|
+
payload["actionCard"] = {
|
|
460
|
+
"title": title or "Action Card",
|
|
461
|
+
"text": content,
|
|
462
|
+
"singleTitle": "Read More",
|
|
463
|
+
"singleURL": "https://www.dingtalk.com/",
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
# Add signature if secret is provided
|
|
467
|
+
if secret:
|
|
468
|
+
timestamp = str(round(time.time() * 1000))
|
|
469
|
+
sign = _generate_signature(secret, timestamp)
|
|
470
|
+
url += f"×tamp={timestamp}&sign={urllib.parse.quote(sign)}"
|
|
471
|
+
|
|
472
|
+
try:
|
|
473
|
+
response = requests.post(
|
|
474
|
+
url,
|
|
475
|
+
json=payload,
|
|
476
|
+
headers={"Content-Type": "application/json"},
|
|
477
|
+
timeout=self.timeout,
|
|
478
|
+
)
|
|
479
|
+
response.raise_for_status()
|
|
480
|
+
|
|
481
|
+
result = response.json()
|
|
482
|
+
if result.get("errcode") == 0:
|
|
483
|
+
return "Webhook message sent successfully"
|
|
484
|
+
else:
|
|
485
|
+
return (
|
|
486
|
+
f"Webhook error: {result.get('errmsg', 'Unknown error')}"
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
except Exception as e:
|
|
490
|
+
return f"Failed to send webhook message: {e!s}"
|
|
491
|
+
|
|
492
|
+
@api_keys_required(
|
|
493
|
+
[
|
|
494
|
+
(None, "DINGTALK_APP_KEY"),
|
|
495
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
496
|
+
]
|
|
497
|
+
)
|
|
498
|
+
def dingtalk_create_group(
|
|
499
|
+
self,
|
|
500
|
+
name: str,
|
|
501
|
+
owner: str,
|
|
502
|
+
useridlist: List[str],
|
|
503
|
+
) -> Dict[str, Any]:
|
|
504
|
+
r"""Creates a Dingtalk group.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
name (str): Group name.
|
|
508
|
+
owner (str): Group owner's userid.
|
|
509
|
+
useridlist (List[str]): List of user IDs to add to the group.
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
Dict[str, Any]: Group creation result with chatid or error.
|
|
513
|
+
|
|
514
|
+
References:
|
|
515
|
+
https://open.dingtalk.com/document/orgapp-server/create-group-session
|
|
516
|
+
"""
|
|
517
|
+
payload = {
|
|
518
|
+
"name": name,
|
|
519
|
+
"owner": owner,
|
|
520
|
+
"useridlist": useridlist,
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
try:
|
|
524
|
+
data = _make_dingtalk_request(
|
|
525
|
+
"POST",
|
|
526
|
+
"/topapi/im/chat/create",
|
|
527
|
+
headers={"Content-Type": "application/json"},
|
|
528
|
+
json=payload,
|
|
529
|
+
)
|
|
530
|
+
return data
|
|
531
|
+
except Exception as e:
|
|
532
|
+
return {"error": f"Failed to create group: {e!s}"}
|
|
533
|
+
|
|
534
|
+
@api_keys_required(
|
|
535
|
+
[
|
|
536
|
+
(None, "DINGTALK_APP_KEY"),
|
|
537
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
538
|
+
]
|
|
539
|
+
)
|
|
540
|
+
def dingtalk_send_group_message(
|
|
541
|
+
self,
|
|
542
|
+
chatid: str,
|
|
543
|
+
content: str,
|
|
544
|
+
msgtype: Literal["text", "markdown"] = "text",
|
|
545
|
+
) -> str:
|
|
546
|
+
r"""Sends a message to a Dingtalk group.
|
|
547
|
+
|
|
548
|
+
Args:
|
|
549
|
+
chatid (str): Group chat ID.
|
|
550
|
+
content (str): Message content.
|
|
551
|
+
msgtype (Literal["text", "markdown"]): Message type.
|
|
552
|
+
|
|
553
|
+
Returns:
|
|
554
|
+
str: Success or error message.
|
|
555
|
+
|
|
556
|
+
References:
|
|
557
|
+
https://open.dingtalk.com/document/orgapp-server/send-group-messages
|
|
558
|
+
"""
|
|
559
|
+
msg_data: Dict[str, Any] = {"msgtype": msgtype}
|
|
560
|
+
|
|
561
|
+
if msgtype == "text":
|
|
562
|
+
msg_data["text"] = {"content": content}
|
|
563
|
+
elif msgtype == "markdown":
|
|
564
|
+
msg_data["markdown"] = {"text": content}
|
|
565
|
+
|
|
566
|
+
payload = {
|
|
567
|
+
"msg": msg_data,
|
|
568
|
+
"chatid": chatid,
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
try:
|
|
572
|
+
_make_dingtalk_request(
|
|
573
|
+
"POST",
|
|
574
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
575
|
+
headers={"Content-Type": "application/json"},
|
|
576
|
+
json=payload,
|
|
577
|
+
)
|
|
578
|
+
return f"Message sent successfully to group {chatid}."
|
|
579
|
+
except Exception as e:
|
|
580
|
+
return f"Failed to send group message: {e!s}"
|
|
581
|
+
|
|
582
|
+
@api_keys_required(
|
|
583
|
+
[
|
|
584
|
+
(None, "DINGTALK_APP_KEY"),
|
|
585
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
586
|
+
]
|
|
587
|
+
)
|
|
588
|
+
def dingtalk_send_link_message(
|
|
589
|
+
self,
|
|
590
|
+
userid: str,
|
|
591
|
+
title: str,
|
|
592
|
+
text: str,
|
|
593
|
+
message_url: str,
|
|
594
|
+
pic_url: Optional[str] = None,
|
|
595
|
+
) -> str:
|
|
596
|
+
r"""Sends a link message to a Dingtalk user.
|
|
597
|
+
|
|
598
|
+
Args:
|
|
599
|
+
userid (str): The user's userid.
|
|
600
|
+
title (str): Link title.
|
|
601
|
+
text (str): Link description text.
|
|
602
|
+
message_url (str): URL to link to.
|
|
603
|
+
pic_url (Optional[str]): Picture URL for the link.
|
|
604
|
+
|
|
605
|
+
Returns:
|
|
606
|
+
str: Success or error message.
|
|
607
|
+
|
|
608
|
+
References:
|
|
609
|
+
https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
|
|
610
|
+
"""
|
|
611
|
+
link_data: Dict[str, Any] = {
|
|
612
|
+
"title": title,
|
|
613
|
+
"text": text,
|
|
614
|
+
"messageUrl": message_url,
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if pic_url:
|
|
618
|
+
link_data["picUrl"] = pic_url
|
|
619
|
+
|
|
620
|
+
payload = {
|
|
621
|
+
"msg": {"msgtype": "link", "link": link_data},
|
|
622
|
+
"userid": userid,
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
try:
|
|
626
|
+
_make_dingtalk_request(
|
|
627
|
+
"POST",
|
|
628
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
629
|
+
headers={"Content-Type": "application/json"},
|
|
630
|
+
json=payload,
|
|
631
|
+
)
|
|
632
|
+
return f"Link message sent successfully to user {userid}."
|
|
633
|
+
except Exception as e:
|
|
634
|
+
return f"Failed to send link message: {e!s}"
|
|
635
|
+
|
|
636
|
+
@api_keys_required(
|
|
637
|
+
[
|
|
638
|
+
(None, "DINGTALK_APP_KEY"),
|
|
639
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
640
|
+
]
|
|
641
|
+
)
|
|
642
|
+
def dingtalk_send_action_card_message(
|
|
643
|
+
self,
|
|
644
|
+
userid: str,
|
|
645
|
+
title: str,
|
|
646
|
+
text: str,
|
|
647
|
+
single_title: str,
|
|
648
|
+
single_url: str,
|
|
649
|
+
) -> str:
|
|
650
|
+
r"""Sends an action card message to a Dingtalk user.
|
|
651
|
+
|
|
652
|
+
Args:
|
|
653
|
+
userid (str): The user's userid.
|
|
654
|
+
title (str): Card title.
|
|
655
|
+
text (str): Card content text.
|
|
656
|
+
single_title (str): Action button title.
|
|
657
|
+
single_url (str): Action button URL.
|
|
658
|
+
|
|
659
|
+
Returns:
|
|
660
|
+
str: Success or error message.
|
|
661
|
+
|
|
662
|
+
References:
|
|
663
|
+
https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
|
|
664
|
+
"""
|
|
665
|
+
payload = {
|
|
666
|
+
"msg": {
|
|
667
|
+
"msgtype": "actionCard",
|
|
668
|
+
"actionCard": {
|
|
669
|
+
"title": title,
|
|
670
|
+
"text": text,
|
|
671
|
+
"singleTitle": single_title,
|
|
672
|
+
"singleURL": single_url,
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
"userid": userid,
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
try:
|
|
679
|
+
_make_dingtalk_request(
|
|
680
|
+
"POST",
|
|
681
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
682
|
+
headers={"Content-Type": "application/json"},
|
|
683
|
+
json=payload,
|
|
684
|
+
)
|
|
685
|
+
return f"Action card message sent successfully to user {userid}."
|
|
686
|
+
except Exception as e:
|
|
687
|
+
return f"Failed to send action card message: {e!s}"
|
|
688
|
+
|
|
689
|
+
@api_keys_required(
|
|
690
|
+
[
|
|
691
|
+
(None, "DINGTALK_APP_KEY"),
|
|
692
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
693
|
+
]
|
|
694
|
+
)
|
|
695
|
+
def dingtalk_get_user_by_mobile(self, mobile: str) -> Dict[str, Any]:
|
|
696
|
+
r"""Gets user information by mobile number.
|
|
697
|
+
|
|
698
|
+
Args:
|
|
699
|
+
mobile (str): User's mobile number. Should be a valid Chinese
|
|
700
|
+
mobile number format (11 digits starting with 1).
|
|
701
|
+
|
|
702
|
+
Returns:
|
|
703
|
+
Dict[str, Any]: User information or error information.
|
|
704
|
+
"""
|
|
705
|
+
# Validate mobile number format (Chinese mobile number: 11 digits
|
|
706
|
+
# starting with 1)
|
|
707
|
+
mobile_pattern = r'^1[3-9]\d{9}$'
|
|
708
|
+
if not re.match(mobile_pattern, mobile):
|
|
709
|
+
return {
|
|
710
|
+
"error": "Invalid mobile number format. Expected 11 digits "
|
|
711
|
+
"starting with 1 (e.g., 13800000000)."
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
payload = {"mobile": mobile}
|
|
715
|
+
|
|
716
|
+
try:
|
|
717
|
+
data = _make_dingtalk_request(
|
|
718
|
+
"POST",
|
|
719
|
+
"/topapi/v2/user/getbymobile",
|
|
720
|
+
headers={"Content-Type": "application/json"},
|
|
721
|
+
json=payload,
|
|
722
|
+
)
|
|
723
|
+
return data
|
|
724
|
+
except Exception as e:
|
|
725
|
+
return {"error": f"Failed to get user by mobile: {e!s}"}
|
|
726
|
+
|
|
727
|
+
@api_keys_required(
|
|
728
|
+
[
|
|
729
|
+
(None, "DINGTALK_APP_KEY"),
|
|
730
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
731
|
+
]
|
|
732
|
+
)
|
|
733
|
+
def dingtalk_get_user_by_unionid(self, unionid: str) -> Dict[str, Any]:
|
|
734
|
+
r"""Gets user information by unionid.
|
|
735
|
+
|
|
736
|
+
Args:
|
|
737
|
+
unionid (str): User's unique identifier across all DingTalk
|
|
738
|
+
organizations. This is a global identifier that remains
|
|
739
|
+
consistent even if the user belongs to multiple DingTalk
|
|
740
|
+
organizations, unlike userid which is organization-specific.
|
|
741
|
+
|
|
742
|
+
Returns:
|
|
743
|
+
Dict[str, Any]: User information or error information.
|
|
744
|
+
|
|
745
|
+
References:
|
|
746
|
+
https://open.dingtalk.com/document/orgapp-server/query-a-user-by-the-union-id
|
|
747
|
+
"""
|
|
748
|
+
try:
|
|
749
|
+
data = _make_dingtalk_request(
|
|
750
|
+
"POST",
|
|
751
|
+
"/topapi/user/getbyunionid",
|
|
752
|
+
headers={"Content-Type": "application/json"},
|
|
753
|
+
json={"unionid": unionid},
|
|
754
|
+
)
|
|
755
|
+
return data
|
|
756
|
+
except Exception as e:
|
|
757
|
+
return {"error": f"Failed to get user by unionid: {e!s}"}
|
|
758
|
+
|
|
759
|
+
@api_keys_required(
|
|
760
|
+
[
|
|
761
|
+
(None, "DINGTALK_APP_KEY"),
|
|
762
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
763
|
+
]
|
|
764
|
+
)
|
|
765
|
+
def dingtalk_get_department_detail(self, dept_id: int) -> Dict[str, Any]:
|
|
766
|
+
r"""Gets detailed information about a department.
|
|
767
|
+
|
|
768
|
+
Args:
|
|
769
|
+
dept_id (int): Department ID.
|
|
770
|
+
|
|
771
|
+
Returns:
|
|
772
|
+
Dict[str, Any]: Department details or error information.
|
|
773
|
+
|
|
774
|
+
References:
|
|
775
|
+
https://open.dingtalk.com/document/orgapp-server/query-department-details0-v2
|
|
776
|
+
"""
|
|
777
|
+
payload = {"dept_id": dept_id}
|
|
778
|
+
|
|
779
|
+
try:
|
|
780
|
+
data = _make_dingtalk_request(
|
|
781
|
+
"POST",
|
|
782
|
+
"/topapi/v2/department/get",
|
|
783
|
+
headers={"Content-Type": "application/json"},
|
|
784
|
+
json=payload,
|
|
785
|
+
)
|
|
786
|
+
return data
|
|
787
|
+
except Exception as e:
|
|
788
|
+
return {"error": f"Failed to get department detail: {e!s}"}
|
|
789
|
+
|
|
790
|
+
@api_keys_required(
|
|
791
|
+
[
|
|
792
|
+
(None, "DINGTALK_APP_KEY"),
|
|
793
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
794
|
+
]
|
|
795
|
+
)
|
|
796
|
+
def dingtalk_send_oa_message(
|
|
797
|
+
self,
|
|
798
|
+
userid: str,
|
|
799
|
+
message_url: str,
|
|
800
|
+
head_bgcolor: str,
|
|
801
|
+
head_text: str,
|
|
802
|
+
body_title: str,
|
|
803
|
+
body_content: str,
|
|
804
|
+
) -> str:
|
|
805
|
+
r"""Sends an OA (Office Automation) message to a Dingtalk user.
|
|
806
|
+
|
|
807
|
+
Args:
|
|
808
|
+
userid (str): The user's userid.
|
|
809
|
+
message_url (str): URL for the message action.
|
|
810
|
+
head_bgcolor (str): Header background color (hex format).
|
|
811
|
+
head_text (str): Header text.
|
|
812
|
+
body_title (str): Body title.
|
|
813
|
+
body_content (str): Body content.
|
|
814
|
+
|
|
815
|
+
Returns:
|
|
816
|
+
str: Success or error message.
|
|
817
|
+
|
|
818
|
+
References:
|
|
819
|
+
https://open.dingtalk.com/document/orgapp-server/send-single-chat-message
|
|
820
|
+
"""
|
|
821
|
+
oa_data: Dict[str, Any] = {
|
|
822
|
+
"message_url": message_url,
|
|
823
|
+
"head": {
|
|
824
|
+
"bgcolor": head_bgcolor,
|
|
825
|
+
"text": head_text,
|
|
826
|
+
},
|
|
827
|
+
"body": {
|
|
828
|
+
"title": body_title,
|
|
829
|
+
"form": [{"key": "Content:", "value": body_content}],
|
|
830
|
+
},
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
payload = {
|
|
834
|
+
"msg": {"msgtype": "oa", "oa": oa_data},
|
|
835
|
+
"userid": userid,
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
try:
|
|
839
|
+
_make_dingtalk_request(
|
|
840
|
+
"POST",
|
|
841
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
842
|
+
headers={"Content-Type": "application/json"},
|
|
843
|
+
json=payload,
|
|
844
|
+
)
|
|
845
|
+
return f"OA message sent successfully to user {userid}."
|
|
846
|
+
except Exception as e:
|
|
847
|
+
return f"Failed to send OA message: {e!s}"
|
|
848
|
+
|
|
849
|
+
@api_keys_required(
|
|
850
|
+
[
|
|
851
|
+
(None, "DINGTALK_APP_KEY"),
|
|
852
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
853
|
+
]
|
|
854
|
+
)
|
|
855
|
+
def dingtalk_get_group_info(self, chatid: str) -> Dict[str, Any]:
|
|
856
|
+
r"""Gets information about a group chat.
|
|
857
|
+
|
|
858
|
+
Args:
|
|
859
|
+
chatid (str): Group chat ID.
|
|
860
|
+
|
|
861
|
+
Returns:
|
|
862
|
+
Dict[str, Any]: Group information or error information.
|
|
863
|
+
|
|
864
|
+
References:
|
|
865
|
+
https://open.dingtalk.com/document/orgapp-server/query-group-session-information
|
|
866
|
+
"""
|
|
867
|
+
payload = {"chatid": chatid}
|
|
868
|
+
|
|
869
|
+
try:
|
|
870
|
+
data = _make_dingtalk_request(
|
|
871
|
+
"POST",
|
|
872
|
+
"/topapi/im/chat/get",
|
|
873
|
+
headers={"Content-Type": "application/json"},
|
|
874
|
+
json=payload,
|
|
875
|
+
)
|
|
876
|
+
return data
|
|
877
|
+
except Exception as e:
|
|
878
|
+
return {"error": f"Failed to get group info: {e!s}"}
|
|
879
|
+
|
|
880
|
+
@api_keys_required(
|
|
881
|
+
[
|
|
882
|
+
(None, "DINGTALK_APP_KEY"),
|
|
883
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
884
|
+
]
|
|
885
|
+
)
|
|
886
|
+
def dingtalk_update_group(
|
|
887
|
+
self,
|
|
888
|
+
chatid: str,
|
|
889
|
+
name: Optional[str] = None,
|
|
890
|
+
owner: Optional[str] = None,
|
|
891
|
+
add_useridlist: Optional[List[str]] = None,
|
|
892
|
+
del_useridlist: Optional[List[str]] = None,
|
|
893
|
+
) -> Dict[str, Any]:
|
|
894
|
+
r"""Updates a Dingtalk group configuration.
|
|
895
|
+
|
|
896
|
+
Args:
|
|
897
|
+
chatid (str): Group chat ID.
|
|
898
|
+
name (Optional[str]): New group name.
|
|
899
|
+
owner (Optional[str]): New group owner userid.
|
|
900
|
+
add_useridlist (Optional[List[str]]): List of user IDs to add.
|
|
901
|
+
Note: Internally converted to comma-separated string as
|
|
902
|
+
required by the DingTalk API.
|
|
903
|
+
del_useridlist (Optional[List[str]]): List of user IDs to remove.
|
|
904
|
+
Note: Internally converted to comma-separated string as
|
|
905
|
+
required by the DingTalk API.
|
|
906
|
+
|
|
907
|
+
Returns:
|
|
908
|
+
Dict[str, Any]: Update result or error information.
|
|
909
|
+
|
|
910
|
+
References:
|
|
911
|
+
https://open.dingtalk.com/document/orgapp-server/modify-group-session
|
|
912
|
+
"""
|
|
913
|
+
payload: Dict[str, Any] = {"chatid": chatid}
|
|
914
|
+
|
|
915
|
+
if name:
|
|
916
|
+
payload["name"] = name
|
|
917
|
+
if owner:
|
|
918
|
+
payload["owner"] = owner
|
|
919
|
+
# Note: DingTalk update group API requires comma-separated string
|
|
920
|
+
# format
|
|
921
|
+
# This is different from send_work_notification which uses array format
|
|
922
|
+
if add_useridlist:
|
|
923
|
+
payload["add_useridlist"] = ",".join(add_useridlist)
|
|
924
|
+
if del_useridlist:
|
|
925
|
+
payload["del_useridlist"] = ",".join(del_useridlist)
|
|
926
|
+
|
|
927
|
+
try:
|
|
928
|
+
data = _make_dingtalk_request(
|
|
929
|
+
"POST",
|
|
930
|
+
"/topapi/im/chat/update",
|
|
931
|
+
headers={"Content-Type": "application/json"},
|
|
932
|
+
json=payload,
|
|
933
|
+
)
|
|
934
|
+
return data
|
|
935
|
+
except Exception as e:
|
|
936
|
+
return {"error": f"Failed to update group: {e!s}"}
|
|
937
|
+
|
|
938
|
+
@api_keys_required(
|
|
939
|
+
[
|
|
940
|
+
(None, "DINGTALK_APP_KEY"),
|
|
941
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
942
|
+
]
|
|
943
|
+
)
|
|
944
|
+
def dingtalk_send_work_notification(
|
|
945
|
+
self,
|
|
946
|
+
userid_list: List[str],
|
|
947
|
+
msg_content: str,
|
|
948
|
+
msg_type: Literal["text", "markdown"] = "text",
|
|
949
|
+
) -> str:
|
|
950
|
+
r"""Sends work notification to multiple users.
|
|
951
|
+
|
|
952
|
+
Args:
|
|
953
|
+
userid_list (List[str]): List of user IDs to send to.
|
|
954
|
+
Note: This API accepts array format, unlike some other APIs
|
|
955
|
+
that require comma-separated strings.
|
|
956
|
+
msg_content (str): Message content.
|
|
957
|
+
msg_type (Literal["text", "markdown"]): Message type.
|
|
958
|
+
|
|
959
|
+
Returns:
|
|
960
|
+
str: Success or error message.
|
|
961
|
+
|
|
962
|
+
References:
|
|
963
|
+
https://open.dingtalk.com/document/orgapp-server/asynchronous-sending-of-enterprise-session-messages
|
|
964
|
+
"""
|
|
965
|
+
if not userid_list:
|
|
966
|
+
return "Error: userid_list cannot be empty"
|
|
967
|
+
|
|
968
|
+
if len(userid_list) > 100:
|
|
969
|
+
return "Error: Cannot send to more than 100 users at once"
|
|
970
|
+
|
|
971
|
+
msg_data: Dict[str, Any] = {"msgtype": msg_type}
|
|
972
|
+
if msg_type == "text":
|
|
973
|
+
msg_data["text"] = {"content": msg_content}
|
|
974
|
+
elif msg_type == "markdown":
|
|
975
|
+
msg_data["markdown"] = {"text": msg_content}
|
|
976
|
+
|
|
977
|
+
payload = {
|
|
978
|
+
"msg": msg_data,
|
|
979
|
+
"userid_list": userid_list, # Array format for this API
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
try:
|
|
983
|
+
_make_dingtalk_request(
|
|
984
|
+
"POST",
|
|
985
|
+
"/topapi/message/corpconversation/asyncsend_v2",
|
|
986
|
+
headers={"Content-Type": "application/json"},
|
|
987
|
+
json=payload,
|
|
988
|
+
)
|
|
989
|
+
return (
|
|
990
|
+
f"Work notification sent successfully to "
|
|
991
|
+
f"{len(userid_list)} users."
|
|
992
|
+
)
|
|
993
|
+
except Exception as e:
|
|
994
|
+
return f"Failed to send work notification: {e!s}"
|
|
995
|
+
|
|
996
|
+
@api_keys_required(
|
|
997
|
+
[
|
|
998
|
+
(None, "DINGTALK_APP_KEY"),
|
|
999
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
1000
|
+
]
|
|
1001
|
+
)
|
|
1002
|
+
def dingtalk_get_userid_by_phone(self, phone_number: str) -> str:
|
|
1003
|
+
r"""Gets user ID by phone number for LLM agents.
|
|
1004
|
+
|
|
1005
|
+
Args:
|
|
1006
|
+
phone_number (str): User's phone number.
|
|
1007
|
+
|
|
1008
|
+
Returns:
|
|
1009
|
+
str: User ID or error message.
|
|
1010
|
+
|
|
1011
|
+
References:
|
|
1012
|
+
https://open.dingtalk.com/document/orgapp-server/query-user-details
|
|
1013
|
+
"""
|
|
1014
|
+
try:
|
|
1015
|
+
user_info = self.dingtalk_get_user_by_mobile(phone_number)
|
|
1016
|
+
if 'result' in user_info and 'userid' in user_info['result']:
|
|
1017
|
+
return user_info['result']['userid']
|
|
1018
|
+
else:
|
|
1019
|
+
return f"User not found for phone number: {phone_number}"
|
|
1020
|
+
except Exception as e:
|
|
1021
|
+
return f"Failed to get user ID by phone: {e!s}"
|
|
1022
|
+
|
|
1023
|
+
@api_keys_required(
|
|
1024
|
+
[
|
|
1025
|
+
(None, "DINGTALK_APP_KEY"),
|
|
1026
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
1027
|
+
]
|
|
1028
|
+
)
|
|
1029
|
+
def dingtalk_get_userid_by_name(self, user_name: str) -> str:
|
|
1030
|
+
r"""Gets user ID by user name for LLM agents.
|
|
1031
|
+
|
|
1032
|
+
Args:
|
|
1033
|
+
user_name (str): User's display name.
|
|
1034
|
+
|
|
1035
|
+
Returns:
|
|
1036
|
+
str: User ID or error message.
|
|
1037
|
+
|
|
1038
|
+
References:
|
|
1039
|
+
https://open.dingtalk.com/document/orgapp-server/query-users
|
|
1040
|
+
"""
|
|
1041
|
+
try:
|
|
1042
|
+
search_result = self.dingtalk_search_users_by_name(user_name)
|
|
1043
|
+
if search_result.get('result'):
|
|
1044
|
+
# Return the first match
|
|
1045
|
+
return search_result['result'][0].get(
|
|
1046
|
+
'userid', f"No userid found for user: {user_name}"
|
|
1047
|
+
)
|
|
1048
|
+
else:
|
|
1049
|
+
return f"User not found with name: {user_name}"
|
|
1050
|
+
except Exception as e:
|
|
1051
|
+
return f"Failed to get user ID by name: {e!s}"
|
|
1052
|
+
|
|
1053
|
+
@api_keys_required(
|
|
1054
|
+
[
|
|
1055
|
+
(None, "DINGTALK_APP_KEY"),
|
|
1056
|
+
(None, "DINGTALK_APP_SECRET"),
|
|
1057
|
+
]
|
|
1058
|
+
)
|
|
1059
|
+
def dingtalk_get_department_id_by_name(self, department_name: str) -> str:
|
|
1060
|
+
r"""Gets department ID by department name for LLM agents.
|
|
1061
|
+
|
|
1062
|
+
Args:
|
|
1063
|
+
department_name (str): Department name to search for.
|
|
1064
|
+
|
|
1065
|
+
Returns:
|
|
1066
|
+
str: Department ID or error message.
|
|
1067
|
+
"""
|
|
1068
|
+
try:
|
|
1069
|
+
dept_list = self.dingtalk_get_department_list()
|
|
1070
|
+
if 'result' in dept_list:
|
|
1071
|
+
for dept in dept_list['result']:
|
|
1072
|
+
if dept.get('name') == department_name:
|
|
1073
|
+
return str(dept.get('id', ''))
|
|
1074
|
+
return f"Department not found: {department_name}"
|
|
1075
|
+
else:
|
|
1076
|
+
return "Failed to get department list"
|
|
1077
|
+
except Exception as e:
|
|
1078
|
+
return f"Failed to get department ID by name: {e!s}"
|
|
1079
|
+
|
|
1080
|
+
def dingtalk_get_chatid_by_group_name(self, group_name: str) -> str:
|
|
1081
|
+
r"""Gets chat ID by group name for LLM agents.
|
|
1082
|
+
|
|
1083
|
+
Note: This function provides guidance as Dingtalk API doesn't directly
|
|
1084
|
+
support searching groups by name. Users should use the group creation
|
|
1085
|
+
response or group management features to obtain chat IDs.
|
|
1086
|
+
|
|
1087
|
+
Args:
|
|
1088
|
+
group_name (str): Group name to search for.
|
|
1089
|
+
|
|
1090
|
+
Returns:
|
|
1091
|
+
str: Guidance message for obtaining chat ID.
|
|
1092
|
+
"""
|
|
1093
|
+
return (
|
|
1094
|
+
f"To get chat ID for group '{group_name}': "
|
|
1095
|
+
"1. Use dingtalk_create_group() and save the returned chatid, or "
|
|
1096
|
+
"2. Contact group admin to provide the chat ID, or "
|
|
1097
|
+
"3. Use Dingtalk admin console to find the group ID. "
|
|
1098
|
+
"Chat IDs are typically returned when creating groups."
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
def get_tools(self) -> List[FunctionTool]:
|
|
1102
|
+
r"""Returns toolkit functions as tools."""
|
|
1103
|
+
return [
|
|
1104
|
+
# Original message functions
|
|
1105
|
+
FunctionTool(self.dingtalk_send_text_message),
|
|
1106
|
+
FunctionTool(self.dingtalk_send_markdown_message),
|
|
1107
|
+
FunctionTool(self.dingtalk_send_webhook_message),
|
|
1108
|
+
# New message types
|
|
1109
|
+
FunctionTool(self.dingtalk_send_link_message),
|
|
1110
|
+
FunctionTool(self.dingtalk_send_action_card_message),
|
|
1111
|
+
FunctionTool(self.dingtalk_send_oa_message),
|
|
1112
|
+
FunctionTool(self.dingtalk_send_work_notification),
|
|
1113
|
+
# User management functions
|
|
1114
|
+
FunctionTool(self.dingtalk_get_user_info),
|
|
1115
|
+
FunctionTool(self.dingtalk_get_user_by_mobile),
|
|
1116
|
+
FunctionTool(self.dingtalk_get_user_by_unionid),
|
|
1117
|
+
FunctionTool(self.dingtalk_search_users_by_name),
|
|
1118
|
+
# Department management functions
|
|
1119
|
+
FunctionTool(self.dingtalk_get_department_list),
|
|
1120
|
+
FunctionTool(self.dingtalk_get_department_users),
|
|
1121
|
+
FunctionTool(self.dingtalk_get_department_detail),
|
|
1122
|
+
# Group management functions
|
|
1123
|
+
FunctionTool(self.dingtalk_create_group),
|
|
1124
|
+
FunctionTool(self.dingtalk_send_group_message),
|
|
1125
|
+
FunctionTool(self.dingtalk_get_group_info),
|
|
1126
|
+
FunctionTool(self.dingtalk_update_group),
|
|
1127
|
+
# Helper functions for LLM agents to get IDs
|
|
1128
|
+
FunctionTool(self.dingtalk_get_userid_by_phone),
|
|
1129
|
+
FunctionTool(self.dingtalk_get_userid_by_name),
|
|
1130
|
+
FunctionTool(self.dingtalk_get_department_id_by_name),
|
|
1131
|
+
FunctionTool(self.dingtalk_get_chatid_by_group_name),
|
|
1132
|
+
]
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
# many other functionalities, cards etc build on top after initial works
|