camel-ai 0.2.59__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 (506) 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 +5012 -902
  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 +39 -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 +94 -0
  28. camel/benchmarks/mock_website/mock_web.py +299 -0
  29. camel/benchmarks/mock_website/requirements.txt +3 -0
  30. camel/benchmarks/mock_website/shopping_mall/app.py +465 -0
  31. camel/benchmarks/mock_website/task.json +104 -0
  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 +26 -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 +8 -7
  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 +3 -3
  52. camel/configs/cometapi_config.py +106 -0
  53. camel/configs/crynux_config.py +94 -0
  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 +3 -3
  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 +8 -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 +3 -3
  75. camel/configs/samba_config.py +8 -6
  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_collector → data_collectors}/__init__.py +2 -2
  84. camel/{data_collector → data_collectors}/alpaca_collector.py +19 -10
  85. camel/{data_collector → data_collectors}/base.py +2 -2
  86. camel/{data_collector → data_collectors}/sharegpt_collector.py +3 -3
  87. camel/datagen/__init__.py +2 -2
  88. camel/datagen/cot_datagen.py +32 -37
  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 +24 -25
  92. camel/datagen/evol_instruct/templates.py +48 -48
  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 +3 -3
  113. camel/datasets/self_instruct_generator.py +2 -2
  114. camel/datasets/static_dataset.py +152 -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 +10 -3
  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 +4 -4
  124. camel/embeddings/together_embedding.py +2 -2
  125. camel/embeddings/vlm_embedding.py +11 -4
  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 +16 -3
  139. camel/interpreters/docker/Dockerfile +53 -7
  140. camel/interpreters/docker_interpreter.py +70 -11
  141. camel/interpreters/e2b_interpreter.py +59 -11
  142. camel/interpreters/internal_python_interpreter.py +81 -4
  143. camel/interpreters/interpreter_error.py +2 -2
  144. camel/interpreters/ipython_interpreter.py +23 -5
  145. camel/interpreters/microsandbox_interpreter.py +395 -0
  146. camel/interpreters/subprocess_interpreter.py +36 -4
  147. camel/loaders/__init__.py +17 -5
  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 +128 -93
  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 +148 -0
  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 +126 -9
  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 +98 -13
  170. camel/messages/__init__.py +2 -2
  171. camel/messages/base.py +193 -46
  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 +18 -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 +263 -63
  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 +81 -21
  192. camel/models/cometapi_model.py +83 -0
  193. camel/models/crynux_model.py +87 -0
  194. camel/models/deepseek_model.py +61 -59
  195. camel/models/fish_audio_model.py +8 -2
  196. camel/models/gemini_model.py +439 -30
  197. camel/models/groq_model.py +11 -19
  198. camel/models/internlm_model.py +11 -18
  199. camel/models/litellm_model.py +94 -34
  200. camel/models/lmstudio_model.py +17 -20
  201. camel/models/minimax_model.py +83 -0
  202. camel/models/mistral_model.py +84 -19
  203. camel/models/model_factory.py +49 -6
  204. camel/models/model_manager.py +33 -11
  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 +234 -27
  215. camel/models/openai_model.py +255 -39
  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 +90 -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 +117 -49
  227. camel/models/sglang_model.py +162 -42
  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 +69 -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 +23 -3
  262. camel/retrievers/base.py +2 -2
  263. camel/retrievers/bm25_retriever.py +3 -4
  264. camel/retrievers/cohere_rerank_retriever.py +2 -2
  265. camel/retrievers/hybrid_retrival.py +4 -4
  266. camel/retrievers/vector_retriever.py +2 -2
  267. camel/runtimes/Dockerfile.multi-toolkit +90 -0
  268. camel/{runtime → runtimes}/__init__.py +2 -2
  269. camel/runtimes/api.py +153 -0
  270. camel/{runtime → runtimes}/base.py +2 -2
  271. camel/{runtime → runtimes}/configs.py +13 -13
  272. camel/{runtime → runtimes}/daytona_runtime.py +18 -19
  273. camel/{runtime → runtimes}/docker_runtime.py +13 -13
  274. camel/{runtime → runtimes}/llm_guard_runtime.py +28 -28
  275. camel/{runtime → runtimes}/remote_http_runtime.py +12 -12
  276. camel/{runtime → runtimes}/ubuntu_docker_runtime.py +3 -3
  277. camel/{runtime → runtimes}/utils/__init__.py +2 -2
  278. camel/{runtime → runtimes}/utils/function_risk_toolkit.py +2 -2
  279. camel/{runtime → 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 +9 -5
  290. camel/societies/workforce/events.py +143 -0
  291. camel/societies/workforce/prompts.py +258 -33
  292. camel/societies/workforce/role_playing_worker.py +95 -30
  293. camel/societies/workforce/single_agent_worker.py +659 -30
  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 +784 -18
  297. camel/societies/workforce/worker.py +96 -28
  298. camel/societies/workforce/workflow_memory_manager.py +1746 -0
  299. camel/societies/workforce/workforce.py +5730 -366
  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 +10 -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 +12 -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 +712 -0
  324. camel/storages/vectordb_storages/milvus.py +2 -2
  325. camel/storages/vectordb_storages/oceanbase.py +16 -17
  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 +714 -0
  331. camel/tasks/__init__.py +2 -2
  332. camel/tasks/task.py +366 -27
  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 +58 -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 +174 -575
  343. camel/toolkits/audio_analysis_toolkit.py +3 -3
  344. camel/toolkits/base.py +65 -7
  345. camel/toolkits/bohrium_toolkit.py +318 -0
  346. camel/toolkits/browser_toolkit.py +306 -566
  347. camel/toolkits/browser_toolkit_commons.py +568 -0
  348. camel/toolkits/code_execution.py +67 -11
  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 +910 -70
  357. camel/toolkits/file_toolkit.py +1402 -0
  358. camel/toolkits/function_tool.py +128 -20
  359. camel/toolkits/github_toolkit.py +148 -43
  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 -3
  395. camel/toolkits/image_generation_toolkit.py +390 -0
  396. camel/toolkits/jina_reranker_toolkit.py +195 -79
  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 +841 -600
  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 +86 -74
  434. camel/toolkits/playwright_mcp_toolkit.py +27 -32
  435. camel/toolkits/pptx_toolkit.py +790 -0
  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 +134 -0
  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 +8 -3
  456. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  457. camel/toolkits/video_analysis_toolkit.py +112 -29
  458. camel/toolkits/video_download_toolkit.py +22 -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 +53 -25
  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 +454 -35
  469. camel/types/mcp_registries.py +2 -2
  470. camel/types/openai_types.py +4 -4
  471. camel/types/unified_model_type.py +43 -6
  472. camel/utils/__init__.py +20 -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 +65 -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 +258 -0
  484. camel/utils/mcp.py +140 -6
  485. camel/utils/mcp_client.py +1056 -0
  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.59.dist-info → camel_ai-0.2.82.dist-info}/METADATA +349 -108
  497. camel_ai-0.2.82.dist-info/RECORD +507 -0
  498. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/WHEEL +1 -1
  499. {camel_ai-0.2.59.dist-info → camel_ai-0.2.82.dist-info}/licenses/LICENSE +1 -1
  500. camel/loaders/pandas_reader.py +0 -368
  501. camel/runtime/api.py +0 -97
  502. camel/toolkits/dalle_toolkit.py +0 -171
  503. camel/toolkits/file_write_toolkit.py +0 -395
  504. camel/toolkits/openai_agent_toolkit.py +0 -135
  505. camel/toolkits/terminal_toolkit.py +0 -1037
  506. camel_ai-0.2.59.dist-info/RECORD +0 -410
