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.

Files changed (509) 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 +5107 -995
  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 +29 -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/function_gemma_config.py +59 -0
  56. camel/configs/gemini_config.py +6 -4
  57. camel/configs/groq_config.py +6 -4
  58. camel/configs/internlm_config.py +6 -4
  59. camel/configs/litellm_config.py +2 -2
  60. camel/configs/lmstudio_config.py +6 -4
  61. camel/configs/minimax_config.py +95 -0
  62. camel/configs/mistral_config.py +2 -2
  63. camel/configs/modelscope_config.py +5 -3
  64. camel/configs/moonshot_config.py +2 -2
  65. camel/configs/nebius_config.py +105 -0
  66. camel/configs/netmind_config.py +2 -2
  67. camel/configs/novita_config.py +2 -2
  68. camel/configs/nvidia_config.py +2 -2
  69. camel/configs/ollama_config.py +2 -2
  70. camel/configs/openai_config.py +5 -3
  71. camel/configs/openrouter_config.py +6 -4
  72. camel/configs/ppio_config.py +2 -2
  73. camel/configs/qianfan_config.py +85 -0
  74. camel/configs/qwen_config.py +2 -2
  75. camel/configs/reka_config.py +2 -2
  76. camel/configs/samba_config.py +6 -4
  77. camel/configs/sglang_config.py +2 -2
  78. camel/configs/siliconflow_config.py +2 -2
  79. camel/configs/togetherai_config.py +2 -2
  80. camel/configs/vllm_config.py +4 -2
  81. camel/configs/watsonx_config.py +2 -2
  82. camel/configs/yi_config.py +6 -4
  83. camel/configs/zhipuai_config.py +6 -4
  84. camel/data_collectors/__init__.py +2 -2
  85. camel/data_collectors/alpaca_collector.py +18 -9
  86. camel/data_collectors/base.py +2 -2
  87. camel/data_collectors/sharegpt_collector.py +2 -2
  88. camel/datagen/__init__.py +2 -2
  89. camel/datagen/cot_datagen.py +3 -3
  90. camel/datagen/evol_instruct/__init__.py +2 -2
  91. camel/datagen/evol_instruct/evol_instruct.py +2 -2
  92. camel/datagen/evol_instruct/scorer.py +12 -12
  93. camel/datagen/evol_instruct/templates.py +16 -16
  94. camel/datagen/self_improving_cot.py +5 -5
  95. camel/datagen/self_instruct/__init__.py +2 -2
  96. camel/datagen/self_instruct/filter/__init__.py +2 -2
  97. camel/datagen/self_instruct/filter/filter_function.py +2 -2
  98. camel/datagen/self_instruct/filter/filter_registry.py +2 -2
  99. camel/datagen/self_instruct/filter/instruction_filter.py +2 -2
  100. camel/datagen/self_instruct/self_instruct.py +2 -2
  101. camel/datagen/self_instruct/templates.py +47 -47
  102. camel/datagen/source2synth/__init__.py +2 -2
  103. camel/datagen/source2synth/data_processor.py +2 -2
  104. camel/datagen/source2synth/models.py +2 -2
  105. camel/datagen/source2synth/user_data_processor_config.py +2 -2
  106. camel/datahubs/__init__.py +2 -2
  107. camel/datahubs/base.py +2 -2
  108. camel/datahubs/huggingface.py +2 -2
  109. camel/datahubs/models.py +2 -2
  110. camel/datasets/__init__.py +2 -2
  111. camel/datasets/base_generator.py +41 -12
  112. camel/datasets/few_shot_generator.py +18 -18
  113. camel/datasets/models.py +2 -2
  114. camel/datasets/self_instruct_generator.py +2 -2
  115. camel/datasets/static_dataset.py +2 -2
  116. camel/embeddings/__init__.py +2 -2
  117. camel/embeddings/azure_embedding.py +2 -2
  118. camel/embeddings/base.py +2 -2
  119. camel/embeddings/gemini_embedding.py +2 -2
  120. camel/embeddings/jina_embedding.py +2 -2
  121. camel/embeddings/mistral_embedding.py +2 -2
  122. camel/embeddings/openai_compatible_embedding.py +2 -2
  123. camel/embeddings/openai_embedding.py +2 -2
  124. camel/embeddings/sentence_transformers_embeddings.py +2 -2
  125. camel/embeddings/together_embedding.py +2 -2
  126. camel/embeddings/vlm_embedding.py +2 -2
  127. camel/environments/__init__.py +14 -2
  128. camel/environments/models.py +2 -2
  129. camel/environments/multi_step.py +2 -2
  130. camel/environments/rlcards_env.py +860 -0
  131. camel/environments/single_step.py +30 -5
  132. camel/environments/tic_tac_toe.py +3 -3
  133. camel/extractors/__init__.py +2 -2
  134. camel/extractors/base.py +2 -2
  135. camel/extractors/python_strategies.py +2 -2
  136. camel/generators.py +2 -2
  137. camel/human.py +2 -2
  138. camel/interpreters/__init__.py +4 -2
  139. camel/interpreters/base.py +2 -2
  140. camel/interpreters/docker/Dockerfile +14 -24
  141. camel/interpreters/docker_interpreter.py +5 -4
  142. camel/interpreters/e2b_interpreter.py +36 -3
  143. camel/interpreters/internal_python_interpreter.py +53 -4
  144. camel/interpreters/interpreter_error.py +2 -2
  145. camel/interpreters/ipython_interpreter.py +2 -2
  146. camel/interpreters/microsandbox_interpreter.py +395 -0
  147. camel/interpreters/subprocess_interpreter.py +2 -2
  148. camel/loaders/__init__.py +13 -4
  149. camel/loaders/apify_reader.py +2 -2
  150. camel/loaders/base_io.py +2 -2
  151. camel/loaders/base_loader.py +85 -0
  152. camel/loaders/chunkr_reader.py +11 -2
  153. camel/loaders/crawl4ai_reader.py +2 -2
  154. camel/loaders/firecrawl_reader.py +6 -6
  155. camel/loaders/jina_url_reader.py +2 -2
  156. camel/loaders/markitdown.py +2 -2
  157. camel/loaders/mineru_extractor.py +2 -2
  158. camel/loaders/mistral_reader.py +2 -2
  159. camel/loaders/scrapegraph_reader.py +2 -2
  160. camel/loaders/unstructured_io.py +2 -2
  161. camel/logger.py +5 -5
  162. camel/memories/__init__.py +2 -2
  163. camel/memories/agent_memories.py +86 -3
  164. camel/memories/base.py +36 -2
  165. camel/memories/blocks/__init__.py +2 -2
  166. camel/memories/blocks/chat_history_block.py +125 -7
  167. camel/memories/blocks/vectordb_block.py +10 -3
  168. camel/memories/context_creators/__init__.py +2 -2
  169. camel/memories/context_creators/score_based.py +109 -230
  170. camel/memories/records.py +90 -10
  171. camel/messages/__init__.py +2 -2
  172. camel/messages/base.py +178 -43
  173. camel/messages/conversion/__init__.py +2 -2
  174. camel/messages/conversion/alpaca.py +2 -2
  175. camel/messages/conversion/conversation_models.py +2 -2
  176. camel/messages/conversion/sharegpt/__init__.py +2 -2
  177. camel/messages/conversion/sharegpt/function_call_formatter.py +2 -2
  178. camel/messages/conversion/sharegpt/hermes/__init__.py +2 -2
  179. camel/messages/conversion/sharegpt/hermes/hermes_function_formatter.py +2 -2
  180. camel/messages/func_message.py +54 -17
  181. camel/models/__init__.py +18 -2
  182. camel/models/_utils.py +3 -3
  183. camel/models/aihubmix_model.py +83 -0
  184. camel/models/aiml_model.py +11 -18
  185. camel/models/amd_model.py +101 -0
  186. camel/models/anthropic_model.py +127 -20
  187. camel/models/aws_bedrock_model.py +12 -35
  188. camel/models/azure_openai_model.py +214 -115
  189. camel/models/base_audio_model.py +5 -3
  190. camel/models/base_model.py +378 -31
  191. camel/models/cerebras_model.py +83 -0
  192. camel/models/cohere_model.py +18 -49
  193. camel/models/cometapi_model.py +83 -0
  194. camel/models/crynux_model.py +11 -18
  195. camel/models/deepseek_model.py +20 -84
  196. camel/models/fish_audio_model.py +8 -2
  197. camel/models/function_gemma_model.py +889 -0
  198. camel/models/gemini_model.py +391 -52
  199. camel/models/groq_model.py +11 -19
  200. camel/models/internlm_model.py +11 -18
  201. camel/models/litellm_model.py +57 -49
  202. camel/models/lmstudio_model.py +17 -20
  203. camel/models/minimax_model.py +83 -0
  204. camel/models/mistral_model.py +20 -47
  205. camel/models/model_factory.py +39 -3
  206. camel/models/model_manager.py +26 -8
  207. camel/models/modelscope_model.py +13 -193
  208. camel/models/moonshot_model.py +183 -21
  209. camel/models/nebius_model.py +83 -0
  210. camel/models/nemotron_model.py +19 -9
  211. camel/models/netmind_model.py +11 -18
  212. camel/models/novita_model.py +11 -18
  213. camel/models/nvidia_model.py +11 -18
  214. camel/models/ollama_model.py +14 -21
  215. camel/models/openai_audio_models.py +2 -2
  216. camel/models/openai_compatible_model.py +190 -71
  217. camel/models/openai_model.py +192 -86
  218. camel/models/openrouter_model.py +11 -19
  219. camel/models/ppio_model.py +11 -18
  220. camel/models/qianfan_model.py +89 -0
  221. camel/models/qwen_model.py +13 -193
  222. camel/models/reka_model.py +23 -49
  223. camel/models/reward/__init__.py +2 -2
  224. camel/models/reward/base_reward_model.py +2 -2
  225. camel/models/reward/evaluator.py +2 -2
  226. camel/models/reward/nemotron_model.py +2 -2
  227. camel/models/reward/skywork_model.py +2 -2
  228. camel/models/samba_model.py +50 -75
  229. camel/models/sglang_model.py +90 -68
  230. camel/models/siliconflow_model.py +12 -35
  231. camel/models/stub_model.py +10 -7
  232. camel/models/togetherai_model.py +11 -18
  233. camel/models/vllm_model.py +10 -18
  234. camel/models/volcano_model.py +158 -19
  235. camel/models/watsonx_model.py +9 -47
  236. camel/models/yi_model.py +11 -18
  237. camel/models/zhipuai_model.py +70 -18
  238. camel/parsers/__init__.py +18 -0
  239. camel/parsers/mcp_tool_call_parser.py +176 -0
  240. camel/personas/__init__.py +2 -2
  241. camel/personas/persona.py +2 -2
  242. camel/personas/persona_hub.py +2 -2
  243. camel/prompts/__init__.py +2 -2
  244. camel/prompts/ai_society.py +2 -2
  245. camel/prompts/base.py +2 -2
  246. camel/prompts/code.py +2 -2
  247. camel/prompts/evaluation.py +2 -2
  248. camel/prompts/generate_text_embedding_data.py +2 -2
  249. camel/prompts/image_craft.py +2 -2
  250. camel/prompts/misalignment.py +2 -2
  251. camel/prompts/multi_condition_image_craft.py +2 -2
  252. camel/prompts/object_recognition.py +2 -2
  253. camel/prompts/persona_hub.py +3 -3
  254. camel/prompts/prompt_templates.py +2 -2
  255. camel/prompts/role_description_prompt_template.py +2 -2
  256. camel/prompts/solution_extraction.py +8 -8
  257. camel/prompts/task_prompt_template.py +2 -2
  258. camel/prompts/translation.py +2 -2
  259. camel/prompts/video_description_prompt.py +3 -3
  260. camel/responses/__init__.py +2 -2
  261. camel/responses/agent_responses.py +2 -2
  262. camel/retrievers/__init__.py +2 -2
  263. camel/retrievers/auto_retriever.py +3 -2
  264. camel/retrievers/base.py +2 -2
  265. camel/retrievers/bm25_retriever.py +2 -2
  266. camel/retrievers/cohere_rerank_retriever.py +2 -2
  267. camel/retrievers/hybrid_retrival.py +2 -2
  268. camel/retrievers/vector_retriever.py +2 -2
  269. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  270. camel/runtimes/__init__.py +2 -2
  271. camel/runtimes/api.py +79 -23
  272. camel/runtimes/base.py +2 -2
  273. camel/runtimes/configs.py +13 -13
  274. camel/runtimes/daytona_runtime.py +17 -18
  275. camel/runtimes/docker_runtime.py +12 -12
  276. camel/runtimes/llm_guard_runtime.py +26 -26
  277. camel/runtimes/remote_http_runtime.py +11 -11
  278. camel/runtimes/ubuntu_docker_runtime.py +2 -2
  279. camel/runtimes/utils/__init__.py +2 -2
  280. camel/runtimes/utils/function_risk_toolkit.py +2 -2
  281. camel/runtimes/utils/ignore_risk_toolkit.py +2 -2
  282. camel/schemas/__init__.py +2 -2
  283. camel/schemas/base.py +2 -2
  284. camel/schemas/openai_converter.py +3 -3
  285. camel/schemas/outlines_converter.py +2 -2
  286. camel/services/agent_openapi_server.py +380 -0
  287. camel/societies/__init__.py +4 -2
  288. camel/societies/babyagi_playing.py +2 -2
  289. camel/societies/role_playing.py +201 -80
  290. camel/societies/workforce/__init__.py +10 -3
  291. camel/societies/workforce/base.py +2 -2
  292. camel/societies/workforce/events.py +145 -0
  293. camel/societies/workforce/prompts.py +259 -33
  294. camel/societies/workforce/role_playing_worker.py +88 -31
  295. camel/societies/workforce/single_agent_worker.py +638 -40
  296. camel/societies/workforce/structured_output_handler.py +512 -0
  297. camel/societies/workforce/task_channel.py +182 -38
  298. camel/societies/workforce/utils.py +780 -65
  299. camel/societies/workforce/worker.py +92 -26
  300. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  301. camel/societies/workforce/workforce.py +5354 -372
  302. camel/societies/workforce/workforce_callback.py +103 -0
  303. camel/societies/workforce/workforce_logger.py +647 -0
  304. camel/societies/workforce/workforce_metrics.py +33 -0
  305. camel/storages/__init__.py +6 -2
  306. camel/storages/graph_storages/__init__.py +2 -2
  307. camel/storages/graph_storages/base.py +2 -2
  308. camel/storages/graph_storages/graph_element.py +2 -2
  309. camel/storages/graph_storages/nebula_graph.py +4 -4
  310. camel/storages/graph_storages/neo4j_graph.py +7 -7
  311. camel/storages/key_value_storages/__init__.py +2 -2
  312. camel/storages/key_value_storages/base.py +2 -2
  313. camel/storages/key_value_storages/in_memory.py +2 -2
  314. camel/storages/key_value_storages/json.py +17 -4
  315. camel/storages/key_value_storages/mem0_cloud.py +50 -49
  316. camel/storages/key_value_storages/redis.py +2 -2
  317. camel/storages/object_storages/__init__.py +2 -2
  318. camel/storages/object_storages/amazon_s3.py +2 -2
  319. camel/storages/object_storages/azure_blob.py +2 -2
  320. camel/storages/object_storages/base.py +2 -2
  321. camel/storages/object_storages/google_cloud.py +3 -3
  322. camel/storages/vectordb_storages/__init__.py +8 -2
  323. camel/storages/vectordb_storages/base.py +2 -2
  324. camel/storages/vectordb_storages/chroma.py +731 -0
  325. camel/storages/vectordb_storages/faiss.py +2 -2
  326. camel/storages/vectordb_storages/milvus.py +2 -2
  327. camel/storages/vectordb_storages/oceanbase.py +15 -15
  328. camel/storages/vectordb_storages/pgvector.py +349 -0
  329. camel/storages/vectordb_storages/qdrant.py +6 -6
  330. camel/storages/vectordb_storages/surreal.py +372 -0
  331. camel/storages/vectordb_storages/tidb.py +11 -8
  332. camel/storages/vectordb_storages/weaviate.py +2 -2
  333. camel/tasks/__init__.py +2 -2
  334. camel/tasks/task.py +348 -26
  335. camel/tasks/task_prompt.py +3 -3
  336. camel/terminators/__init__.py +2 -2
  337. camel/terminators/base.py +2 -2
  338. camel/terminators/response_terminator.py +2 -2
  339. camel/terminators/token_limit_terminator.py +2 -2
  340. camel/toolkits/__init__.py +57 -10
  341. camel/toolkits/aci_toolkit.py +66 -21
  342. camel/toolkits/arxiv_toolkit.py +8 -8
  343. camel/toolkits/ask_news_toolkit.py +2 -2
  344. camel/toolkits/async_browser_toolkit.py +4 -4
  345. camel/toolkits/audio_analysis_toolkit.py +3 -3
  346. camel/toolkits/base.py +106 -6
  347. camel/toolkits/bohrium_toolkit.py +2 -2
  348. camel/toolkits/browser_toolkit.py +34 -21
  349. camel/toolkits/browser_toolkit_commons.py +4 -4
  350. camel/toolkits/code_execution.py +31 -4
  351. camel/toolkits/context_summarizer_toolkit.py +684 -0
  352. camel/toolkits/craw4ai_toolkit.py +93 -0
  353. camel/toolkits/dappier_toolkit.py +12 -8
  354. camel/toolkits/data_commons_toolkit.py +2 -2
  355. camel/toolkits/dingtalk.py +1135 -0
  356. camel/toolkits/earth_science_toolkit.py +5367 -0
  357. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  358. camel/toolkits/excel_toolkit.py +905 -71
  359. camel/toolkits/file_toolkit.py +1402 -0
  360. camel/toolkits/function_tool.py +205 -27
  361. camel/toolkits/github_toolkit.py +109 -22
  362. camel/toolkits/gmail_toolkit.py +1839 -0
  363. camel/toolkits/google_calendar_toolkit.py +40 -6
  364. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  365. camel/toolkits/google_maps_toolkit.py +2 -2
  366. camel/toolkits/google_scholar_toolkit.py +2 -2
  367. camel/toolkits/human_toolkit.py +36 -12
  368. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  369. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  370. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  371. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1958 -0
  372. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  373. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +4589 -0
  374. camel/toolkits/hybrid_browser_toolkit/ts/package.json +33 -0
  375. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  376. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1940 -0
  377. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  378. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +589 -0
  379. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  380. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  381. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  382. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  383. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +129 -0
  384. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +27 -0
  385. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +325 -0
  386. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1037 -0
  387. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  388. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  389. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  390. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  391. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  392. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  393. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  394. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  395. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  396. camel/toolkits/image_analysis_toolkit.py +3 -6
  397. camel/toolkits/image_generation_toolkit.py +390 -0
  398. camel/toolkits/jina_reranker_toolkit.py +5 -6
  399. camel/toolkits/klavis_toolkit.py +7 -3
  400. camel/toolkits/linkedin_toolkit.py +2 -2
  401. camel/toolkits/markitdown_toolkit.py +104 -0
  402. camel/toolkits/math_toolkit.py +66 -12
  403. camel/toolkits/mcp_toolkit.py +412 -36
  404. camel/toolkits/memory_toolkit.py +7 -3
  405. camel/toolkits/meshy_toolkit.py +2 -2
  406. camel/toolkits/message_agent_toolkit.py +608 -0
  407. camel/toolkits/message_integration.py +728 -0
  408. camel/toolkits/microsoft_outlook_mail_toolkit.py +1885 -0
  409. camel/toolkits/mineru_toolkit.py +2 -2
  410. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  411. camel/toolkits/networkx_toolkit.py +2 -2
  412. camel/toolkits/note_taking_toolkit.py +277 -0
  413. camel/toolkits/notion_mcp_toolkit.py +224 -0
  414. camel/toolkits/notion_toolkit.py +2 -2
  415. camel/toolkits/open_api_specs/biztoc/__init__.py +2 -2
  416. camel/toolkits/open_api_specs/biztoc/ai-plugin.json +1 -1
  417. camel/toolkits/open_api_specs/coursera/__init__.py +2 -2
  418. camel/toolkits/open_api_specs/create_qr_code/__init__.py +2 -2
  419. camel/toolkits/open_api_specs/klarna/__init__.py +2 -2
  420. camel/toolkits/open_api_specs/nasa_apod/__init__.py +2 -2
  421. camel/toolkits/open_api_specs/outschool/__init__.py +2 -2
  422. camel/toolkits/open_api_specs/outschool/ai-plugin.json +1 -1
  423. camel/toolkits/open_api_specs/outschool/openapi.yaml +1 -1
  424. camel/toolkits/open_api_specs/outschool/paths/__init__.py +2 -2
  425. camel/toolkits/open_api_specs/outschool/paths/get_classes.py +2 -2
  426. camel/toolkits/open_api_specs/outschool/paths/search_teachers.py +2 -2
  427. camel/toolkits/open_api_specs/security_config.py +2 -2
  428. camel/toolkits/open_api_specs/speak/__init__.py +2 -2
  429. camel/toolkits/open_api_specs/web_scraper/__init__.py +2 -2
  430. camel/toolkits/open_api_specs/web_scraper/ai-plugin.json +1 -1
  431. camel/toolkits/open_api_specs/web_scraper/paths/__init__.py +2 -2
  432. camel/toolkits/open_api_specs/web_scraper/paths/scraper.py +2 -2
  433. camel/toolkits/open_api_toolkit.py +2 -2
  434. camel/toolkits/openbb_toolkit.py +7 -3
  435. camel/toolkits/origene_mcp_toolkit.py +56 -0
  436. camel/toolkits/page_script.js +53 -53
  437. camel/toolkits/playwright_mcp_toolkit.py +13 -31
  438. camel/toolkits/pptx_toolkit.py +36 -23
  439. camel/toolkits/pubmed_toolkit.py +2 -2
  440. camel/toolkits/pulse_mcp_search_toolkit.py +2 -2
  441. camel/toolkits/pyautogui_toolkit.py +2 -2
  442. camel/toolkits/reddit_toolkit.py +2 -2
  443. camel/toolkits/resend_toolkit.py +168 -0
  444. camel/toolkits/retrieval_toolkit.py +2 -2
  445. camel/toolkits/screenshot_toolkit.py +213 -0
  446. camel/toolkits/search_toolkit.py +606 -156
  447. camel/toolkits/searxng_toolkit.py +2 -2
  448. camel/toolkits/semantic_scholar_toolkit.py +2 -2
  449. camel/toolkits/slack_toolkit.py +108 -58
  450. camel/toolkits/sql_toolkit.py +712 -0
  451. camel/toolkits/stripe_toolkit.py +2 -2
  452. camel/toolkits/sympy_toolkit.py +3 -3
  453. camel/toolkits/task_planning_toolkit.py +5 -5
  454. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  455. camel/toolkits/terminal_toolkit/terminal_toolkit.py +1281 -0
  456. camel/toolkits/terminal_toolkit/utils.py +659 -0
  457. camel/toolkits/thinking_toolkit.py +3 -3
  458. camel/toolkits/twitter_toolkit.py +2 -2
  459. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  460. camel/toolkits/video_analysis_toolkit.py +109 -29
  461. camel/toolkits/video_download_toolkit.py +19 -16
  462. camel/toolkits/weather_toolkit.py +2 -2
  463. camel/toolkits/web_deploy_toolkit.py +1219 -0
  464. camel/toolkits/wechat_official_toolkit.py +483 -0
  465. camel/toolkits/whatsapp_toolkit.py +2 -2
  466. camel/toolkits/wolfram_alpha_toolkit.py +2 -2
  467. camel/toolkits/zapier_toolkit.py +7 -3
  468. camel/types/__init__.py +4 -4
  469. camel/types/agents/__init__.py +2 -2
  470. camel/types/agents/tool_calling_record.py +6 -3
  471. camel/types/enums.py +381 -41
  472. camel/types/mcp_registries.py +2 -2
  473. camel/types/openai_types.py +4 -4
  474. camel/types/unified_model_type.py +46 -10
  475. camel/utils/__init__.py +5 -2
  476. camel/utils/agent_context.py +41 -0
  477. camel/utils/async_func.py +2 -2
  478. camel/utils/chunker/__init__.py +2 -2
  479. camel/utils/chunker/base.py +2 -2
  480. camel/utils/chunker/code_chunker.py +2 -2
  481. camel/utils/chunker/uio_chunker.py +2 -2
  482. camel/utils/commons.py +38 -7
  483. camel/utils/constants.py +5 -2
  484. camel/utils/context_utils.py +1134 -0
  485. camel/utils/deduplication.py +2 -2
  486. camel/utils/filename.py +2 -2
  487. camel/utils/langfuse.py +18 -10
  488. camel/utils/mcp.py +140 -6
  489. camel/utils/mcp_client.py +48 -38
  490. camel/utils/message_summarizer.py +148 -0
  491. camel/utils/response_format.py +2 -2
  492. camel/utils/token_counting.py +45 -22
  493. camel/utils/tool_result.py +44 -0
  494. camel/verifiers/__init__.py +2 -2
  495. camel/verifiers/base.py +2 -2
  496. camel/verifiers/math_verifier.py +2 -2
  497. camel/verifiers/models.py +2 -2
  498. camel/verifiers/physics_verifier.py +2 -2
  499. camel/verifiers/python_verifier.py +2 -2
  500. {camel_ai-0.2.65.dist-info → camel_ai-0.2.83a6.dist-info}/METADATA +355 -117
  501. camel_ai-0.2.83a6.dist-info/RECORD +511 -0
  502. {camel_ai-0.2.65.dist-info → camel_ai-0.2.83a6.dist-info}/WHEEL +1 -1
  503. {camel_ai-0.2.65.dist-info → camel_ai-0.2.83a6.dist-info}/licenses/LICENSE +1 -1
  504. camel/loaders/pandas_reader.py +0 -368
  505. camel/toolkits/dalle_toolkit.py +0 -175
  506. camel/toolkits/file_write_toolkit.py +0 -444
  507. camel/toolkits/openai_agent_toolkit.py +0 -135
  508. camel/toolkits/terminal_toolkit.py +0 -1037
  509. camel_ai-0.2.65.dist-info/RECORD +0 -426
