opensandbox-code-interpreter 0.1.1__tar.gz → 0.1.2__tar.gz

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 (25) hide show
  1. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/.gitignore +11 -0
  2. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/PKG-INFO +15 -7
  3. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/README.md +10 -2
  4. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/pyproject.toml +4 -4
  5. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/adapters/code_adapter.py +21 -1
  6. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/adapters/code_adapter.py +50 -9
  7. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/LICENSE +0 -0
  8. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/__init__.py +0 -0
  9. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/adapters/__init__.py +0 -0
  10. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/adapters/converter/__init__.py +0 -0
  11. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/adapters/converter/code_execution_converter.py +0 -0
  12. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/adapters/factory.py +0 -0
  13. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/code_interpreter.py +0 -0
  14. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/models/__init__.py +0 -0
  15. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/models/code.py +0 -0
  16. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/models/code_sync.py +0 -0
  17. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/py.typed +0 -0
  18. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/services/__init__.py +0 -0
  19. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/services/code.py +0 -0
  20. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/__init__.py +0 -0
  21. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/adapters/__init__.py +0 -0
  22. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/adapters/factory.py +0 -0
  23. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/code_interpreter.py +0 -0
  24. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/services/__init__.py +0 -0
  25. {opensandbox_code_interpreter-0.1.1 → opensandbox_code_interpreter-0.1.2}/src/code_interpreter/sync/services/code.py +0 -0
@@ -133,6 +133,8 @@ dist
133
133
  # Gatsby files
134
134
  .cache/
135
135
  public
136
+ !docs/public/
137
+ !docs/public/CNAME
136
138
 
137
139
  # Serverless directories
138
140
  .serverless/
@@ -222,6 +224,9 @@ secrets/
222
224
 
223
225
  # Generated API documentation
224
226
  docs/generated/
227
+ docs/.vitepress/generated/
228
+ docs/.vitepress/dist/
229
+ docs/.vitepress/cache/
225
230
  apidocs/
226
231
 
227
232
  # Test results
@@ -263,3 +268,9 @@ nbdist/
263
268
  # Generated files
264
269
  generated/