@@ -1,4 +1,4 @@
1
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
1
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
2
2
  # Licensed under the Apache License, Version 2.0 (the "License");
3
3
  # you may not use this file except in compliance with the License.
4
4
  # You may obtain a copy of the License at
@@ -10,17 +10,22 @@
10
10
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
11
  # See the License for the specific language governing permissions and
12
12
  # limitations under the License.
13
- # ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
13
+ # ========= Copyright 2023-2025 @ CAMEL-AI.org. All Rights Reserved. =========
14
14
 
15
- from typing import List, Optional
16
-
17
- import pandas as pd
15
+ # Enables postponed evaluation of annotations (for string-based type hints)
16
+ import os
17
+ from pathlib import Path
18
+ from typing import TYPE_CHECKING, List, Optional, Union
18
19
 
19
20
  from camel.logger import get_logger
20
21
  from camel.toolkits.base import BaseToolkit
21
22
  from camel.toolkits.function_tool import FunctionTool
22
23
  from camel.utils import MCPServer
23
24
 
25
+ # Import only for type hints (not executed at runtime)
26
+ if TYPE_CHECKING:
27
+ from pandas import DataFrame
28
+
24
29
  logger = get_logger(__name__)
25
30
 
26
31
 
@@ -37,6 +42,7 @@ class ExcelToolkit(BaseToolkit):
37
42
  def __init__(
38
43
  self,
39
44
  timeout: Optional[float] = None,
45
+ working_directory: Optional[str] = None,
40
46
  ):
41
47
  r"""Initializes a new instance of the ExcelToolkit class.
42
48
 
@@ -44,14 +50,50 @@ class ExcelToolkit(BaseToolkit):
44
50
  timeout (Optional[float]): The timeout value for API requests
45
51
  in seconds. If None, no timeout is applied.
46
52
  (default: :obj:`None`)
53
+ working_directory (str, optional): The default directory for
54
+ output files. If not provided, it will be determined by the
55
+ `CAMEL_WORKDIR` environment variable (if set). If the
56
+ environment variable is not set, it defaults to
57
+ `camel_working_dir`.
47
58
  """
48
59
  super().__init__(timeout=timeout)
60
+ self.wb = None
61
+ if working_directory:
62
+ self.working_directory = Path(working_directory).resolve()
63
+ else:
64
+ camel_workdir = os.environ.get("CAMEL_WORKDIR")
65
+ if camel_workdir:
66
+ self.working_directory = Path(camel_workdir).resolve()
67
+ else:
68
+ self.working_directory = Path("./camel_working_dir").resolve()
69
+
70
+ self.working_directory.mkdir(parents=True, exist_ok=True)
71
+ logger.info(
72
+ f"ExcelToolkit initialized with output directory: "
73
+ f"{self.working_directory}"
74
+ )
75
+
76
+ def _validate_file_path(self, file_path: str) -> bool:
77
+ r"""Validate file path for security.
78
+
79
+ Args:
80
+ file_path (str): The file path to validate.
81
+
82
+ Returns:
83
+ bool: True if path is safe, False otherwise.
84
+ """
85
+ normalized_path = os.path.normpath(file_path)
86
+
87
+ if '..' in normalized_path.split(os.path.sep):
88
+ return False
89
+
90
+ return True
49
91
 
