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
@@ -1,1037 +0,0 @@
1
- # ========= Copyright 2023-2024 @ 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-2024 @ CAMEL-AI.org. All Rights Reserved. =========
14
-
15
- import atexit
16
- import os
17
- import platform
18
- import queue
19
- import subprocess
20
- import sys
21
- import threading
22
- import venv
23
- from queue import Queue
24
- from typing import Any, Dict, List, Optional, Tuple
25
-
26
- from camel.logger import get_logger
27
- from camel.toolkits.base import BaseToolkit
28
- from camel.toolkits.function_tool import FunctionTool
29
- from camel.utils import MCPServer
30
-
31
- logger = get_logger(__name__)
32
-
33
-
34
- @MCPServer()
35
- class TerminalToolkit(BaseToolkit):
36
- r"""A toolkit for terminal operations across multiple operating systems.
37
-
38
- This toolkit provides a set of functions for terminal operations such as
39
- searching for files by name or content, executing shell commands, and
40
- managing terminal sessions.
41
-
42
- Args:
43
- timeout (Optional[float]): The timeout for terminal operations.
44
- shell_sessions (Optional[Dict[str, Any]]): A dictionary to store
45
- shell session information. If None, an empty dictionary will be
46
- used. (default: :obj:`{}`)
47
- working_dir (str): The working directory for operations.
48
- If specified, all execution and write operations will be restricted
49
- to this directory. Read operations can access paths outside this
50
- directory.(default: :obj:`"./workspace"`)
51
- need_terminal (bool): Whether to create a terminal interface.
52
- (default: :obj:`True`)
53
- use_shell_mode (bool): Whether to use shell mode for command execution.
54
- (default: :obj:`True`)
55
- clone_current_env (bool): Whether to clone the current Python
56
- environment.(default: :obj:`False`)
57
- safe_mode (bool): Whether to enable safe mode to restrict operations.
58
- (default: :obj:`True`)
59
-
60
- Note:
61
- Most functions are compatible with Unix-based systems (macOS, Linux).
62
- For Windows compatibility, additional implementation details are
63
- needed.
64
- """
65
-
66
- def __init__(
67
- self,
68
- timeout: Optional[float] = None,
69
- shell_sessions: Optional[Dict[str, Any]] = None,
70
- working_dir: str = "./workspace",
71
- need_terminal: bool = True,
72
- use_shell_mode: bool = True,
73
- clone_current_env: bool = False,
74
- safe_mode: bool = True,
75
- ):
76
- super().__init__(timeout=timeout)
77
- self.shell_sessions = shell_sessions or {}
78
- self.os_type = platform.system()
79
- self.output_queue: Queue[str] = Queue()
80
- self.agent_queue: Queue[str] = Queue()
81
- self.terminal_ready = threading.Event()
82
- self.gui_thread = None
83
- self.safe_mode = safe_mode
84
-
85
- self.cloned_env_path = None
86
- self.use_shell_mode = use_shell_mode
87
-
88
- self.python_executable = sys.executable
89
- self.is_macos = platform.system() == 'Darwin'
90
-
91
- atexit.register(self.__del__)
92
-
93
- if not os.path.exists(working_dir):
94
- os.makedirs(working_dir, exist_ok=True)
95
- self.working_dir = os.path.abspath(working_dir)
96
- self._update_terminal_output(
97
- f"Working directory set to: {self.working_dir}\n"
98
- )
99
- if self.safe_mode:
100
- self._update_terminal_output(
101
- "Safe mode enabled: Write operations can only "
102
- "be performed within the working directory\n"
103
- )
104
-
105
- if clone_current_env:
106
- self.cloned_env_path = os.path.join(self.working_dir, ".venv")
107
- self._clone_current_environment()
108
- else:
109
- self.cloned_env_path = None
110
-
111
- if need_terminal:
112
- if self.is_macos:
113
- # macOS uses non-GUI mode
114
- logger.info("Detected macOS environment, using non-GUI mode")
115
- self._setup_file_output()
116
- self.terminal_ready.set()
117
- else:
118
- # Other platforms use normal GUI
119
- self.gui_thread = threading.Thread(
120
- target=self._create_terminal, daemon=True
121
- )
122
- self.gui_thread.start()
123
- self.terminal_ready.wait(timeout=5)
124
-
125
- def _setup_file_output(self):
126
- r"""Set up file output to replace GUI, using a fixed file to simulate
127
- terminal.
128
- """
129
-
130
- self.log_file = os.path.join(os.getcwd(), "camel_terminal.txt")
131
-
132
- if os.path.exists(self.log_file):
133
- with open(self.log_file, "w") as f:
134
- f.truncate(0)
135
- f.write("CAMEL Terminal Session\n")
136
- f.write("=" * 50 + "\n")
137
- f.write(f"Working Directory: {os.getcwd()}\n")
138
- f.write("=" * 50 + "\n\n")
139
- else:
140
- with open(self.log_file, "w") as f:
141
- f.write("CAMEL Terminal Session\n")
142
- f.write("=" * 50 + "\n")
143
- f.write(f"Working Directory: {os.getcwd()}\n")
144
- f.write("=" * 50 + "\n\n")
145
-
146
- # Inform the user
147
- logger.info(f"Terminal output redirected to: {self.log_file}")
148
-
149
- def file_update(output: str):
150
- try:
151
- # Directly append to the end of the file
152
- with open(self.log_file, "a") as f:
153
- f.write(output)
154
- # If the output does not end with a newline, add one
155
- if output and not output.endswith('\n'):
156
- f.write('\n')
157
- # Ensure the agent also receives the output
158
- self.agent_queue.put(output)
159
- except Exception as e:
160
- logger.error(f"Failed to write to terminal: {e}")
161
-
162
- # Replace the update method
163
- self._update_terminal_output = file_update
164
-
165
- def _clone_current_environment(self):
166
- r"""Create a new Python virtual environment."""
167
- try:
168
- if os.path.exists(self.cloned_env_path):
169
- self._update_terminal_output(
170
- f"Using existing environment: {self.cloned_env_path}\n"
171
- )
172
- return
173
-
174
- self._update_terminal_output(
175
- f"Creating new Python environment at:{self.cloned_env_path}\n"
176
- )
177
-
178
- venv.create(self.cloned_env_path, with_pip=True)
179
- self._update_terminal_output(
180
- "New Python environment created successfully!\n"
181
- )
182
-
183
- except Exception as e:
184
- self._update_terminal_output(
185
- f"Failed to create environment: {e!s}\n"
186
- )
187
- logger.error(f"Failed to create environment: {e}")
188
-
189
- def _create_terminal(self):
190
- r"""Create a terminal GUI."""
191
-
192
- try:
193
- import tkinter as tk
194
- from tkinter import scrolledtext
195
-
196
- def update_terminal():
197
- try:
198
- while True:
199
- output = self.output_queue.get_nowait()
200
- if isinstance(output, bytes):
201
- output = output.decode('utf-8', errors='replace')
202
- self.terminal.insert(tk.END, output)
203
- self.terminal.see(tk.END)
204
- except queue.Empty:
205
- if hasattr(self, 'root') and self.root:
206
- self.root.after(100, update_terminal)
207
-
208
- self.root = tk.Tk()
209
- self.root.title(f"{self.os_type} Terminal")
210
-
211
- self.root.geometry("800x600")
212
- self.root.minsize(400, 300)
213
-
214
- self.terminal = scrolledtext.ScrolledText(
215
- self.root,
216
- wrap=tk.WORD,
217
- bg='black',
218
- fg='white',
219
- font=('Consolas', 10),
220
- insertbackground='white', # Cursor color
221
- )
222
- self.terminal.pack(fill=tk.BOTH, expand=True)
223
-
224
- # Set the handling for closing the window
225
- def on_closing():
226
- self.root.quit()
227
- self.root.destroy()
228
- self.root = None
229
-
230
- self.root.protocol("WM_DELETE_WINDOW", on_closing)
231
-
232
- # Start updating
233
- update_terminal()
234
-
235
- # Mark the terminal as ready
236
- self.terminal_ready.set()
237
-
238
- # Start the main loop
239
- self.root.mainloop()
240
-
241
- except Exception as e:
242
- logger.error(f"Failed to create terminal: {e}")
243
- self.terminal_ready.set()
244
-
245
- def _update_terminal_output(self, output: str):
246
- r"""Update terminal output and send to agent.
247
-
248
- Args:
249
- output (str): The output to be sent to the agent
250
- """
251
- try:
252
- # If it is macOS , only write to file
253
- if self.is_macos:
254
- if hasattr(self, 'log_file'):
255
- with open(self.log_file, "a") as f:
256
- f.write(output)
257
- # Ensure the agent also receives the output
258
- self.agent_queue.put(output)
259
- return
260
-
261
- # For other cases, try to update the GUI (if it exists)
262
- if hasattr(self, 'root') and self.root:
263
- self.output_queue.put(output)
264
-
265
- # Always send to agent queue
266
- self.agent_queue.put(output)
267
-
268
- except Exception as e:
269
- logger.error(f"Failed to update terminal output: {e}")
270
-
271
- def _is_path_within_working_dir(self, path: str) -> bool:
272
- r"""Check if the path is within the working directory.
273
-
274
- Args:
275
- path (str): The path to check
276
-
277
- Returns:
278
- bool: Returns True if the path is within the working directory,
279
- otherwise returns False
280
- """
281
- abs_path = os.path.abspath(path)
282
- return abs_path.startswith(self.working_dir)
283
-
284
- def _enforce_working_dir_for_execution(self, path: str) -> Optional[str]:
285
- r"""Enforce working directory restrictions, return error message
286
- if execution path is not within the working directory.
287
-
288
- Args:
289
- path (str): The path to be used for executing operations
290
-
291
- Returns:
292
- Optional[str]: Returns error message if the path is not within
293
- the working directory, otherwise returns None
294
- """
295
- if not self._is_path_within_working_dir(path):
296
- return (
297
- f"Operation restriction: Execution path {path} must "
298
- f"be within working directory {self.working_dir}"
299
- )
300
- return None
301
-
302
- def _copy_external_file_to_workdir(
303
- self, external_file: str
304
- ) -> Optional[str]:
305
- r"""Copy external file to working directory.
306
-
307
- Args:
308
- external_file (str): The path of the external file
309
-
310
- Returns:
311
- Optional[str]: New path after copying to the working directory,
312
- returns None on failure
313
- """
314
- try:
315
- import shutil
316
-
317
- filename = os.path.basename(external_file)
318
- new_path = os.path.join(self.working_dir, filename)
319
- shutil.copy2(external_file, new_path)
320
- return new_path
321
- except Exception as e:
322
- logger.error(f"Failed to copy file: {e}")
323
- return None
324
-
325
- def file_find_in_content(
326
- self, file: str, regex: str, sudo: bool = False
327
- ) -> str:
328
- r"""Search for matching text within file content.
329
-
330
- Args:
331
- file (str): Absolute path of the file to search within.
332
- regex (str): Regular expression pattern to match.
333
- sudo (bool, optional): Whether to use sudo privileges. Defaults to
334
- False. Note: Using sudo requires the process to have
335
- appropriate permissions.
336
-
337
- Returns:
338
- str: Matching content found in the file.
339
- """
340
-
341
- if not os.path.exists(file):
342
- return f"File not found: {file}"
343
-
344
- if not os.path.isfile(file):
345
- return f"The path provided is not a file: {file}"
346
-
347
- command = []
348
- if sudo:
349
- error_msg = self._enforce_working_dir_for_execution(file)
350
- if error_msg:
351
- return error_msg
352
- command.extend(["sudo"])
353
-
354
- if self.os_type in ['Darwin', 'Linux']: # macOS or Linux
355
- command.extend(["grep", "-E", regex, file])
356
- else: # Windows
357
- # For Windows, we could use PowerShell or findstr
358
- command.extend(["findstr", "/R", regex, file])
359
-
360
- try:
361
- result = subprocess.run(
362
- command, check=False, capture_output=True, text=True
363
- )
364
- return result.stdout.strip()
365
- except subprocess.SubprocessError as e:
366
- logger.error(f"Error searching in file content: {e}")
367
- return f"Error: {e!s}"
368
-
369
- def file_find_by_name(self, path: str, glob: str) -> str:
370
- r"""Find files by name pattern in specified directory.
371
-
372
- Args:
373
- path (str): Absolute path of directory to search.
374
- glob (str): Filename pattern using glob syntax wildcards.
375
-
376
- Returns:
377
- str: List of files matching the pattern.
378
- """
379
- if not os.path.exists(path):
380
- return f"Directory not found: {path}"
381
-
382
- if not os.path.isdir(path):
383
- return f"The path provided is not a directory: {path}"
384
-
385
- command = []
386
- if self.os_type in ['Darwin', 'Linux']: # macOS or Linux
387
- command.extend(["find", path, "-name", glob])
388
- else: # Windows
389
- # For Windows, we use dir command with /s for recursive search
390
- # and /b for bare format
391
-
392
- pattern = glob
393
- file_path = os.path.join(path, pattern).replace('/', '\\')
394
- command.extend(["cmd", "/c", "dir", "/s", "/b", file_path])
395
-
396
- try:
397
- result = subprocess.run(
398
- command,
399
- check=False,
400
- capture_output=True,
401
- text=True,
402
- shell=False,
403
- )
404
-
405
- output = result.stdout.strip()
406
- if self.os_type == 'Windows':
407
- output = output.replace('\\', '/')
408
- return output
409
- except subprocess.SubprocessError as e:
410
- logger.error(f"Error finding files by name: {e}")
411
- return f"Error: {e!s}"
412
-
413
- def _sanitize_command(self, command: str, exec_dir: str) -> Tuple:
414
- r"""Check and modify command to ensure safety.
415
-
416
- Args:
417
- command (str): The command to check
418
- exec_dir (str): The directory to execute the command in
419
-
420
- Returns:
421
- Tuple: (is safe, modified command or error message)
422
- """
423
- if not self.safe_mode:
424
- return True, command
425
-
426
- if not command or command.strip() == "":
427
- return False, "Empty command"
428
-
429
- # Use shlex for safer command parsing
430
- import shlex
431
-
432
- try:
433
- parts = shlex.split(command)
434
- except ValueError as e:
435
- # Handle malformed commands (e.g., unbalanced quotes)
436
- return False, f"Invalid command format: {e}"
437
-
438
- if not parts:
439
- return False, "Empty command"
440
-
441
- # Get base command
442
- base_cmd = parts[0].lower()
443
-
444
- # Handle special commands
445
- if base_cmd in ['cd', 'chdir']:
446
- # Check if cd command attempts to leave the working directory
447
- if len(parts) > 1:
448
- target_dir = parts[1].strip('"\'')
449
- if (
450
- target_dir.startswith('/')
451
- or target_dir.startswith('\\')
452
- or ':' in target_dir
453
- ):
454
- # Absolute path
455
- abs_path = os.path.abspath(target_dir)
456
- else:
457
- # Relative path
458
- abs_path = os.path.abspath(
459
- os.path.join(exec_dir, target_dir)
460
- )
461
-
462
- if not self._is_path_within_working_dir(abs_path):
463
- return False, (
464
- f"Safety restriction: Cannot change to directory "
465
- f"outside of working directory {self.working_dir}"
466
- )
467
-
468
- # Check file operation commands
469
- elif base_cmd in [
470
- 'rm',
471
- 'del',
472
- 'rmdir',
473
- 'rd',
474
- 'deltree',
475
- 'erase',
476
- 'unlink',
477
- 'shred',
478
- 'srm',
479
- 'wipe',
480
- 'remove',
481
- ]:
482
- # Check targets of delete commands
483
- for _, part in enumerate(parts[1:], 1):
484
- if part.startswith('-') or part.startswith(
485
- '/'
486
- ): # Skip options
487
- continue
488
-
489
- target = part.strip('"\'')
490
- if (
491
- target.startswith('/')
492
- or target.startswith('\\')
493
- or ':' in target
494
- ):
495
- # Absolute path
496
- abs_path = os.path.abspath(target)
497
- else:
498
- # Relative path
499
- abs_path = os.path.abspath(os.path.join(exec_dir, target))
500
-
501
- if not self._is_path_within_working_dir(abs_path):
502
- return False, (
503
- f"Safety restriction: Cannot delete files outside "
504
- f"of working directory {self.working_dir}"
505
- )
506
-
507
- # Check write/modify commands
508
- elif base_cmd in [
509
- 'touch',
510
- 'mkdir',
511
- 'md',
512
- 'echo',
513
- 'cat',
514
- 'cp',
515
- 'copy',
516
- 'mv',
517
- 'move',
518
- 'rename',
519
- 'ren',
520
- 'write',
521
- 'output',
522
- ]:
523
- # Check for redirection symbols
524
- full_cmd = command.lower()
525
- if '>' in full_cmd:
526
- # Find the file path after redirection
527
- redirect_parts = command.split('>')
528
- if len(redirect_parts) > 1:
529
- output_file = (
530
- redirect_parts[1].strip().split()[0].strip('"\'')
531
- )
532
- if (
533
- output_file.startswith('/')
534
- or output_file.startswith('\\')
535
- or ':' in output_file
536
- ):
537
- # Absolute path
538
- abs_path = os.path.abspath(output_file)
539
- else:
540
- # Relative path
541
- abs_path = os.path.abspath(
542
- os.path.join(exec_dir, output_file)
543
- )
544
-
545
- if not self._is_path_within_working_dir(abs_path):
546
- return False, (
547
- f"Safety restriction: Cannot write to file "
548
- f"outside of working directory {self.working_dir}"
549
- )
550
-
551
- # For cp/mv commands, check target paths
552
- if base_cmd in ['cp', 'copy', 'mv', 'move']:
553
- # Simple handling, assuming the last parameter is the target
554
- if len(parts) > 2:
555
- target = parts[-1].strip('"\'')
556
- if (
557
- target.startswith('/')
558
- or target.startswith('\\')
559
- or ':' in target
560
- ):
561
- # Absolute path
562
- abs_path = os.path.abspath(target)
563
- else:
564
- # Relative path
565
- abs_path = os.path.abspath(
566
- os.path.join(exec_dir, target)
567
- )
568
-
569
- if not self._is_path_within_working_dir(abs_path):
570
- return False, (
571
- f"Safety restriction: Cannot write to file "
572
- f"outside of working directory {self.working_dir}"
573
- )
574
-
575
- # Check dangerous commands
576
- elif base_cmd in [
577
- 'sudo',
578
- 'su',
579
- 'chmod',
580
- 'chown',
581
- 'chgrp',
582
- 'passwd',
583
- 'mkfs',
584
- 'fdisk',
585
- 'dd',
586
- 'shutdown',
587
- 'reboot',
588
- 'halt',
589
- 'poweroff',
590
- 'init',
591
- ]:
592
- return False, (
593
- f"Safety restriction: Command '{base_cmd}' may affect system "
594
- f"security and is prohibited"
595
- )
596
-
597
- # Check network commands
598
- elif base_cmd in ['ssh', 'telnet', 'ftp', 'sftp', 'nc', 'netcat']:
599
- return False, (
600
- f"Safety restriction: Network command '{base_cmd}' "
601
- f"is prohibited"
602
- )
603
-
604
- # Add copy functionality - copy from external to working directory
605
- elif base_cmd == 'safecopy':
606
- # Custom command: safecopy <source file> <target file>
607
- if len(parts) != 3:
608
- return False, "Usage: safecopy <source file> <target file>"
609
-
610
- source = parts[1].strip('\'"')
611
- target = parts[2].strip('\'"')
612
-
613
- # Check if source file exists
614
- if not os.path.exists(source):
615
- return False, f"Source file does not exist: {source}"
616
-
617
- # Ensure target is within working directory
618
- if (
619
- target.startswith('/')
620
- or target.startswith('\\')
621
- or ':' in target
622
- ):
623
- # Absolute path
624
- abs_target = os.path.abspath(target)
625
- else:
626
- # Relative path
627
- abs_target = os.path.abspath(os.path.join(exec_dir, target))
628
-
629
- if not self._is_path_within_working_dir(abs_target):
630
- return False, (
631
- f"Safety restriction: Target file must be within "
632
- f"working directory {self.working_dir}"
633
- )
634
-
635
- # Replace with safe copy command
636
- if self.os_type == 'Windows':
637
- return True, f"copy \"{source}\" \"{abs_target}\""
638
- else:
639
- return True, f"cp \"{source}\" \"{abs_target}\""
640
-
641
- return True, command
642
-
643
- def shell_exec(self, id: str, command: str) -> str:
644
- r"""Execute commands. This can be used to execute various commands,
645
- such as writing code, executing code, and running commands.
646
-
647
- Args:
648
- id (str): Unique identifier of the target shell session.
649
- command (str): Shell command to execute.
650
-
651
- Returns:
652
- str: Output of the command execution or error message.
653
- """
654
- # Command execution must be within the working directory
655
- error_msg = self._enforce_working_dir_for_execution(self.working_dir)
656
- if error_msg:
657
- return error_msg
658
-
659
- if self.safe_mode:
660
- is_safe, sanitized_command = self._sanitize_command(
661
- command, self.working_dir
662
- )
663
- if not is_safe:
664
- return f"Command rejected: {sanitized_command}"
665
- command = sanitized_command
666
-
667
- # If the session does not exist, create a new session
668
- if id not in self.shell_sessions:
669
- self.shell_sessions[id] = {
670
- "process": None,
671
- "output": "",
672
- "running": False,
673
- }
674
-
675
- try:
676
- # First, log the command to be executed
677
- self._update_terminal_output(f"\n$ {command}\n")
678
-
679
- if command.startswith('python') or command.startswith('pip'):
680
- if self.cloned_env_path:
681
- if self.os_type == 'Windows':
682
- base_path = os.path.join(
683
- self.cloned_env_path, "Scripts"
684
- )
685
- python_path = os.path.join(base_path, "python.exe")
686
- pip_path = os.path.join(base_path, "pip.exe")
687
- else:
688
- base_path = os.path.join(self.cloned_env_path, "bin")
689
- python_path = os.path.join(base_path, "python")
690
- pip_path = os.path.join(base_path, "pip")
691
- else:
692
- python_path = self.python_executable
693
- pip_path = f'"{python_path}" -m pip'
694
-
695
- if command.startswith('python'):
696
- command = command.replace('python', f'"{python_path}"', 1)
697
- elif command.startswith('pip'):
698
- command = command.replace('pip', pip_path, 1)
699
-
700
- if self.is_macos:
701
- # Type safe version - macOS uses subprocess.run
702
- process = subprocess.run(
703
- command,
704
- shell=True,
705
- cwd=self.working_dir,
706
- capture_output=True,
707
- text=True,
708
- env=os.environ.copy(),
709
- )
710
-
711
- # Process the output
712
- output = process.stdout or ""
713
- if process.stderr:
714
- output += f"\nStderr Output:\n{process.stderr}"
715
-
716
- # Update session information and terminal
717
- self.shell_sessions[id]["output"] = output
718
- self._update_terminal_output(output + "\n")
719
-
720
- return output
721
-
722
- else:
723
- # Non-macOS systems use the Popen method
724
- proc = subprocess.Popen(
725
- command,
726
- shell=True,
727
- cwd=self.working_dir,
728
- stdout=subprocess.PIPE,
729
- stderr=subprocess.PIPE,
730
- stdin=subprocess.PIPE,
731
- text=True,
732
- bufsize=1,
733
- universal_newlines=True,
734
- env=os.environ.copy(),
735
- )
736
-
737
- # Store the process and mark it as running
738
- self.shell_sessions[id]["process"] = proc
739
- self.shell_sessions[id]["running"] = True
740
-
741
- # Get output
742
- stdout, stderr = proc.communicate()
743
-
744
- output = stdout or ""
745
- if stderr:
746
- output += f"\nStderr Output:\n{stderr}"
747
-
748
- # Update session information and terminal
749
- self.shell_sessions[id]["output"] = output
750
- self._update_terminal_output(output + "\n")
751
-
752
- return output
753
-
754
- except Exception as e:
755
- error_msg = f"Command execution error: {e!s}"
756
- logger.error(error_msg)
757
- self._update_terminal_output(f"\nError: {error_msg}\n")
758
-
759
- # More detailed error information
760
- import traceback
761
-
762
- detailed_error = traceback.format_exc()
763
- return (
764
- f"Error: {error_msg}\n\n"
765
- f"Detailed information: {detailed_error}"
766
- )
767
-
768
- def shell_view(self, id: str) -> str:
769
- r"""View the content of a specified shell session.
770
-
771
- Args:
772
- id (str): Unique identifier of the target shell session.
773
-
774
- Returns:
775
- str: Current output content of the shell session.
776
- """
777
- if id not in self.shell_sessions:
778
- return f"Shell session not found: {id}"
779
-
780
- session = self.shell_sessions[id]
781
-
782
- try:
783
- # Check process status
784
- if session["process"].poll() is not None:
785
- session["running"] = False
786
-
787
- # Collect all new output from agent queue
788
- new_output = ""
789
- try:
790
- while True:
791
- output = self.agent_queue.get_nowait()
792
- new_output += output
793
- session["output"] += output
794
- except queue.Empty:
795
- pass
796
-
797
- return new_output or session["output"]
798
-
799
- except Exception as e:
800
- error_msg = f"Error reading terminal output: {e}"
801
- self._update_terminal_output(f"\nError: {error_msg}\n")
802
- logger.error(error_msg)
803
- return f"Error: {e!s}"
804
-
805
- def shell_wait(self, id: str, seconds: Optional[int] = None) -> str:
806
- r"""Wait for the running process in a specified shell session to
807
- return.
808
-
809
- Args:
810
- id (str): Unique identifier of the target shell session.
811
- seconds (Optional[int], optional): Wait duration in seconds.
812
- If None, wait indefinitely. Defaults to None.
813
-
814
- Returns:
815
- str: Final output content after waiting.
816
- """
817
- if id not in self.shell_sessions:
818
- return f"Shell session not found: {id}"
819
-
820
- session = self.shell_sessions[id]
821
- process = session.get("process")
822
-
823
- if process is None:
824
- return f"No active process in session '{id}'"
825
-
826
- if not session["running"] or process.poll() is not None:
827
- return f"Process in session '{id}' is not running"
828
-
829
- try:
830
- if hasattr(process, 'communicate'):
831
- # Use communicate with timeout
832
- stdout, stderr = process.communicate(timeout=seconds)
833
-
834
- if stdout:
835
- stdout_str = (
836
- stdout.decode('utf-8')
837
- if isinstance(stdout, bytes)
838
- else stdout
839
- )
840
- session["output"] += stdout_str
841
- if stderr:
842
- stderr_str = (
843
- stderr.decode('utf-8')
844
- if isinstance(stderr, bytes)
845
- else stderr
846
- )
847
- if stderr_str:
848
- session["output"] += f"\nStderr Output:\n{stderr_str}"
849
-
850
- session["running"] = False
851
- return (
852
- f"Process completed in session '{id}'. "
853
- f"Output: {session['output']}"
854
- )
855
- else:
856
- return (
857
- f"Process already completed in session '{id}'. "
858
- f"Output: {session['output']}"
859
- )
860
-
861
- except subprocess.TimeoutExpired:
862
- return (
863
- f"Process in session '{id}' is still running "
864
- f"after {seconds} seconds"
865
- )
866
- except Exception as e:
867
- logger.error(f"Error waiting for process: {e}")
868
- return f"Error waiting for process: {e!s}"
869
-
870
- def shell_write_to_process(
871
- self, id: str, input: str, press_enter: bool
872
- ) -> str:
873
- r"""Write input to a running process in a specified shell session.
874
-
875
- Args:
876
- id (str): Unique identifier of the target shell session.
877
- input (str): Input content to write to the process.
878
- press_enter (bool): Whether to press Enter key after input.
879
-
880
- Returns:
881
- str: Status message indicating whether the input was sent.
882
- """
883
- if id not in self.shell_sessions:
884
- return f"Shell session not found: {id}"
885
-
886
- session = self.shell_sessions[id]
887
- process = session.get("process")
888
-
889
- if process is None:
890
- return f"No active process in session '{id}'"
891
-
892
- if not session["running"] or process.poll() is not None:
893
- return f"Process in session '{id}' is not running"
894
-
895
- try:
896
- if not process.stdin or process.stdin.closed:
897
- return (
898
- f"Cannot write to process in session '{id}': "
899
- f"stdin is closed"
900
- )
901
-
902
- if press_enter:
903
- input = input + "\n"
904
-
905
- # Write bytes to stdin
906
- process.stdin.write(input.encode('utf-8'))
907
- process.stdin.flush()
908
-
909
- return f"Input sent to process in session '{id}'"
910
- except Exception as e:
911
- logger.error(f"Error writing to process: {e}")
912
- return f"Error writing to process: {e!s}"
913
-
914
- def shell_kill_process(self, id: str) -> str:
915
- r"""Terminate a running process in a specified shell session.
916
-
917
- Args:
918
- id (str): Unique identifier of the target shell session.
919
-
920
- Returns:
921
- str: Status message indicating whether the process was terminated.
922
- """
923
- if id not in self.shell_sessions:
924
- return f"Shell session not found: {id}"
925
-
926
- session = self.shell_sessions[id]
927
- process = session.get("process")
928
-
929
- if process is None:
930
- return f"No active process in session '{id}'"
931
-
932
- if not session["running"] or process.poll() is not None:
933
- return f"Process in session '{id}' is not running"
934
-
935
- try:
936
- # Clean up process resources before termination
937
- if process.stdin and not process.stdin.closed:
938
- process.stdin.close()
939
-
940
- process.terminate()
941
- try:
942
- process.wait(timeout=5)
943
- except subprocess.TimeoutExpired:
944
- logger.warning(
945
- f"Process in session '{id}' did not terminate gracefully"
946
- f", forcing kill"
947
- )
948
- process.kill()
949
-
950
- session["running"] = False
951
- return f"Process in session '{id}' has been terminated"
952
- except Exception as e:
953
- logger.error(f"Error killing process: {e}")
954
- return f"Error killing process: {e!s}"
955
-
956
- def __del__(self):
957
- r"""Clean up resources when the object is being destroyed.
958
- Terminates all running processes and closes any open file handles.
959
- """
960
- # Log that cleanup is starting
961
- logger.info("TerminalToolkit cleanup initiated")
962
-
963
- # Clean up all processes in shell sessions
964
- for session_id, session in self.shell_sessions.items():
965
- process = session.get("process")
966
- if process is not None and session.get("running", False):
967
- try:
968
- logger.info(
969
- f"Terminating process in session '{session_id}'"
970
- )
971
-
972
- # Close process input/output streams if open
973
- if (
974
- hasattr(process, 'stdin')
975
- and process.stdin
976
- and not process.stdin.closed
977
- ):
978
- process.stdin.close()
979
-
980
- # Terminate the process
981
- process.terminate()
982
- try:
983
- # Give the process a short time to terminate gracefully
984
- process.wait(timeout=3)
985
- except subprocess.TimeoutExpired:
986
- # Force kill if the process doesn't terminate
987
- # gracefully
988
- logger.warning(
989
- f"Process in session '{session_id}' did not "
990
- f"terminate gracefully, forcing kill"
991
- )
992
- process.kill()
993
-
994
- # Mark the session as not running
995
- session["running"] = False
996
-
997
- except Exception as e:
998
- logger.error(
999
- f"Error cleaning up process in session "
1000
- f"'{session_id}': {e}"
1001
- )
1002
-
1003
- # Close file output if it exists
1004
- if hasattr(self, 'log_file') and self.is_macos:
1005
- try:
1006
- logger.info(f"Final terminal log saved to: {self.log_file}")
1007
- except Exception as e:
1008
- logger.error(f"Error logging file information: {e}")
1009
-
1010
- # Clean up GUI resources if they exist
1011
- if hasattr(self, 'root') and self.root:
1012
- try:
1013
- logger.info("Closing terminal GUI")
1014
- self.root.quit()
1015
- self.root.destroy()
1016
- except Exception as e:
1017
- logger.error(f"Error closing terminal GUI: {e}")
1018
-
1019
- logger.info("TerminalToolkit cleanup completed")
1020
-
1021
- def get_tools(self) -> List[FunctionTool]:
1022
- r"""Returns a list of FunctionTool objects representing the functions
1023
- in the toolkit.
1024
-
1025
- Returns:
1026
- List[FunctionTool]: A list of FunctionTool objects representing the
1027
- functions in the toolkit.
1028
- """
1029
- return [
1030
- FunctionTool(self.file_find_in_content),
1031
- FunctionTool(self.file_find_by_name),
1032
- FunctionTool(self.shell_exec),
1033
- FunctionTool(self.shell_view),
1034
- FunctionTool(self.shell_wait),
1035
- FunctionTool(self.shell_write_to_process),
1036
- FunctionTool(self.shell_kill_process),
1037
- ]