camel-ai 0.2.67__py3-none-any.whl → 0.2.80a2__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.
Files changed (224) hide show
  1. camel/__init__.py +1 -1
  2. camel/agents/_types.py +6 -2
  3. camel/agents/_utils.py +38 -0
  4. camel/agents/chat_agent.py +4014 -410
  5. camel/agents/mcp_agent.py +30 -27
  6. camel/agents/repo_agent.py +2 -1
  7. camel/benchmarks/browsecomp.py +6 -6
  8. camel/configs/__init__.py +15 -0
  9. camel/configs/aihubmix_config.py +88 -0
  10. camel/configs/amd_config.py +70 -0
  11. camel/configs/cometapi_config.py +104 -0
  12. camel/configs/minimax_config.py +93 -0
  13. camel/configs/nebius_config.py +103 -0
  14. camel/configs/vllm_config.py +2 -0
  15. camel/data_collectors/alpaca_collector.py +15 -6
  16. camel/datagen/self_improving_cot.py +1 -1
  17. camel/datasets/base_generator.py +39 -10
  18. camel/environments/__init__.py +12 -0
  19. camel/environments/rlcards_env.py +860 -0
  20. camel/environments/single_step.py +28 -3
  21. camel/environments/tic_tac_toe.py +1 -1
  22. camel/interpreters/__init__.py +2 -0
  23. camel/interpreters/docker/Dockerfile +4 -16
  24. camel/interpreters/docker_interpreter.py +3 -2
  25. camel/interpreters/e2b_interpreter.py +34 -1
  26. camel/interpreters/internal_python_interpreter.py +51 -2
  27. camel/interpreters/microsandbox_interpreter.py +395 -0
  28. camel/loaders/__init__.py +11 -2
  29. camel/loaders/base_loader.py +85 -0
  30. camel/loaders/chunkr_reader.py +9 -0
  31. camel/loaders/firecrawl_reader.py +4 -4
  32. camel/logger.py +1 -1
  33. camel/memories/agent_memories.py +84 -1
  34. camel/memories/base.py +34 -0
  35. camel/memories/blocks/chat_history_block.py +122 -4
  36. camel/memories/blocks/vectordb_block.py +8 -1
  37. camel/memories/context_creators/score_based.py +29 -237
  38. camel/memories/records.py +88 -8
  39. camel/messages/base.py +166 -40
  40. camel/messages/func_message.py +32 -5
  41. camel/models/__init__.py +10 -0
  42. camel/models/aihubmix_model.py +83 -0
  43. camel/models/aiml_model.py +1 -16
  44. camel/models/amd_model.py +101 -0
  45. camel/models/anthropic_model.py +117 -18
  46. camel/models/aws_bedrock_model.py +2 -33
  47. camel/models/azure_openai_model.py +205 -91
  48. camel/models/base_audio_model.py +3 -1
  49. camel/models/base_model.py +189 -24
  50. camel/models/cohere_model.py +5 -17
  51. camel/models/cometapi_model.py +83 -0
  52. camel/models/crynux_model.py +1 -16
  53. camel/models/deepseek_model.py +6 -16
  54. camel/models/fish_audio_model.py +6 -0
  55. camel/models/gemini_model.py +71 -20
  56. camel/models/groq_model.py +1 -17
  57. camel/models/internlm_model.py +1 -16
  58. camel/models/litellm_model.py +49 -32
  59. camel/models/lmstudio_model.py +1 -17
  60. camel/models/minimax_model.py +83 -0
  61. camel/models/mistral_model.py +1 -16
  62. camel/models/model_factory.py +27 -1
  63. camel/models/model_manager.py +24 -6
  64. camel/models/modelscope_model.py +1 -16
  65. camel/models/moonshot_model.py +185 -19
  66. camel/models/nebius_model.py +83 -0
  67. camel/models/nemotron_model.py +0 -5
  68. camel/models/netmind_model.py +1 -16
  69. camel/models/novita_model.py +1 -16
  70. camel/models/nvidia_model.py +1 -16
  71. camel/models/ollama_model.py +4 -19
  72. camel/models/openai_compatible_model.py +171 -46
  73. camel/models/openai_model.py +205 -77
  74. camel/models/openrouter_model.py +1 -17
  75. camel/models/ppio_model.py +1 -16
  76. camel/models/qianfan_model.py +1 -16
  77. camel/models/qwen_model.py +1 -16
  78. camel/models/reka_model.py +1 -16
  79. camel/models/samba_model.py +34 -47
  80. camel/models/sglang_model.py +64 -31
  81. camel/models/siliconflow_model.py +1 -16
  82. camel/models/stub_model.py +0 -4
  83. camel/models/togetherai_model.py +1 -16
  84. camel/models/vllm_model.py +1 -16
  85. camel/models/volcano_model.py +0 -17
  86. camel/models/watsonx_model.py +1 -16
  87. camel/models/yi_model.py +1 -16
  88. camel/models/zhipuai_model.py +60 -16
  89. camel/parsers/__init__.py +18 -0
  90. camel/parsers/mcp_tool_call_parser.py +176 -0
  91. camel/retrievers/auto_retriever.py +1 -0
  92. camel/runtimes/configs.py +11 -11
  93. camel/runtimes/daytona_runtime.py +15 -16
  94. camel/runtimes/docker_runtime.py +6 -6
  95. camel/runtimes/remote_http_runtime.py +5 -5
  96. camel/services/agent_openapi_server.py +380 -0
  97. camel/societies/__init__.py +2 -0
  98. camel/societies/role_playing.py +26 -28
  99. camel/societies/workforce/__init__.py +2 -0
  100. camel/societies/workforce/events.py +122 -0
  101. camel/societies/workforce/prompts.py +249 -38
  102. camel/societies/workforce/role_playing_worker.py +82 -20
  103. camel/societies/workforce/single_agent_worker.py +634 -34
  104. camel/societies/workforce/structured_output_handler.py +512 -0
  105. camel/societies/workforce/task_channel.py +169 -23
  106. camel/societies/workforce/utils.py +176 -9
  107. camel/societies/workforce/worker.py +77 -23
  108. camel/societies/workforce/workflow_memory_manager.py +772 -0
  109. camel/societies/workforce/workforce.py +3168 -478
  110. camel/societies/workforce/workforce_callback.py +74 -0
  111. camel/societies/workforce/workforce_logger.py +203 -175
  112. camel/societies/workforce/workforce_metrics.py +33 -0
  113. camel/storages/__init__.py +4 -0
  114. camel/storages/key_value_storages/json.py +15 -2
  115. camel/storages/key_value_storages/mem0_cloud.py +48 -47
  116. camel/storages/object_storages/google_cloud.py +1 -1
  117. camel/storages/vectordb_storages/__init__.py +6 -0
  118. camel/storages/vectordb_storages/chroma.py +731 -0
  119. camel/storages/vectordb_storages/oceanbase.py +13 -13
  120. camel/storages/vectordb_storages/pgvector.py +349 -0
  121. camel/storages/vectordb_storages/qdrant.py +3 -3
  122. camel/storages/vectordb_storages/surreal.py +365 -0
  123. camel/storages/vectordb_storages/tidb.py +8 -6
  124. camel/tasks/task.py +244 -27
  125. camel/toolkits/__init__.py +46 -8
  126. camel/toolkits/aci_toolkit.py +64 -19
  127. camel/toolkits/arxiv_toolkit.py +6 -6
  128. camel/toolkits/base.py +63 -5
  129. camel/toolkits/code_execution.py +28 -1
  130. camel/toolkits/context_summarizer_toolkit.py +684 -0
  131. camel/toolkits/craw4ai_toolkit.py +93 -0
  132. camel/toolkits/dappier_toolkit.py +10 -6
  133. camel/toolkits/dingtalk.py +1135 -0
  134. camel/toolkits/edgeone_pages_mcp_toolkit.py +49 -0
  135. camel/toolkits/excel_toolkit.py +901 -67
  136. camel/toolkits/file_toolkit.py +1402 -0
  137. camel/toolkits/function_tool.py +30 -6
  138. camel/toolkits/github_toolkit.py +107 -20
  139. camel/toolkits/gmail_toolkit.py +1839 -0
  140. camel/toolkits/google_calendar_toolkit.py +38 -4
  141. camel/toolkits/google_drive_mcp_toolkit.py +54 -0
  142. camel/toolkits/human_toolkit.py +34 -10
  143. camel/toolkits/hybrid_browser_toolkit/__init__.py +18 -0
  144. camel/toolkits/hybrid_browser_toolkit/config_loader.py +185 -0
  145. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit.py +246 -0
  146. camel/toolkits/hybrid_browser_toolkit/hybrid_browser_toolkit_ts.py +1973 -0
  147. camel/toolkits/hybrid_browser_toolkit/installer.py +203 -0
  148. camel/toolkits/hybrid_browser_toolkit/ts/package-lock.json +3749 -0
  149. camel/toolkits/hybrid_browser_toolkit/ts/package.json +32 -0
  150. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-scripts.js +125 -0
  151. camel/toolkits/hybrid_browser_toolkit/ts/src/browser-session.ts +1815 -0
  152. camel/toolkits/hybrid_browser_toolkit/ts/src/config-loader.ts +233 -0
  153. camel/toolkits/hybrid_browser_toolkit/ts/src/hybrid-browser-toolkit.ts +590 -0
  154. camel/toolkits/hybrid_browser_toolkit/ts/src/index.ts +7 -0
  155. camel/toolkits/hybrid_browser_toolkit/ts/src/parent-child-filter.ts +226 -0
  156. camel/toolkits/hybrid_browser_toolkit/ts/src/snapshot-parser.ts +219 -0
  157. camel/toolkits/hybrid_browser_toolkit/ts/src/som-screenshot-injected.ts +543 -0
  158. camel/toolkits/hybrid_browser_toolkit/ts/src/types.ts +130 -0
  159. camel/toolkits/hybrid_browser_toolkit/ts/tsconfig.json +26 -0
  160. camel/toolkits/hybrid_browser_toolkit/ts/websocket-server.js +319 -0
  161. camel/toolkits/hybrid_browser_toolkit/ws_wrapper.py +1032 -0
  162. camel/toolkits/hybrid_browser_toolkit_py/__init__.py +17 -0
  163. camel/toolkits/hybrid_browser_toolkit_py/actions.py +575 -0
  164. camel/toolkits/hybrid_browser_toolkit_py/agent.py +311 -0
  165. camel/toolkits/hybrid_browser_toolkit_py/browser_session.py +787 -0
  166. camel/toolkits/hybrid_browser_toolkit_py/config_loader.py +490 -0
  167. camel/toolkits/hybrid_browser_toolkit_py/hybrid_browser_toolkit.py +2390 -0
  168. camel/toolkits/hybrid_browser_toolkit_py/snapshot.py +233 -0
  169. camel/toolkits/hybrid_browser_toolkit_py/stealth_script.js +0 -0
  170. camel/toolkits/hybrid_browser_toolkit_py/unified_analyzer.js +1043 -0
  171. camel/toolkits/image_generation_toolkit.py +390 -0
  172. camel/toolkits/jina_reranker_toolkit.py +3 -4
  173. camel/toolkits/klavis_toolkit.py +5 -1
  174. camel/toolkits/markitdown_toolkit.py +104 -0
  175. camel/toolkits/math_toolkit.py +64 -10
  176. camel/toolkits/mcp_toolkit.py +370 -45
  177. camel/toolkits/memory_toolkit.py +5 -1
  178. camel/toolkits/message_agent_toolkit.py +608 -0
  179. camel/toolkits/message_integration.py +724 -0
  180. camel/toolkits/minimax_mcp_toolkit.py +195 -0
  181. camel/toolkits/note_taking_toolkit.py +277 -0
  182. camel/toolkits/notion_mcp_toolkit.py +224 -0
  183. camel/toolkits/openbb_toolkit.py +5 -1
  184. camel/toolkits/origene_mcp_toolkit.py +56 -0
  185. camel/toolkits/playwright_mcp_toolkit.py +12 -31
  186. camel/toolkits/pptx_toolkit.py +25 -12
  187. camel/toolkits/resend_toolkit.py +168 -0
  188. camel/toolkits/screenshot_toolkit.py +213 -0
  189. camel/toolkits/search_toolkit.py +437 -142
  190. camel/toolkits/slack_toolkit.py +104 -50
  191. camel/toolkits/sympy_toolkit.py +1 -1
  192. camel/toolkits/task_planning_toolkit.py +3 -3
  193. camel/toolkits/terminal_toolkit/__init__.py +18 -0
  194. camel/toolkits/terminal_toolkit/terminal_toolkit.py +957 -0
  195. camel/toolkits/terminal_toolkit/utils.py +532 -0
  196. camel/toolkits/thinking_toolkit.py +1 -1
  197. camel/toolkits/vertex_ai_veo_toolkit.py +590 -0
  198. camel/toolkits/video_analysis_toolkit.py +106 -26
  199. camel/toolkits/video_download_toolkit.py +17 -14
  200. camel/toolkits/web_deploy_toolkit.py +1219 -0
  201. camel/toolkits/wechat_official_toolkit.py +483 -0
  202. camel/toolkits/zapier_toolkit.py +5 -1
  203. camel/types/__init__.py +2 -2
  204. camel/types/agents/tool_calling_record.py +4 -1
  205. camel/types/enums.py +316 -40
  206. camel/types/openai_types.py +2 -2
  207. camel/types/unified_model_type.py +31 -4
  208. camel/utils/commons.py +36 -5
  209. camel/utils/constants.py +3 -0
  210. camel/utils/context_utils.py +1003 -0
  211. camel/utils/mcp.py +138 -4
  212. camel/utils/mcp_client.py +45 -1
  213. camel/utils/message_summarizer.py +148 -0
  214. camel/utils/token_counting.py +43 -20
  215. camel/utils/tool_result.py +44 -0
  216. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/METADATA +296 -85
  217. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/RECORD +219 -146
  218. camel/loaders/pandas_reader.py +0 -368
  219. camel/toolkits/dalle_toolkit.py +0 -175
  220. camel/toolkits/file_write_toolkit.py +0 -444
  221. camel/toolkits/openai_agent_toolkit.py +0 -135
  222. camel/toolkits/terminal_toolkit.py +0 -1037
  223. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/WHEEL +0 -0
  224. {camel_ai-0.2.67.dist-info → camel_ai-0.2.80a2.dist-info}/licenses/LICENSE +0 -0
