opensandbox-code-interpreter 1.1.0__tar.gz → 1.2.0__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-1.1.0 → opensandbox_code_interpreter-1.2.0}/PKG-INFO +9 -18
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/README.md +7 -16
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/pyproject.toml +1 -1
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/adapters/code_adapter.py +42 -30
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/code_interpreter.py +44 -10
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/models/code_sync.py +1 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/services/code.py +3 -3
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/adapters/code_adapter.py +39 -20
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/code_interpreter.py +34 -6
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/.gitignore +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/LICENSE +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/adapters/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/adapters/converter/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/adapters/converter/code_execution_converter.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/adapters/factory.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/models/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/models/code.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/py.typed +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/services/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/adapters/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/adapters/factory.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/services/__init__.py +0 -0
- {opensandbox_code_interpreter-1.1.0 → opensandbox_code_interpreter-1.2.0}/src/code_interpreter/sync/services/code.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: opensandbox-code-interpreter
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: OpenSandbox Code Interpreter Python SDK - Advanced code execution with persistent contexts
|
|
5
5
|
Project-URL: Homepage, https://open-sandbox.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/opensandbox-group/OpenSandbox
|
|
@@ -223,24 +223,20 @@ 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<2.0.0,>=1.
|
|
226
|
+
Requires-Dist: opensandbox<2.0.0,>=1.2.0
|
|
227
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
232
|
|
|
233
|
-
A Python SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java,
|
|
234
|
-
Go, TypeScript, and other languages safely, with support for code execution contexts.
|
|
233
|
+
A Python SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java, Go, TypeScript, and other languages safely, with support for code execution contexts.
|
|
235
234
|
|
|
236
235
|
## Prerequisites
|
|
237
236
|
|
|
238
|
-
This SDK requires a Docker image containing the Code Interpreter runtime environment. You must use the
|
|
239
|
-
`opensandbox/code-interpreter` image (or a derivative) which includes pre-installed runtimes for Python, Java, Go,
|
|
240
|
-
Node.js, etc.
|
|
237
|
+
This SDK requires a Docker image containing the Code Interpreter runtime environment. You must use the `opensandbox/code-interpreter` image (or a derivative) which includes pre-installed runtimes for Python, Java, Go, Node.js, etc.
|
|
241
238
|
|
|
242
|
-
For detailed information about supported languages and versions, refer to the
|
|
243
|
-
[Environment Documentation](https://github.com/opensandbox-group/sandbox-images).
|
|
239
|
+
For detailed information about supported languages and versions, refer to the [Environment Documentation](https://github.com/opensandbox-group/sandbox-images).
|
|
244
240
|
|
|
245
241
|
## Installation
|
|
246
242
|
|
|
@@ -258,8 +254,7 @@ uv add opensandbox-code-interpreter
|
|
|
258
254
|
|
|
259
255
|
## Quick Start
|
|
260
256
|
|
|
261
|
-
The following example demonstrates how to create a sandbox with a specific runtime configuration and execute a simple
|
|
262
|
-
script.
|
|
257
|
+
The following example demonstrates how to create a sandbox with a specific runtime configuration and execute a simple script.
|
|
263
258
|
|
|
264
259
|
```python
|
|
265
260
|
import asyncio
|
|
@@ -366,13 +361,11 @@ execution = await sandbox.commands.run("pip install pandas numpy")
|
|
|
366
361
|
|
|
367
362
|
### Docker Image
|
|
368
363
|
|
|
369
|
-
The Code Interpreter SDK relies on a specialized environment. Ensure your sandbox provider has the
|
|
370
|
-
`opensandbox/code-interpreter` image available.
|
|
364
|
+
The Code Interpreter SDK relies on a specialized environment. Ensure your sandbox provider has the `opensandbox/code-interpreter` image available.
|
|
371
365
|
|
|
372
366
|
### Language Version Selection
|
|
373
367
|
|
|
374
|
-
You can specify the desired version of a programming language by setting the corresponding environment variable when
|
|
375
|
-
creating the `Sandbox`.
|
|
368
|
+
You can specify the desired version of a programming language by setting the corresponding environment variable when creating the `Sandbox`.
|
|
376
369
|
|
|
377
370
|
| Language | Environment Variable | Example Value | Default (if unset) |
|
|
378
371
|
| -------- | -------------------- | ------------- | ------------------ |
|
|
@@ -385,9 +378,7 @@ creating the `Sandbox`.
|
|
|
385
378
|
|
|
386
379
|
### 0. Run with `language` (default language context)
|
|
387
380
|
|
|
388
|
-
You can pass `language` directly (recommended: `SupportedLanguage.*`) and skip `create_context`.
|
|
389
|
-
When `context.id` is omitted, **execd will create/reuse a default session for that language**, so
|
|
390
|
-
state can persist across runs:
|
|
381
|
+
You can pass `language` directly (recommended: `SupportedLanguage.*`) and skip `create_context`. When `context.id` is omitted, **execd will create/reuse a default session for that language**, so state can persist across runs:
|
|
391
382
|
|
|
392
383
|
```python
|
|
393
384
|
from code_interpreter import SupportedLanguage
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
# OpenSandbox Code Interpreter SDK for Python
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
A Python SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java,
|
|
5
|
-
Go, TypeScript, and other languages safely, with support for code execution contexts.
|
|
4
|
+
A Python SDK for executing code in secure, isolated sandboxes. It provides a high-level API for running Python, Java, Go, TypeScript, and other languages safely, with support for code execution contexts.
|
|
6
5
|
|
|
7
6
|
## Prerequisites
|
|
8
7
|
|
|
9
|
-
This SDK requires a Docker image containing the Code Interpreter runtime environment. You must use the
|
|
10
|
-
`opensandbox/code-interpreter` image (or a derivative) which includes pre-installed runtimes for Python, Java, Go,
|
|
11
|
-
Node.js, etc.
|
|
8
|
+
This SDK requires a Docker image containing the Code Interpreter runtime environment. You must use the `opensandbox/code-interpreter` image (or a derivative) which includes pre-installed runtimes for Python, Java, Go, Node.js, etc.
|
|
12
9
|
|
|
13
|
-
For detailed information about supported languages and versions, refer to the
|
|
14
|
-
[Environment Documentation](https://github.com/opensandbox-group/sandbox-images).
|
|
10
|
+
For detailed information about supported languages and versions, refer to the [Environment Documentation](https://github.com/opensandbox-group/sandbox-images).
|
|
15
11
|
|
|
16
12
|
## Installation
|
|
17
13
|
|
|
@@ -29,8 +25,7 @@ uv add opensandbox-code-interpreter
|
|
|
29
25
|
|
|
30
26
|
## Quick Start
|
|
31
27
|
|
|
32
|
-
The following example demonstrates how to create a sandbox with a specific runtime configuration and execute a simple
|
|
33
|
-
script.
|
|
28
|
+
The following example demonstrates how to create a sandbox with a specific runtime configuration and execute a simple script.
|
|
34
29
|
|
|
35
30
|
```python
|
|
36
31
|
import asyncio
|
|
@@ -137,13 +132,11 @@ execution = await sandbox.commands.run("pip install pandas numpy")
|
|
|
137
132
|
|
|
138
133
|
### Docker Image
|
|
139
134
|
|
|
140
|
-
The Code Interpreter SDK relies on a specialized environment. Ensure your sandbox provider has the
|
|
141
|
-
`opensandbox/code-interpreter` image available.
|
|
135
|
+
The Code Interpreter SDK relies on a specialized environment. Ensure your sandbox provider has the `opensandbox/code-interpreter` image available.
|
|
142
136
|
|
|
143
137
|
### Language Version Selection
|
|
144
138
|
|
|
145
|
-
You can specify the desired version of a programming language by setting the corresponding environment variable when
|
|
146
|
-
creating the `Sandbox`.
|
|
139
|
+
You can specify the desired version of a programming language by setting the corresponding environment variable when creating the `Sandbox`.
|
|
147
140
|
|
|
148
141
|
| Language | Environment Variable | Example Value | Default (if unset) |
|
|
149
142
|
| -------- | -------------------- | ------------- | ------------------ |
|
|
@@ -156,9 +149,7 @@ creating the `Sandbox`.
|
|
|
156
149
|
|
|
157
150
|
### 0. Run with `language` (default language context)
|
|
158
151
|
|
|
159
|
-
You can pass `language` directly (recommended: `SupportedLanguage.*`) and skip `create_context`.
|
|
160
|
-
When `context.id` is omitted, **execd will create/reuse a default session for that language**, so
|
|
161
|
-
state can persist across runs:
|
|
152
|
+
You can pass `language` directly (recommended: `SupportedLanguage.*`) and skip `create_context`. When `context.id` is omitted, **execd will create/reuse a default session for that language**, so state can persist across runs:
|
|
162
153
|
|
|
163
154
|
```python
|
|
164
155
|
from code_interpreter import SupportedLanguage
|
|
@@ -33,15 +33,15 @@ from opensandbox.adapters.converter.execution_event_dispatcher import (
|
|
|
33
33
|
ExecutionEventDispatcher,
|
|
34
34
|
)
|
|
35
35
|
from opensandbox.adapters.converter.response_handler import (
|
|
36
|
-
|
|
36
|
+
build_api_exception_from_httpx,
|
|
37
37
|
handle_api_error,
|
|
38
38
|
require_parsed,
|
|
39
39
|
)
|
|
40
40
|
from opensandbox.config import ConnectionConfig
|
|
41
|
-
from opensandbox.exceptions import InvalidArgumentException
|
|
41
|
+
from opensandbox.exceptions import InvalidArgumentException
|
|
42
42
|
from opensandbox.models.execd import Execution, ExecutionHandlers
|
|
43
43
|
from opensandbox.models.sandboxes import SandboxEndpoint
|
|
44
|
-
from opensandbox.transport import
|
|
44
|
+
from opensandbox.transport import streaming_transport
|
|
45
45
|
|
|
46
46
|
from code_interpreter.adapters.converter.code_execution_converter import (
|
|
47
47
|
CodeExecutionConverter,
|
|
@@ -72,14 +72,8 @@ class CodesAdapter(Codes):
|
|
|
72
72
|
"""
|
|
73
73
|
Adapter implementation for code execution service.
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
API calls and SSE streaming for real-time code execution output.
|
|
78
|
-
|
|
79
|
-
Similar to CommandServiceAdapter, this adapter uses:
|
|
80
|
-
- Generated API clients for simple operations (create_context, interrupt)
|
|
81
|
-
- Direct httpx SSE streaming for run
|
|
82
|
-
- ExceptionConverter for unified exception handling
|
|
75
|
+
Handles standard API calls plus direct httpx SSE streaming for
|
|
76
|
+
real-time code execution output.
|
|
83
77
|
"""
|
|
84
78
|
|
|
85
79
|
RUN_CODE_PATH = "/code"
|
|
@@ -104,11 +98,7 @@ class CodesAdapter(Codes):
|
|
|
104
98
|
timeout_seconds = self.connection_config.request_timeout.total_seconds()
|
|
105
99
|
timeout = httpx.Timeout(timeout_seconds)
|
|
106
100
|
|
|
107
|
-
headers =
|
|
108
|
-
"User-Agent": self.connection_config.user_agent,
|
|
109
|
-
**self.connection_config.headers,
|
|
110
|
-
**self.execd_endpoint.headers,
|
|
111
|
-
}
|
|
101
|
+
headers = self.execd_endpoint.build_request_headers(self.connection_config)
|
|
112
102
|
|
|
113
103
|
# Execd API does not require authentication
|
|
114
104
|
self._client = Client(
|
|
@@ -116,12 +106,19 @@ class CodesAdapter(Codes):
|
|
|
116
106
|
timeout=timeout,
|
|
117
107
|
)
|
|
118
108
|
|
|
119
|
-
#
|
|
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
|
|
120
117
|
self._httpx_client = httpx.AsyncClient(
|
|
121
118
|
base_url=base_url,
|
|
122
119
|
headers=headers,
|
|
123
120
|
timeout=timeout,
|
|
124
|
-
transport=
|
|
121
|
+
transport=main_transport,
|
|
125
122
|
)
|
|
126
123
|
self._client.set_async_httpx_client(self._httpx_client)
|
|
127
124
|
|
|
@@ -131,9 +128,13 @@ class CodesAdapter(Codes):
|
|
|
131
128
|
"Accept": "text/event-stream",
|
|
132
129
|
"Cache-Control": "no-cache",
|
|
133
130
|
}
|
|
134
|
-
# SSE bootstraps
|
|
135
|
-
#
|
|
136
|
-
|
|
131
|
+
# SSE bootstraps keep only the wake-timeout retry: a non-idempotent
|
|
132
|
+
# status opt-in would cause duplicate execution on a resent SSE POST.
|
|
133
|
+
sse_transport = (
|
|
134
|
+
self.connection_config.new_owned_transport()
|
|
135
|
+
if self._owns_clients
|
|
136
|
+
else self.connection_config.transport
|
|
137
|
+
)
|
|
137
138
|
self._sse_client = httpx.AsyncClient(
|
|
138
139
|
headers=sse_headers,
|
|
139
140
|
timeout=httpx.Timeout(
|
|
@@ -142,7 +143,7 @@ class CodesAdapter(Codes):
|
|
|
142
143
|
write=timeout_seconds,
|
|
143
144
|
pool=None,
|
|
144
145
|
),
|
|
145
|
-
transport=
|
|
146
|
+
transport=streaming_transport(sse_transport),
|
|
146
147
|
)
|
|
147
148
|
|
|
148
149
|
async def _get_client(self):
|
|
@@ -158,6 +159,21 @@ class CodesAdapter(Codes):
|
|
|
158
159
|
"""Return SSE client (read timeout disabled) for execd streaming."""
|
|
159
160
|
return self._sse_client
|
|
160
161
|
|
|
162
|
+
async def aclose(self) -> None:
|
|
163
|
+
"""Release the adapter-owned HTTP clients.
|
|
164
|
+
|
|
165
|
+
Never closes the shared ``connection_config.transport``; the sandbox
|
|
166
|
+
stays usable after ``aclose()``. Clients wrapping a user-supplied
|
|
167
|
+
transport are left open (httpx would close the caller's transport
|
|
168
|
+
with them).
|
|
169
|
+
"""
|
|
170
|
+
if not self._owns_clients:
|
|
171
|
+
return
|
|
172
|
+
try:
|
|
173
|
+
await self._httpx_client.aclose()
|
|
174
|
+
finally:
|
|
175
|
+
await self._sse_client.aclose()
|
|
176
|
+
|
|
161
177
|
async def create_context(self, language: str) -> CodeContext:
|
|
162
178
|
"""
|
|
163
179
|
Creates a new execution context for code interpretation.
|
|
@@ -268,7 +284,7 @@ class CodesAdapter(Codes):
|
|
|
268
284
|
"""
|
|
269
285
|
Executes code within the specified context using SSE streaming.
|
|
270
286
|
|
|
271
|
-
|
|
287
|
+
Uses direct httpx
|
|
272
288
|
streaming to handle SSE responses from the execd service.
|
|
273
289
|
"""
|
|
274
290
|
if not code.strip():
|
|
@@ -299,6 +315,7 @@ class CodesAdapter(Codes):
|
|
|
299
315
|
result=[],
|
|
300
316
|
error=None,
|
|
301
317
|
)
|
|
318
|
+
execution._is_code_execution = True
|
|
302
319
|
|
|
303
320
|
# Use SSE client for streaming responses (read timeout disabled)
|
|
304
321
|
client = await self._get_sse_client()
|
|
@@ -307,15 +324,10 @@ class CodesAdapter(Codes):
|
|
|
307
324
|
async with client.stream("POST", url, json=api_request) as response:
|
|
308
325
|
if response.status_code != 200:
|
|
309
326
|
await response.aread()
|
|
310
|
-
error_body = response.text
|
|
311
327
|
logger.error(
|
|
312
|
-
f"Failed to run code. Status: {response.status_code}, Body: {
|
|
313
|
-
)
|
|
314
|
-
raise SandboxApiException(
|
|
315
|
-
message=f"Failed to run code. Status code: {response.status_code}, Body: {error_body}",
|
|
316
|
-
status_code=response.status_code,
|
|
317
|
-
request_id=extract_request_id(response.headers),
|
|
328
|
+
f"Failed to run code. Status: {response.status_code}, Body: {response.text}"
|
|
318
329
|
)
|
|
330
|
+
raise build_api_exception_from_httpx(response, "Run code")
|
|
319
331
|
|
|
320
332
|
dispatcher = ExecutionEventDispatcher(execution, handlers)
|
|
321
333
|
|
|
@@ -22,6 +22,7 @@ support, session management, and variable persistence.
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
import asyncio
|
|
25
|
+
import contextlib
|
|
25
26
|
import logging
|
|
26
27
|
import time
|
|
27
28
|
from datetime import timedelta
|
|
@@ -64,7 +65,7 @@ class CodeInterpreter:
|
|
|
64
65
|
|
|
65
66
|
Key Features:
|
|
66
67
|
|
|
67
|
-
- Multi-language Code Execution:
|
|
68
|
+
- Multi-language Code Execution: Python, JavaScript, TypeScript, Go, Bash
|
|
68
69
|
- Session Management: Persistent execution contexts with variable state
|
|
69
70
|
- Sandbox Integration: Full access to underlying sandbox file system and command execution
|
|
70
71
|
- Streaming Execution: Real-time code execution with output streaming
|
|
@@ -73,11 +74,12 @@ class CodeInterpreter:
|
|
|
73
74
|
Usage Example:
|
|
74
75
|
|
|
75
76
|
```python
|
|
76
|
-
#
|
|
77
|
-
|
|
77
|
+
# The sandbox must use the opensandbox/code-interpreter image (or a
|
|
78
|
+
# derivative); the strict health check requires the Jupyter runtime.
|
|
78
79
|
sandbox = await Sandbox.create(
|
|
79
|
-
"
|
|
80
|
-
|
|
80
|
+
"opensandbox/code-interpreter:latest",
|
|
81
|
+
entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
|
|
82
|
+
resource={"cpu": "1", "memory": "2Gi"},
|
|
81
83
|
)
|
|
82
84
|
|
|
83
85
|
# Then create a code interpreter wrapping the sandbox
|
|
@@ -87,9 +89,10 @@ class CodeInterpreter:
|
|
|
87
89
|
from code_interpreter.models.code import SupportedLanguage
|
|
88
90
|
context = await interpreter.codes.create_context(SupportedLanguage.PYTHON)
|
|
89
91
|
result = await interpreter.codes.run("print('Hello World')", context=context)
|
|
90
|
-
print(result.
|
|
92
|
+
print(result.text) # Output: Hello World
|
|
91
93
|
|
|
92
94
|
# Access underlying sandbox for file operations
|
|
95
|
+
from opensandbox.models.filesystem import WriteEntry
|
|
93
96
|
await interpreter.sandbox.files.write_files([
|
|
94
97
|
WriteEntry(path="data.txt", data="Hello")
|
|
95
98
|
])
|
|
@@ -98,10 +101,16 @@ class CodeInterpreter:
|
|
|
98
101
|
context=context,
|
|
99
102
|
)
|
|
100
103
|
|
|
101
|
-
#
|
|
104
|
+
# Clean up: closing the interpreter releases its HTTP clients; the
|
|
105
|
+
# underlying sandbox lifecycle stays caller-owned.
|
|
106
|
+
await interpreter.aclose()
|
|
102
107
|
await sandbox.kill()
|
|
103
108
|
await sandbox.close()
|
|
104
109
|
```
|
|
110
|
+
|
|
111
|
+
CodeInterpreter also supports the async context-manager protocol; exiting
|
|
112
|
+
the ``async with`` block releases the interpreter's HTTP clients (not the
|
|
113
|
+
sandbox).
|
|
105
114
|
"""
|
|
106
115
|
|
|
107
116
|
def __init__(self, sandbox: Sandbox, code_service: Codes) -> None:
|
|
@@ -161,6 +170,24 @@ class CodeInterpreter:
|
|
|
161
170
|
"""
|
|
162
171
|
return self._sandbox.commands
|
|
163
172
|
|
|
173
|
+
async def aclose(self) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Release resources owned by the code interpreter.
|
|
176
|
+
|
|
177
|
+
Closes the HTTP clients used by the code execution service. The
|
|
178
|
+
underlying sandbox is NOT affected; manage its lifecycle separately
|
|
179
|
+
(``sandbox.kill()`` / ``sandbox.close()``). Safe to call multiple times.
|
|
180
|
+
"""
|
|
181
|
+
aclose = getattr(self._code_service, "aclose", None)
|
|
182
|
+
if aclose is not None:
|
|
183
|
+
await aclose()
|
|
184
|
+
|
|
185
|
+
async def __aenter__(self) -> "CodeInterpreter":
|
|
186
|
+
return self
|
|
187
|
+
|
|
188
|
+
async def __aexit__(self, exc_type, exc, tb) -> None:
|
|
189
|
+
await self.aclose()
|
|
190
|
+
|
|
164
191
|
@property
|
|
165
192
|
def metrics(self):
|
|
166
193
|
"""
|
|
@@ -335,9 +362,16 @@ class CodeInterpreter:
|
|
|
335
362
|
)
|
|
336
363
|
|
|
337
364
|
interpreter = cls(sandbox, code_execution_service)
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
365
|
+
try:
|
|
366
|
+
if not skip_health_check:
|
|
367
|
+
await interpreter.check_ready(
|
|
368
|
+
ready_timeout, health_check_polling_interval
|
|
369
|
+
)
|
|
370
|
+
except Exception:
|
|
371
|
+
# Don't leak the service's clients on a failed creation.
|
|
372
|
+
with contextlib.suppress(Exception):
|
|
373
|
+
await interpreter.aclose()
|
|
374
|
+
raise
|
|
341
375
|
|
|
342
376
|
logger.info(f"Code interpreter {sandbox.id} created successfully")
|
|
343
377
|
|
|
@@ -38,10 +38,10 @@ class Codes(Protocol):
|
|
|
38
38
|
Supported Languages:
|
|
39
39
|
|
|
40
40
|
- Python: Full Python 3.x support with package management
|
|
41
|
-
- JavaScript
|
|
41
|
+
- JavaScript: ES6+ with npm package support
|
|
42
|
+
- TypeScript
|
|
43
|
+
- Go
|
|
42
44
|
- Bash: Shell scripting with full system access
|
|
43
|
-
- Java: Compilation and execution with classpath management
|
|
44
|
-
- Kotlin: Script and compiled Kotlin execution
|
|
45
45
|
|
|
46
46
|
Key Features:
|
|
47
47
|
|
|
@@ -27,19 +27,19 @@ from opensandbox.adapters.converter.exception_converter import (
|
|
|
27
27
|
ExceptionConverter,
|
|
28
28
|
)
|
|
29
29
|
from opensandbox.adapters.converter.response_handler import (
|
|
30
|
-
|
|
30
|
+
build_api_exception_from_httpx,
|
|
31
31
|
handle_api_error,
|
|
32
32
|
require_parsed,
|
|
33
33
|
)
|
|
34
34
|
from opensandbox.config.connection_sync import ConnectionConfigSync
|
|
35
|
-
from opensandbox.exceptions import InvalidArgumentException
|
|
35
|
+
from opensandbox.exceptions import InvalidArgumentException
|
|
36
36
|
from opensandbox.models.execd import Execution
|
|
37
37
|
from opensandbox.models.execd_sync import ExecutionHandlersSync
|
|
38
38
|
from opensandbox.models.sandboxes import SandboxEndpoint
|
|
39
39
|
from opensandbox.sync.adapters.converter.execution_event_dispatcher import (
|
|
40
40
|
ExecutionEventDispatcherSync,
|
|
41
41
|
)
|
|
42
|
-
from opensandbox.transport import
|
|
42
|
+
from opensandbox.transport import streaming_transport
|
|
43
43
|
|
|
44
44
|
from code_interpreter.models.code_sync import CodeContextSync, SupportedLanguageSync
|
|
45
45
|
from code_interpreter.sync.services.code import CodesSync
|
|
@@ -99,18 +99,22 @@ class CodesAdapterSync(CodesSync):
|
|
|
99
99
|
base_url = f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}"
|
|
100
100
|
timeout_seconds = self.connection_config.request_timeout.total_seconds()
|
|
101
101
|
timeout = httpx.Timeout(timeout_seconds)
|
|
102
|
-
headers =
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
102
|
+
headers = self.execd_endpoint.build_request_headers(self.connection_config)
|
|
103
|
+
|
|
104
|
+
# Adapter clients must own their transports: close() closes them,
|
|
105
|
+
# and the shared/user connection_config transport must survive that.
|
|
106
|
+
self._owns_clients = self.connection_config.owns_transport
|
|
107
|
+
main_transport: httpx.BaseTransport | None
|
|
108
|
+
if self._owns_clients:
|
|
109
|
+
main_transport = self.connection_config.new_owned_transport()
|
|
110
|
+
else:
|
|
111
|
+
main_transport = self.connection_config.transport
|
|
108
112
|
self._client = Client(base_url=base_url, timeout=timeout)
|
|
109
113
|
self._httpx_client = httpx.Client(
|
|
110
114
|
base_url=base_url,
|
|
111
115
|
headers=headers,
|
|
112
116
|
timeout=timeout,
|
|
113
|
-
transport=
|
|
117
|
+
transport=main_transport,
|
|
114
118
|
)
|
|
115
119
|
self._client.set_httpx_client(self._httpx_client)
|
|
116
120
|
|
|
@@ -119,15 +123,19 @@ class CodesAdapterSync(CodesSync):
|
|
|
119
123
|
"Accept": "text/event-stream",
|
|
120
124
|
"Cache-Control": "no-cache",
|
|
121
125
|
}
|
|
122
|
-
# SSE bootstraps
|
|
123
|
-
#
|
|
124
|
-
|
|
126
|
+
# SSE bootstraps keep only the wake-timeout retry: a non-idempotent
|
|
127
|
+
# status opt-in would cause duplicate execution on a resent SSE POST.
|
|
128
|
+
sse_transport = (
|
|
129
|
+
self.connection_config.new_owned_transport()
|
|
130
|
+
if self._owns_clients
|
|
131
|
+
else self.connection_config.transport
|
|
132
|
+
)
|
|
125
133
|
self._sse_client = httpx.Client(
|
|
126
134
|
headers=sse_headers,
|
|
127
135
|
timeout=httpx.Timeout(
|
|
128
136
|
connect=timeout_seconds, read=None, write=timeout_seconds, pool=None
|
|
129
137
|
),
|
|
130
|
-
transport=
|
|
138
|
+
transport=streaming_transport(sse_transport),
|
|
131
139
|
)
|
|
132
140
|
|
|
133
141
|
def _get_execd_url(self, path: str) -> str:
|
|
@@ -136,6 +144,21 @@ class CodesAdapterSync(CodesSync):
|
|
|
136
144
|
f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}{path}"
|
|
137
145
|
)
|
|
138
146
|
|
|
147
|
+
def close(self) -> None:
|
|
148
|
+
"""Release the adapter-owned HTTP clients.
|
|
149
|
+
|
|
150
|
+
Never closes the shared ``connection_config.transport``; the sandbox
|
|
151
|
+
stays usable after ``close()``. Clients wrapping a user-supplied
|
|
152
|
+
transport are left open (httpx would close the caller's transport
|
|
153
|
+
with them).
|
|
154
|
+
"""
|
|
155
|
+
if not self._owns_clients:
|
|
156
|
+
return
|
|
157
|
+
try:
|
|
158
|
+
self._httpx_client.close()
|
|
159
|
+
finally:
|
|
160
|
+
self._sse_client.close()
|
|
161
|
+
|
|
139
162
|
def create_context(self, language: str) -> CodeContextSync:
|
|
140
163
|
"""
|
|
141
164
|
Create a new execution context for code interpretation (sync).
|
|
@@ -286,17 +309,13 @@ class CodesAdapterSync(CodesSync):
|
|
|
286
309
|
|
|
287
310
|
url = self._get_execd_url(self.RUN_CODE_PATH)
|
|
288
311
|
execution = Execution(id=None, execution_count=None, result=[], error=None)
|
|
312
|
+
execution._is_code_execution = True
|
|
289
313
|
dispatcher = ExecutionEventDispatcherSync(execution, handlers)
|
|
290
314
|
|
|
291
315
|
with self._sse_client.stream("POST", url, json=api_request) as response:
|
|
292
316
|
if response.status_code != 200:
|
|
293
317
|
response.read()
|
|
294
|
-
|
|
295
|
-
raise SandboxApiException(
|
|
296
|
-
message=f"Failed to run code. Status code: {response.status_code}, Body: {error_body}",
|
|
297
|
-
status_code=response.status_code,
|
|
298
|
-
request_id=extract_request_id(response.headers),
|
|
299
|
-
)
|
|
318
|
+
raise build_api_exception_from_httpx(response, "Run code")
|
|
300
319
|
|
|
301
320
|
for line in response.iter_lines():
|
|
302
321
|
if not line or not line.strip():
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
Synchronous Code Interpreter SDK.
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
+
import contextlib
|
|
20
21
|
import logging
|
|
21
22
|
import time
|
|
22
23
|
from datetime import timedelta
|
|
@@ -73,14 +74,18 @@ class CodeInterpreterSync:
|
|
|
73
74
|
```python
|
|
74
75
|
from opensandbox.sync.sandbox import SandboxSync
|
|
75
76
|
from code_interpreter.sync.code_interpreter import CodeInterpreterSync
|
|
76
|
-
from code_interpreter.models.
|
|
77
|
+
from code_interpreter.models.code_sync import SupportedLanguageSync
|
|
77
78
|
|
|
78
|
-
sandbox = SandboxSync.create(
|
|
79
|
+
sandbox = SandboxSync.create(
|
|
80
|
+
"opensandbox/code-interpreter:latest",
|
|
81
|
+
entrypoint=["/opt/code-interpreter/code-interpreter.sh"],
|
|
82
|
+
)
|
|
79
83
|
interpreter = CodeInterpreterSync.create(sandbox=sandbox)
|
|
80
84
|
|
|
81
|
-
ctx = interpreter.codes.create_context(
|
|
85
|
+
ctx = interpreter.codes.create_context(SupportedLanguageSync.PYTHON)
|
|
82
86
|
result = interpreter.codes.run("print('hi')", context=ctx)
|
|
83
87
|
|
|
88
|
+
interpreter.close()
|
|
84
89
|
sandbox.kill()
|
|
85
90
|
sandbox.close()
|
|
86
91
|
```
|
|
@@ -139,6 +144,24 @@ class CodeInterpreterSync:
|
|
|
139
144
|
"""
|
|
140
145
|
return self._sandbox.commands
|
|
141
146
|
|
|
147
|
+
def close(self) -> None:
|
|
148
|
+
"""
|
|
149
|
+
Release resources owned by the code interpreter.
|
|
150
|
+
|
|
151
|
+
Closes the HTTP clients used by the code execution service. The
|
|
152
|
+
underlying sandbox is NOT affected; manage its lifecycle separately
|
|
153
|
+
(``sandbox.kill()`` / ``sandbox.close()``). Safe to call multiple times.
|
|
154
|
+
"""
|
|
155
|
+
close = getattr(self._code_service, "close", None)
|
|
156
|
+
if close is not None:
|
|
157
|
+
close()
|
|
158
|
+
|
|
159
|
+
def __enter__(self) -> "CodeInterpreterSync":
|
|
160
|
+
return self
|
|
161
|
+
|
|
162
|
+
def __exit__(self, exc_type, exc, tb) -> None:
|
|
163
|
+
self.close()
|
|
164
|
+
|
|
142
165
|
@property
|
|
143
166
|
def metrics(self):
|
|
144
167
|
"""
|
|
@@ -289,9 +312,14 @@ class CodeInterpreterSync:
|
|
|
289
312
|
code_service = factory.create_code_execution_service(endpoint)
|
|
290
313
|
|
|
291
314
|
interpreter = cls(sandbox, code_service)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
315
|
+
try:
|
|
316
|
+
if not skip_health_check:
|
|
317
|
+
interpreter.check_ready(ready_timeout, health_check_polling_interval)
|
|
318
|
+
except Exception:
|
|
319
|
+
# Don't leak the service's clients on a failed creation.
|
|
320
|
+
with contextlib.suppress(Exception):
|
|
321
|
+
interpreter.close()
|
|
322
|
+
raise
|
|
295
323
|
|
|
296
324
|
logger.info(f"Code interpreter {sandbox.id} created successfully")
|
|
297
325
|
return interpreter
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|