50
- def _convert_to_markdown(self, df: pd.DataFrame) -> str:
92
+ def _convert_to_markdown(self, df: "DataFrame") -> str:
51
93
  r"""Convert DataFrame to Markdown format table.
52
94
 
53
95
  Args:
54
- df (pd.DataFrame): DataFrame containing the Excel data.
96
+ df (DataFrame): DataFrame containing the Excel data.
55
97
 
56
98
  Returns:
57
99
  str: Markdown formatted table.
@@ -62,15 +104,24 @@ class ExcelToolkit(BaseToolkit):
62
104
  return str(md_table)
63
105
 
64
106
  def extract_excel_content(self, document_path: str) -> str:
65
- r"""Extract detailed cell information from an Excel file, including
66
- multiple sheets.
107
+ r"""Extract and analyze the full content of an Excel file (.xlsx/.xls/.
108
+ csv).
109
+
110
+ Use this tool to read and understand the structure and content of
111
+ Excel files. This is typically the first step when working with
112
+ existing Excel files.
67
113
 
68
114
  Args:
69
- document_path (str): The path of the Excel file.
115
+ document_path (str): The file path to the Excel file.
70
116
 
71
117
  Returns:
72
- str: Extracted excel information, including details of each sheet.
118
+ str: A comprehensive report containing:
119
+ - Sheet names and their content in markdown table format
120
+ - Detailed cell information including values, colors, and
121
+ positions
122
+ - Formatted data that's easy to understand and analyze
73
123
  """
124
+ import pandas as pd
74
125
  from openpyxl import load_workbook
75
126
  from xls2xlsx import XLS2XLSX
76
127
 
@@ -79,6 +130,9 @@ class ExcelToolkit(BaseToolkit):
79
130
  f": {document_path}"
80
131
  )
81
132
 
133
+ if not self._validate_file_path(document_path):
134
+ return "Error: Invalid file path."
135
+
82
136
  if not (
83
137
  document_path.endswith("xls")
84
138
  or document_path.endswith("xlsx")
@@ -90,6 +144,9 @@ class ExcelToolkit(BaseToolkit):
90
144
  f"It is not excel format. Please try other ways."
91
145
  )
92
146
 
147
+ if not os.path.exists(document_path):
148
+ return f"Error: File {document_path} does not exist."
149
+
93
150
  if document_path.endswith("csv"):
94
151
  try:
95
152
  df = pd.read_csv(document_path)
@@ -105,75 +162,835 @@ class ExcelToolkit(BaseToolkit):
105
162
  x2x.to_xlsx(output_path)
106
163
  document_path = output_path
107
164
 
108
- # Load the Excel workbook
109
- wb = load_workbook(document_path, data_only=True)
110
- sheet_info_list = []
165
+ try:
166
+ # Load the Excel workbook
167
+ wb = load_workbook(document_path, data_only=True)
168
+ sheet_info_list = []
111
169
 
112
- # Iterate through all sheets
113
- for sheet in wb.sheetnames:
114
- ws = wb[sheet]
115
- cell_info_list = []
170
+ # Iterate through all sheets
171
+ for sheet in wb.sheetnames:
172
+ ws = wb[sheet]
173
+ cell_info_list = []
116
174
 
117
- for row in ws.iter_rows():
118
- for cell in row:
119
- row_num = cell.row
120
- col_letter = cell.column_letter
121
-
122
- cell_value = cell.value
123
-
124
- font_color = None
125
- if (
126
- cell.font
127
- and cell.font.color
128
- and "rgb=None" not in str(cell.font.color)
129
- ): # Handle font color
130
- font_color = cell.font.color.rgb
131
-
132
- fill_color = None
133
- if (
134
- cell.fill
135
- and cell.fill.fgColor
136
- and "rgb=None" not in str(cell.fill.fgColor)
137
- ): # Handle fill color
138
- fill_color = cell.fill.fgColor.rgb
139
-
140
- cell_info_list.append(
141
- {
142
- "index": f"{row_num}{col_letter}",
143
- "value": cell_value,
144
- "font_color": font_color,
145
- "fill_color": fill_color,
146
- }
147
- )
148
-
149
- # Convert the sheet to a DataFrame and then to markdown
150
- sheet_df = pd.read_excel(
151
- document_path, sheet_name=sheet, engine='openpyxl'
152
- )
153
- markdown_content = self._convert_to_markdown(sheet_df)
154
-
155
- # Collect all information for the sheet
156
- sheet_info = {
157
- "sheet_name": sheet,
158
- "cell_info_list": cell_info_list,
159
- "markdown_content": markdown_content,
160
- }
161
- sheet_info_list.append(sheet_info)
162
-
163
- result_str = ""
164
- for sheet_info in sheet_info_list:
165
- result_str += f"""
175
+ for row in ws.iter_rows():
176
+ for cell in row:
177
+ # Skip cells that don't have proper coordinates (like
178
+ # merged cells)
179
+ if (
180
+ not hasattr(cell, 'column_letter')
181
+ or cell.value is None
182
+ ):
183
+ continue
184
+
185
+ row_num = cell.row
186
+ # Use getattr with fallback for column_letter
187
+ col_letter = getattr(cell, 'column_letter', 'A')
188
+
189
+ cell_value = cell.value
190
+
191
+ font_color = None
192
+ if (
193
+ cell.font
194
+ and cell.font.color
195
+ and "rgb=None" not in str(cell.font.color)
196
+ ): # Handle font color
197
+ font_color = cell.font.color.rgb
198
+
199
+ fill_color = None
200
+ if (
201
+ cell.fill
202
+ and cell.fill.fgColor
203
+ and "rgb=None" not in str(cell.fill.fgColor)
204
+ ): # Handle fill color
205
+ fill_color = cell.fill.fgColor.rgb
206
+
207
+ cell_info_list.append(
208
+ {
209
+ "index": f"{row_num}{col_letter}",
210
+ "value": cell_value,
211
+ "font_color": font_color,
212
+ "fill_color": fill_color,
213
+ }
214
+ )
215
+
216
+ # Convert the sheet to a DataFrame and then to markdown
217
+ sheet_df = pd.read_excel(
218
+ document_path, sheet_name=sheet, engine='openpyxl'
219
+ )
220
+ markdown_content = self._convert_to_markdown(sheet_df)
221
+
222
+ # Collect all information for the sheet
223
+ sheet_info = {
224
+ "sheet_name": sheet,
225
+ "cell_info_list": cell_info_list,
226
+ "markdown_content": markdown_content,
227
+ }
228
+ sheet_info_list.append(sheet_info)
229
+
230
+ result_str = ""
231
+ for sheet_info in sheet_info_list:
232
+ result_str += f"""
166
233
  Sheet Name: {sheet_info['sheet_name']}