265
270
  **/generated/**
271
+
272
+ # gVisor runtime binaries (downloaded dynamically)
273
+ kubernetes/test/kind/gvisor/runsc
274
+ kubernetes/test/kind/gvisor/containerd-shim-runsc-v1
275
+ bin/
276
+ obj/
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensandbox-code-interpreter
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: OpenSandbox Code Interpreter Python SDK - Advanced code execution with persistent contexts
5
- Project-URL: Homepage, https://github.com/alibaba/OpenSandbox
5
+ Project-URL: Homepage, https://open-sandbox.ai
6
6
  Project-URL: Repository, https://github.com/alibaba/OpenSandbox
7
- Project-URL: Documentation, https://docs.opensandbox.io
7
+ Project-URL: Documentation, https://open-sandbox.ai
8
8
  Project-URL: Issues, https://github.com/alibaba/OpenSandbox/issues
9
9
  Author-email: OpenSandbox Team <ninan.nn@alibaba-inc.com>
10
10
  License: Apache License
@@ -223,8 +223,8 @@ Classifier: Programming Language :: Python :: 3.13
223
223
  Classifier: Topic :: Software Development :: Libraries
224
224
  Classifier: Typing :: Typed
225
225
  Requires-Python: >=3.10
226
- Requires-Dist: opensandbox<0.2.0,>=0.1.1
227
- Requires-Dist: pydantic<3.0,>=2.0.0
226
+ Requires-Dist: opensandbox<0.2.0,>=0.1.6
227
+ Requires-Dist: pydantic<3.0,>=2.4.2
228
228
  Description-Content-Type: text/markdown
229
229
 
230
230
  # OpenSandbox Code Interpreter SDK for Python
@@ -281,7 +281,7 @@ async def main() -> None:
281
281
 
282
282
  # 2. Create a Sandbox with the code-interpreter image + runtime versions
283
283
  sandbox = await Sandbox.create(
284
- "opensandbox/code-interpreter:latest",
284
+ "opensandbox/code-interpreter:v1.0.2",
285
285
  connection_config=config,
286
286
  entrypoint=["/opt/opensandbox/code-interpreter.sh"],
287
287
  env={
@@ -342,7 +342,7 @@ config = ConnectionConfigSync(
342
342
  )
343
343
 
344
344
  sandbox = SandboxSync.create(
345
- "opensandbox/code-interpreter:latest",
345
+ "opensandbox/code-interpreter:v1.0.2",
346
346
  connection_config=config,
347
347
  entrypoint=["/opt/opensandbox/code-interpreter.sh"],
348
348
  env={"PYTHON_VERSION": "3.11"},
@@ -355,6 +355,14 @@ with sandbox:
355
355
  sandbox.kill()
356
356
  ```
357
357
 
358
+ ### Installing Python packages at runtime
359
+
360
+ You can install packages directly via `sandbox.commands.run(...)`:
361
+
362
+ ```python
363
+ execution = await sandbox.commands.run("pip install pandas numpy")
364
+ ```
365
+
358
366
  ## Runtime Configuration
359
367
 
360
368
  ### Docker Image
@@ -52,7 +52,7 @@ async def main() -> None:
52
52
 
53
53
  # 2. Create a Sandbox with the code-interpreter image + runtime versions
54
54
  sandbox = await Sandbox.create(
55
- "opensandbox/code-interpreter:latest",
55
+ "opensandbox/code-interpreter:v1.0.2",
56
56
  connection_config=config,
57
57
  entrypoint=["/opt/opensandbox/code-interpreter.sh"],
58
58
  env={
@@ -113,7 +113,7 @@ config = ConnectionConfigSync(
113
113
  )
114
114
 
115
115
  sandbox = SandboxSync.create(
116
- "opensandbox/code-interpreter:latest",
116
+ "opensandbox/code-interpreter:v1.0.2",
117
117
  connection_config=config,
118
118
  entrypoint=["/opt/opensandbox/code-interpreter.sh"],
119
119
  env={"PYTHON_VERSION": "3.11"},
@@ -126,6 +126,14 @@ with sandbox:
126
126
  sandbox.kill()
127
127
  ```
128
128
 
129
+ ### Installing Python packages at runtime
130
+
131
+ You can install packages directly via `sandbox.commands.run(...)`:
132
+
133
+ ```python
134
+ execution = await sandbox.commands.run("pip install pandas numpy")
135
+ ```
136
+
129
137
  ## Runtime Configuration
130
138
 
131
139
  ### Docker Image
@@ -42,14 +42,14 @@ classifiers = [
42
42
  "Typing :: Typed",
43
43
  ]
44
44
  dependencies = [
45
- "pydantic>=2.0.0,<3.0",
46
- "opensandbox>=0.1.1,<0.2.0",
45
+ "pydantic>=2.4.2,<3.0",
46
+ "opensandbox>=0.1.6,<0.2.0",
47
47
  ]
48
48
 
49
49
  [project.urls]
50
- Homepage = "https://github.com/alibaba/OpenSandbox"
50
+ Homepage = "https://open-sandbox.ai"
51
51
  Repository = "https://github.com/alibaba/OpenSandbox"
52
- Documentation = "https://docs.opensandbox.io"
52
+ Documentation = "https://open-sandbox.ai"
53
53
  Issues = "https://github.com/alibaba/OpenSandbox/issues"
54
54
 
55
55
  [tool.hatch.version]
@@ -22,6 +22,7 @@ API clients and handling SSE streaming for real-time code execution.
22
22
 
23
23
  import json
24
24
  import logging
25
+ import time
25
26
 
26
27
  import httpx
27
28
  from opensandbox.adapters.converter.event_node import EventNode
@@ -32,6 +33,7 @@ from opensandbox.adapters.converter.execution_event_dispatcher import (
32
33
  ExecutionEventDispatcher,
33
34
  )
34
35
  from opensandbox.adapters.converter.response_handler import (
36
+ extract_request_id,
35
37
  handle_api_error,
36
38
  require_parsed,
37
39
  )
@@ -49,6 +51,22 @@ from code_interpreter.services.code import Codes
49
51
  logger = logging.getLogger(__name__)
50
52
 
51
53
 
54
+ def _normalize_sse_event(event_dict: dict) -> dict:
55
+ if "type" in event_dict and "timestamp" in event_dict:
56
+ return event_dict
57
+ if "code" in event_dict and "message" in event_dict:
58
+ return {
59
+ "type": "error",
60
+ "timestamp": int(time.time() * 1000),
61
+ "error": {
62
+ "ename": str(event_dict["code"]),
63
+ "evalue": str(event_dict["message"]),
64
+ "traceback": [],
65
+ },
66
+ }
67
+ return event_dict
68
+
69
+
52
70
  class CodesAdapter(Codes):
53
71
  """
54
72
  Adapter implementation for code execution service.
@@ -88,6 +106,7 @@ class CodesAdapter(Codes):
88
106
  headers = {
89
107
  "User-Agent": self.connection_config.user_agent,
90
108
  **self.connection_config.headers,
109
+ **self.execd_endpoint.headers,
91
110
  }
92
111
 
93
112
  # Execd API does not require authentication
@@ -287,6 +306,7 @@ class CodesAdapter(Codes):
287
306
  raise SandboxApiException(
288
307
  message=f"Failed to run code. Status code: {response.status_code}",
289
308
  status_code=response.status_code,
309
+ request_id=extract_request_id(response.headers),
290
310
  )
291
311
 
292
312
  dispatcher = ExecutionEventDispatcher(execution, handlers)
@@ -301,7 +321,7 @@ class CodesAdapter(Codes):
301
321
  data = data[5:].strip()
302
322
 
303
323
  try:
304
- event_dict = json.loads(data)
324
+ event_dict = _normalize_sse_event(json.loads(data))
305
325
  event_node = EventNode(**event_dict)
306
326
  await dispatcher.dispatch(event_node)
307
327
  except json.JSONDecodeError:
@@ -19,6 +19,7 @@ Synchronous adapter for code execution service (including SSE streaming).
19
19
 
20
20
  import json
21
21
  import logging
22
+ import time
22
23
 
23
24
  import httpx
24
25
  from opensandbox.adapters.converter.event_node import EventNode
@@ -26,6 +27,7 @@ from opensandbox.adapters.converter.exception_converter import (
26
27
  ExceptionConverter,
27
28
  )
28
29
  from opensandbox.adapters.converter.response_handler import (
30
+ extract_request_id,
29
31
  handle_api_error,
30
32
  require_parsed,
31
33
  )
@@ -44,6 +46,22 @@ from code_interpreter.sync.services.code import CodesSync
44
46
  logger = logging.getLogger(__name__)
45
47
 
46
48
 
49
+ def _normalize_sse_event(event_dict: dict) -> dict:
50
+ if "type" in event_dict and "timestamp" in event_dict:
51
+ return event_dict
52
+ if "code" in event_dict and "message" in event_dict:
53
+ return {
54
+ "type": "error",
55
+ "timestamp": int(time.time() * 1000),
56
+ "error": {
57
+ "ename": str(event_dict["code"]),
58
+ "evalue": str(event_dict["message"]),
59
+ "traceback": [],
60
+ },
61
+ }
62
+ return event_dict
63
+
64
+
47
65
  class CodesAdapterSync(CodesSync):
48
66
  """
49
67
  Synchronous adapter for code execution service.
@@ -63,7 +81,9 @@ class CodesAdapterSync(CodesSync):
63
81
  RUN_CODE_PATH = "/code"
64
82
  CREATE_CONTEXT_PATH = "/code/context"
65
83
 
66
- def __init__(self, execd_endpoint: SandboxEndpoint, connection_config: ConnectionConfigSync) -> None:
84
+ def __init__(
85
+ self, execd_endpoint: SandboxEndpoint, connection_config: ConnectionConfigSync
86
+ ) -> None:
67
87
  """
68
88
  Initialize the code service adapter (sync).
69
89
 
@@ -78,7 +98,11 @@ class CodesAdapterSync(CodesSync):
78
98
  base_url = f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}"
79
99
  timeout_seconds = self.connection_config.request_timeout.total_seconds()
80
100
  timeout = httpx.Timeout(timeout_seconds)
81
- headers = {"User-Agent": self.connection_config.user_agent, **self.connection_config.headers}
101
+ headers = {
102
+ "User-Agent": self.connection_config.user_agent,
103
+ **self.connection_config.headers,
104
+ **(self.execd_endpoint.headers or {}),
105
+ }
82
106
 
83
107
  self._client = Client(base_url=base_url, timeout=timeout)
84
108
  self._httpx_client = httpx.Client(
@@ -89,16 +113,24 @@ class CodesAdapterSync(CodesSync):
89
113
  )
90
114
  self._client.set_httpx_client(self._httpx_client)
91
115
 
92
- sse_headers = {**headers, "Accept": "text/event-stream", "Cache-Control": "no-cache"}
116
+ sse_headers = {
117
+ **headers,
118
+ "Accept": "text/event-stream",
119
+ "Cache-Control": "no-cache",
120
+ }
93
121
  self._sse_client = httpx.Client(
94
122
  headers=sse_headers,
95
- timeout=httpx.Timeout(connect=timeout_seconds, read=None, write=timeout_seconds, pool=None),
123
+ timeout=httpx.Timeout(
124
+ connect=timeout_seconds, read=None, write=timeout_seconds, pool=None
125
+ ),
96
126
  transport=self.connection_config.transport,
97
127
  )
98
128
 
99
129
  def _get_execd_url(self, path: str) -> str:
100
130
  """Build URL for execd endpoint."""
101
- return f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}{path}"
131
+ return (
132
+ f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}{path}"
133
+ )
102
134
 
103
135
  def create_context(self, language: str) -> CodeContextSync:
104
136
  """
@@ -225,7 +257,11 @@ class CodesAdapterSync(CodesSync):
225
257
  raise InvalidArgumentException("Code cannot be empty")
226
258
 
227
259
  try:
228
- if context is not None and language is not None and context.language != language:
260
+ if (
261
+ context is not None
262
+ and language is not None
263
+ and context.language != language
264
+ ):
229
265
  raise InvalidArgumentException(
230
266
  f"language '{language}' must match context.language '{context.language}'"
231
267
  )
@@ -233,7 +269,9 @@ class CodesAdapterSync(CodesSync):
233
269
  if context is None:
234
270
  # Default context: language default context (server-side behavior).
235
271
  # When context.id is omitted, execd will create/reuse a default session per language.
236
- context = CodeContextSync(language=language or SupportedLanguageSync.PYTHON)
272
+ context = CodeContextSync(
273
+ language=language or SupportedLanguageSync.PYTHON
274
+ )
237
275
  api_request = {
238
276
  "code": code,
239
277
  "context": {
@@ -252,6 +290,7 @@ class CodesAdapterSync(CodesSync):
252
290
  raise SandboxApiException(
253
291
  message=f"Failed to run code. Status code: {response.status_code}",
254
292
  status_code=response.status_code,
293
+ request_id=extract_request_id(response.headers),
255
294
  )
256
295
 
257
296
  for line in response.iter_lines():
@@ -261,7 +300,7 @@ class CodesAdapterSync(CodesSync):
261
300
  if data.startswith("data:"):
262
301
  data = data[5:].strip()
263
302
  try:
264
- event_dict = json.loads(data)
303
+ event_dict = _normalize_sse_event(json.loads(data))
265
304
  event_node = EventNode(**event_dict)
266
305
  dispatcher.dispatch(event_node)
267
306
  except json.JSONDecodeError:
@@ -286,7 +325,9 @@ class CodesAdapterSync(CodesSync):
286
325
  try:
287
326
  from opensandbox.api.execd.api.code_interpreting import interrupt_code
288
327
 
289
- response_obj = interrupt_code.sync_detailed(client=self._client, id=execution_id)
328
+ response_obj = interrupt_code.sync_detailed(
329
+ client=self._client, id=execution_id
330
+ )
290
331
  handle_api_error(response_obj, "Interrupt code execution")
291
332
  except Exception as e:
292
333
  logger.error("Failed to interrupt code execution", exc_info=e)