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.
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/.gitignore +31 -0
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/PKG-INFO +21 -14
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/README.md +13 -6
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/pyproject.toml +10 -10
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/code_adapter.py +92 -29
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/converter/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/converter/code_execution_converter.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/adapters/factory.py +1 -1
- opensandbox_code_interpreter-1.1.1/src/code_interpreter/code_interpreter.py +384 -0
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/models/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/models/code.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/models/code_sync.py +2 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/services/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/services/code.py +18 -4
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/adapters/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/adapters/code_adapter.py +104 -17
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/adapters/factory.py +1 -1
- opensandbox_code_interpreter-1.1.1/src/code_interpreter/sync/code_interpreter.py +331 -0
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/services/__init__.py +1 -1
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/src/code_interpreter/sync/services/code.py +15 -1
- opensandbox_code_interpreter-0.1.2.dev0/src/code_interpreter/code_interpreter.py +0 -217
- opensandbox_code_interpreter-0.1.2.dev0/src/code_interpreter/sync/code_interpreter.py +0 -178
- {opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/LICENSE +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: opensandbox-code-interpreter
|
|
3
|
-
Version:
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: OpenSandbox Code Interpreter Python SDK - Advanced code execution with persistent contexts
|
|
5
|
-
Project-URL: Homepage, https://
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
-
Project-URL: Documentation, https://
|
|
8
|
-
Project-URL: Issues, https://github.com/
|
|
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<
|
|
227
|
-
Requires-Dist: pydantic<3.0,>=2.
|
|
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](
|
|
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:
|
|
283
|
+
"opensandbox/code-interpreter:v1.1.0",
|
|
285
284
|
connection_config=config,
|
|
286
|
-
entrypoint=["/opt/
|
|
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:
|
|
344
|
+
"opensandbox/code-interpreter:v1.1.0",
|
|
346
345
|
connection_config=config,
|
|
347
|
-
entrypoint=["/opt/
|
|
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](
|
|
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:
|
|
54
|
+
"opensandbox/code-interpreter:v1.1.0",
|
|
56
55
|
connection_config=config,
|
|
57
|
-
entrypoint=["/opt/
|
|
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:
|
|
115
|
+
"opensandbox/code-interpreter:v1.1.0",
|
|
117
116
|
connection_config=config,
|
|
118
|
-
entrypoint=["/opt/
|
|
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
|
{opensandbox_code_interpreter-0.1.2.dev0 → opensandbox_code_interpreter-1.1.1}/pyproject.toml
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Copyright 2025
|
|
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.
|
|
46
|
-
"opensandbox>=
|
|
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://
|
|
51
|
-
Repository = "https://github.com/
|
|
52
|
-
Documentation = "https://
|
|
53
|
-
Issues = "https://github.com/
|
|
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 = "^
|
|
62
|
-
git_describe_command = 'git describe --dirty --tags --long --match "
|
|
63
|
-
fallback_version = "0.1.
|
|
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
|
|
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
|
-
|
|
57
|
-
|
|
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
|
-
#
|
|
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=
|
|
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=
|
|
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 [
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|