@@ -0,0 +1,728 @@
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
+ 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_func = self._create_bound_method_wrapper(
174
+ enhanced_func,
175
+ toolkit,
176
+ )
177
+ enhanced_methods[method_name] = enhanced_func
178
+ setattr(toolkit, method_name, enhanced_func)
179
+ original_get_tools_method = toolkit.get_tools
180
+
181
+ def enhanced_get_tools() -> List[FunctionTool]:
182
+ tools = []
183
+ for _, enhanced_method in enhanced_methods.items():
184
+ tools.append(FunctionTool(enhanced_method))
185
+ original_tools_list = original_get_tools_method()
186
+ for tool in original_tools_list:
187
+ if tool.func.__name__ not in enhanced_methods:
188
+ tools.append(tool)
189
+
190
+ return tools
191
+
192
+ toolkit.get_tools = enhanced_get_tools # type: ignore[method-assign]
193
+
194
+ # Also handle clone_for_new_session
195
+ # if it exists to ensure cloned toolkits
196
+ # also have message integration
197
+ if hasattr(toolkit, 'clone_for_new_session'):
198
+ original_clone_method = toolkit.clone_for_new_session
199
+ message_integration_instance = self
200
+
201
+ def enhanced_clone_for_new_session(new_session_id=None):
202
+ cloned_toolkit = original_clone_method(new_session_id)
203
+ return message_integration_instance.register_toolkits(
204
+ cloned_toolkit
205
+ )
206
+
207
+ toolkit.clone_for_new_session = enhanced_clone_for_new_session
208
+
209
+ return toolkit
210
+
211
+ def _create_bound_method_wrapper(
212
+ self, enhanced_func: Callable, toolkit_instance
213
+ ) -> Callable:
214
+ r"""Create a wrapper that mimics a bound method for _clone_tools.
215
+
216
+ This wrapper preserves the toolkit instance reference while maintaining
217
+ the enhanced messaging functionality.
218
+ """
219
+
220
+ # Create a wrapper that appears as a bound method to _clone_tools
221
+ @wraps(enhanced_func)
222
+ def bound_method_wrapper(*args, **kwargs):
223
+ return enhanced_func(*args, **kwargs)
224
+
225
+ # Make it appear as a bound method by setting __self__
226
+ bound_method_wrapper.__self__ = toolkit_instance # type: ignore[attr-defined]
227
+
228
+ # Preserve other important attributes
229
+ if hasattr(enhanced_func, '__signature__'):
230
+ bound_method_wrapper.__signature__ = enhanced_func.__signature__ # type: ignore[attr-defined]
231
+ if hasattr(enhanced_func, '__doc__'):
232
+ bound_method_wrapper.__doc__ = enhanced_func.__doc__
233
+
234
+ return bound_method_wrapper
235
+
236
+ def register_functions(
237
+ self,
238
+ functions: Union[List[FunctionTool], List[Callable]],
239
+ function_names: Optional[List[str]] = None,
240
+ ) -> List[FunctionTool]:
241
+ r"""Add messaging capabilities to a list of functions or FunctionTools.
242
+
243
+ This method enhances functions so they can send status messages to
244
+ users while executing. The enhanced functions will accept optional
245
+ messaging parameters that trigger status updates.
246
+
247
+ Args:
248
+ functions (Union[List[FunctionTool], List[Callable]]): List of
249
+ FunctionTool objects or callable functions to enhance.
250
+ function_names (Optional[List[str]]): List of specific function
251
+ names to modify. If None, messaging is added to all functions.
252
+
253
+ Returns:
254
+ List[FunctionTool]: List of enhanced FunctionTool objects
255
+
256
+ Example:
257
+ >>> # With FunctionTools
258
+ >>> tools = [FunctionTool(search_func), FunctionTool(analyze_func)]
259
+ >>> enhanced_tools = message_integration.register_functions
260
+ (tools)
261
+
262
+ >>> # With callable functions
263
+ >>> funcs = [search_web, analyze_data, generate_report]
264
+ >>> enhanced_tools = message_integration.register_functions
265
+ (
266
+ ... funcs,
267
+ ... function_names=['search_web', 'analyze_data']
268
+ ... )
269
+ """
270
+ enhanced_tools = []
271
+
272
+ for item in functions:
273
+ # Extract the function based on input type
274
+ if isinstance(item, FunctionTool):
275
+ func = item.func
276
+ elif callable(item):
277
+ func = item
278
+ else:
279
+ raise ValueError(
280
+ f"Invalid item type: {type(item)}. Expected "
281
+ f"FunctionTool or callable."
282
+ )
283
+
284
+ # Check if this function should be enhanced
285
+ if function_names is None or func.__name__ in function_names:
286
+ enhanced_func = self._add_messaging_to_tool(func)
287
+ enhanced_tools.append(FunctionTool(enhanced_func))
288
+ else:
289
+ # Return as FunctionTool regardless of input type
290
+ if isinstance(item, FunctionTool):
291
+ enhanced_tools.append(item)
292
+ else:
293
+ enhanced_tools.append(FunctionTool(func))
294
+
295
+ return enhanced_tools
296
+
297
+ def _add_messaging_to_tool(self, func: Callable) -> Callable:
298
+ r"""Add messaging parameters to a tool function.
299
+
300
+ This internal method modifies the function signature and docstring
301
+ to include optional messaging parameters that trigger status updates.
302
+ """
303
+ if getattr(func, "__message_integration_enhanced__", False):
304
+ logger.debug(
305
+ f"Function {func.__name__} already enhanced, skipping"
306
+ )
307
+ return func
308
+
309
+ # Get the original signature
310
+ original_sig = inspect.signature(func)
311
+
312
+ # Check if the function is async
313
+ is_async = inspect.iscoroutinefunction(func)
314
+
315
+ # Create new parameters for the enhanced function
316
+ new_params = list(original_sig.parameters.values())
317
+
318
+ # Determine which parameters to add based on handler type
319
+ if self.use_custom_handler:
320
+ # Use the custom handler's signature
321
+ handler_sig = inspect.signature(self.message_handler)
322
+ message_params = []
323
+
324
+ # Add parameters from the custom handler (excluding self if it's a
325
+ # method)
326
+ for param_name, param in handler_sig.parameters.items():
327
+ if param_name != 'self':
328
+ # Create a keyword-only parameter with the same annotation
329
+ # and default
330
+ new_param = inspect.Parameter(
331
+ param_name,
332
+ inspect.Parameter.KEYWORD_ONLY,
333
+ default=param.default
334
+ if param.default != inspect.Parameter.empty
335
+ else None,
336
+ annotation=param.annotation
337
+ if param.annotation != inspect.Parameter.empty
338
+ else inspect.Parameter.empty,
339
+ )
340
+ message_params.append(new_param)
341
+ else:
342
+ # Use default parameters for built-in handler
343
+ message_params = [
344
+ inspect.Parameter(
345
+ 'message_title',
346
+ inspect.Parameter.KEYWORD_ONLY,
347
+ default="",
348
+ annotation=str,
349
+ ),
350
+ inspect.Parameter(
351
+ 'message_description',
352
+ inspect.Parameter.KEYWORD_ONLY,
353
+ default="",
354
+ annotation=str,
355
+ ),
356
+ inspect.Parameter(
357
+ 'message_attachment',
358
+ inspect.Parameter.KEYWORD_ONLY,
359
+ default="",
360
+ annotation=str,
361
+ ),
362
+ ]
363
+
364
+ # Find where to insert the new parameters (before **kwargs if it
365
+ # exists)
366
+ insert_index = len(new_params)
367
+ for i, param in enumerate(new_params):
368
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
369
+ insert_index = i
370
+ break
371
+
372
+ # Insert the message parameters
373
+ for param in reversed(message_params):
374
+ new_params.insert(insert_index, param)
375
+
376
+ # Create the new signature
377
+ new_sig = original_sig.replace(parameters=new_params)
378
+
379
+ if is_async:
380
+
381
+ @wraps(func)
382
+ async def wrapper(*args, **kwargs):
383
+ try:
384
+ params = self.extract_params_callback(kwargs)
385
+ except KeyError:
386
+ return await func(*args, **kwargs)
387
+
388
+ # Check if we should send a message
389
+ should_send = False
390
+ if self.use_custom_handler:
391
+ should_send = any(
392
+ p is not None and p != '' for p in params
393
+ )
394
+ else:
395
+ # For default handler, params
396
+ # (title, description, attachment)
397
+ should_send = bool(params[0]) or bool(params[1])
398
+
399
+ # Send message if needed (handle async properly)
400
+ if should_send:
401
+ try:
402
+ if self.use_custom_handler:
403
+ # Check if message handler is async
404
+ if inspect.iscoroutinefunction(
405
+ self.message_handler
406
+ ):
407
+ await self.message_handler(*params)
408
+ else:
409
+ self.message_handler(*params)
410
+ else:
411
+ # For built-in handler, provide defaults
412
+ title, desc, attach = params
413
+ self.message_handler(
414
+ title or "Executing Tool",
415
+ desc or f"Running {func.__name__}",
416
+ attach or '',
417
+ )
418
+ except Exception as msg_error:
419
+ # Don't let message handler
420
+ # errors break the main function
421
+ logger.warning(f"Message handler error: {msg_error}")
422
+
423
+ # Execute the original function
424
+ # (kwargs have been modified to remove message params)
425
+ result = await func(*args, **kwargs)
426
+
427
+ return result
428
+ else:
429
+
430
+ @wraps(func)
431
+ def wrapper(*args, **kwargs):
432
+ # Extract parameters using the callback
433
+ # (this will modify kwargs by removing message params)
434
+ try:
435
+ params = self.extract_params_callback(kwargs)
436
+ except KeyError:
437
+ # If parameters are missing,
438
+ # just execute the original function
439
+ return func(*args, **kwargs)
440
+
441
+ # Check if we should send a message
442
+ should_send = False
443
+ if self.use_custom_handler:
444
+ should_send = any(
445
+ p is not None and p != '' for p in params
446
+ )
447
+ else:
448
+ should_send = bool(params[0]) or bool(params[1])
449
+
450
+ # Send message if needed
451
+ if should_send:
452
+ try:
453
+ if self.use_custom_handler:
454
+ self.message_handler(*params)
455
+ else:
456
+ # For built-in handler, provide defaults
457
+ title, desc, attach = params
458
+ self.message_handler(
459
+ title or "Executing Tool",
460
+ desc or f"Running {func.__name__}",
461
+ attach or '',
462
+ )
463
+ except Exception as msg_error:
464
+ logger.warning(f"Message handler error: {msg_error}")
465
+
466
+ result = func(*args, **kwargs)
467
+
468
+ return result
469
+
470
+ # Apply the new signature to the wrapper
471
+ wrapper.__signature__ = new_sig # type: ignore[attr-defined]
472
+
473
+ # Mark this function as enhanced by message integration
474
+ wrapper.__message_integration_enhanced__ = True # type: ignore[attr-defined]
475
+
476
+ # Create a hybrid approach:
477
+ # store toolkit instance info but preserve calling behavior
478
+ # We'll use a property-like
479
+ # approach to make __self__ available when needed
480
+ if hasattr(func, '__self__'):
481
+ toolkit_instance = func.__self__
482
+
483
+ # Store the toolkit instance as an attribute
484
+ # Use setattr to avoid MyPy type checking issues
485
+ wrapper.__toolkit_instance__ = toolkit_instance # type: ignore[attr-defined]
486
+
487
+ # Create a dynamic __self__ property
488
+ # that only appears during introspection
489
+ # but doesn't interfere with normal function calls
490
+ def get_self():
491
+ return toolkit_instance
492
+
493
+ # Only set __self__
494
+ # if we're being called in an introspection context
495
+ # (like from _clone_tools)
496
+ # Use setattr to avoid MyPy type checking issues
497
+ wrapper.__self__ = toolkit_instance # type: ignore[attr-defined]
498
+
499
+ # Enhance the docstring
500
+ if func.__doc__:
501
+ enhanced_doc = func.__doc__.rstrip()
502
+ lines = enhanced_doc.split('\n')
503
+
504
+ # Find where to insert parameters
505
+ insert_idx = self._find_docstring_insert_point(lines)
506
+
507
+ # Check if we need to create an Args section
508
+ has_args_section = any(
509
+ 'Args:' in line
510
+ or 'Arguments:' in line
511
+ or 'Parameters:' in line
512
+ for line in lines
513
+ )
514
+
515
+ if not has_args_section and insert_idx is not None:
516
+ # Need to create Args section
517
+ base_indent = self._get_base_indent(lines)
518
+
519
+ # Add blank line before Args section if needed
520
+ if insert_idx > 0 and lines[insert_idx - 1].strip():
521
+ lines.insert(insert_idx, "")
522
+ insert_idx += 1
523
+
524
+ # Add Args section header
525
+ lines.insert(insert_idx, f"{base_indent}Args:")
526
+ insert_idx += 1
527
+
528
+ # Get parameter documentation
529
+ if self.use_custom_handler and self.message_handler.__doc__:
530
+ # Extract parameter docs from custom handler's docstring
531
+ param_docs = self._extract_param_docs_from_handler()
532
+ else:
533
+ # Use default parameter docs
534
+ param_docs = [
535
+ "message_title (str, optional): Title for status "
536
+ "message to user.",
537
+ "message_description (str, optional): Description for "
538
+ "status message.",
539
+ "message_attachment (str, optional): File path or URL to "
540
+ "attach to message.",
541
+ ]
542
+
543
+ # Insert the parameter documentation
544
+ if insert_idx is not None and param_docs:
545
+ # Get proper indentation for parameters
546
+ indent = self._get_docstring_indent(lines, insert_idx)
547
+
548
+ # Insert each parameter doc with proper indentation
549
+ for doc in param_docs:
550
+ lines.insert(insert_idx, f"{indent}{doc}")
551
+ insert_idx += 1
552
+
553
+ wrapper.__doc__ = '\n'.join(lines)
554
+
555
+ return wrapper
556
+
557
+ def _find_docstring_insert_point(self, lines: List[str]) -> Optional[int]:
558
+ r"""Find where to insert parameters in a docstring."""
559
+ current_indent = ""
560
+
561
+ # First, look for existing Args section
562
+ for i, line in enumerate(lines):
563
+ if (
564
+ 'Args:' in line
565
+ or 'Arguments:' in line
566
+ or 'Parameters:' in line
567
+ ):
568
+ current_indent = line[: len(line) - len(line.lstrip())]
569
+ # Find where Args section ends by looking for the last
570
+ # parameter
571
+ last_param_idx = None
572
+ for j in range(i + 1, len(lines)):
573
+ stripped = lines[j].strip()
574
+ # If it's an empty line, skip it
575
+ if not stripped:
576
+ continue
577
+ # If it's a parameter line (has proper indentation and
578
+ # content)
579
+ if lines[j].startswith(current_indent + ' '):
580
+ last_param_idx = j
581
+ else:
582
+ # Hit a line with different indentation or a new
583
+ # section
584
+ if last_param_idx is not None:
585
+ return last_param_idx + 1
586
+ else:
587
+ # No parameters found, insert right after Args:
588
+ return i + 1
589
+ # Args is the last section, return after last parameter
590
+ if last_param_idx is not None:
591
+ return last_param_idx + 1
592
+ else:
593
+ # No parameters found, insert right after Args:
594
+ return i + 1
595
+
596
+ # No Args section, need to create one
597
+ # Try to insert before Returns/Yields/Raises/Examples sections
598
+ for i, line in enumerate(lines):
599
+ stripped = line.strip()
600
+ if any(
601
+ section in line
602
+ for section in [
603
+ 'Returns:',
604
+ 'Return:',
605
+ 'Yields:',
606
+ 'Raises:',
607
+ 'Examples:',
608
+ 'Example:',
609
+ 'Note:',
610
+ 'Notes:',
611
+ ]
612
+ ):
613
+ return i
614
+
615
+ # No special sections, add at the end
616
+ return len(lines)
617
+
618
+ def _get_docstring_indent(self, lines: List[str], insert_idx: int) -> str:
619
+ r"""Get the proper indentation for docstring parameters."""
620
+ # Look for Args: or similar section to match indentation
621
+ for i, line in enumerate(lines):
622
+ if (
623
+ 'Args:' in line
624
+ or 'Arguments:' in line
625
+ or 'Parameters:' in line
626
+ ):
627
+ base_indent = line[: len(line) - len(line.lstrip())]
628
+ # Look at the next line to see parameter indentation
629
+ if i + 1 < len(lines):
630
+ if lines[i + 1].strip():
631
+ next_indent = lines[i + 1][
632
+ : len(lines[i + 1]) - len(lines[i + 1].lstrip())
633
+ ]
634
+ if len(next_indent) > len(base_indent):
635
+ return next_indent
636
+ return base_indent + ' '
637
+
638
+ # No Args section, use base indent + 4 spaces
639
+ base_indent = self._get_base_indent(lines)
640
+ return base_indent + ' '
641
+
642
+ def _get_base_indent(self, lines: List[str]) -> str:
643
+ r"""Get the base indentation level of the docstring."""
644
+ # Find first non-empty line to determine base indentation
645
+ for line in lines:
646
+ if line.strip() and not line.strip().startswith('"""'):
647
+ return line[: len(line) - len(line.lstrip())]
648
+ return ' ' # Default indentation
649
+
650
+ def _extract_param_docs_from_handler(self) -> List[str]:
651
+ r"""Extract parameter documentation from the custom handler's
652
+ docstring.
653
+ """
654
+ if not self.message_handler.__doc__:
655
+ return []
656
+
657
+ docs = []
658
+ handler_sig = inspect.signature(self.message_handler)
659
+
660
+ # Parse the handler's docstring to find parameter descriptions
661
+ lines = self.message_handler.__doc__.split('\n')
662
+ in_args = False
663
+ param_docs = {}
664
+
665
+ for line in lines:
666
+ if (
667
+ 'Args:' in line
668
+ or 'Arguments:' in line
669
+ or 'Parameters:' in line
670
+ ):
671
+ in_args = True
672
+ continue
673
+ elif in_args and line.strip():
674
+ # Check if we've reached a new section
675
+ stripped_line = line.lstrip()
676
+ if any(
677
+ section in stripped_line
678
+ for section in [
679
+ 'Returns:',
680
+ 'Return:',
681
+ 'Yields:',
682
+ 'Raises:',
683
+ 'Examples:',
684
+ 'Example:',
685
+ 'Note:',
686
+ 'Notes:',
687
+ ]
688
+ ):
689
+ # End of Args section
690
+ break
691
+ elif in_args and ':' in line:
692
+ # Parse parameter documentation
693
+ parts = line.strip().split(':', 1)
694
+ if len(parts) == 2:
695
+ param_name = parts[0].strip()
696
+ param_desc = parts[1].strip()
697
+ # Extract just the parameter name (before any type
698
+ # annotation)
699
+ param_name = param_name.split()[0].strip()
700
+ param_docs[param_name] = param_desc
701
+
702
+ # Build documentation for each parameter
703
+ for param_name, param in handler_sig.parameters.items():
704
+ if param_name != 'self':
705
+ # Get type annotation
706
+ annotation = ''
707
+ if param.annotation != inspect.Parameter.empty:
708
+ if hasattr(param.annotation, '__name__'):
709
+ annotation = f" ({param.annotation.__name__})"
710
+ else:
711
+ annotation = f" ({param.annotation!s})"
712
+
713
+ # Check if optional
714
+ optional = (
715
+ ', optional'
716
+ if param.default != inspect.Parameter.empty
717
+ else ''
718
+ )
719
+
720
+ # Get description from parsed docs
721
+ desc = param_docs.get(
722
+ param_name,
723
+ f"Parameter for {self.message_handler.__name__}",
724
+ )
725
+
726
+ docs.append(f"{param_name}{annotation}{optional}: {desc}")
727
+
728
+ return docs