@@ -218,9 +218,34 @@ class SingleStepEnv:
218
218
  return observations[0] if batch_size == 1 else observations
219
219
 
220
220
  elif isinstance(self.dataset, BaseGenerator):
221
- self._states = [
222
- await self.dataset.async_sample() for _ in range(batch_size)
223
- ]
221
+ # Generate more data if needed
222
+ if batch_size > len(self.dataset):
223
+ new_datapoints_needed = batch_size - len(self.dataset)
224
+ await self.dataset.generate_new(n=new_datapoints_needed)
225
+
226
+ # Verify that enough data was generated
227
+ if len(self.dataset) < batch_size:
228
+ raise RuntimeError(
229
+ f"Failed to generate enough datapoints. "
230
+ f"Requested {batch_size}, but only "
231
+ f"{len(self.dataset)} available after generation."
232
+ )
233
+
234
+ # Choose sampling strategy based on whether seed is provided
235
+ if seed is not None:
236
+ # Deterministic random sampling when seed is provided
237
+ random_indices = rng.sample(
238
+ range(len(self.dataset)), batch_size
239
+ )
240
+ self._states = [self.dataset[ind] for ind in random_indices]
241
+ else:
242
+ # Sequential sampling when no seed (backward compatible)
243
+ # Use async_sample to maintain sequential behavior
244
+ self._states = [
245
+ await self.dataset.async_sample()
246
+ for _ in range(batch_size)
247
+ ]
248
+
224
249
  self.current_batch_size = batch_size
