opensandbox-code-interpreter 0.1.2.dev0__tar.gz → 1.1.1__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 (27) hide show
  1. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/.gitignore +31 -0
  2. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/PKG-INFO +21 -14
  3. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/README.md +13 -6
  4. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/pyproject.toml +10 -10
  5. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/__init__.py +1 -1
  6. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/__init__.py +1 -1
  7. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/code_adapter.py +92 -29
  8. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/converter/__init__.py +1 -1
  9. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/converter/code_execution_converter.py +1 -1
  10. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/factory.py +1 -1
  11. opensandbox_code_interpreter-1.1.1/src/code_interpreter/code_interpreter.py +384 -0
  12. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/models/__init__.py +1 -1
  13. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/models/code.py +1 -1
  14. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/models/code_sync.py +2 -1
  15. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/services/__init__.py +1 -1
  16. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/services/code.py +18 -4
  17. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/__init__.py +1 -1
  18. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/adapters/__init__.py +1 -1
  19. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/adapters/code_adapter.py +104 -17
  20. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/adapters/factory.py +1 -1
  21. opensandbox_code_interpreter-1.1.1/src/code_interpreter/sync/code_interpreter.py +331 -0
  22. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/services/__init__.py +1 -1
  23. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/services/code.py +15 -1
  24. opensandbox_code_interpreter-0.1.2.dev0/src/code_interpreter/code_interpreter.py +0 -217
  25. opensandbox_code_interpreter-0.1.2.dev0/src/code_interpreter/sync/code_interpreter.py +0 -178
  26. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/LICENSE +0 -0
  27. {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/py.typed +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,29 @@ nbdist/
263
268
  # Generated files
264
269
  generated/
265
270
  **/generated/**
271
+
272
+ # Fast Sandbox FastPath gRPC stubs are committed intentionally (deterministic protoc
273
+ # output; the server package needs them at install time, so they are checked
274
+ # in rather than regenerated).
275
+ !server/opensandbox_server/services/fast_sandbox/generated/
276
+ !server/opensandbox_server/services/fast_sandbox/generated/**
277
+ server/opensandbox_server/services/fast_sandbox/generated/__pycache__/
278
+
279
+ # execd eBPF kernel BTF dumps (bpf2go build-time only, never committed —
280
+ # see components/execd/pkg/ebpf/prog/audit.bpf.c; regenerate with:
281
+ # bpftool btf dump file /sys/kernel/btf/vmlinux format c > .../vmlinux.h)
282
+ components/execd/pkg/ebpf/prog/vmlinux.h
283
+ components/execd/pkg/ebpf/prog/vmlinux_*.h
284
+
285
+ # gVisor runtime binaries (downloaded dynamically)
286
+ kubernetes/test/kind/gvisor/runsc
287
+ kubernetes/test/kind/gvisor/containerd-shim-runsc-v1
288
+ bin/
289
+ obj/
290
+
291
+ .qoder/
292
+ /.vs
293
+
294
+ # fast-sandbox pinned source checkout (materialized by
295
+ # manifests/release/build-fast-sandbox.sh from manifests/third-party/fast-sandbox.commit)
296
+ .fast-sandbox/
@@ -1,11 +1,11 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: opensandbox-code-interpreter
3
- Version: 0.1.2.dev0
3
+ Version: 1.1.1
4
4
  Summary: OpenSandbox Code Interpreter Python SDK - Advanced code execution with persistent contexts
5
- Project-URL: Homepage, https://github.com/alibaba/OpenSandbox
6
- Project-URL: Repository, https://github.com/alibaba/OpenSandbox
7
- Project-URL: Documentation, https://docs.opensandbox.io
8
- Project-URL: Issues, https://github.com/alibaba/OpenSandbox/issues
5
+ Project-URL: Homepage, https://open-sandbox.ai
6
+ Project-URL: Repository, https://github.com/opensandbox-group/OpenSandbox
7
+ Project-URL: Documentation, https://open-sandbox.ai
8
+ Project-URL: Issues, https://github.com/opensandbox-group/OpenSandbox/issues
9
9
  Author-email: OpenSandbox Team <ninan.nn@alibaba-inc.com>
10
10
  License: Apache License
11
11
  Version 2.0, January 2004
@@ -223,13 +223,12 @@ 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<2.0.0,>=1.1.1
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
231
231
 
232
- English | [中文](README_zh.md)
233
232
 
234
233
  A Python SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java,
235
234
  Go, TypeScript, and other languages safely, with support for code execution contexts.
@@ -241,7 +240,7 @@ This SDK requires a Docker image containing the Code Interpreter runtime environ
241
240
  Node.js, etc.
242
241
 
243
242
  For detailed information about supported languages and versions, refer to the
244
- [Environment Documentation](../../../sandboxes/code-interpreter/README.md).
243
+ [Environment Documentation](https://github.com/opensandbox-group/sandbox-images).
245
244
 
246
245
  ## Installation
247
246
 
@@ -281,9 +280,9 @@ async def main() -> None:
281
280
 
282
281
  # 2. Create a Sandbox with the code-interpreter image + runtime versions
283
282
  sandbox = await Sandbox.create(
284
- "opensandbox/code-interpreter:latest",
283
+ "opensandbox/code-interpreter:v1.1.0",
285
284
  connection_config=config,
286
- entrypoint=["/opt/opensandbox/code-interpreter.sh"],
285
+ entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
287
286
  env={
288
287
  "PYTHON_VERSION": "3.11",
289
288
  "JAVA_VERSION": "17",
@@ -342,9 +341,9 @@ config = ConnectionConfigSync(
342
341
  )
343
342
 
344
343
  sandbox = SandboxSync.create(
345
- "opensandbox/code-interpreter:latest",
344
+ "opensandbox/code-interpreter:v1.1.0",
346
345
  connection_config=config,
347
- entrypoint=["/opt/opensandbox/code-interpreter.sh"],
346
+ entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
348
347
  env={"PYTHON_VERSION": "3.11"},
349
348
  )
350
349
  with sandbox:
@@ -355,6 +354,14 @@ with sandbox:
355
354
  sandbox.kill()
356
355
  ```
357
356
 
357
+ ### Installing Python packages at runtime
358
+
359
+ You can install packages directly via `sandbox.commands.run(...)`:
360
+
361
+ ```python
362
+ execution = await sandbox.commands.run("pip install pandas numpy")
363
+ ```
364
+
358
365
  ## Runtime Configuration
359
366
 
360
367
  ### Docker Image
@@ -1,6 +1,5 @@
1
1
  # OpenSandbox Code Interpreter SDK for Python
2
2
 
3
- English | [中文](README_zh.md)
4
3
 
5
4
  A Python SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java,
6
5
  Go, TypeScript, and other languages safely, with support for code execution contexts.
@@ -12,7 +11,7 @@ This SDK requires a Docker image containing the Code Interpreter runtime environ
12
11
  Node.js, etc.
13
12
 
14
13
  For detailed information about supported languages and versions, refer to the
15
- [Environment Documentation](../../../sandboxes/code-interpreter/README.md).
14
+ [Environment Documentation](https://github.com/opensandbox-group/sandbox-images).
16
15
 
17
16
  ## Installation
18
17
 
@@ -52,9 +51,9 @@ async def main() -> None:
52
51
 
53
52
  # 2. Create a Sandbox with the code-interpreter image + runtime versions
54
53
  sandbox = await Sandbox.create(
55
- "opensandbox/code-interpreter:latest",
54
+ "opensandbox/code-interpreter:v1.1.0",
56
55
  connection_config=config,
57
- entrypoint=["/opt/opensandbox/code-interpreter.sh"],
56
+ entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
58
57
  env={
59
58
  "PYTHON_VERSION": "3.11",
60
59
  "JAVA_VERSION": "17",
@@ -113,9 +112,9 @@ config = ConnectionConfigSync(
113
112
  )
114
113
 
115
114
  sandbox = SandboxSync.create(
116
- "opensandbox/code-interpreter:latest",
115
+ "opensandbox/code-interpreter:v1.1.0",
117
116
  connection_config=config,
118
- entrypoint=["/opt/opensandbox/code-interpreter.sh"],
117
+ entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
119
118
  env={"PYTHON_VERSION": "3.11"},
120
119
  )
121
120
  with sandbox:
@@ -126,6 +125,14 @@ with sandbox:
126
125
  sandbox.kill()
127
126
  ```
128
127
 
128
+ ### Installing Python packages at runtime
129
+
130
+ You can install packages directly via `sandbox.commands.run(...)`:
131
+
132
+ ```python
133
+ execution = await sandbox.commands.run("pip install pandas numpy")
134
+ ```
135
+
129
136
  ## Runtime Configuration
130
137
 
131
138
  ### Docker Image
@@ -1,4 +1,4 @@
1
- # Copyright 2025 Alibaba Group Holding Ltd.
1
+ # Copyright 2025 The OpenSandbox Authors
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -42,15 +42,15 @@ 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>=1.1.1,<2.0.0",
47
47
  ]
48
48
 
49
49
  [project.urls]
50
- Homepage = "https://github.com/alibaba/OpenSandbox"
51
- Repository = "https://github.com/alibaba/OpenSandbox"
52
- Documentation = "https://docs.opensandbox.io"
53
- Issues = "https://github.com/alibaba/OpenSandbox/issues"
50
+ Homepage = "https://open-sandbox.ai"
51
+ Repository = "https://github.com/opensandbox-group/OpenSandbox"
52
+ Documentation = "https://open-sandbox.ai"
53
+ Issues = "https://github.com/opensandbox-group/OpenSandbox/issues"
54
54
 
55
55
  [tool.hatch.version]
56
56
  source = "vcs"
@@ -58,9 +58,9 @@ source = "vcs"
58
58
  [tool.hatch.version.raw-options]
59
59
  # This package is in a subdirectory; explicitly point setuptools-scm at the git root.
60
60
  root = "../../.."
61
- tag_regex = "^python/code-interpreter/v(?P<version>\\d+\\.\\d+\\.\\d+(?:[\\.\\w\\+\\-]*)?)$"
62
- git_describe_command = 'git describe --dirty --tags --long --match "python/code-interpreter/v*"'
63
- fallback_version = "0.1.0"
61
+ tag_regex = "^release-(?P<version>\\d+\\.\\d+\\.\\d+(?:[\\.\\w\\+\\-]*)?)$"
62
+ git_describe_command = 'git describe --dirty --tags --long --match "release-*"'
63
+ fallback_version = "0.1.3"
64
64
 
65
65
  [tool.hatch.build]
66
66
  include = [
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -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
  )
@@ -39,6 +41,7 @@ from opensandbox.config import ConnectionConfig
39
41
  from opensandbox.exceptions import InvalidArgumentException, SandboxApiException
40
42
  from opensandbox.models.execd import Execution, ExecutionHandlers
41
43
  from opensandbox.models.sandboxes import SandboxEndpoint
44
+ from opensandbox.transport import unwrap_retry_transport
42
45
 
43
46
  from code_interpreter.adapters.converter.code_execution_converter import (
44
47
  CodeExecutionConverter,
@@ -49,18 +52,28 @@ from code_interpreter.services.code import Codes
49
52
  logger = logging.getLogger(__name__)
50
53
 
51
54
 
55
+ def _normalize_sse_event(event_dict: dict) -> dict:
56
+ if "type" in event_dict and "timestamp" in event_dict:
57
+ return event_dict
58
+ if "code" in event_dict and "message" in event_dict:
59
+ return {
60
+ "type": "error",
61
+ "timestamp": int(time.time() * 1000),
62
+ "error": {
63
+ "ename": str(event_dict["code"]),
64
+ "evalue": str(event_dict["message"]),
65
+ "traceback": [],
66
+ },
67
+ }
68
+ return event_dict
69
+
70
+
52
71
  class CodesAdapter(Codes):
53
72
  """
54
73
  Adapter implementation for code execution service.
55
74
 
56
- This adapter wraps auto-generated API clients and provides the concrete
57
- implementation of the Codes interface. It handles both standard
58
- API calls and SSE streaming for real-time code execution output.
59
-
60
- Similar to CommandServiceAdapter, this adapter uses:
61
- - Generated API clients for simple operations (create_context, interrupt)
62
- - Direct httpx SSE streaming for run
63
- - ExceptionConverter for unified exception handling
75
+ Handles standard API calls plus direct httpx SSE streaming for
76
+ real-time code execution output.
64
77
  """
65
78
 
66
79
  RUN_CODE_PATH = "/code"
@@ -85,10 +98,7 @@ class CodesAdapter(Codes):
85
98
  timeout_seconds = self.connection_config.request_timeout.total_seconds()
86
99
  timeout = httpx.Timeout(timeout_seconds)
87
100
 
88
- headers = {
89
- "User-Agent": self.connection_config.user_agent,
90
- **self.connection_config.headers,
91
- }
101
+ headers = self.execd_endpoint.build_request_headers(self.connection_config)
92
102
 
93
103
  # Execd API does not require authentication
94
104
  self._client = Client(
@@ -96,12 +106,19 @@ class CodesAdapter(Codes):
96
106
  timeout=timeout,
97
107
  )
98
108
 
99
- # Inject httpx client (adapter-owned)
109
+ # Adapter clients must own their transports: aclose() closes them,
110
+ # and the shared/user connection_config transport must survive that.
111
+ self._owns_clients = self.connection_config.owns_transport
112
+ main_transport: httpx.AsyncBaseTransport | None
113
+ if self._owns_clients:
114
+ main_transport = self.connection_config.new_owned_transport()
115
+ else:
116
+ main_transport = self.connection_config.transport
100
117
  self._httpx_client = httpx.AsyncClient(
101
118
  base_url=base_url,
102
119
  headers=headers,
103
120
  timeout=timeout,
104
- transport=self.connection_config.transport,
121
+ transport=main_transport,
105
122
  )
106
123
  self._client.set_async_httpx_client(self._httpx_client)
107
124
 
@@ -111,6 +128,14 @@ class CodesAdapter(Codes):
111
128
  "Accept": "text/event-stream",
112
129
  "Cache-Control": "no-cache",
113
130
  }
131
+ # SSE bootstraps bypass the retry wrapper: request bodies are
132
+ # not replayable and a non-idempotent status opt-in would cause
133
+ # duplicate execution on a resent SSE POST.
134
+ sse_transport = (
135
+ self.connection_config.new_owned_transport()
136
+ if self._owns_clients
137
+ else self.connection_config.transport
138
+ )
114
139
  self._sse_client = httpx.AsyncClient(
115
140
  headers=sse_headers,
116
141
  timeout=httpx.Timeout(
@@ -119,7 +144,7 @@ class CodesAdapter(Codes):
119
144
  write=timeout_seconds,
120
145
  pool=None,
121
146
  ),
122
- transport=self.connection_config.transport,
147
+ transport=unwrap_retry_transport(sse_transport),
123
148
  )
124
149
 
125
150
  async def _get_client(self):
@@ -135,6 +160,21 @@ class CodesAdapter(Codes):
135
160
  """Return SSE client (read timeout disabled) for execd streaming."""
136
161
  return self._sse_client
137
162
 
163
+ async def aclose(self) -> None:
164
+ """Release the adapter-owned HTTP clients.
165
+
166
+ Never closes the shared ``connection_config.transport``; the sandbox
167
+ stays usable after ``aclose()``. Clients wrapping a user-supplied
168
+ transport are left open (httpx would close the caller's transport
169
+ with them).
170
+ """
171
+ if not self._owns_clients:
172
+ return
173
+ try:
174
+ await self._httpx_client.aclose()
175
+ finally:
176
+ await self._sse_client.aclose()
177
+
138
178
  async def create_context(self, language: str) -> CodeContext:
139
179
  """
140
180
  Creates a new execution context for code interpretation.
@@ -197,7 +237,9 @@ class CodesAdapter(Codes):
197
237
  )
198
238
  handle_api_error(response_obj, "List code contexts")
199
239
  parsed_list = require_parsed(response_obj, list, "List code contexts")
200
- return [CodeExecutionConverter.from_api_code_context(c) for c in parsed_list]
240
+ return [
241
+ CodeExecutionConverter.from_api_code_context(c) for c in parsed_list
242
+ ]
201
243
  except Exception as e:
202
244
  logger.error("Failed to list contexts", exc_info=e)
203
245
  raise ExceptionConverter.to_sandbox_exception(e) from e
@@ -243,14 +285,18 @@ class CodesAdapter(Codes):
243
285
  """
244
286
  Executes code within the specified context using SSE streaming.
245
287
 
246
- Similar to CommandServiceAdapter.run, this uses direct httpx
288
+ Uses direct httpx
247
289
  streaming to handle SSE responses from the execd service.
248
290
  """
249
291
  if not code.strip():
250
292
  raise InvalidArgumentException("Code cannot be empty")
251
293
 
252
294
  try:
253
- if context is not None and language is not None and context.language != language:
295
+ if (
296
+ context is not None
297
+ and language is not None
298
+ and context.language != language
299
+ ):
254
300
  raise InvalidArgumentException(
255
301
  f"language '{language}' must match context.language '{context.language}'"
256
302
  )
@@ -280,13 +326,12 @@ class CodesAdapter(Codes):
280
326
  await response.aread()
281
327
  error_body = response.text
282
328
  logger.error(
283
- "Failed to run code. Status: %s, Body: %s",
284
- response.status_code,
285
- error_body,
329
+ f"Failed to run code. Status: {response.status_code}, Body: {error_body}"
286
330
  )
287
331
  raise SandboxApiException(
288
- message=f"Failed to run code. Status code: {response.status_code}",
332
+ message=f"Failed to run code. Status code: {response.status_code}, Body: {error_body}",
289
333
  status_code=response.status_code,
334
+ request_id=extract_request_id(response.headers),
290
335
  )
291
336
 
292
337
  dispatcher = ExecutionEventDispatcher(execution, handlers)
@@ -301,22 +346,20 @@ class CodesAdapter(Codes):
301
346
  data = data[5:].strip()
302
347
 
303
348
  try:
304
- event_dict = json.loads(data)
349
+ event_dict = _normalize_sse_event(json.loads(data))
305
350
  event_node = EventNode(**event_dict)
306
351
  await dispatcher.dispatch(event_node)
307
352
  except json.JSONDecodeError:
308
- logger.debug("Failed to parse SSE line: %s", line)
353
+ logger.debug(f"Failed to parse SSE line: {line}")
309
354
  continue
310
355
  except Exception as e:
311
- logger.error("Error processing event: %s", data, exc_info=e)
356
+ logger.error(f"Error processing event: {data}", exc_info=e)
312
357
  continue
313
358
 
314
359
  return execution
315
360
 
316
361
  except Exception as e:
317
- logger.error(
318
- "Failed to run code (length: %s)", len(code), exc_info=e
319
- )
362
+ logger.error(f"Failed to run code (length: {len(code)})", exc_info=e)
320
363
  raise ExceptionConverter.to_sandbox_exception(e) from e
321
364
 
322
365
  async def interrupt(self, execution_id: str) -> None:
@@ -339,3 +382,23 @@ class CodesAdapter(Codes):
339
382
  except Exception as e:
340
383
  logger.error("Failed to interrupt code execution", exc_info=e)
341
384
  raise ExceptionConverter.to_sandbox_exception(e) from e
385
+
386
+ async def ping(self) -> bool:
387
+ """
388
+ Check if the execd code execution service is alive.
389
+
390
+ Uses the shared generated API client so the ping exercises the same
391
+ HTTP client and endpoint that serve code execution requests.
392
+ """
393
+ try:
394
+ from opensandbox.api.execd.api.health import ping as ping_api
395
+
396
+ client = await self._get_client()
397
+ response_obj = await ping_api.asyncio_detailed(client=client)
398
+
399
+ handle_api_error(response_obj, "Ping code interpreter")
400
+ return True
401
+
402
+ except Exception as e:
403
+ logger.debug(f"Code interpreter ping failed: {e}")
404
+ return False
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright 2025 Alibaba Group Holding Ltd.
2
+ # Copyright 2025 The OpenSandbox Authors
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.