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.

Files changed (505) hide show
  1. camel/__init__.py +3 -3
  2. camel/agents/__init__.py +2 -2
  3. camel/agents/_types.py +9 -4
  4. camel/agents/_utils.py +40 -2
  5. camel/agents/base.py +2 -2
  6. camel/agents/chat_agent.py +4835 -947
  7. camel/agents/critic_agent.py +2 -2
  8. camel/agents/deductive_reasoner_agent.py +56 -56
  9. camel/agents/embodied_agent.py +2 -2
  10. camel/agents/knowledge_graph_agent.py +20 -20
  11. camel/agents/mcp_agent.py +35 -36
  12. camel/agents/multi_hop_generator_agent.py +3 -3
  13. camel/agents/programmed_agent_instruction.py +2 -2
  14. camel/agents/repo_agent.py +4 -3
  15. camel/agents/role_assignment_agent.py +2 -2
  16. camel/agents/search_agent.py +2 -2
  17. camel/agents/task_agent.py +2 -2
  18. camel/agents/tool_agents/__init__.py +2 -2
  19. camel/agents/tool_agents/base.py +2 -2
  20. camel/agents/tool_agents/hugging_face_tool_agent.py +3 -3
  21. camel/benchmarks/__init__.py +2 -2
  22. camel/benchmarks/apibank.py +5 -5
  23. camel/benchmarks/apibench.py +2 -2
  24. camel/benchmarks/base.py +2 -2
  25. camel/benchmarks/browsecomp.py +44 -33
  26. camel/benchmarks/gaia.py +17 -13
  27. camel/benchmarks/mock_website/README.md +1 -3
  28. camel/benchmarks/mock_website/mock_web.py +2 -2
  29. camel/benchmarks/mock_website/requirements.txt +1 -1
  30. camel/benchmarks/mock_website/shopping_mall/app.py +2 -2
  31. camel/benchmarks/mock_website/task.json +1 -1
  32. camel/benchmarks/nexus.py +3 -3
  33. camel/benchmarks/ragbench.py +2 -2
  34. camel/bots/__init__.py +2 -2
  35. camel/bots/discord/__init__.py +2 -2
  36. camel/bots/discord/discord_app.py +2 -2
  37. camel/bots/discord/discord_installation.py +2 -2
  38. camel/bots/discord/discord_store.py +3 -3
  39. camel/bots/slack/__init__.py +2 -2
  40. camel/bots/slack/models.py +4 -4
  41. camel/bots/slack/slack_app.py +2 -2
  42. camel/bots/telegram_bot.py +2 -2
  43. camel/configs/__init__.py +23 -2
  44. camel/configs/aihubmix_config.py +90 -0
  45. camel/configs/aiml_config.py +2 -2
  46. camel/configs/amd_config.py +70 -0
  47. camel/configs/anthropic_config.py +2 -2
  48. camel/configs/base_config.py +2 -2
  49. camel/configs/bedrock_config.py +5 -3
  50. camel/configs/cerebras_config.py +98 -0
  51. camel/configs/cohere_config.py +2 -2
  52. camel/configs/cometapi_config.py +106 -0
  53. camel/configs/crynux_config.py +2 -2
  54. camel/configs/deepseek_config.py +9 -8
  55. camel/configs/gemini_config.py +6 -4
  56. camel/configs/groq_config.py +6 -4
  57. camel/configs/internlm_config.py +6 -4
  58. camel/configs/litellm_config.py +2 -2
  59. camel/configs/lmstudio_config.py +6 -4
  60. camel/configs/minimax_config.py +95 -0
  61. camel/configs/mistral_config.py +2 -2
  62. camel/configs/modelscope_config.py +5 -3
  63. camel/configs/moonshot_config.py +2 -2
  64. camel/configs/nebius_config.py +105 -0
  65. camel/configs/netmind_config.py +2 -2
  66. camel/configs/novita_config.py +2 -2
  67. camel/configs/nvidia_config.py +2 -2
  68. camel/configs/ollama_config.py +2 -2
  69. camel/configs/openai_config.py +5 -3
  70. camel/configs/openrouter_config.py +6 -4
  71. camel/configs/ppio_config.py +2 -2
  72. camel/configs/qianfan_config.py +85 -0
  73. camel/configs/qwen_config.py +2 -2
  74. camel/configs/reka_config.py +2 -2
  75. camel/configs/samba_config.py +6 -4
  76. camel/configs/sglang_config.py +2 -2
  77. camel/configs/siliconflow_config.py +2 -2
  78. camel/configs/togetherai_config.py +2 -2
  79. camel/configs/vllm_config.py +4 -2
  80. camel/configs/watsonx_config.py +2 -2
  81. camel/configs/yi_config.py +6 -4
  82. camel/configs/zhipuai_config.py +6 -4
  83. camel/data_collectors/__init__.py +2 -2
  84. camel/data_collectors/alpaca_collector.py +18 -9
  85. camel/data_collectors/base.py +2 -2
  86. camel/data_collectors/sharegpt_collector.py +2 -2
  87. camel/datagen/__init__.py +2 -2
  88. camel/datagen/cot_datagen.py +3 -3
  89. camel/datagen/evol_instruct/__init__.py +2 -2
  90. camel/datagen/evol_instruct/evol_instruct.py +2 -2
  91. camel/datagen/evol_instruct/scorer.py +12 -12
  92. camel/datagen/evol_instruct/templates.py +16 -16
  93. camel/datagen/self_improving_cot.py +5 -5
  94. camel/datagen/self_instruct/__init__.py +2 -2
  95. camel/datagen/self_instruct/filter/__init__.py +2 -2
  96. camel/datagen/self_instruct/filter/filter_function.py +2 -2
  97. camel/datagen/self_instruct/filter/filter_registry.py +2 -2
  98. camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
  99. camel/datagen/self_instruct/self_instruct.py +2 -2
  100. camel/datagen/self_instruct/templates.py +47 -47
  101. camel/datagen/source2synth/__init__.py +2 -2
  102. camel/datagen/source2synth/data_processor.py +2 -2
  103. camel/datagen/source2synth/models.py +2 -2
  104. camel/datagen/source2synth/user_data_processor_config.py +2 -2
  105. camel/datahubs/__init__.py +2 -2
  106. camel/datahubs/base.py +2 -2
  107. camel/datahubs/huggingface.py +2 -2
  108. camel/datahubs/models.py +2 -2
  109. camel/datasets/__init__.py +2 -2
  110. camel/datasets/base_generator.py +41 -12
  111. camel/datasets/few_shot_generator.py +18 -18
  112. camel/datasets/models.py +2 -2
  113. camel/datasets/self_instruct_generator.py +2 -2
  114. camel/datasets/static_dataset.py +2 -2
  115. camel/embeddings/__init__.py +2 -2
  116. camel/embeddings/azure_embedding.py +2 -2
  117. camel/embeddings/base.py +2 -2
  118. camel/embeddings/gemini_embedding.py +2 -2
  119. camel/embeddings/jina_embedding.py +2 -2
  120. camel/embeddings/mistral_embedding.py +2 -2
  121. camel/embeddings/openai_compatible_embedding.py +2 -2
  122. camel/embeddings/openai_embedding.py +2 -2
  123. camel/embeddings/sentence_transformers_embeddings.py +2 -2
  124. camel/embeddings/together_embedding.py +2 -2
  125. camel/embeddings/vlm_embedding.py +2 -2
  126. camel/environments/__init__.py +14 -2
  127. camel/environments/models.py +2 -2
  128. camel/environments/multi_step.py +2 -2
  129. camel/environments/rlcards_env.py +860 -0
  130. camel/environments/single_step.py +30 -5
  131. camel/environments/tic_tac_toe.py +3 -3
  132. camel/extractors/__init__.py +2 -2
  133. camel/extractors/base.py +2 -2
  134. camel/extractors/python_strategies.py +2 -2
  135. camel/generators.py +2 -2
  136. camel/human.py +2 -2
  137. camel/interpreters/__init__.py +4 -2
  138. camel/interpreters/base.py +2 -2
  139. camel/interpreters/docker/Dockerfile +14 -24
  140. camel/interpreters/docker_interpreter.py +5 -4
  141. camel/interpreters/e2b_interpreter.py +36 -3
  142. camel/interpreters/internal_python_interpreter.py +53 -4
  143. camel/interpreters/interpreter_error.py +2 -2
  144. camel/interpreters/ipython_interpreter.py +2 -2
  145. camel/interpreters/microsandbox_interpreter.py +395 -0
  146. camel/interpreters/subprocess_interpreter.py +2 -2
  147. camel/loaders/__init__.py +13 -4
  148. camel/loaders/apify_reader.py +2 -2
  149. camel/loaders/base_io.py +2 -2
  150. camel/loaders/base_loader.py +85 -0
  151. camel/loaders/chunkr_reader.py +11 -2
  152. camel/loaders/crawl4ai_reader.py +2 -2
  153. camel/loaders/firecrawl_reader.py +6 -6
  154. camel/loaders/jina_url_reader.py +2 -2
  155. camel/loaders/markitdown.py +2 -2
  156. camel/loaders/mineru_extractor.py +2 -2
  157. camel/loaders/mistral_reader.py +2 -2
  158. camel/loaders/scrapegraph_reader.py +2 -2
  159. camel/loaders/unstructured_io.py +2 -2
  160. camel/logger.py +5 -5
  161. camel/memories/__init__.py +2 -2
  162. camel/memories/agent_memories.py +86 -3
  163. camel/memories/base.py +36 -2
  164. camel/memories/blocks/__init__.py +2 -2
  165. camel/memories/blocks/chat_history_block.py +125 -7
  166. camel/memories/blocks/vectordb_block.py +10 -3
  167. camel/memories/context_creators/__init__.py +2 -2
  168. camel/memories/context_creators/score_based.py +31 -239
  169. camel/memories/records.py +90 -10
  170. camel/messages/__init__.py +2 -2
  171. camel/messages/base.py +178 -43
  172. camel/messages/conversion/__init__.py +2 -2
  173. camel/messages/conversion/alpaca.py +2 -2
  174. camel/messages/conversion/conversation_models.py +2 -2
  175. camel/messages/conversion/sharegpt/__init__.py +2 -2
  176. camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
  177. camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
  178. camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
  179. camel/messages/func_message.py +54 -17
  180. camel/models/__init__.py +16 -2
  181. camel/models/_utils.py +3 -3
  182. camel/models/aihubmix_model.py +83 -0
  183. camel/models/aiml_model.py +11 -18
  184. camel/models/amd_model.py +101 -0
  185. camel/models/anthropic_model.py +127 -20
  186. camel/models/aws_bedrock_model.py +12 -35
  187. camel/models/azure_openai_model.py +212 -89
  188. camel/models/base_audio_model.py +5 -3
  189. camel/models/base_model.py +195 -26
  190. camel/models/cerebras_model.py +83 -0
  191. camel/models/cohere_model.py +16 -21
  192. camel/models/cometapi_model.py +83 -0
  193. camel/models/crynux_model.py +11 -18
  194. camel/models/deepseek_model.py +18 -58
  195. camel/models/fish_audio_model.py +8 -2
  196. camel/models/gemini_model.py +389 -26
  197. camel/models/groq_model.py +11 -19
  198. camel/models/internlm_model.py +11 -18
  199. camel/models/litellm_model.py +56 -34
  200. camel/models/lmstudio_model.py +17 -20
  201. camel/models/minimax_model.py +83 -0
  202. camel/models/mistral_model.py +18 -19
  203. camel/models/model_factory.py +37 -3
  204. camel/models/model_manager.py +26 -8
  205. camel/models/modelscope_model.py +13 -193
  206. camel/models/moonshot_model.py +195 -21
  207. camel/models/nebius_model.py +83 -0
  208. camel/models/nemotron_model.py +19 -9
  209. camel/models/netmind_model.py +11 -18
  210. camel/models/novita_model.py +11 -18
  211. camel/models/nvidia_model.py +11 -18
  212. camel/models/ollama_model.py +14 -21
  213. camel/models/openai_audio_models.py +2 -2
  214. camel/models/openai_compatible_model.py +188 -45
  215. camel/models/openai_model.py +216 -71
  216. camel/models/openrouter_model.py +11 -19
  217. camel/models/ppio_model.py +11 -18
  218. camel/models/qianfan_model.py +89 -0
  219. camel/models/qwen_model.py +13 -193
  220. camel/models/reka_model.py +21 -21
  221. camel/models/reward/__init__.py +2 -2
  222. camel/models/reward/base_reward_model.py +2 -2
  223. camel/models/reward/evaluator.py +2 -2
  224. camel/models/reward/nemotron_model.py +2 -2
  225. camel/models/reward/skywork_model.py +2 -2
  226. camel/models/samba_model.py +48 -47
  227. camel/models/sglang_model.py +88 -40
  228. camel/models/siliconflow_model.py +12 -35
  229. camel/models/stub_model.py +10 -7
  230. camel/models/togetherai_model.py +11 -18
  231. camel/models/vllm_model.py +10 -18
  232. camel/models/volcano_model.py +16 -20
  233. camel/models/watsonx_model.py +7 -19
  234. camel/models/yi_model.py +11 -18
  235. camel/models/zhipuai_model.py +70 -18
  236. camel/parsers/__init__.py +18 -0
  237. camel/parsers/mcp_tool_call_parser.py +176 -0
  238. camel/personas/__init__.py +2 -2
  239. camel/personas/persona.py +2 -2
  240. camel/personas/persona_hub.py +2 -2
  241. camel/prompts/__init__.py +2 -2
  242. camel/prompts/ai_society.py +2 -2
  243. camel/prompts/base.py +2 -2
  244. camel/prompts/code.py +2 -2
  245. camel/prompts/evaluation.py +2 -2
  246. camel/prompts/generate_text_embedding_data.py +2 -2
  247. camel/prompts/image_craft.py +2 -2
  248. camel/prompts/misalignment.py +2 -2
  249. camel/prompts/multi_condition_image_craft.py +2 -2
  250. camel/prompts/object_recognition.py +2 -2
  251. camel/prompts/persona_hub.py +3 -3
  252. camel/prompts/prompt_templates.py +2 -2
  253. camel/prompts/role_description_prompt_template.py +2 -2
  254. camel/prompts/solution_extraction.py +8 -8
  255. camel/prompts/task_prompt_template.py +2 -2
  256. camel/prompts/translation.py +2 -2
  257. camel/prompts/video_description_prompt.py +3 -3
  258. camel/responses/__init__.py +2 -2
  259. camel/responses/agent_responses.py +2 -2
  260. camel/retrievers/__init__.py +2 -2
  261. camel/retrievers/auto_retriever.py +3 -2
  262. camel/retrievers/base.py +2 -2
  263. camel/retrievers/bm25_retriever.py +2 -2
  264. camel/retrievers/cohere_rerank_retriever.py +2 -2
  265. camel/retrievers/hybrid_retrival.py +2 -2
  266. camel/retrievers/vector_retriever.py +2 -2
  267. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  268. camel/runtimes/__init__.py +2 -2
  269. camel/runtimes/api.py +79 -23
  270. camel/runtimes/base.py +2 -2
  271. camel/runtimes/configs.py +13 -13
  272. camel/runtimes/daytona_runtime.py +17 -18
  273. camel/runtimes/docker_runtime.py +12 -12
  274. camel/runtimes/llm_guard_runtime.py +26 -26
  275. camel/runtimes/remote_http_runtime.py +11 -11
  276. camel/runtimes/ubuntu_docker_runtime.py +2 -2
  277. camel/runtimes/utils/__init__.py +2 -2
  278. camel/runtimes/utils/function_risk_toolkit.py +2 -2
  279. camel/runtimes/utils/ignore_risk_toolkit.py +2 -2
  280. camel/schemas/__init__.py +2 -2
  281. camel/schemas/base.py +2 -2
  282. camel/schemas/openai_converter.py +3 -3
  283. camel/schemas/outlines_converter.py +2 -2
  284. camel/services/agent_openapi_server.py +380 -0
  285. camel/societies/__init__.py +4 -2
  286. camel/societies/babyagi_playing.py +2 -2
  287. camel/societies/role_playing.py +201 -80
  288. camel/societies/workforce/__init__.py +10 -3
  289. camel/societies/workforce/base.py +2 -2
  290. camel/societies/workforce/events.py +143 -0
  291. camel/societies/workforce/prompts.py +258 -33
  292. camel/societies/workforce/role_playing_worker.py +88 -31
  293. camel/societies/workforce/single_agent_worker.py +638 -40
  294. camel/societies/workforce/structured_output_handler.py +512 -0
  295. camel/societies/workforce/task_channel.py +182 -38
  296. camel/societies/workforce/utils.py +780 -65
  297. camel/societies/workforce/worker.py +92 -26
  298. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  299. camel/societies/workforce/workforce.py +5276 -355
  300. camel/societies/workforce/workforce_callback.py +103 -0
  301. camel/societies/workforce/workforce_logger.py +647 -0
  302. camel/societies/workforce/workforce_metrics.py +33 -0
  303. camel/storages/__init__.py +6 -2
  304. camel/storages/graph_storages/__init__.py +2 -2
  305. camel/storages/graph_storages/base.py +2 -2
  306. camel/storages/graph_storages/graph_element.py +2 -2
  307. camel/storages/graph_storages/nebula_graph.py +4 -4
  308. camel/storages/graph_storages/neo4j_graph.py +7 -7
  309. camel/storages/key_value_storages/__init__.py +2 -2
  310. camel/storages/key_value_storages/base.py +2 -2
  311. camel/storages/key_value_storages/in_memory.py +2 -2
  312. camel/storages/key_value_storages/json.py +17 -4
  313. camel/storages/key_value_storages/mem0_cloud.py +50 -49
  314. camel/storages/key_value_storages/redis.py +2 -2
  315. camel/storages/object_storages/__init__.py +2 -2
  316. camel/storages/object_storages/amazon_s3.py +2 -2
  317. camel/storages/object_storages/azure_blob.py +2 -2
  318. camel/storages/object_storages/base.py +2 -2
  319. camel/storages/object_storages/google_cloud.py +3 -3
  320. camel/storages/vectordb_storages/__init__.py +8 -2
  321. camel/storages/vectordb_storages/base.py +2 -2
  322. camel/storages/vectordb_storages/chroma.py +731 -0
  323. camel/storages/vectordb_storages/faiss.py +2 -2
  324. camel/storages/vectordb_storages/milvus.py +2 -2
  325. camel/storages/vectordb_storages/oceanbase.py +15 -15
  326. camel/storages/vectordb_storages/pgvector.py +349 -0
  327. camel/storages/vectordb_storages/qdrant.py +6 -6
  328. camel/storages/vectordb_storages/surreal.py +372 -0
  329. camel/storages/vectordb_storages/tidb.py +11 -8
  330. camel/storages/vectordb_storages/weaviate.py +2 -2
  331. camel/tasks/__init__.py +2 -2
  332. camel/tasks/task.py +348 -26
  333. camel/tasks/task_prompt.py +3 -3
  334. camel/terminators/__init__.py +2 -2
  335. camel/terminators/base.py +2 -2
  336. camel/terminators/response_terminator.py +2 -2
  337. camel/terminators/token_limit_terminator.py +2 -2
  338. camel/toolkits/__init__.py +54 -10
  339. camel/toolkits/aci_toolkit.py +66 -21
  340. camel/toolkits/arxiv_toolkit.py +8 -8
  341. camel/toolkits/ask_news_toolkit.py +2 -2
  342. camel/toolkits/async_browser_toolkit.py +4 -4
  343. camel/toolkits/audio_analysis_toolkit.py +3 -3
  344. camel/toolkits/base.py +65 -7
  345. camel/toolkits/bohrium_toolkit.py +2 -2
  346. camel/toolkits/browser_toolkit.py +34 -21
  347. camel/toolkits/browser_toolkit_commons.py +4 -4
  348. camel/toolkits/code_execution.py +31 -4
  349. camel/toolkits/context_summarizer_toolkit.py +684 -0
  350. camel/toolkits/craw4ai_toolkit.py +93 -0
  351. camel/toolkits/dappier_toolkit.py +12 -8
  352. camel/toolkits/data_commons_toolkit.py +2 -2
  353. camel/toolkits/dingtalk.py +1135 -0
  354. camel/toolkits/earth_science_toolkit.py +5367 -0
  355. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  356. camel/toolkits/excel_toolkit.py +905 -71
  357. camel/toolkits/file_toolkit.py +1402 -0
  358. camel/toolkits/function_tool.py +126 -18
  359. camel/toolkits/github_toolkit.py +109 -22
  360. camel/toolkits/gmail_toolkit.py +1839 -0
  361. camel/toolkits/google_calendar_toolkit.py +40 -6
  362. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  363. camel/toolkits/google_maps_toolkit.py +2 -2
  364. camel/toolkits/google_scholar_toolkit.py +2 -2
  365. camel/toolkits/human_toolkit.py +36 -12
  366. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  367. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  368. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  369. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  370. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  371. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
  372. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  373. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  374. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1929 -0
  375. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  376. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
  377. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  378. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  379. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  380. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  381. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
  382. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
  383. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  384. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
  385. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  386. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  387. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  388. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  389. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  390. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  391. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  392. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  393. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  394. camel/toolkits/image_analysis_toolkit.py +3 -6
  395. camel/toolkits/image_generation_toolkit.py +390 -0
  396. camel/toolkits/jina_reranker_toolkit.py +5 -6
  397. camel/toolkits/klavis_toolkit.py +7 -3
  398. camel/toolkits/linkedin_toolkit.py +2 -2
  399. camel/toolkits/markitdown_toolkit.py +104 -0
  400. camel/toolkits/math_toolkit.py +66 -12
  401. camel/toolkits/mcp_toolkit.py +412 -36
  402. camel/toolkits/memory_toolkit.py +7 -3
  403. camel/toolkits/meshy_toolkit.py +2 -2
  404. camel/toolkits/message_agent_toolkit.py +608 -0
  405. camel/toolkits/message_integration.py +724 -0
  406. camel/toolkits/mineru_toolkit.py +2 -2
  407. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  408. camel/toolkits/networkx_toolkit.py +2 -2
  409. camel/toolkits/note_taking_toolkit.py +277 -0
  410. camel/toolkits/notion_mcp_toolkit.py +224 -0
  411. camel/toolkits/notion_toolkit.py +2 -2
  412. camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
  413. camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
  414. camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
  415. camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
  416. camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
  417. camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
  418. camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
  419. camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
  420. camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
  421. camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
  422. camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
  423. camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
  424. camel/toolkits/open_api_specs/security_config.py +2 -2
  425. camel/toolkits/open_api_specs/speak/__init__.py +2 -2
  426. camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
  427. camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
  428. camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
  429. camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
  430. camel/toolkits/open_api_toolkit.py +2 -2
  431. camel/toolkits/openbb_toolkit.py +7 -3
  432. camel/toolkits/origene_mcp_toolkit.py +56 -0
  433. camel/toolkits/page_script.js +53 -53
  434. camel/toolkits/playwright_mcp_toolkit.py +13 -31
  435. camel/toolkits/pptx_toolkit.py +36 -23
  436. camel/toolkits/pubmed_toolkit.py +2 -2
  437. camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
  438. camel/toolkits/pyautogui_toolkit.py +2 -2
  439. camel/toolkits/reddit_toolkit.py +2 -2
  440. camel/toolkits/resend_toolkit.py +168 -0
  441. camel/toolkits/retrieval_toolkit.py +2 -2
  442. camel/toolkits/screenshot_toolkit.py +213 -0
  443. camel/toolkits/search_toolkit.py +539 -146
  444. camel/toolkits/searxng_toolkit.py +2 -2
  445. camel/toolkits/semantic_scholar_toolkit.py +2 -2
  446. camel/toolkits/slack_toolkit.py +108 -58
  447. camel/toolkits/sql_toolkit.py +712 -0
  448. camel/toolkits/stripe_toolkit.py +2 -2
  449. camel/toolkits/sympy_toolkit.py +3 -3
  450. camel/toolkits/task_planning_toolkit.py +5 -5
  451. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  452. camel/toolkits/terminal_toolkit/terminal_toolkit.py +1070 -0
  453. camel/toolkits/terminal_toolkit/utils.py +532 -0
  454. camel/toolkits/thinking_toolkit.py +3 -3
  455. camel/toolkits/twitter_toolkit.py +2 -2
  456. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  457. camel/toolkits/video_analysis_toolkit.py +109 -29
  458. camel/toolkits/video_download_toolkit.py +19 -16
  459. camel/toolkits/weather_toolkit.py +2 -2
  460. camel/toolkits/web_deploy_toolkit.py +1219 -0
  461. camel/toolkits/wechat_official_toolkit.py +483 -0
  462. camel/toolkits/whatsapp_toolkit.py +2 -2
  463. camel/toolkits/wolfram_alpha_toolkit.py +2 -2
  464. camel/toolkits/zapier_toolkit.py +7 -3
  465. camel/types/__init__.py +4 -4
  466. camel/types/agents/__init__.py +2 -2
  467. camel/types/agents/tool_calling_record.py +6 -3
  468. camel/types/enums.py +378 -39
  469. camel/types/mcp_registries.py +2 -2
  470. camel/types/openai_types.py +4 -4
  471. camel/types/unified_model_type.py +38 -6
  472. camel/utils/__init__.py +2 -2
  473. camel/utils/async_func.py +2 -2
  474. camel/utils/chunker/__init__.py +2 -2
  475. camel/utils/chunker/base.py +2 -2
  476. camel/utils/chunker/code_chunker.py +2 -2
  477. camel/utils/chunker/uio_chunker.py +2 -2
  478. camel/utils/commons.py +38 -7
  479. camel/utils/constants.py +5 -2
  480. camel/utils/context_utils.py +1134 -0
  481. camel/utils/deduplication.py +2 -2
  482. camel/utils/filename.py +2 -2
  483. camel/utils/langfuse.py +2 -2
  484. camel/utils/mcp.py +140 -6
  485. camel/utils/mcp_client.py +48 -38
  486. camel/utils/message_summarizer.py +148 -0
  487. camel/utils/response_format.py +2 -2
  488. camel/utils/token_counting.py +45 -22
  489. camel/utils/tool_result.py +44 -0
  490. camel/verifiers/__init__.py +2 -2
  491. camel/verifiers/base.py +2 -2
  492. camel/verifiers/math_verifier.py +2 -2
  493. camel/verifiers/models.py +2 -2
  494. camel/verifiers/physics_verifier.py +2 -2
  495. camel/verifiers/python_verifier.py +2 -2
  496. {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.dist-info}/METADATA +327 -94
  497. camel_ai-0.2.82.dist-info/RECORD +507 -0
  498. {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
  499. {camel_ai-0.2.65.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
  500. camel/loaders/pandas_reader.py +0 -368
  501. camel/toolkits/dalle_toolkit.py +0 -175
  502. camel/toolkits/file_write_toolkit.py +0 -444
  503. camel/toolkits/openai_agent_toolkit.py +0 -135
  504. camel/toolkits/terminal_toolkit.py +0 -1037
  505. 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