e2b-code-interpreter 0.0.1a1__tar.gz → 0.0.2__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.1a1 → e2b_code_interpreter-0.0.2}/PKG-INFO +21 -16
- {e2b_code_interpreter-0.0.1a1 → e2b_code_interpreter-0.0.2}/README.md +19 -15
- e2b_code_interpreter-0.0.2/e2b_code_interpreter/__init__.py +2 -0
- {e2b_code_interpreter-0.0.1a1 → e2b_code_interpreter-0.0.2}/e2b_code_interpreter/main.py +5 -6
- {e2b_code_interpreter-0.0.1a1 → e2b_code_interpreter-0.0.2}/e2b_code_interpreter/messaging.py +35 -30
- {e2b_code_interpreter-0.0.1a1 → e2b_code_interpreter-0.0.2}/e2b_code_interpreter/models.py +38 -15
- {e2b_code_interpreter-0.0.1a1 → e2b_code_interpreter-0.0.2}/pyproject.toml +1 -1
- e2b_code_interpreter-0.0.1a1/e2b_code_interpreter/__init__.py +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: e2b-code-interpreter
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.2
|
|
4
4
|
Summary: E2B Code Interpreter - Stateful code execution
|
|
5
5
|
Home-page: https://e2b.dev/
|
|
6
6
|
License: Apache-2.0
|
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
17
|
Requires-Dist: e2b (>=0.14.11)
|
|
17
18
|
Requires-Dist: pydantic (>1,<3)
|
|
18
19
|
Requires-Dist: websocket-client (>=1.7.0,<2.0.0)
|
|
@@ -43,10 +44,10 @@ pip install e2b-code-interpreter
|
|
|
43
44
|
from e2b_code_interpreter import CodeInterpreter
|
|
44
45
|
|
|
45
46
|
with CodeInterpreter() as sandbox:
|
|
46
|
-
sandbox.exec_cell("x = 1")
|
|
47
|
+
sandbox.notebook.exec_cell("x = 1")
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
print(
|
|
49
|
+
execution = sandbox.notebook.exec_cell("x+=1; x")
|
|
50
|
+
print(execution.text) # outputs 2
|
|
50
51
|
|
|
51
52
|
```
|
|
52
53
|
|
|
@@ -73,21 +74,21 @@ plt.show()
|
|
|
73
74
|
|
|
74
75
|
with CodeInterpreter() as sandbox:
|
|
75
76
|
# you can install dependencies in "jupyter notebook style"
|
|
76
|
-
sandbox.exec_cell("!pip install matplotlib")
|
|
77
|
+
sandbox.notebook.exec_cell("!pip install matplotlib")
|
|
77
78
|
|
|
78
79
|
# plot random graph
|
|
79
|
-
|
|
80
|
+
execution = sandbox.notebook.exec_cell(code)
|
|
80
81
|
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
# there's your image
|
|
83
|
+
image = execution.results[0].png
|
|
83
84
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
# example how to show the image / prove it works
|
|
86
|
+
i = base64.b64decode(image)
|
|
87
|
+
i = io.BytesIO(i)
|
|
88
|
+
i = mpimg.imread(i, format='PNG')
|
|
88
89
|
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
plt.imshow(i, interpolation='nearest')
|
|
91
|
+
plt.show()
|
|
91
92
|
```
|
|
92
93
|
|
|
93
94
|
### Streaming code output
|
|
@@ -97,14 +98,18 @@ from e2b_code_interpreter import CodeInterpreter
|
|
|
97
98
|
|
|
98
99
|
code = """
|
|
99
100
|
import time
|
|
101
|
+
import pandas as pd
|
|
100
102
|
|
|
101
103
|
print("hello")
|
|
102
|
-
time.sleep(
|
|
104
|
+
time.sleep(3)
|
|
105
|
+
data = pd.DataFrame(data=[[1, 2], [3, 4]], columns=["A", "B"])
|
|
106
|
+
display(data.head(10))
|
|
107
|
+
time.sleep(3)
|
|
103
108
|
print("world")
|
|
104
109
|
"""
|
|
105
110
|
|
|
106
111
|
with CodeInterpreter() as sandbox:
|
|
107
|
-
sandbox.exec_cell(code, on_stdout=print, on_stderr=print)
|
|
112
|
+
sandbox.notebook.exec_cell(code, on_stdout=print, on_stderr=print, on_result=(lambda result: print(result.text)))
|
|
108
113
|
```
|
|
109
114
|
|
|
110
115
|
### Pre-installed Python packages inside the sandbox
|
|
@@ -21,10 +21,10 @@ pip install e2b-code-interpreter
|
|
|
21
21
|
from e2b_code_interpreter import CodeInterpreter
|
|
22
22
|
|
|
23
23
|
with CodeInterpreter() as sandbox:
|
|
24
|
-
sandbox.exec_cell("x = 1")
|
|
24
|
+
sandbox.notebook.exec_cell("x = 1")
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
print(
|
|
26
|
+
execution = sandbox.notebook.exec_cell("x+=1; x")
|
|
27
|
+
print(execution.text) # outputs 2
|
|
28
28
|
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -51,21 +51,21 @@ plt.show()
|
|
|
51
51
|
|
|
52
52
|
with CodeInterpreter() as sandbox:
|
|
53
53
|
# you can install dependencies in "jupyter notebook style"
|
|
54
|
-
sandbox.exec_cell("!pip install matplotlib")
|
|
54
|
+
sandbox.notebook.exec_cell("!pip install matplotlib")
|
|
55
55
|
|
|
56
56
|
# plot random graph
|
|
57
|
-
|
|
57
|
+
execution = sandbox.notebook.exec_cell(code)
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
# there's your image
|
|
60
|
+
image = execution.results[0].png
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
# example how to show the image / prove it works
|
|
63
|
+
i = base64.b64decode(image)
|
|
64
|
+
i = io.BytesIO(i)
|
|
65
|
+
i = mpimg.imread(i, format='PNG')
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
plt.imshow(i, interpolation='nearest')
|
|
68
|
+
plt.show()
|
|
69
69
|
```
|
|
70
70
|
|
|
71
71
|
### Streaming code output
|
|
@@ -75,14 +75,18 @@ from e2b_code_interpreter import CodeInterpreter
|
|
|
75
75
|
|
|
76
76
|
code = """
|
|
77
77
|
import time
|
|
78
|
+
import pandas as pd
|
|
78
79
|
|
|
79
80
|
print("hello")
|
|
80
|
-
time.sleep(
|
|
81
|
+
time.sleep(3)
|
|
82
|
+
data = pd.DataFrame(data=[[1, 2], [3, 4]], columns=["A", "B"])
|
|
83
|
+
display(data.head(10))
|
|
84
|
+
time.sleep(3)
|
|
81
85
|
print("world")
|
|
82
86
|
"""
|
|
83
87
|
|
|
84
88
|
with CodeInterpreter() as sandbox:
|
|
85
|
-
sandbox.exec_cell(code, on_stdout=print, on_stderr=print)
|
|
89
|
+
sandbox.notebook.exec_cell(code, on_stdout=print, on_stderr=print, on_result=(lambda result: print(result.text)))
|
|
86
90
|
```
|
|
87
91
|
|
|
88
92
|
### Pre-installed Python packages inside the sandbox
|
|
@@ -10,8 +10,7 @@ from e2b import EnvVars, ProcessMessage, Sandbox
|
|
|
10
10
|
from e2b.constants import TIMEOUT
|
|
11
11
|
|
|
12
12
|
from e2b_code_interpreter.messaging import JupyterKernelWebSocket
|
|
13
|
-
from e2b_code_interpreter.models import KernelException, Result
|
|
14
|
-
|
|
13
|
+
from e2b_code_interpreter.models import KernelException, Execution, Result
|
|
15
14
|
|
|
16
15
|
logger = logging.getLogger(__name__)
|
|
17
16
|
|
|
@@ -66,9 +65,9 @@ class JupyterExtension:
|
|
|
66
65
|
kernel_id: Optional[str] = None,
|
|
67
66
|
on_stdout: Optional[Callable[[ProcessMessage], Any]] = None,
|
|
68
67
|
on_stderr: Optional[Callable[[ProcessMessage], Any]] = None,
|
|
69
|
-
|
|
68
|
+
on_result: Optional[Callable[[Result], Any]] = None,
|
|
70
69
|
timeout: Optional[float] = TIMEOUT,
|
|
71
|
-
) ->
|
|
70
|
+
) -> Execution:
|
|
72
71
|
"""
|
|
73
72
|
Execute code in a notebook cell.
|
|
74
73
|
|
|
@@ -76,7 +75,7 @@ class JupyterExtension:
|
|
|
76
75
|
:param kernel_id: The ID of the kernel to execute the code on. If not provided, the default kernel is used.
|
|
77
76
|
:param on_stdout: A callback function to handle standard output messages from the code execution.
|
|
78
77
|
:param on_stderr: A callback function to handle standard error messages from the code execution.
|
|
79
|
-
:param
|
|
78
|
+
:param on_result: A callback function to handle the result and display calls of the code execution.
|
|
80
79
|
:param timeout: Timeout for the call
|
|
81
80
|
|
|
82
81
|
:return: Result of the execution
|
|
@@ -93,7 +92,7 @@ class JupyterExtension:
|
|
|
93
92
|
logger.debug(f"Creating new websocket connection to kernel {kernel_id}")
|
|
94
93
|
ws = self._connect_to_kernel_ws(kernel_id, timeout=timeout)
|
|
95
94
|
|
|
96
|
-
session_id = ws.send_execution_message(code, on_stdout, on_stderr,
|
|
95
|
+
session_id = ws.send_execution_message(code, on_stdout, on_stderr, on_result)
|
|
97
96
|
logger.debug(
|
|
98
97
|
f"Sent execution message to kernel {kernel_id}, session_id: {session_id}"
|
|
99
98
|
)
|
{e2b_code_interpreter-0.0.1a1 → e2b_code_interpreter-0.0.2}/e2b_code_interpreter/messaging.py
RENAMED
|
@@ -14,7 +14,7 @@ from e2b.sandbox.websocket_client import WebSocket
|
|
|
14
14
|
from e2b.utils.future import DeferredFuture
|
|
15
15
|
from pydantic import ConfigDict, PrivateAttr, BaseModel
|
|
16
16
|
|
|
17
|
-
from e2b_code_interpreter.models import
|
|
17
|
+
from e2b_code_interpreter.models import Execution, Result, Error
|
|
18
18
|
|
|
19
19
|
logger = logging.getLogger(__name__)
|
|
20
20
|
|
|
@@ -26,21 +26,21 @@ class CellExecution:
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
input_accepted: bool = False
|
|
29
|
-
on_stdout: Optional[Callable[[ProcessMessage],
|
|
30
|
-
on_stderr: Optional[Callable[[ProcessMessage],
|
|
31
|
-
|
|
29
|
+
on_stdout: Optional[Callable[[ProcessMessage], Any]] = None
|
|
30
|
+
on_stderr: Optional[Callable[[ProcessMessage], Any]] = None
|
|
31
|
+
on_result: Optional[Callable[[Result], Any]] = None
|
|
32
32
|
|
|
33
33
|
def __init__(
|
|
34
34
|
self,
|
|
35
|
-
on_stdout: Optional[Callable[[ProcessMessage],
|
|
36
|
-
on_stderr: Optional[Callable[[ProcessMessage],
|
|
37
|
-
|
|
35
|
+
on_stdout: Optional[Callable[[ProcessMessage], Any]] = None,
|
|
36
|
+
on_stderr: Optional[Callable[[ProcessMessage], Any]] = None,
|
|
37
|
+
on_result: Optional[Callable[[Result], Any]] = None,
|
|
38
38
|
):
|
|
39
|
-
self.partial_result =
|
|
40
|
-
self.
|
|
39
|
+
self.partial_result = Execution()
|
|
40
|
+
self.execution = Future()
|
|
41
41
|
self.on_stdout = on_stdout
|
|
42
42
|
self.on_stderr = on_stderr
|
|
43
|
-
self.
|
|
43
|
+
self.on_result = on_result
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
class JupyterKernelWebSocket(BaseModel):
|
|
@@ -129,9 +129,9 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
129
129
|
def send_execution_message(
|
|
130
130
|
self,
|
|
131
131
|
code: str,
|
|
132
|
-
on_stdout: Optional[Callable[[ProcessMessage],
|
|
133
|
-
on_stderr: Optional[Callable[[ProcessMessage],
|
|
134
|
-
|
|
132
|
+
on_stdout: Optional[Callable[[ProcessMessage], Any]] = None,
|
|
133
|
+
on_stderr: Optional[Callable[[ProcessMessage], Any]] = None,
|
|
134
|
+
on_result: Optional[Callable[[Result], Any]] = None,
|
|
135
135
|
) -> str:
|
|
136
136
|
message_id = str(uuid.uuid4())
|
|
137
137
|
logger.debug(f"Sending execution message: {message_id}")
|
|
@@ -139,14 +139,16 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
139
139
|
self._cells[message_id] = CellExecution(
|
|
140
140
|
on_stdout=on_stdout,
|
|
141
141
|
on_stderr=on_stderr,
|
|
142
|
-
|
|
142
|
+
on_result=on_result,
|
|
143
143
|
)
|
|
144
144
|
request = self._get_execute_request(message_id, code)
|
|
145
145
|
self._queue_in.put(request)
|
|
146
146
|
return message_id
|
|
147
147
|
|
|
148
|
-
def get_result(
|
|
149
|
-
|
|
148
|
+
def get_result(
|
|
149
|
+
self, message_id: str, timeout: Optional[float] = TIMEOUT
|
|
150
|
+
) -> Execution:
|
|
151
|
+
result = self._cells[message_id].execution.result(timeout=timeout)
|
|
150
152
|
logger.debug(f"Got result for message: {message_id}")
|
|
151
153
|
del self._cells[message_id]
|
|
152
154
|
return result
|
|
@@ -167,11 +169,11 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
167
169
|
if not cell:
|
|
168
170
|
return
|
|
169
171
|
|
|
170
|
-
|
|
172
|
+
execution = cell.partial_result
|
|
171
173
|
|
|
172
174
|
if data["msg_type"] == "error":
|
|
173
175
|
logger.debug(f"Cell {parent_msg_ig} finished execution with error")
|
|
174
|
-
|
|
176
|
+
execution.error = Error(
|
|
175
177
|
name=data["content"]["ename"],
|
|
176
178
|
value=data["content"]["evalue"],
|
|
177
179
|
traceback_raw=data["content"]["traceback"],
|
|
@@ -179,7 +181,7 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
179
181
|
|
|
180
182
|
elif data["msg_type"] == "stream":
|
|
181
183
|
if data["content"]["name"] == "stdout":
|
|
182
|
-
|
|
184
|
+
execution.logs.stdout.append(data["content"]["text"])
|
|
183
185
|
if cell.on_stdout:
|
|
184
186
|
cell.on_stdout(
|
|
185
187
|
ProcessMessage(
|
|
@@ -189,7 +191,7 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
189
191
|
)
|
|
190
192
|
|
|
191
193
|
elif data["content"]["name"] == "stderr":
|
|
192
|
-
|
|
194
|
+
execution.logs.stderr.append(data["content"]["text"])
|
|
193
195
|
if cell.on_stderr:
|
|
194
196
|
cell.on_stderr(
|
|
195
197
|
ProcessMessage(
|
|
@@ -200,30 +202,34 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
200
202
|
)
|
|
201
203
|
|
|
202
204
|
elif data["msg_type"] in "display_data":
|
|
203
|
-
result
|
|
204
|
-
|
|
205
|
-
|
|
205
|
+
result = Result(is_main_result=False, data=data["content"]["data"])
|
|
206
|
+
execution.results.append(result)
|
|
207
|
+
if cell.on_result:
|
|
208
|
+
cell.on_result(result)
|
|
206
209
|
elif data["msg_type"] == "execute_result":
|
|
207
|
-
result
|
|
210
|
+
result = Result(is_main_result=True, data=data["content"]["data"])
|
|
211
|
+
execution.results.append(result)
|
|
212
|
+
if cell.on_result:
|
|
213
|
+
cell.on_result(result)
|
|
208
214
|
elif data["msg_type"] == "status":
|
|
209
215
|
if data["content"]["execution_state"] == "idle":
|
|
210
216
|
if cell.input_accepted:
|
|
211
217
|
logger.debug(f"Cell {parent_msg_ig} finished execution")
|
|
212
|
-
cell.
|
|
218
|
+
cell.execution.set_result(execution)
|
|
213
219
|
|
|
214
220
|
elif data["content"]["execution_state"] == "error":
|
|
215
221
|
logger.debug(f"Cell {parent_msg_ig} finished execution with error")
|
|
216
|
-
|
|
222
|
+
execution.error = Error(
|
|
217
223
|
name=data["content"]["ename"],
|
|
218
224
|
value=data["content"]["evalue"],
|
|
219
225
|
traceback_raw=data["content"]["traceback"],
|
|
220
226
|
)
|
|
221
|
-
cell.
|
|
227
|
+
cell.execution.set_result(execution)
|
|
222
228
|
|
|
223
229
|
elif data["msg_type"] == "execute_reply":
|
|
224
230
|
if data["content"]["status"] == "error":
|
|
225
231
|
logger.debug(f"Cell {parent_msg_ig} finished execution with error")
|
|
226
|
-
|
|
232
|
+
execution.error = Error(
|
|
227
233
|
name=data["content"]["ename"],
|
|
228
234
|
value=data["content"]["evalue"],
|
|
229
235
|
traceback_raw=data["content"]["traceback"],
|
|
@@ -235,8 +241,7 @@ class JupyterKernelWebSocket(BaseModel):
|
|
|
235
241
|
logger.debug(f"Input accepted for {parent_msg_ig}")
|
|
236
242
|
cell.input_accepted = True
|
|
237
243
|
else:
|
|
238
|
-
logger.
|
|
239
|
-
print("[UNHANDLED MESSAGE TYPE]:", data["msg_type"])
|
|
244
|
+
logger.warning(f"[UNHANDLED MESSAGE TYPE]: {data['msg_type']}")
|
|
240
245
|
|
|
241
246
|
def close(self):
|
|
242
247
|
logger.debug("Closing WebSocket")
|
|
@@ -32,11 +32,10 @@ class MIMEType(str):
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
|
|
35
|
-
class
|
|
35
|
+
class Result:
|
|
36
36
|
"""
|
|
37
37
|
Represents the data to be displayed as a result of executing a cell in a Jupyter notebook.
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
The result is similar to the structure returned by ipython kernel: https://ipython.readthedocs.io/en/stable/development/execution.html#execution-semantics
|
|
40
39
|
|
|
41
40
|
The result can contain multiple types of data, such as text, images, plots, etc. Each type of data is represented
|
|
42
41
|
as a string, and the result can contain multiple types of data. The text representation is always present, and
|
|
@@ -44,8 +43,9 @@ class Data:
|
|
|
44
43
|
|
|
45
44
|
The class also provides methods to display the data in a Jupyter notebook.
|
|
46
45
|
"""
|
|
46
|
+
|
|
47
47
|
text: str
|
|
48
|
-
"Text representation of the
|
|
48
|
+
"Text representation of the result. Always present."
|
|
49
49
|
html: Optional[str] = None
|
|
50
50
|
markdown: Optional[str] = None
|
|
51
51
|
svg: Optional[str] = None
|
|
@@ -80,13 +80,36 @@ class Data:
|
|
|
80
80
|
self.javascript = data.pop("application/javascript", None)
|
|
81
81
|
self.extra = data
|
|
82
82
|
|
|
83
|
-
def
|
|
84
|
-
"""
|
|
85
|
-
Returns
|
|
86
|
-
|
|
87
|
-
:return:
|
|
88
|
-
"""
|
|
89
|
-
|
|
83
|
+
def formats(self) -> Iterable[str]:
|
|
84
|
+
"""
|
|
85
|
+
Returns all available formats of the result.
|
|
86
|
+
|
|
87
|
+
:return: All available formats of the result in MIME types.
|
|
88
|
+
"""
|
|
89
|
+
formats = []
|
|
90
|
+
if self.html:
|
|
91
|
+
formats.append("html")
|
|
92
|
+
if self.markdown:
|
|
93
|
+
formats.append("markdown")
|
|
94
|
+
if self.svg:
|
|
95
|
+
formats.append("svg")
|
|
96
|
+
if self.png:
|
|
97
|
+
formats.append("png")
|
|
98
|
+
if self.jpeg:
|
|
99
|
+
formats.append("jpeg")
|
|
100
|
+
if self.pdf:
|
|
101
|
+
formats.append("pdf")
|
|
102
|
+
if self.latex:
|
|
103
|
+
formats.append("latex")
|
|
104
|
+
if self.json:
|
|
105
|
+
formats.append("json")
|
|
106
|
+
if self.javascript:
|
|
107
|
+
formats.append("javascript")
|
|
108
|
+
|
|
109
|
+
for key in self.extra:
|
|
110
|
+
formats.append(key)
|
|
111
|
+
|
|
112
|
+
return formats
|
|
90
113
|
|
|
91
114
|
def __str__(self) -> str:
|
|
92
115
|
"""
|
|
@@ -180,7 +203,7 @@ class Logs(BaseModel):
|
|
|
180
203
|
"List of strings printed to stderr by prints, subprocesses, etc."
|
|
181
204
|
|
|
182
205
|
|
|
183
|
-
class
|
|
206
|
+
class Execution(BaseModel):
|
|
184
207
|
"""
|
|
185
208
|
Represents the result of a cell execution.
|
|
186
209
|
"""
|
|
@@ -188,8 +211,8 @@ class Result(BaseModel):
|
|
|
188
211
|
class Config:
|
|
189
212
|
arbitrary_types_allowed = True
|
|
190
213
|
|
|
191
|
-
|
|
192
|
-
"List of result of the cell (interactively interpreted last line), display calls
|
|
214
|
+
results: List[Result] = []
|
|
215
|
+
"List of the result of the cell (interactively interpreted last line), display calls (e.g. matplotlib plots)."
|
|
193
216
|
logs: Logs = Logs()
|
|
194
217
|
"Logs printed to stdout and stderr during execution."
|
|
195
218
|
error: Optional[Error] = None
|
|
@@ -202,7 +225,7 @@ class Result(BaseModel):
|
|
|
202
225
|
|
|
203
226
|
:return: The text representation of the result.
|
|
204
227
|
"""
|
|
205
|
-
for d in self.
|
|
228
|
+
for d in self.results:
|
|
206
229
|
if d.is_main_result:
|
|
207
230
|
return d.text
|
|
208
231
|
|