167
234
  Cell information list:
168
235
  {sheet_info['cell_info_list']}
169
-
236
+
170
237
  Markdown View of the content:
171
238
  {sheet_info['markdown_content']}
172
-
239
+
173
240
  {'-'*40}
174
241
  """
175
242
 
176
- return result_str
243
+ return result_str
244
+ except Exception as e:
245
+ logger.error(f"Failed to process Excel file {document_path}: {e}")
246
+ return f"Failed to process Excel file {document_path}: {e}"
247
+
248
+ def _save_workbook(self, file_path: str) -> str:
249
+ r"""Save the current workbook to file.
250
+
251
+ Args:
252
+ file_path (str): The path to save the workbook.
253
+
254
+ Returns:
255
+ str: Success or error message.
256
+ """
257
+ if not self.wb:
258
+ return "Error: No workbook loaded to save."
259
+
260
+ if not self._validate_file_path(file_path):
261
+ return "Error: Invalid file path for saving."
262
+
263
+ try:
264
+ self.wb.save(file_path)
265
+ return f"Workbook saved successfully to {file_path}"
266
+ except Exception as e:
267
+ logger.error(f"Failed to save workbook: {e}")
268
+ return f"Error: Failed to save workbook: {e}"
269
+
270
+ def save_workbook(self, filename: str) -> str:
271
+ r"""Save the current in-memory workbook to a file.
272
+
273
+ Args:
274
+ filename (str): The filename to save the workbook. Must end with
275
+ .xlsx extension. The file will be saved in self.
276
+ working_directory.
277
+
278
+ Returns:
279
+ str: Success message or error details.
280
+ """
281
+ if not self.wb:
282
+ return "Error: No workbook is currently loaded in memory."
283
+
284
+ # Validate filename
285
+ if not filename:
286
+ return "Error: Filename is required."
287
+
288
+ if not filename.endswith('.xlsx'):
289
+ return "Error: Filename must end with .xlsx extension."
290
+
291
+ # Create full path in working directory
292
+ file_path = self.working_directory / filename
293
+ resolved_file_path = str(file_path.resolve())
294
+
295
+ return self._save_workbook(resolved_file_path)
296
+
297
+ def create_workbook(
298
+ self,
299
+ filename: Optional[str] = None,
300
+ sheet_name: Optional[str] = None,
301
+ data: Optional[List[List[Union[str, int, float, None]]]] = None,
302
+ ) -> str:
303
+ r"""Create a new Excel workbook from scratch.
304
+
305
+ Use this when you need to create a new Excel file. This sets up the
306
+ toolkit to work with the new file and optionally adds initial data.
307
+
308
+ Args:
309
+ filename (Optional[str]): The filename for the workbook. Must end
310
+ with .xlsx extension. The file will be saved in
311
+ self.working_directory. (default: :obj:`None`)
312
+ sheet_name (Optional[str]): Name for the first sheet. If None,
313
+ creates "Sheet1". (default: :obj:`None`)
314
+ data (Optional[List[List[Union[str, int, float, None]]]]): Initial
315
+ data as rows. Each inner list is one row. (default:
316
+ :obj:`None`)
317
+
318
+ Returns:
319
+ str: Success confirmation message or error details
320
+ """
321
+ from openpyxl import Workbook
322
+
323
+ # Validate filename
324
+ if filename is None:
325
+ return "Error: Filename is required."
326
+
327
+ if not filename.endswith('.xlsx'):
328
+ return "Error: Filename must end with .xlsx extension."
329
+
330
+ # Create full path in working directory
331
+ file_path = self.working_directory / filename
332
+ resolved_file_path = str(file_path.resolve())
333
+
334
+ if not self._validate_file_path(resolved_file_path):
335
+ return "Error: Invalid file path."
336
+
337
+ # Check if file already exists
338
+ if os.path.exists(resolved_file_path):
339
+ return (
340
+ f"Error: File {filename} already exists in "
341
+ f"{self.working_directory}."
342
+ )
343
+
344
+ try:
345
+ # Create a new workbook
346
+ wb = Workbook()
347
+ self.wb = wb
348
+
349
+ # Handle sheet creation safely
350
+ if sheet_name:
351
+ # Remove the default sheet safely
352
+ default_sheet = wb.active
353
+ if default_sheet is not None:
354
+ wb.remove(default_sheet)
355
+ ws = wb.create_sheet(sheet_name)
356
+ else:
357
+ ws = wb.active
358
+ if ws is not None and sheet_name is None:
359
+ sheet_name = "Sheet1"
360
+ ws.title = sheet_name
361
+
362
+ # Add data if provided
363
+ if data and ws is not None:
364
+ for row in data:
365
+ ws.append(row)
366
+
367
+ # Save the workbook to the specified file path
368
+ wb.save(resolved_file_path)
369
+
370
+ return f"Workbook created successfully at {resolved_file_path}"
371
+ except Exception as e:
372
+ logger.error(f"Failed to create workbook: {e}")
373
+ return f"Error: Failed to create workbook: {e}"
374
+
375
+ def delete_workbook(self, filename: str) -> str:
376
+ r"""Delete a spreadsheet file from the working directory.
377
+
378
+ Args:
379
+ filename (str): The filename to delete. Must end with .xlsx
380
+ extension. The file will be deleted from self.
381
+ working_directory.
382
+
383
+ Returns:
384
+ str: Success message or error details.
385
+ """
386
+ # Validate filename
387
+ if not filename:
388
+ return "Error: Filename is required."
389
+
390
+ if not filename.endswith('.xlsx'):
391
+ return "Error: Filename must end with .xlsx extension."
392
+
393
+ # Create full path in working directory
394
+ file_path = self.working_directory / filename
395
+ target_path = str(file_path.resolve())
396
+
397
+ if not self._validate_file_path(target_path):
398
+ return "Error: Invalid file path."
399
+
400
+ if not os.path.exists(target_path):
401
+ return (
402
+ f"Error: File {filename} does not exist in "
403
+ f"{self.working_directory}."
404
+ )
405
+
406
+ try:
407
+ os.remove(target_path)
408
+ # Clean up workbook if one is loaded
409
+ self.wb = None
410
+ return (
411
+ f"Workbook {filename} deleted successfully from "
412
+ f"{self.working_directory}."
413
+ )
414
+ except Exception as e:
415
+ logger.error(f"Failed to delete workbook: {e}")
416
+ return f"Error: Failed to delete workbook {filename}: {e}"
417
+
418
+ def create_sheet(
419
+ self,
420
+ sheet_name: str,
421
+ data: Optional[List[List[Union[str, int, float, None]]]] = None,
422
+ ) -> str:
423
+ r"""Create a new sheet with the given sheet name and data.
424
+
425
+ Args:
426
+ sheet_name (str): The name of the sheet to create.
427
+ data (Optional[List[List[Union[str, int, float, None]]]]):
428
+ The data to write to the sheet.
429
+
430
+ Returns:
431
+ str: Success message.
432
+ """
433
+ if not self.wb:
434
+ return (
435
+ "Error: Workbook not initialized. "
436
+ "Please create a workbook first."
437
+ )
438
+
439
+ if sheet_name in self.wb.sheetnames:
440
+ return f"Error: Sheet {sheet_name} already exists."
441
+
442
+ try:
443
+ ws = self.wb.create_sheet(sheet_name)
444
+ if data:
445
+ for row in data:
446
+ ws.append(row)
447
+
448
+ return f"Sheet {sheet_name} created successfully."
449
+ except Exception as e:
450
+ logger.error(f"Failed to create sheet: {e}")
451
+ return f"Error: Failed to create sheet {sheet_name}: {e}"
452
+
453
+ def delete_sheet(self, sheet_name: str) -> str:
454
+ r"""Delete a sheet from the workbook.
455
+
456
+ Args:
457
+ sheet_name (str): The name of the sheet to delete.
458
+
459
+ Returns:
460
+ str: Success message.
461
+ """
462
+ if not self.wb:
463
+ return "Error: Workbook not initialized."
464
+
465
+ if sheet_name not in self.wb.sheetnames:
466
+ return f"Sheet {sheet_name} does not exist."
467
+
468
+ if len(self.wb.sheetnames) == 1:
469
+ return "Cannot delete the last remaining sheet in the workbook."
470
+
471
+ try:
472
+ ws = self.wb[sheet_name]
473
+ self.wb.remove(ws)
474
+ return f"Sheet {sheet_name} deleted successfully."
475
+ except Exception as e:
476
+ logger.error(f"Failed to delete sheet: {e}")
477
+ return f"Error: Failed to delete sheet {sheet_name}: {e}"
478
+
479
+ def clear_sheet(self, sheet_name: str) -> str:
480
+ r"""Clear all data from a sheet.
481
+
482
+ Args:
483
+ sheet_name (str): The name of the sheet to clear.
484
+
485
+ Returns:
486
+ str: Success message.
487
+ """
488
+ if not self.wb:
489
+ return "Error: Workbook not initialized."
490
+
491
+ if sheet_name not in self.wb.sheetnames:
492
+ return f"Sheet {sheet_name} does not exist."
493
+
494
+ try:
495
+ ws = self.wb[sheet_name]
496
+
497
+ # Clear all cells
498
+ for row in ws.iter_rows():
499
+ for cell in row:
500
+ cell.value = None
501
+
502
+ return f"Sheet {sheet_name} cleared successfully."
503
+ except Exception as e:
504
+ logger.error(f"Failed to clear sheet: {e}")
505
+ return f"Error: Failed to clear sheet {sheet_name}: {e}"
506
+
507
+ def delete_rows(
508
+ self, sheet_name: str, start_row: int, end_row: Optional[int] = None
509
+ ) -> str:
510
+ r"""Delete rows from a sheet.
511
+
512
+ Use this to remove unwanted rows. You can delete single rows or ranges.
513
+
514
+ Args:
515
+ sheet_name (str): Name of the sheet to modify.
516
+ start_row (int): Starting row number to delete (1-based, where 1
517
+ is first row).
518
+ end_row (Optional[int]): Ending row number to delete (1-based).
519
+ If None, deletes only start_row. (default: :obj:`None`)
520
+
521
+ Returns:
522
+ str: Success confirmation message or error details
523
+ """
524
+ if not self.wb:
525
+ return "Error: Workbook not initialized."
526
+
527
+ if sheet_name not in self.wb.sheetnames:
528
+ return f"Sheet {sheet_name} does not exist."
529
+
530
+ ws = self.wb[sheet_name]
531
+
532
+ if end_row is None:
533
+ end_row = start_row
534
+
535
+ # Delete rows (openpyxl uses 1-based indexing)
536
+ num_rows = end_row - start_row + 1
537
+ ws.delete_rows(start_row, num_rows)
538
+
539
+ return (
540
+ f"Deleted rows {start_row} to {end_row} from sheet "
541
+ f"{sheet_name} successfully."
542
+ )
543
+
544
+ def delete_columns(
545
+ self, sheet_name: str, start_col: int, end_col: Optional[int] = None
546
+ ) -> str:
547
+ r"""Delete columns from a sheet.
548
+
549
+ Use this to remove unwanted columns. You can delete single columns or
550
+ ranges.
551
+
552
+ Args:
553
+ sheet_name (str): Name of the sheet to modify.
554
+ start_col (int): Starting column number to delete (1-based, where
555
+ 1 is column A).
556
+ end_col (Optional[int]): Ending column number to delete (1-based).
557
+ If None, deletes only start_col. (default: :obj:`None`)
558
+
559
+ Returns:
560
+ str: Success confirmation message or error details
561
+ """
562
+ if not self.wb:
563
+ return "Error: Workbook not initialized."
564
+
565
+ if sheet_name not in self.wb.sheetnames:
566
+ return f"Sheet {sheet_name} does not exist."
567
+
568
+ ws = self.wb[sheet_name]
569
+
570
+ if end_col is None:
571
+ end_col = start_col
572
+
573
+ # Delete columns (openpyxl uses 1-based indexing)
574
+ num_cols = end_col - start_col + 1
575
+ ws.delete_cols(start_col, num_cols)
576
+
577
+ return (
578
+ f"Deleted columns {start_col} to {end_col} from sheet "
579
+ f"{sheet_name} successfully."
580
+ )
581
+
582
+ def get_cell_value(
583
+ self, sheet_name: str, cell_reference: str
584
+ ) -> Union[str, int, float, None]:
585
+ r"""Get the value from a specific cell.
586
+
587
+ Use this to read a single cell's value. Useful for checking specific
588
+ data points or getting values for calculations.
589
+
590
+ Args:
591
+ sheet_name (str): Name of the sheet containing the cell.
592
+ cell_reference (str): Excel-style cell reference (column letter +
593
+ row number).
594
+
595
+ Returns:
596
+ Union[str, int, float, None]: The cell's value or error message
597
+ Returns None for empty cells.
598
+ """
599
+ if not self.wb:
600
+ return "Error: Workbook not initialized."
601
+
602
+ if sheet_name not in self.wb.sheetnames:
603
+ return f"Error: Sheet {sheet_name} does not exist."
604
+
605
+ ws = self.wb[sheet_name]
606
+ return ws[cell_reference].value
607
+
608
+ def set_cell_value(
609
+ self,
610
+ sheet_name: str,
611
+ cell_reference: str,
612
+ value: Union[str, int, float, None],
613
+ ) -> str:
614
+ r"""Set the value of a specific cell.
615
+
616
+ Use this to update individual cells with new values. Useful for
617
+ corrections, calculations, or updating specific data points.
618
+
619
+ Args:
620
+ sheet_name (str): Name of the sheet containing the cell.
621
+ cell_reference (str): Excel-style cell reference (column letter +
622
+ row number).
623
+ value (Union[str, int, float, None]): New value for the cell.
624
+ (default: :obj:`None`)
625
+
626
+ Returns:
627
+ str: Success confirmation message or error details.
628
+ """
629
+ if not self.wb:
630
+ return "Error: Workbook not initialized."
631
+
632
+ if sheet_name not in self.wb.sheetnames:
633
+ return f"Sheet {sheet_name} does not exist."
634
+
635
+ try:
636
+ ws = self.wb[sheet_name]
637
+ # Handle None values properly - openpyxl doesn't accept None
638
+ # directly
639
+ if value is None:
640
+ ws[cell_reference].value = None
641
+ else:
642
+ ws[cell_reference] = value
643
+ return (
644
+ f"Cell {cell_reference} updated successfully in sheet "
645
+ f"{sheet_name}."
646
+ )
647
+ except Exception as e:
648
+ logger.error(f"Failed to set cell value: {e}")
649
+ return f"Error: Failed to set cell value: {e}"
650
+
651
+ def get_column_data(
652
+ self, sheet_name: str, column: Union[int, str]
653
+ ) -> Union[List[Union[str, int, float, None]], str]:
654
+ r"""Get all data from a specific column.
655
+
656
+ Use this to extract all values from a column for analysis or
657
+ processing.
658
+
659
+ Args:
660
+ sheet_name (str): Name of the sheet to read from.
661
+ column (Union[int, str]): Column identifier - either number
662
+ (1-based) or letter.
663
+
664
+ Returns:
665
+ Union[List[Union[str, int, float, None]], str]:
666
+ List of all non-empty values in the column or error message
667
+ """
668
+ if not self.wb:
669
+ return "Error: Workbook not initialized."
670
+
671
+ if sheet_name not in self.wb.sheetnames:
672
+ return f"Error: Sheet {sheet_name} does not exist."
673
+
674
+ ws = self.wb[sheet_name]
675
+
676
+ if isinstance(column, str):
677
+ col_letter = column.upper()
678
+ else:
679
+ from openpyxl.utils import ( # type: ignore[import]
680
+ get_column_letter,
681
+ )
682
+
683
+ col_letter = get_column_letter(column)
684
+
685
+ column_data = []
686
+ for cell in ws[col_letter]:
687
+ if cell.value is not None:
688
+ column_data.append(cell.value)
689
+
690
+ return column_data
691
+
692
+ def find_cells(
693
+ self,
694
+ sheet_name: str,
695
+ search_value: Union[str, int, float],
696
+ search_column: Optional[Union[int, str]] = None,
697
+ ) -> Union[List[str], str]:
698
+ r"""Find cells containing a specific value.
699
+
700
+ Use this to locate where specific data appears in the sheet.
701
+
702
+ Args:
703
+ sheet_name (str): Name of the sheet to search in.
704
+ search_value (Union[str, int, float]): Value to search for.
705
+ search_column (Optional[Union[int, str]]): Limit search to
706
+ specific column. If None, searches entire sheet. (default:
707
+ :obj:`None`)
708
+
709
+ Returns:
710
+ Union[List[str], str]: List of cell references (like "A5", "B12")
711
+ where the value was found, or error message.
712
+ """
713
+ if not self.wb:
714
+ return "Error: Workbook not initialized."
715
+
716
+ if sheet_name not in self.wb.sheetnames:
717
+ return f"Error: Sheet {sheet_name} does not exist."
718
+
719
+ ws = self.wb[sheet_name]
720
+ found_cells = []
721
+
722
+ if search_column:
723
+ # Search in specific column
724
+ if isinstance(search_column, str):
725
+ col_letter = search_column.upper()
726
+ for cell in ws[col_letter]:
727
+ if cell.value == search_value:
728
+ found_cells.append(cell.coordinate)
729
+ else:
730
+ from openpyxl.utils import ( # type: ignore[import]
731
+ get_column_letter,
732
+ )
733
+
734
+ col_letter = get_column_letter(search_column)
735
+ for cell in ws[col_letter]:
736
+ if cell.value == search_value:
737
+ found_cells.append(cell.coordinate)
738
+ else:
739
+ # Search entire sheet
740
+ for row in ws.iter_rows():
741
+ for cell in row:
742
+ if cell.value == search_value:
743
+ found_cells.append(cell.coordinate)
744
+
745
+ return found_cells
746
+
747
+ def get_range_values(
748
+ self, sheet_name: str, cell_range: str
749
+ ) -> Union[List[List[Union[str, int, float, None]]], str]:
750
+ r"""Get values from a specific range of cells.
751
+
752
+ Use this to read a rectangular block of cells at once.
753
+
754
+ Args:
755
+ sheet_name (str): Name of the sheet to read from.
756
+ cell_range (str): Range in Excel format (start:end).
757
+
758
+ Returns:
759
+ Union[List[List[Union[str, int, float, None]]], str]:
760
+ 2D list where each inner list is a row of cell values, or
761
+ error message.
762
+ """
763
+ if not self.wb:
764
+ return "Error: Workbook not initialized."
765
+
766
+ if sheet_name not in self.wb.sheetnames:
767
+ return f"Error: Sheet {sheet_name} does not exist."
768
+
769
+ ws = self.wb[sheet_name]
770
+ range_values = []
771
+
772
+ for row in ws[cell_range]:
773
+ row_values = []
774
+ for cell in row:
775
+ row_values.append(cell.value)
776
+ range_values.append(row_values)
777
+
778
+ return range_values
779
+
780
+ def set_range_values(
781
+ self,
782
+ sheet_name: str,
783
+ cell_range: str,
784
+ values: List[List[Union[str, int, float, None]]],
785
+ ) -> str:
786
+ r"""Set values for a specific range of cells.
787
+
788
+ Use this to update multiple cells at once with a 2D array of data.
789
+
790
+ Args:
791
+ sheet_name (str): Name of the sheet to modify.
792
+ cell_range (str): Range in Excel format to update.
793
+ values (List[List[Union[str, int, float, None]]]): 2D array of
794
+ values. Each inner list represents a row.
795
+
796
+ Returns:
797
+ str: Success confirmation message or error details.
798
+ """
799
+ if not self.wb:
800
+ return "Error: Workbook not initialized."
801
+
802
+ if sheet_name not in self.wb.sheetnames:
803
+ return f"Error: Sheet {sheet_name} does not exist."
804
+
805
+ ws = self.wb[sheet_name]
806
+
807
+ # Get the range
808
+ cell_range_obj = ws[cell_range]
809
+
810
+ # If it's a single row or column, convert to 2D format
811
+ if not isinstance(cell_range_obj[0], tuple):
812
+ cell_range_obj = [cell_range_obj]
813
+
814
+ for row_idx, row in enumerate(cell_range_obj):
815
+ if row_idx < len(values):
816
+ for col_idx, cell in enumerate(row):
817
+ if col_idx < len(values[row_idx]):
818
+ cell.value = values[row_idx][col_idx]
819
+
820
+ return f"Values set for range {cell_range} in sheet {sheet_name}."
821
+
822
+ def export_sheet_to_csv(self, sheet_name: str, csv_filename: str) -> str:
823
+ r"""Export a specific sheet to CSV format.
824
+
825
+ Use this to convert Excel sheets to CSV files for compatibility or
826
+ data exchange.
827
+
828
+ Args:
829
+ sheet_name (str): Name of the sheet to export.
830
+ csv_filename (str): Filename for the CSV file. Must end with .csv
831
+ extension. The file will be saved in self.working_directory.
832
+
833
+ Returns:
834
+ str: Success confirmation message or error details.
835
+ """
836
+ if not self.wb:
837
+ return (
838
+ "Error: No workbook is currently loaded. Use "
839
+ "extract_excel_content to load a workbook first."
840
+ )
841
+
842
+ if sheet_name not in self.wb.sheetnames:
843
+ return (
844
+ f"Error: Sheet {sheet_name} does not exist in the current "
845
+ "workbook."
846
+ )
847
+
848
+ # Validate filename
849
+ if not csv_filename:
850
+ return "Error: CSV filename is required."
851
+
852
+ if not csv_filename.endswith('.csv'):
853
+ return "Error: CSV filename must end with .csv extension."
854
+
855
+ # Create full path in working directory
856
+ csv_path = self.working_directory / csv_filename
857
+ resolved_csv_path = str(csv_path.resolve())
858
+
859
+ if not self._validate_file_path(resolved_csv_path):
860
+ return "Error: Invalid file path."
861
+
862
+ try:
863
+ # Get the worksheet
864
+ ws = self.wb[sheet_name]
865
+
866
+ # Convert worksheet to list of lists
867
+ data = []
868
+ for row in ws.iter_rows(values_only=True):
869
+ data.append(list(row))
870
+
871
+ # Write to CSV
872
+ import csv
873
+
874
+ with open(
875
+ resolved_csv_path, 'w', newline='', encoding='utf-8-sig'
876
+ ) as csvfile:
877
+ writer = csv.writer(csvfile)
878
+ writer.writerows(data)
879
+
880
+ return (
881
+ f"Sheet {sheet_name} exported to {csv_filename} "
882
+ f"in {self.working_directory}."
883
+ )
884
+ except Exception as e:
885
+ logger.error(f"Failed to export sheet to CSV: {e}")
886
+ return f"Error: Failed to export sheet {sheet_name} to CSV: {e}"
887
+
888
+ def get_rows(
889
+ self,
890
+ sheet_name: str,
891
+ start_row: Optional[int] = None,
892
+ end_row: Optional[int] = None,
893
+ ) -> Union[List[List[Union[str, int, float, None]]], str]:
894
+ r"""Retrieve rows of data from a sheet.
895
+
896
+ Use this to read data from a sheet. You can get all rows or specify a
897
+ range. Returns actual data as lists, making it easy to process
898
+ programmatically.
899
+
900
+ Args:
901
+ sheet_name (str): Name of the sheet to read from.
902
+ start_row (Optional[int]): First row to read (1-based). If None,
903
+ starts from row 1. (default: :obj:`None`)
904
+ end_row (Optional[int]): Last row to read (1-based). If None,
905
+ reads to the end. (default: :obj:`None`)
906
+
907
+ Returns:
908
+ Union[List[List[Union[str, int, float, None]]], str]:
909
+ List of rows (each row is a list of cell values) or error
910
+ message.
911
+ """
912
+ if not self.wb:
913
+ return "Error: Workbook not initialized."
914
+
915
+ if sheet_name not in self.wb.sheetnames:
916
+ return f"Error: Sheet {sheet_name} does not exist."
917
+
918
+ ws = self.wb[sheet_name]
919
+ rows = []
920
+
921
+ # Get all rows with data
922
+ for row in ws.iter_rows(
923
+ min_row=start_row, max_row=end_row, values_only=True
924
+ ):
925
+ # Skip completely empty rows
926
+ if any(cell is not None for cell in row):
927
+ rows.append(list(row))
928
+
929
+ return rows
930
+
931
+ def append_row(
932
+ self,
933
+ sheet_name: str,
934
+ row_data: List[Union[str, int, float, None]],
935
+ ) -> str:
936
+ r"""Add a single row to the end of a sheet.
937
+
938
+ Use this to add one row of data to the end of existing content.
939
+ For multiple rows, use multiple calls to this function.
940
+
941
+ Args:
942
+ sheet_name (str): Name of the target sheet.
943
+ row_data (List[Union[str, int, float, None]]): Single row of data
944
+ to add.
945
+
946
+ Returns:
947
+ str: Success confirmation message or error details.
948
+ """
949
+ if not self.wb:
950
+ return "Error: Workbook not initialized."
951
+ if sheet_name not in self.wb.sheetnames:
952
+ return f"Error: Sheet {sheet_name} does not exist."
953
+ ws = self.wb[sheet_name]
954
+ ws.append(row_data)
955
+ return f"Row appended to sheet {sheet_name} successfully."
956
+
957
+ def update_row(
958
+ self,
959
+ sheet_name: str,
960
+ row_number: int,
961
+ row_data: List[Union[str, int, float, None]],
962
+ ) -> str:
963
+ r"""Update a specific row in the sheet.
964
+
965
+ Use this to replace all data in a specific row with new values.
966
+
967
+ Args:
968
+ sheet_name (str): Name of the sheet to modify.
969
+ row_number (int): The row number to update (1-based, where 1 is
970
+ first row).
971
+ row_data (List[Union[str, int, float, None]]): New data for the
972
+ entire row.
973
+
974
+ Returns:
975
+ str: Success confirmation message or error details.
976
+ """
977
+ if not self.wb:
978
+ return "Error: Workbook not initialized."
979
+
980
+ if sheet_name not in self.wb.sheetnames:
981
+ return f"Sheet {sheet_name} does not exist."
982
+
983
+ ws = self.wb[sheet_name]
984
+
985
+ # Clear the existing row first
986
+ for col_idx in range(1, ws.max_column + 1):
987
+ ws.cell(row=row_number, column=col_idx).value = None
988
+
989
+ # Set new values
990
+ for col_idx, value in enumerate(row_data, 1):
991
+ ws.cell(row=row_number, column=col_idx).value = value
992
+
993
+ return f"Row {row_number} updated in sheet {sheet_name} successfully."
177
994
 
178
995
  def get_tools(self) -> List[FunctionTool]:
179
996
  r"""Returns a list of FunctionTool objects representing the functions
