opensandbox-code-interpreter 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_interpreter/__init__.py +45 -0
- code_interpreter/adapters/__init__.py +30 -0
- code_interpreter/adapters/code_adapter.py +268 -0
- code_interpreter/adapters/converter/__init__.py +26 -0
- code_interpreter/adapters/converter/code_execution_converter.py +108 -0
- code_interpreter/adapters/factory.py +58 -0
- code_interpreter/code_interpreter.py +343 -0
- code_interpreter/models/__init__.py +28 -0
- code_interpreter/models/code.py +67 -0
- code_interpreter/models/code_sync.py +41 -0
- code_interpreter/py.typed +0 -0
- code_interpreter/services/__init__.py +24 -0
- code_interpreter/services/code.py +149 -0
- code_interpreter/sync/__init__.py +18 -0
- code_interpreter/sync/adapters/__init__.py +26 -0
- code_interpreter/sync/adapters/code_adapter.py +215 -0
- code_interpreter/sync/adapters/factory.py +54 -0
- code_interpreter/sync/code_interpreter.py +286 -0
- code_interpreter/sync/services/__init__.py +27 -0
- code_interpreter/sync/services/code.py +122 -0
- opensandbox_code_interpreter-0.1.0.dist-info/METADATA +462 -0
- opensandbox_code_interpreter-0.1.0.dist-info/RECORD +24 -0
- opensandbox_code_interpreter-0.1.0.dist-info/WHEEL +4 -0
- opensandbox_code_interpreter-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Alibaba Group Holding Ltd.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""
|
|
17
|
+
Adapter implementations for Code Interpreter sync services.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from code_interpreter.sync.adapters.code_adapter import CodesAdapterSync
|
|
21
|
+
from code_interpreter.sync.adapters.factory import AdapterFactorySync
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"AdapterFactorySync",
|
|
25
|
+
"CodesAdapterSync",
|
|
26
|
+
]
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Alibaba Group Holding Ltd.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""
|
|
17
|
+
Synchronous adapter for code execution service (including SSE streaming).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
|
|
23
|
+
import httpx
|
|
24
|
+
from opensandbox.adapters.converter.event_node import EventNode
|
|
25
|
+
from opensandbox.adapters.converter.exception_converter import (
|
|
26
|
+
ExceptionConverter,
|
|
27
|
+
)
|
|
28
|
+
from opensandbox.adapters.converter.response_handler import (
|
|
29
|
+
handle_api_error,
|
|
30
|
+
require_parsed,
|
|
31
|
+
)
|
|
32
|
+
from opensandbox.config.connection_sync import ConnectionConfigSync
|
|
33
|
+
from opensandbox.exceptions import InvalidArgumentException, SandboxApiException
|
|
34
|
+
from opensandbox.models.execd import Execution
|
|
35
|
+
from opensandbox.models.execd_sync import ExecutionHandlersSync
|
|
36
|
+
from opensandbox.models.sandboxes import SandboxEndpoint
|
|
37
|
+
from opensandbox.sync.adapters.converter.execution_event_dispatcher import (
|
|
38
|
+
ExecutionEventDispatcherSync,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
from code_interpreter.models.code_sync import CodeContextSync, SupportedLanguageSync
|
|
42
|
+
from code_interpreter.sync.services.code import CodesSync
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CodesAdapterSync(CodesSync):
|
|
48
|
+
"""
|
|
49
|
+
Synchronous adapter for code execution service.
|
|
50
|
+
|
|
51
|
+
This adapter is the sync counterpart of :class:`code_interpreter.adapters.code_adapter.CodesAdapter`.
|
|
52
|
+
It wraps the generated execd API client for non-streaming operations and uses direct ``httpx``
|
|
53
|
+
streaming for SSE output while running code.
|
|
54
|
+
|
|
55
|
+
Notes:
|
|
56
|
+
|
|
57
|
+
- ``run`` performs blocking SSE streaming via ``httpx.Client.stream``.
|
|
58
|
+
- Each SSE line is parsed into an :class:`EventNode` and dispatched via
|
|
59
|
+
:class:`ExecutionEventDispatcherSync` to update the shared :class:`Execution` object
|
|
60
|
+
and invoke any user-provided handlers.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
RUN_CODE_PATH = "/code"
|
|
64
|
+
CREATE_CONTEXT_PATH = "/code/context"
|
|
65
|
+
|
|
66
|
+
def __init__(self, execd_endpoint: SandboxEndpoint, connection_config: ConnectionConfigSync) -> None:
|
|
67
|
+
"""
|
|
68
|
+
Initialize the code service adapter (sync).
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
execd_endpoint: Endpoint for execd daemon connection
|
|
72
|
+
connection_config: Shared connection configuration (transport, headers, timeouts)
|
|
73
|
+
"""
|
|
74
|
+
self.execd_endpoint = execd_endpoint
|
|
75
|
+
self.connection_config = connection_config
|
|
76
|
+
from opensandbox.api.execd import Client
|
|
77
|
+
|
|
78
|
+
base_url = f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}"
|
|
79
|
+
timeout_seconds = self.connection_config.request_timeout.total_seconds()
|
|
80
|
+
timeout = httpx.Timeout(timeout_seconds)
|
|
81
|
+
headers = {"User-Agent": self.connection_config.user_agent, **self.connection_config.headers}
|
|
82
|
+
|
|
83
|
+
self._client = Client(base_url=base_url, timeout=timeout)
|
|
84
|
+
self._httpx_client = httpx.Client(
|
|
85
|
+
base_url=base_url,
|
|
86
|
+
headers=headers,
|
|
87
|
+
timeout=timeout,
|
|
88
|
+
transport=self.connection_config.transport,
|
|
89
|
+
)
|
|
90
|
+
self._client.set_httpx_client(self._httpx_client)
|
|
91
|
+
|
|
92
|
+
sse_headers = {**headers, "Accept": "text/event-stream", "Cache-Control": "no-cache"}
|
|
93
|
+
self._sse_client = httpx.Client(
|
|
94
|
+
headers=sse_headers,
|
|
95
|
+
timeout=httpx.Timeout(connect=timeout_seconds, read=None, write=timeout_seconds, pool=None),
|
|
96
|
+
transport=self.connection_config.transport,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def _get_execd_url(self, path: str) -> str:
|
|
100
|
+
"""Build URL for execd endpoint."""
|
|
101
|
+
return f"{self.connection_config.protocol}://{self.execd_endpoint.endpoint}{path}"
|
|
102
|
+
|
|
103
|
+
def create_context(self, language: str) -> CodeContextSync:
|
|
104
|
+
"""
|
|
105
|
+
Create a new execution context for code interpretation (sync).
|
|
106
|
+
|
|
107
|
+
Uses the generated API client for this non-streaming operation.
|
|
108
|
+
"""
|
|
109
|
+
try:
|
|
110
|
+
from opensandbox.api.execd.api.code_interpreting import create_code_context
|
|
111
|
+
from opensandbox.api.execd.models.code_context import (
|
|
112
|
+
CodeContext as ApiCodeContext,
|
|
113
|
+
)
|
|
114
|
+
from opensandbox.api.execd.models.code_context_request import (
|
|
115
|
+
CodeContextRequest,
|
|
116
|
+
)
|
|
117
|
+
from opensandbox.api.execd.types import UNSET
|
|
118
|
+
|
|
119
|
+
response_obj = create_code_context.sync_detailed(
|
|
120
|
+
client=self._client,
|
|
121
|
+
body=CodeContextRequest(language=language),
|
|
122
|
+
)
|
|
123
|
+
handle_api_error(response_obj, "Create code context")
|
|
124
|
+
parsed = require_parsed(response_obj, ApiCodeContext, "Create code context")
|
|
125
|
+
context_id = parsed.id if parsed.id is not UNSET else None
|
|
126
|
+
return CodeContextSync(id=context_id, language=parsed.language)
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error("Failed to create context", exc_info=e)
|
|
129
|
+
raise ExceptionConverter.to_sandbox_exception(e) from e
|
|
130
|
+
|
|
131
|
+
def run(
|
|
132
|
+
self,
|
|
133
|
+
code: str,
|
|
134
|
+
*,
|
|
135
|
+
context: CodeContextSync | None = None,
|
|
136
|
+
handlers: ExecutionHandlersSync | None = None,
|
|
137
|
+
) -> Execution:
|
|
138
|
+
"""
|
|
139
|
+
Execute code within the specified context using SSE streaming (sync).
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
code: Source code to execute.
|
|
143
|
+
context: Execution context (language + optional id). If None, a temporary Python context is used.
|
|
144
|
+
handlers: Optional streaming handlers for stdout/stderr/events.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Execution result populated incrementally while streaming events
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
InvalidArgumentException: if code is empty
|
|
151
|
+
SandboxApiException: if execd returns a non-200 response
|
|
152
|
+
SandboxException: for other errors converted by :class:`ExceptionConverter`
|
|
153
|
+
"""
|
|
154
|
+
if not code.strip():
|
|
155
|
+
raise InvalidArgumentException("Code cannot be empty")
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
context = context or CodeContextSync(language=SupportedLanguageSync.PYTHON)
|
|
159
|
+
api_request = {
|
|
160
|
+
"code": code,
|
|
161
|
+
"context": {
|
|
162
|
+
"language": context.language,
|
|
163
|
+
**({"id": context.id} if context.id else {}),
|
|
164
|
+
},
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
url = self._get_execd_url(self.RUN_CODE_PATH)
|
|
168
|
+
execution = Execution(id=None, execution_count=None, result=[], error=None)
|
|
169
|
+
dispatcher = ExecutionEventDispatcherSync(execution, handlers)
|
|
170
|
+
|
|
171
|
+
with self._sse_client.stream("POST", url, json=api_request) as response:
|
|
172
|
+
if response.status_code != 200:
|
|
173
|
+
response.read()
|
|
174
|
+
raise SandboxApiException(
|
|
175
|
+
message=f"Failed to run code. Status code: {response.status_code}",
|
|
176
|
+
status_code=response.status_code,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
for line in response.iter_lines():
|
|
180
|
+
if not line or not line.strip():
|
|
181
|
+
continue
|
|
182
|
+
data = line
|
|
183
|
+
if data.startswith("data:"):
|
|
184
|
+
data = data[5:].strip()
|
|
185
|
+
try:
|
|
186
|
+
event_dict = json.loads(data)
|
|
187
|
+
event_node = EventNode(**event_dict)
|
|
188
|
+
dispatcher.dispatch(event_node)
|
|
189
|
+
except json.JSONDecodeError:
|
|
190
|
+
logger.debug("Failed to parse SSE line: %s", line)
|
|
191
|
+
continue
|
|
192
|
+
except Exception as e:
|
|
193
|
+
logger.error("Error processing event: %s", data, exc_info=e)
|
|
194
|
+
continue
|
|
195
|
+
|
|
196
|
+
return execution
|
|
197
|
+
except Exception as e:
|
|
198
|
+
logger.error("Failed to run code (length: %s)", len(code), exc_info=e)
|
|
199
|
+
raise ExceptionConverter.to_sandbox_exception(e) from e
|
|
200
|
+
|
|
201
|
+
def interrupt(self, execution_id: str) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Interrupt a currently running code execution.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
execution_id: Execution id returned by execd for the running code execution
|
|
207
|
+
"""
|
|
208
|
+
try:
|
|
209
|
+
from opensandbox.api.execd.api.code_interpreting import interrupt_code
|
|
210
|
+
|
|
211
|
+
response_obj = interrupt_code.sync_detailed(client=self._client, id=execution_id)
|
|
212
|
+
handle_api_error(response_obj, "Interrupt code execution")
|
|
213
|
+
except Exception as e:
|
|
214
|
+
logger.error("Failed to interrupt code execution", exc_info=e)
|
|
215
|
+
raise ExceptionConverter.to_sandbox_exception(e) from e
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Alibaba Group Holding Ltd.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""
|
|
17
|
+
Factory for creating Code Interpreter sync services.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from opensandbox.config.connection_sync import ConnectionConfigSync
|
|
21
|
+
from opensandbox.models.sandboxes import SandboxEndpoint
|
|
22
|
+
|
|
23
|
+
from code_interpreter.sync.adapters.code_adapter import CodesAdapterSync
|
|
24
|
+
from code_interpreter.sync.services.code import CodesSync
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AdapterFactorySync:
|
|
28
|
+
"""
|
|
29
|
+
Factory for creating Code Interpreter sync service instances.
|
|
30
|
+
|
|
31
|
+
This factory centralizes construction of sync services so they all share the same
|
|
32
|
+
connection configuration (transport, headers, timeouts).
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self, connection_config: ConnectionConfigSync) -> None:
|
|
36
|
+
"""
|
|
37
|
+
Initialize the factory with shared connection configuration (sync).
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
connection_config: Shared connection configuration (transport, headers, timeouts).
|
|
41
|
+
"""
|
|
42
|
+
self.connection_config = connection_config
|
|
43
|
+
|
|
44
|
+
def create_code_execution_service(self, endpoint: SandboxEndpoint) -> CodesSync:
|
|
45
|
+
"""
|
|
46
|
+
Create a code execution service for the specified endpoint (sync).
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
endpoint: Sandbox endpoint for code execution services.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
Configured sync code service instance.
|
|
53
|
+
"""
|
|
54
|
+
return CodesAdapterSync(endpoint, self.connection_config)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Alibaba Group Holding Ltd.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""
|
|
17
|
+
Synchronous Code Interpreter SDK.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import logging
|
|
21
|
+
from datetime import datetime, timedelta, timezone
|
|
22
|
+
from uuid import UUID
|
|
23
|
+
|
|
24
|
+
from opensandbox.constants import DEFAULT_EXECD_PORT
|
|
25
|
+
from opensandbox.exceptions import (
|
|
26
|
+
InvalidArgumentException,
|
|
27
|
+
SandboxException,
|
|
28
|
+
SandboxInternalException,
|
|
29
|
+
)
|
|
30
|
+
from opensandbox.models.sandboxes import (
|
|
31
|
+
SandboxEndpoint,
|
|
32
|
+
SandboxInfo,
|
|
33
|
+
SandboxMetrics,
|
|
34
|
+
)
|
|
35
|
+
from opensandbox.sync.sandbox import SandboxSync
|
|
36
|
+
|
|
37
|
+
from code_interpreter.sync.adapters.factory import AdapterFactorySync
|
|
38
|
+
from code_interpreter.sync.services.code import CodesSync
|
|
39
|
+
|
|
40
|
+
logger = logging.getLogger(__name__)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CodeInterpreterSync:
|
|
44
|
+
"""
|
|
45
|
+
Synchronous Code Interpreter SDK providing secure, isolated code execution capabilities.
|
|
46
|
+
|
|
47
|
+
This class mirrors the async :class:`code_interpreter.code_interpreter.CodeInterpreter`, but all
|
|
48
|
+
operations are **blocking** and executed in the current thread.
|
|
49
|
+
|
|
50
|
+
It wraps an existing :class:`opensandbox.sync.sandbox.SandboxSync` instance and adds
|
|
51
|
+
code-execution APIs (contexts, run with SSE streaming, interrupts) on top.
|
|
52
|
+
|
|
53
|
+
Notes:
|
|
54
|
+
|
|
55
|
+
- **Blocking**: Do not call these methods directly from an asyncio event loop thread.
|
|
56
|
+
If you need non-blocking behavior, prefer the async :class:`~code_interpreter.code_interpreter.CodeInterpreter`.
|
|
57
|
+
- **Lifecycle**: Remote lifecycle is owned by the underlying sandbox. This class delegates
|
|
58
|
+
pause/resume/kill/renew/metrics to the sandbox.
|
|
59
|
+
|
|
60
|
+
Usage Example:
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from opensandbox.sync.sandbox import SandboxSync
|
|
64
|
+
from code_interpreter.sync.code_interpreter import CodeInterpreterSync
|
|
65
|
+
from code_interpreter.models.code import SupportedLanguage
|
|
66
|
+
|
|
67
|
+
sandbox = SandboxSync.create("python:3.11")
|
|
68
|
+
interpreter = CodeInterpreterSync.create(sandbox=sandbox)
|
|
69
|
+
|
|
70
|
+
ctx = interpreter.codes.create_context(SupportedLanguage.PYTHON)
|
|
71
|
+
result = interpreter.codes.run("print('hi')", context=ctx)
|
|
72
|
+
|
|
73
|
+
sandbox.kill()
|
|
74
|
+
sandbox.close()
|
|
75
|
+
```
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, sandbox: SandboxSync, code_service: CodesSync) -> None:
|
|
79
|
+
"""
|
|
80
|
+
Initialize CodeInterpreterSync with sandbox and code service.
|
|
81
|
+
|
|
82
|
+
Note: This constructor is for internal use. Use :meth:`create` instead.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
sandbox: Underlying sandbox instance
|
|
86
|
+
code_service: Code execution service implementation (sync)
|
|
87
|
+
"""
|
|
88
|
+
self._sandbox = sandbox
|
|
89
|
+
self._code_service = code_service
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def sandbox(self) -> SandboxSync:
|
|
93
|
+
"""
|
|
94
|
+
Provides access to the underlying sandbox instance.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
The underlying sandbox instance
|
|
98
|
+
"""
|
|
99
|
+
return self._sandbox
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def id(self) -> UUID:
|
|
103
|
+
"""
|
|
104
|
+
Gets the unique identifier of this code interpreter (same as underlying sandbox ID).
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
UUID of the code interpreter/sandbox
|
|
108
|
+
"""
|
|
109
|
+
return self._sandbox.id
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def files(self):
|
|
113
|
+
"""
|
|
114
|
+
Provides access to file system operations within the sandbox.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Service for filesystem manipulation
|
|
118
|
+
"""
|
|
119
|
+
return self._sandbox.files
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def commands(self):
|
|
123
|
+
"""
|
|
124
|
+
Provides access to command execution operations.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Service for command execution
|
|
128
|
+
"""
|
|
129
|
+
return self._sandbox.commands
|
|
130
|
+
|
|
131
|
+
@property
|
|
132
|
+
def metrics(self):
|
|
133
|
+
"""
|
|
134
|
+
Provides access to sandbox metrics and monitoring.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Service for metrics retrieval
|
|
138
|
+
"""
|
|
139
|
+
return self._sandbox.metrics
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def codes(self) -> CodesSync:
|
|
143
|
+
"""
|
|
144
|
+
Provides access to code execution operations (sync).
|
|
145
|
+
|
|
146
|
+
This service enables:
|
|
147
|
+
- Multi-language code execution (Python, JavaScript, Bash, etc.)
|
|
148
|
+
- Execution context management with persistent variables
|
|
149
|
+
- Real-time output streaming and interruption capabilities
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Service for advanced code execution with session support
|
|
153
|
+
"""
|
|
154
|
+
return self._code_service
|
|
155
|
+
|
|
156
|
+
def get_endpoint(self, port: int) -> SandboxEndpoint:
|
|
157
|
+
"""
|
|
158
|
+
Gets a specific network endpoint for the underlying sandbox.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
port: The port number to get the endpoint for
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Endpoint information including host, port, and connection details
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
SandboxException: If endpoint cannot be retrieved
|
|
168
|
+
"""
|
|
169
|
+
return self._sandbox.get_endpoint(port)
|
|
170
|
+
|
|
171
|
+
def get_info(self) -> SandboxInfo:
|
|
172
|
+
"""
|
|
173
|
+
Gets the current status of this sandbox.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Current sandbox status including state and metadata
|
|
177
|
+
|
|
178
|
+
Raises:
|
|
179
|
+
SandboxException: If status cannot be retrieved
|
|
180
|
+
"""
|
|
181
|
+
return self._sandbox.get_info()
|
|
182
|
+
|
|
183
|
+
def get_metrics(self) -> SandboxMetrics:
|
|
184
|
+
"""
|
|
185
|
+
Gets the current resource usage metrics for the underlying sandbox.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Current sandbox metrics including CPU, memory, and I/O statistics
|
|
189
|
+
|
|
190
|
+
Raises:
|
|
191
|
+
SandboxException: If metrics cannot be retrieved
|
|
192
|
+
"""
|
|
193
|
+
return self._sandbox.get_metrics()
|
|
194
|
+
|
|
195
|
+
def renew(self, timeout: timedelta | int) -> None:
|
|
196
|
+
"""
|
|
197
|
+
Renew the sandbox expiration time to delay automatic termination.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
timeout: Duration to add to the current time to set the new expiration.
|
|
201
|
+
Can be timedelta or seconds as int.
|
|
202
|
+
|
|
203
|
+
Raises:
|
|
204
|
+
SandboxException: If the operation fails
|
|
205
|
+
"""
|
|
206
|
+
if isinstance(timeout, int):
|
|
207
|
+
timeout = timedelta(seconds=timeout)
|
|
208
|
+
logger.info(
|
|
209
|
+
"Renew code interpreter %s timeout, estimated expiration to %s",
|
|
210
|
+
self.id,
|
|
211
|
+
datetime.now(timezone.utc) + timeout,
|
|
212
|
+
)
|
|
213
|
+
self._sandbox.renew(timeout)
|
|
214
|
+
|
|
215
|
+
def pause(self) -> None:
|
|
216
|
+
"""
|
|
217
|
+
Pauses the sandbox while preserving its state.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
SandboxException: If pause operation fails
|
|
221
|
+
"""
|
|
222
|
+
logger.info("Pausing code interpreter: %s", self.id)
|
|
223
|
+
self._sandbox.pause()
|
|
224
|
+
|
|
225
|
+
def resume(self) -> None:
|
|
226
|
+
"""
|
|
227
|
+
Resumes a previously paused sandbox.
|
|
228
|
+
|
|
229
|
+
Raises:
|
|
230
|
+
SandboxException: If resume operation fails
|
|
231
|
+
"""
|
|
232
|
+
logger.info("Resuming code interpreter: %s", self.id)
|
|
233
|
+
self._sandbox.resume()
|
|
234
|
+
|
|
235
|
+
def kill(self) -> None:
|
|
236
|
+
"""
|
|
237
|
+
Terminate the remote sandbox instance (irreversible).
|
|
238
|
+
|
|
239
|
+
Note: This method does NOT close the local `SandboxSync` object resources (like connection pools).
|
|
240
|
+
You should call `sandbox().close()` or use the sync context manager on the sandbox to clean up.
|
|
241
|
+
|
|
242
|
+
Raises:
|
|
243
|
+
SandboxException: If termination fails
|
|
244
|
+
"""
|
|
245
|
+
logger.info("Killing code interpreter: %s", self.id)
|
|
246
|
+
self._sandbox.kill()
|
|
247
|
+
|
|
248
|
+
def is_healthy(self) -> bool:
|
|
249
|
+
"""
|
|
250
|
+
Checks if the code interpreter and its underlying sandbox are healthy and responsive.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
True if sandbox is healthy, False otherwise
|
|
254
|
+
"""
|
|
255
|
+
return self._sandbox.is_healthy()
|
|
256
|
+
|
|
257
|
+
@classmethod
|
|
258
|
+
def create(cls, sandbox: SandboxSync) -> "CodeInterpreterSync":
|
|
259
|
+
"""
|
|
260
|
+
Create a CodeInterpreterSync from an existing SandboxSync instance (blocking).
|
|
261
|
+
|
|
262
|
+
Args:
|
|
263
|
+
sandbox: Existing sandbox instance to wrap with code execution capabilities
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
CodeInterpreterSync instance wrapping the sandbox
|
|
267
|
+
|
|
268
|
+
Raises:
|
|
269
|
+
InvalidArgumentException: If sandbox is not provided
|
|
270
|
+
SandboxException: If creation fails
|
|
271
|
+
SandboxInternalException: If internal service initialization fails
|
|
272
|
+
"""
|
|
273
|
+
if sandbox is None:
|
|
274
|
+
raise InvalidArgumentException("Sandbox instance must be provided")
|
|
275
|
+
|
|
276
|
+
logger.info("Creating code interpreter from sandbox: %s", sandbox.id)
|
|
277
|
+
factory = AdapterFactorySync(sandbox.connection_config)
|
|
278
|
+
try:
|
|
279
|
+
endpoint = sandbox.get_endpoint(DEFAULT_EXECD_PORT)
|
|
280
|
+
code_service = factory.create_code_execution_service(endpoint)
|
|
281
|
+
logger.info("Code interpreter %s created successfully", sandbox.id)
|
|
282
|
+
return cls(sandbox, code_service)
|
|
283
|
+
except Exception as e:
|
|
284
|
+
if isinstance(e, SandboxException):
|
|
285
|
+
raise
|
|
286
|
+
raise SandboxInternalException(f"Failed to create code interpreter: {e}", cause=e) from e
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright 2025 Alibaba Group Holding Ltd.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
#
|
|
16
|
+
"""
|
|
17
|
+
Synchronous service interfaces (Protocols) for the Code Interpreter sync SDK.
|
|
18
|
+
|
|
19
|
+
These interfaces mirror the async interfaces under :mod:`code_interpreter.services`,
|
|
20
|
+
but are **blocking** and intended for use with :class:`code_interpreter.sync.code_interpreter.CodeInterpreterSync`.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from code_interpreter.sync.services.code import CodesSync
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"CodesSync",
|
|
27
|
+
]
|