e2b-code-interpreter 0.0.6__tar.gz → 0.0.8__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.
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/PKG-INFO +2 -2
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/e2b_code_interpreter/main.py +32 -14
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/e2b_code_interpreter/messaging.py +8 -7
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/e2b_code_interpreter/models.py +6 -1
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/pyproject.toml +2 -2
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/README.md +0 -0
- {e2b_code_interpreter-0.0.6 → e2b_code_interpreter-0.0.8}/e2b_code_interpreter/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: e2b-code-interpreter
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: E2B Code Interpreter - Stateful code execution
|
|
5
5
|
Home-page: https://e2b.dev/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
Requires-Dist: e2b (>=0.17.
|
|
17
|
+
Requires-Dist: e2b (>=0.17.1)
|
|
18
18
|
Requires-Dist: pydantic (>1,<3)
|
|
19
19
|
Requires-Dist: websocket-client (>=1.7.0,<2.0.0)
|
|
20
20
|
Project-URL: Bug Tracker, https://github.com/e2b-dev/code-interpreter/issues
|
|
@@ -2,6 +2,8 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import threading
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
5
7
|
import requests
|
|
6
8
|
|
|
7
9
|
from concurrent.futures import Future
|
|
@@ -91,16 +93,16 @@ class JupyterExtension:
|
|
|
91
93
|
ws = ws_future.result(timeout=timeout)
|
|
92
94
|
else:
|
|
93
95
|
logger.debug(f"Creating new websocket connection to kernel {kernel_id}")
|
|
94
|
-
ws = self._connect_to_kernel_ws(kernel_id, timeout=timeout)
|
|
96
|
+
ws = self._connect_to_kernel_ws(kernel_id, None, timeout=timeout)
|
|
95
97
|
|
|
96
|
-
|
|
98
|
+
message_id = ws.send_execution_message(code, on_stdout, on_stderr, on_result)
|
|
97
99
|
logger.debug(
|
|
98
|
-
f"Sent execution message to kernel {kernel_id},
|
|
100
|
+
f"Sent execution message to kernel {kernel_id}, message_id: {message_id}"
|
|
99
101
|
)
|
|
100
102
|
|
|
101
|
-
result = ws.get_result(
|
|
103
|
+
result = ws.get_result(message_id, timeout=timeout)
|
|
102
104
|
logger.debug(
|
|
103
|
-
f"Received result from kernel {kernel_id},
|
|
105
|
+
f"Received result from kernel {kernel_id}, message_id: {message_id}, result: {result}"
|
|
104
106
|
)
|
|
105
107
|
|
|
106
108
|
return result
|
|
@@ -139,24 +141,35 @@ class JupyterExtension:
|
|
|
139
141
|
:param timeout: Timeout for the kernel creation request.
|
|
140
142
|
:return: Kernel id of the created kernel
|
|
141
143
|
"""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
kernel_name = kernel_name or "python3"
|
|
145
|
+
|
|
146
|
+
data = {"path": str(uuid.uuid4()), "kernel": {"name": kernel_name}, "type": "notebook", "name": str(uuid.uuid4())}
|
|
145
147
|
logger.debug(f"Creating kernel with data: {data}")
|
|
146
148
|
|
|
147
149
|
response = requests.post(
|
|
148
|
-
f"{self._sandbox.get_protocol()}://{self._sandbox.get_hostname(8888)}/api/
|
|
150
|
+
f"{self._sandbox.get_protocol()}://{self._sandbox.get_hostname(8888)}/api/sessions",
|
|
149
151
|
json=data,
|
|
150
152
|
timeout=timeout,
|
|
151
153
|
)
|
|
152
154
|
if not response.ok:
|
|
153
155
|
raise KernelException(f"Failed to create kernel: {response.text}")
|
|
154
156
|
|
|
155
|
-
|
|
157
|
+
session_data = response.json()
|
|
158
|
+
session_id = session_data["id"]
|
|
159
|
+
kernel_id = session_data["kernel"]["id"]
|
|
160
|
+
|
|
161
|
+
response = requests.patch(
|
|
162
|
+
f"{self._sandbox.get_protocol()}://{self._sandbox.get_hostname(8888)}/api/sessions/{session_id}",
|
|
163
|
+
json={"path": cwd},
|
|
164
|
+
timeout=timeout,
|
|
165
|
+
)
|
|
166
|
+
if not response.ok:
|
|
167
|
+
raise KernelException(f"Failed to create kernel: {response.text}")
|
|
168
|
+
|
|
156
169
|
logger.debug(f"Created kernel {kernel_id}")
|
|
157
170
|
|
|
158
171
|
threading.Thread(
|
|
159
|
-
target=self._connect_to_kernel_ws, args=(kernel_id, timeout)
|
|
172
|
+
target=self._connect_to_kernel_ws, args=(kernel_id, session_id, timeout)
|
|
160
173
|
).start()
|
|
161
174
|
return kernel_id
|
|
162
175
|
|
|
@@ -186,7 +199,7 @@ class JupyterExtension:
|
|
|
186
199
|
logger.debug(f"Restarted kernel {kernel_id}")
|
|
187
200
|
|
|
188
201
|
threading.Thread(
|
|
189
|
-
target=self._connect_to_kernel_ws, args=(kernel_id, timeout)
|
|
202
|
+
target=self._connect_to_kernel_ws, args=(kernel_id, None, timeout)
|
|
190
203
|
).start()
|
|
191
204
|
|
|
192
205
|
def shutdown_kernel(
|
|
@@ -243,7 +256,7 @@ class JupyterExtension:
|
|
|
243
256
|
ws.result().close()
|
|
244
257
|
|
|
245
258
|
def _connect_to_kernel_ws(
|
|
246
|
-
self, kernel_id: str, timeout: Optional[float] = TIMEOUT
|
|
259
|
+
self, kernel_id: str, session_id: Optional[str], timeout: Optional[float] = TIMEOUT
|
|
247
260
|
) -> JupyterKernelWebSocket:
|
|
248
261
|
"""
|
|
249
262
|
Establishes a WebSocket connection to a specified Jupyter kernel.
|
|
@@ -257,9 +270,12 @@ class JupyterExtension:
|
|
|
257
270
|
future = Future()
|
|
258
271
|
self._connected_kernels[kernel_id] = future
|
|
259
272
|
|
|
273
|
+
session_id = session_id or str(uuid.uuid4())
|
|
260
274
|
ws = JupyterKernelWebSocket(
|
|
261
275
|
url=f"{self._sandbox.get_protocol('ws')}://{self._sandbox.get_hostname(8888)}/api/kernels/{kernel_id}/channels",
|
|
276
|
+
session_id=session_id
|
|
262
277
|
)
|
|
278
|
+
|
|
263
279
|
ws.connect(timeout=timeout)
|
|
264
280
|
logger.debug(f"Connected to kernel's ({kernel_id}) websocket.")
|
|
265
281
|
|
|
@@ -279,12 +295,14 @@ class JupyterExtension:
|
|
|
279
295
|
kernel_id = self._sandbox.filesystem.read(
|
|
280
296
|
"/root/.jupyter/kernel_id", timeout=timeout
|
|
281
297
|
)
|
|
298
|
+
|
|
282
299
|
if kernel_id is None and not self._sandbox.is_open:
|
|
283
300
|
return
|
|
284
301
|
|
|
285
302
|
kernel_id = kernel_id.strip()
|
|
303
|
+
|
|
286
304
|
logger.debug(f"Default kernel id: {kernel_id}")
|
|
287
|
-
self._connect_to_kernel_ws(kernel_id, timeout=timeout)
|
|
305
|
+
self._connect_to_kernel_ws(kernel_id, None, timeout=timeout)
|
|
288
306
|
self._kernel_id_set.set_result(kernel_id)
|
|
289
307
|
|
|
290
308
|
threading.Thread(target=setup_default_kernel).start()
|
|
@@ -5,14 +5,13 @@ import time
|
|
|
5
5
|
import uuid
|
|
6
6
|
from concurrent.futures import Future
|
|
7
7
|
from queue import Queue
|
|
8
|
-
from typing import Callable, Dict,
|
|
8
|
+
from typing import Callable, Dict, Any, Optional
|
|
9
9
|
|
|
10
10
|
from e2b import ProcessMessage
|
|
11
11
|
from e2b.constants import TIMEOUT
|
|
12
12
|
from e2b.sandbox import TimeoutException
|
|
13
13
|
from e2b.sandbox.websocket_client import WebSocket
|
|
14
14
|
from e2b.utils.future import DeferredFuture
|
|
15
|
-
from pydantic import ConfigDict, PrivateAttr, BaseModel
|
|
16
15
|
|
|
17
16
|
from e2b_code_interpreter.models import Execution, Result, Error
|
|
18
17
|
|
|
@@ -26,6 +25,7 @@ class CellExecution:
|
|
|
26
25
|
"""
|
|
27
26
|
|
|
28
27
|
input_accepted: bool = False
|
|
28
|
+
|
|
29
29
|
on_stdout: Optional[Callable[[ProcessMessage], Any]] = None
|
|
30
30
|
on_stderr: Optional[Callable[[ProcessMessage], Any]] = None
|
|
31
31
|
on_result: Optional[Callable[[Result], Any]] = None
|
|
@@ -45,8 +45,9 @@ class CellExecution:
|
|
|
45
45
|
|
|
46
46
|
class JupyterKernelWebSocket:
|
|
47
47
|
|
|
48
|
-
def __init__(self, url: str):
|
|
48
|
+
def __init__(self, url: str, session_id: str):
|
|
49
49
|
self.url = url
|
|
50
|
+
self.session_id = session_id
|
|
50
51
|
self._cells: Dict[str, CellExecution] = {}
|
|
51
52
|
self._waiting_for_replies: Dict[str, DeferredFuture] = {}
|
|
52
53
|
self._queue_in = Queue()
|
|
@@ -102,14 +103,13 @@ class JupyterKernelWebSocket:
|
|
|
102
103
|
|
|
103
104
|
logger.debug("WebSocket started")
|
|
104
105
|
|
|
105
|
-
|
|
106
|
-
def _get_execute_request(msg_id: str, code: str) -> str:
|
|
106
|
+
def _get_execute_request(self, msg_id: str, code: str) -> str:
|
|
107
107
|
return json.dumps(
|
|
108
108
|
{
|
|
109
109
|
"header": {
|
|
110
110
|
"msg_id": msg_id,
|
|
111
111
|
"username": "e2b",
|
|
112
|
-
"session":
|
|
112
|
+
"session": self.session_id,
|
|
113
113
|
"msg_type": "execute_request",
|
|
114
114
|
"version": "5.3",
|
|
115
115
|
},
|
|
@@ -118,7 +118,7 @@ class JupyterKernelWebSocket:
|
|
|
118
118
|
"content": {
|
|
119
119
|
"code": code,
|
|
120
120
|
"silent": False,
|
|
121
|
-
"store_history":
|
|
121
|
+
"store_history": True,
|
|
122
122
|
"user_expressions": {},
|
|
123
123
|
"allow_stdin": False,
|
|
124
124
|
},
|
|
@@ -238,6 +238,7 @@ class JupyterKernelWebSocket:
|
|
|
238
238
|
|
|
239
239
|
elif data["msg_type"] == "execute_input":
|
|
240
240
|
logger.debug(f"Input accepted for {parent_msg_ig}")
|
|
241
|
+
cell.partial_result.execution_count = data["content"]["execution_count"]
|
|
241
242
|
cell.input_accepted = True
|
|
242
243
|
else:
|
|
243
244
|
logger.warning(f"[UNHANDLED MESSAGE TYPE]: {data['msg_type']}")
|
|
@@ -124,6 +124,9 @@ class Result:
|
|
|
124
124
|
"""
|
|
125
125
|
return self.text
|
|
126
126
|
|
|
127
|
+
def __repr__(self) -> str:
|
|
128
|
+
return f"Result({self.text})"
|
|
129
|
+
|
|
127
130
|
def _repr_html_(self) -> Optional[str]:
|
|
128
131
|
"""
|
|
129
132
|
Returns the HTML representation of the data.
|
|
@@ -222,6 +225,8 @@ class Execution(BaseModel):
|
|
|
222
225
|
"Logs printed to stdout and stderr during execution."
|
|
223
226
|
error: Optional[Error] = None
|
|
224
227
|
"Error object if an error occurred, None otherwise."
|
|
228
|
+
execution_count: Optional[int] = None
|
|
229
|
+
"Execution count of the cell."
|
|
225
230
|
|
|
226
231
|
@property
|
|
227
232
|
def text(self) -> Optional[str]:
|
|
@@ -249,7 +254,7 @@ class Execution(BaseModel):
|
|
|
249
254
|
serialized = []
|
|
250
255
|
for result in results:
|
|
251
256
|
serialized_dict = {key: result[key] for key in result.formats()}
|
|
252
|
-
serialized_dict[
|
|
257
|
+
serialized_dict["text"] = result.text
|
|
253
258
|
serialized.append(serialized_dict)
|
|
254
259
|
return serialized
|
|
255
260
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "e2b-code-interpreter"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.8"
|
|
4
4
|
description = "E2B Code Interpreter - Stateful code execution"
|
|
5
5
|
authors = ["e2b <hello@e2b.dev>"]
|
|
6
6
|
license = "Apache-2.0"
|
|
@@ -14,7 +14,7 @@ python = "^3.8"
|
|
|
14
14
|
|
|
15
15
|
pydantic = ">1, <3"
|
|
16
16
|
websocket-client = "^1.7.0"
|
|
17
|
-
e2b = ">=0.17.
|
|
17
|
+
e2b = ">=0.17.1"
|
|
18
18
|
|
|
19
19
|
[tool.poetry.group.dev.dependencies]
|
|
20
20
|
black = "^24.3.0"
|
|
File without changes
|
|
File without changes
|