@@ -184,5 +1001,28 @@ class ExcelToolkit(BaseToolkit):
184
1001
  the functions in the toolkit.
185
1002
  """
186
1003
  return [
1004
+ # File operations
187
1005
  FunctionTool(self.extract_excel_content),
1006
+ FunctionTool(self.create_workbook),
1007
+ FunctionTool(self.save_workbook),
1008
+ FunctionTool(self.delete_workbook),
1009
+ FunctionTool(self.export_sheet_to_csv),
1010
+ # Sheet operations
1011
+ FunctionTool(self.create_sheet),
1012
+ FunctionTool(self.delete_sheet),
1013
+ FunctionTool(self.clear_sheet),
1014
+ # Data reading
1015
+ FunctionTool(self.get_rows),
1016
+ FunctionTool(self.get_cell_value),
1017
+ FunctionTool(self.get_column_data),
1018
+ FunctionTool(self.get_range_values),
1019
+ FunctionTool(self.find_cells),
1020
+ # Data writing
1021
+ FunctionTool(self.append_row),
1022
+ FunctionTool(self.update_row),
1023
+ FunctionTool(self.set_cell_value),
1024
+ FunctionTool(self.set_range_values),
1025
+ # Structure modification
1026
+ FunctionTool(self.delete_rows),
1027
+ FunctionTool(self.delete_columns),
188
1028
  ]