225
250
  self._states_done = [False] * batch_size
226
251
 
@@ -483,7 +483,7 @@ class TicTacToeEnv(MultiStepEnv):
483
483
  # Check all win combinations.
484
484
  for a, b, c in TicTacToeEnv.WIN_COMBINATIONS:
485
485
  if board[a] != " " and board[a] == board[b] == board[c]:
486
- return board[a]
486
+ return board[a] # type: ignore[return-value]
487
487
  # Check for draw.
488
488
  if all(cell != " " for cell in board):
489
489
  return "draw"
@@ -18,6 +18,7 @@ from .e2b_interpreter import E2BInterpreter
18
18
  from .internal_python_interpreter import InternalPythonInterpreter
19
19
  from .interpreter_error import InterpreterError
20
20
  from .ipython_interpreter import JupyterKernelInterpreter
21
+ from .microsandbox_interpreter import MicrosandboxInterpreter
21
22
  from .subprocess_interpreter import SubprocessInterpreter
22
23
 
23
24
  __all__ = [
@@ -28,4 +29,5 @@ __all__ = [
28
29
  'DockerInterpreter',
29
30
  'JupyterKernelInterpreter',
30
31
  'E2BInterpreter',
32
+ 'MicrosandboxInterpreter',
31
33
  ]
@@ -1,11 +1,8 @@
1
1
  # syntax=docker/dockerfile:1
2
-
3
2
  FROM ubuntu:22.04
4
3
 
5
- # Set environment variable to avoid interactive prompts
6
4
  ENV DEBIAN_FRONTEND=noninteractive
7
5
 
8
- # Update and install base utilities
9
6
  RUN apt-get update && apt-get install -y \
10
7
  build-essential \
11
8
  software-properties-common \
@@ -20,7 +17,6 @@ RUN apt-get update && apt-get install -y \
20
17
  && apt-get clean \
21
18
  && apt-get autoremove -y
22
19
 
23
- # Install Python 3.10 and its dependencies
24
20
  RUN add-apt-repository ppa:deadsnakes/ppa && \
25
21
  apt-get update && \
26
22
  apt-get install -y \
@@ -34,35 +30,27 @@ RUN add-apt-repository ppa:deadsnakes/ppa && \
34
30
  && apt-get clean \
35
31
  && apt-get autoremove -y
36
32
 
37
- # Install R
38
33
  RUN apt-get update && \
39
34
  apt-get install -y r-base && \
40
35
  rm -rf /var/lib/apt/lists/* && \
41
36
  apt-get clean && \
42
37
  apt-get autoremove -y
43
38
 
44
- # Install NodeJS 22.x
45
39
  RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \
46
40
  apt-get install -y nodejs && \
47
41
  rm -rf /var/lib/apt/lists/* && \
48
42
  apt-get clean && \
49
43
  apt-get autoremove -y
50
44
 
51
- # Install Poetry
52
45
  RUN curl -fsSL https://install.python-poetry.org | python3.10 - && \
53
46
  ln -s ~/.local/bin/poetry /usr/local/bin/poetry
54
47
 
55
- # Upgrade pip and install base Python packages
56
48
  RUN python3.10 -m pip install --upgrade pip setuptools wheel
49
+ RUN pip install uv
57
50
 
58
- # Install uv
59
- RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \
60
- mv /root/.local/bin/uv /usr/local/bin/uv && \
61
- mv /root/.local/bin/uvx /usr/local/bin/uvx && \
62
- chmod +x /usr/local/bin/uv /usr/local/bin/uvx
63
-
64
- # Setup working directory
51
+ RUN groupadd -r devuser && useradd -r -m -g devuser devuser
65
52
  WORKDIR /workspace
53
+ RUN chown -R devuser:devuser /workspace
54
+ USER devuser
66
55
 
67
- # Set default shell
68
56
  CMD ["/bin/bash"]
@@ -146,8 +146,9 @@ class DockerInterpreter(BaseInterpreter):
146
146
  tar_stream = io.BytesIO()
147
147
  with tarfile.open(fileobj=tar_stream, mode='w') as tar:
148
148
  tarinfo = tarfile.TarInfo(name=filename)
149
- tarinfo.size = len(content)
150
- tar.addfile(tarinfo, io.BytesIO(content.encode('utf-8')))
149
+ encoded_content = content.encode('utf-8')
150
+ tarinfo.size = len(encoded_content)
151
+ tar.addfile(tarinfo, io.BytesIO(encoded_content))
151
152
  tar_stream.seek(0)
152
153
 
153
154
  # copy the tar into the container
@@ -28,6 +28,11 @@ class E2BInterpreter(BaseInterpreter):
28
28
  Args:
29
29
  require_confirm (bool, optional): If True, prompt user before running
30
30
  code strings for security. (default: :obj:`True`)
31
+
32
+ Environment Variables:
33
+ E2B_API_KEY: The API key for authenticating with the E2B service.
34
+ E2B_DOMAIN: The base URL for the E2B API. If not provided,
35
+ will use the default E2B endpoint.
31
36
  """
32
37
 
33
38
  _CODE_TYPE_MAPPING: ClassVar[Dict[str, Optional[str]]] = {
@@ -55,7 +60,35 @@ class E2BInterpreter(BaseInterpreter):
55
60
  from e2b_code_interpreter import Sandbox
56
61
 
57
62
  self.require_confirm = require_confirm
58
- self._sandbox = Sandbox(api_key=os.environ.get("E2B_API_KEY"))
63
+
64
+ # Get API key from environment variable
65
+ api_key = os.environ.get("E2B_API_KEY")
66
+
67
+ # Get domain from environment variable
68
+ domain = os.environ.get("E2B_DOMAIN")
69
+
70
+ # Create sandbox with appropriate parameters
71
+ sandbox_kwargs = {"api_key": api_key}
72
+
73
+ # Only add domain if it's provided
74
+ # (to maintain compatibility with standard E2B)
75
+ if domain:
76
+ sandbox_kwargs["domain"] = domain
77
+ logger.info(f"Using custom E2B endpoint: {domain}")
78
+
79
+ try:
80
+ self._sandbox = Sandbox(**sandbox_kwargs)
81
+ except TypeError as e:
82
+ if domain and "domain" in str(e):
83
+ logger.warning(
84
+ f"The e2b_code_interpreter library doesn't support "
85
+ f"custom domain. "
86
+ f"Using default E2B endpoint. Error: {e}"
87
+ )
88
+ # Fallback to default configuration without domain
89
+ self._sandbox = Sandbox(api_key=api_key)
90
+ else:
91
+ raise e
59
92
 
60
93
  def __del__(self) -> None:
61
94
  r"""Destructor for the E2BInterpreter class.
@@ -79,6 +79,9 @@ class InternalPythonInterpreter(BaseInterpreter):
79
79
  (default: :obj:`False`)
80
80
  raise_error (bool, optional): Raise error if the interpreter fails.
81
81
  (default: :obj:`False`)
82
+ allow_builtins (bool, optional): If `True`, safe built-in functions
83
+ like print, len, str, etc. are added to the action space.
84
+ (default: :obj:`True`)
82
85
  """
83
86
 
84
87
  _CODE_TYPES: ClassVar[List[str]] = ["python", "py", "python3", "python2"]
@@ -89,16 +92,62 @@ class InternalPythonInterpreter(BaseInterpreter):
89
92
  import_white_list: Optional[List[str]] = None,
90
93
  unsafe_mode: bool = False,
91
94
  raise_error: bool = False,
95
+ allow_builtins: bool = True,
92
96
  ) -> None:
93
97
  self.action_space = action_space or dict()
94
- # Add print to action space
95
- self.action_space['print'] = print
96
98
  self.state = self.action_space.copy()
97
99
  self.fuzz_state: Dict[str, Any] = dict()
98
100
  self.import_white_list = import_white_list or list()
99
101
  self.raise_error = raise_error
100
102
  self.unsafe_mode = unsafe_mode
101
103
 
104
+ # Add safe built-in functions if allowed
105
+ if allow_builtins:
106
+ self._add_safe_builtins()
107
+
108
+ def _add_safe_builtins(self):
109
+ r"""Add safe built-in functions to the action space."""
110
+ safe_builtins = {
111
+ 'print': print,
112
+ 'len': len,
113
+ 'str': str,
114
+ 'int': int,
115
+ 'float': float,
116
+ 'bool': bool,
117
+ 'list': list,
118
+ 'dict': dict,
119
+ 'tuple': tuple,
120
+ 'set': set,
121
+ 'abs': abs,
122
+ 'min': min,
123
+ 'max': max,
124
+ 'sum': sum,
125
+ 'sorted': sorted,
126
+ 'reversed': reversed,
127
+ 'enumerate': enumerate,
128
+ 'zip': zip,
129
+ 'range': range,
130
+ 'round': round,
131
+ 'type': type,
132
+ 'isinstance': isinstance,
133
+ 'hasattr': hasattr,
134
+ 'getattr': getattr,
135
+ 'setattr': setattr,
136
+ 'dir': dir,
137
+ 'help': help,
138
+ 'map': map,
139
+ 'filter': filter,
140
+ 'any': any,
141
+ 'all': all,
142
+ 'ord': ord,
143
+ 'chr': chr,
144
+ 'bin': bin,
145
+ 'oct': oct,
146
+ 'hex': hex,
147
+ }
148
+ self.action_space.update(safe_builtins)
149
+ self.state.update(safe_builtins)
150
+
102
151
  def run(self, code: str, code_type: str = "python") -> str:
103
152
  r"""Executes the given code with specified code type in the
104
153
  interpreter.
@@ -0,0 +1,395 @@
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
+ import asyncio
15
+ from typing import Any, ClassVar, Dict, List, Optional, Tuple, Union
16
+
17
+ from camel.interpreters.base import BaseInterpreter
18
+ from camel.interpreters.interpreter_error import InterpreterError
19
+ from camel.logger import get_logger
20
+
21
+ logger = get_logger(__name__)
22
+
23
+
24
+ class MicrosandboxInterpreter(BaseInterpreter):
25
+ r"""Microsandbox Code Interpreter implementation.
26
+
27
+ This interpreter provides secure code execution using microsandbox,
28
+ a self-hosted platform for secure execution of untrusted user/AI code.
29
+ It supports Python code execution via PythonSandbox, JavaScript/Node.js
30
+ code execution via NodeSandbox, and shell commands via the command
31
+ interface.
32
+
33
+ Args:
34
+ require_confirm (bool, optional): If True, prompt user before running
35
+ code strings for security. (default: :obj:`True`)
36
+ server_url (str, optional): URL of the microsandbox server. If not
37
+ provided, will use MSB_SERVER_URL environment variable, then
38
+ fall back to http://127.0.0.1:5555. (default: :obj:`None`)
39
+ api_key (str, optional): API key for microsandbox authentication.
40
+ If not provided, will use MSB_API_KEY environment variable.
41
+ (default: :obj:`None`)
42
+ namespace (str, optional): Namespace for the sandbox.
43
+ (default: :obj:`"default"`)
44
+ sandbox_name (str, optional): Name of the sandbox instance. If not
45
+ provided, a random name will be generated by the SDK.
46
+ (default: :obj:`None`)
47
+ timeout (int, optional): Default timeout for code execution in seconds.
48
+ (default: :obj:`30`)
49
+
50
+ Environment Variables:
51
+ MSB_SERVER_URL: URL of the microsandbox server.
52
+ MSB_API_KEY: API key for microsandbox authentication.
53
+
54
+ Note:
55
+ The SDK handles parameter priority as: user parameter > environment
56
+ variable > default value.
57
+ """
58
+
59
+ _CODE_TYPE_MAPPING: ClassVar[Dict[str, str]] = {
60
+ # Python code - uses PythonSandbox
61
+ "python": "python_sandbox",
62
+ "py3": "python_sandbox",
63
+ "python3": "python_sandbox",
64
+ "py": "python_sandbox",
65
+ # JavaScript/Node.js code - uses NodeSandbox
66
+ "javascript": "node_sandbox",
67
+ "js": "node_sandbox",
68
+ "node": "node_sandbox",
69
+ "typescript": "node_sandbox",
70
+ "ts": "node_sandbox",
71
+ # Shell commands - uses command.run()
72
+ "bash": "shell_command",
73
+ "shell": "shell_command",
74
+ "sh": "shell_command",
75
+ }
76
+
77
+ def __init__(
78
+ self,
79
+ require_confirm: bool = True,
80
+ server_url: Optional[str] = None,
81
+ api_key: Optional[str] = None,
82
+ namespace: str = "default",
83
+ sandbox_name: Optional[str] = None,
84
+ timeout: int = 30,
85
+ ) -> None:
86
+ from microsandbox import (
87
+ NodeSandbox,
88
+ PythonSandbox,
89
+ )
90
+
91
+ # Store parameters, let SDK handle defaults and environment variables
92
+ self.require_confirm = require_confirm
93
+ self.server_url = server_url # None means use SDK default logic
94
+ self.api_key = api_key # None means use SDK default logic
95
+ self.namespace = namespace
96
+ self.sandbox_name = (
97
+ sandbox_name # None means SDK generates random name
98
+ )
99
+ self.timeout = timeout
100
+
101
+ # Store sandbox configuration
102
+ self._sandbox_config = {
103
+ "server_url": self.server_url,
104
+ "namespace": self.namespace,
105
+ "name": self.sandbox_name,
106
+ "api_key": self.api_key,
107
+ }
108
+
109
+ # Store sandbox classes for reuse
110
+ self._PythonSandbox = PythonSandbox
111
+ self._NodeSandbox = NodeSandbox
112
+
113
+ # Log initialization info
114
+ logger.info("Initialized MicrosandboxInterpreter")
115
+ logger.info(f"Namespace: {self.namespace}")
116
+ if self.sandbox_name:
117
+ logger.info(f"Sandbox name: {self.sandbox_name}")
118
+ else:
119
+ logger.info("Sandbox name: will be auto-generated by SDK")
120
+
121
+ def run(
122
+ self,
123
+ code: str,
124
+ code_type: str = "python",
125
+ ) -> str:
126
+ r"""Executes the given code in the microsandbox.
127
+
128
+ Args:
129
+ code (str): The code string to execute.
130
+ code_type (str): The type of code to execute. Supported types:
131
+ 'python', 'javascript', 'bash'. (default: :obj:`python`)
132
+
133
+ Returns:
134
+ str: The string representation of the output of the executed code.
135
+
136
+ Raises:
137
+ InterpreterError: If the `code_type` is not supported or if any
138
+ runtime error occurs during the execution of the code.
139
+ """
140
+ if code_type not in self._CODE_TYPE_MAPPING:
141
+ raise InterpreterError(
142
+ f"Unsupported code type {code_type}. "
143
+ f"`{self.__class__.__name__}` only supports "
144
+ f"{', '.join(list(self._CODE_TYPE_MAPPING.keys()))}."
145
+ )
146
+
147
+ # Print code for security checking
148
+ if self.require_confirm:
149
+ logger.info(
150
+ f"The following {code_type} code will run on "
151
+ f"microsandbox: {code}"
152
+ )
153
+ self._confirm_execution("code")
154
+
155
+ # Run the code asynchronously
156
+ return asyncio.run(self._run_async(code, code_type))
157
+
158
+ async def _run_async(self, code: str, code_type: str) -> str:
159
+ r"""Asynchronously executes code in microsandbox.
160
+
161
+ Args:
162
+ code (str): The code to execute.
163
+ code_type (str): The type of code to execute.
164
+
165
+ Returns:
166
+ str: The output of the executed code.
167
+
168
+ Raises:
169
+ InterpreterError: If execution fails.
170
+ """
171
+ try:
172
+ execution_method = self._CODE_TYPE_MAPPING[code_type]
173
+
174
+ if execution_method == "python_sandbox":
175
+ return await self._run_python_code(code)
176
+ elif execution_method == "node_sandbox":
177
+ return await self._run_node_code(code)
178
+ elif execution_method == "shell_command":
179
+ return await self._run_shell_command(code)
180
+ else:
181
+ raise InterpreterError(
182
+ f"Unsupported execution method: {execution_method}"
183
+ )
184
+
185
+ except Exception as e:
186
+ raise InterpreterError(
187
+ f"Error executing code in microsandbox: {e}"
188
+ )
189
+
190
+ async def _run_python_code(self, code: str) -> str:
191
+ r"""Execute Python code using PythonSandbox.
192
+
193
+ Args:
194
+ code (str): Python code to execute.
195
+
196
+ Returns:
197
+ str: Execution output.
198
+ """
199
+ async with self._PythonSandbox.create(
200
+ **self._sandbox_config
201
+ ) as sandbox:
202
+ execution = await asyncio.wait_for(
203
+ sandbox.run(code), timeout=self.timeout
204
+ )
205
+ return await self._get_execution_output(execution)
206
+
207
+ async def _run_node_code(self, code: str) -> str:
208
+ r"""Execute JavaScript/Node.js code using NodeSandbox.
209
+
210
+ Args:
211
+ code (str): JavaScript/Node.js code to execute.
212
+
213
+ Returns:
214
+ str: Execution output.
215
+ """
216
+ async with self._NodeSandbox.create(**self._sandbox_config) as sandbox:
217
+ execution = await asyncio.wait_for(
218
+ sandbox.run(code), timeout=self.timeout
219
+ )
220
+ return await self._get_execution_output(execution)
221
+
222
+ async def _run_shell_command(self, code: str) -> str:
223
+ r"""Execute shell commands directly.
224
+
225
+ Args:
226
+ code (str): Shell command to execute.
227
+
228
+ Returns:
229
+ str: Command execution output.
230
+ """
231
+ # Use any sandbox for shell commands
232
+ async with self._PythonSandbox.create(
233
+ **self._sandbox_config
234
+ ) as sandbox:
235
+ execution = await asyncio.wait_for(
236
+ sandbox.command.run("bash", ["-c", code]), timeout=self.timeout
237
+ )
238
+ return await self._get_command_output(execution)
239
+
240
+ async def _get_execution_output(self, execution) -> str:
241
+ r"""Get output from code execution.
242
+
243
+ Args:
244
+ execution: Execution object from sandbox.run().
245
+
246
+ Returns:
247
+ str: Formatted execution output.
248
+ """
249
+ output = await execution.output()
250
+ error = await execution.error()
251
+
252
+ result_parts = []
253
+ if output and output.strip():
254
+ result_parts.append(output.strip())
255
+ if error and error.strip():
256
+ result_parts.append(f"STDERR: {error.strip()}")
257
+
258
+ return (
259
+ "\n".join(result_parts)
260
+ if result_parts
261
+ else "Code executed successfully (no output)"
262
+ )
263
+
264
+ async def _get_command_output(self, execution) -> str:
265
+ r"""Get output from command execution.
266
+
267
+ Args:
268
+ execution: CommandExecution object from sandbox.command.run().
269
+
270
+ Returns:
271
+ str: Formatted command output.
272
+ """
273
+ output = await execution.output()
274
+ error = await execution.error()
275
+
276
+ result_parts = []
277
+ if output and output.strip():
278
+ result_parts.append(output.strip())
279
+ if error and error.strip():
280
+ result_parts.append(f"STDERR: {error.strip()}")
281
+ if hasattr(execution, 'exit_code') and execution.exit_code != 0:
282
+ result_parts.append(f"Exit code: {execution.exit_code}")
283
+
284
+ return (
285
+ "\n".join(result_parts)
286
+ if result_parts
287
+ else "Command executed successfully (no output)"
288
+ )
289
+
290
+ def _confirm_execution(self, execution_type: str) -> None:
291
+ r"""Prompt user for confirmation before executing code or commands.
292
+
293
+ Args:
294
+ execution_type (str): Type of execution ('code' or 'command').
295
+
296
+ Raises:
297
+ InterpreterError: If user declines to run the code/command.
298
+ """
299
+ while True:
300
+ choice = input(f"Running {execution_type}? [Y/n]:").lower()
301
+ if choice in ["y", "yes", "ye"]:
302
+ break
303
+ elif choice not in ["no", "n"]:
304
+ continue
305
+ raise InterpreterError(
306
+ f"Execution halted: User opted not to run the "
307
+ f"{execution_type}. "
308
+ f"This choice stops the current operation and any "
309
+ f"further {execution_type} execution."
310
+ )
311
+
312
+ def supported_code_types(self) -> List[str]:
313
+ r"""Provides supported code types by the interpreter."""
314
+ return list(self._CODE_TYPE_MAPPING.keys())
315
+
316
+ def update_action_space(self, action_space: Dict[str, Any]) -> None:
317
+ r"""Updates action space for interpreter.
318
+
319
+ Args:
320
+ action_space: Action space dictionary (unused in microsandbox).
321
+
322
+ Note:
323
+ Microsandbox doesn't support action space updates as it runs
324
+ in isolated environments for each execution.
325
+ """
326
+ # Explicitly acknowledge the parameter to avoid linting warnings
327
+ _ = action_space
328
+ logger.warning(
329
+ "Microsandbox doesn't support action space updates. "
330
+ "Code runs in isolated environments for each execution."
331
+ )
332
+
333
+ def execute_command(self, command: str) -> Union[str, Tuple[str, str]]:
334
+ r"""Execute a shell command in the microsandbox.
335
+
336
+ This method is designed for package management and system
337
+ administration tasks. It executes shell commands directly
338
+ using the microsandbox command interface.
339
+
340
+ Args:
341
+ command (str): The shell command to execute (e.g.,
342
+ "pip install numpy", "ls -la", "apt-get update").
343
+
344
+ Returns:
345
+ Union[str, Tuple[str, str]]: The output of the command.
346
+
347
+ Examples:
348
+ >>> interpreter.execute_command("pip install numpy")
349
+ >>> interpreter.execute_command("npm install express")
350
+ >>> interpreter.execute_command("ls -la /tmp")
351
+ """
352
+ # Print command for security checking
353
+ if self.require_confirm:
354
+ logger.info(
355
+ f"The following shell command will run on "
356
+ f"microsandbox: {command}"
357
+ )
358
+ self._confirm_execution("command")
359
+
360
+ return asyncio.run(self._execute_command_async(command))
361
+
362
+ async def _execute_command_async(self, command: str) -> str:
363
+ r"""Asynchronously executes a shell command in microsandbox.
364
+
365
+ Args:
366
+ command (str): The shell command to execute.
367
+
368
+ Returns:
369
+ str: The output of the command execution.
370
+
371
+ Raises:
372
+ InterpreterError: If execution fails.
373
+ """
374
+ try:
375
+ async with self._PythonSandbox.create(
376
+ **self._sandbox_config
377
+ ) as sandbox:
378
+ execution = await asyncio.wait_for(
379
+ sandbox.command.run("bash", ["-c", command]),
380
+ timeout=self.timeout,
381
+ )
382
+ return await self._get_command_output(execution)
383
+
384
+ except Exception as e:
385
+ raise InterpreterError(
386
+ f"Error executing command in microsandbox: {e}"
387
+ )
388
+
389
+ def __del__(self) -> None:
390
+ r"""Destructor for the MicrosandboxInterpreter class.
391
+
392
+ Microsandbox uses context managers for resource management,
393
+ so no explicit cleanup is needed.
394
+ """
395
+ logger.debug("MicrosandboxInterpreter cleaned up")
camel/loaders/__init__.py CHANGED
@@ -21,7 +21,6 @@ from .jina_url_reader import JinaURLReader
21
21
  from .markitdown import MarkItDownLoader
22
22
  from .mineru_extractor import MinerU
23
23
  from .mistral_reader import MistralReader
24
- from .pandas_reader import PandasReader
25
24
  from .scrapegraph_reader import ScrapeGraphAI
26
25
  from .unstructured_io import UnstructuredIO
27
26
 
@@ -33,7 +32,6 @@ __all__ = [
33
32
  'JinaURLReader',
34
33
  'Firecrawl',
35
34
  'Apify',
36
- 'PandasReader',
37
35
  'ChunkrReader',
38
36
  'ChunkrReaderConfig',
39
37
  'MinerU',
@@ -42,3 +40,14 @@ __all__ = [
42
40
  'ScrapeGraphAI',
43
41
  'MistralReader',
44
42
  ]
43
+
44
+
45
+ def __getattr__(name: str):
46
+ if name == 'PandasReader':
47
+ raise ImportError(
48
+ "PandasReader has been removed from camel.loaders. "
49
+ "The pandasai dependency limited pandas to version 1.5.3. "
50
+ "Please use ExcelToolkit from camel.toolkits instead for "
51
+ "handling structured data."
52
+ )
53
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")