codebind 0.3.0__py3-none-any.whl → 0.4.1__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.
- codebind/__init__.py +2 -0
- codebind/execution.py +283 -8
- codebind/extension.py +9 -1
- codebind/jupyter.py +109 -0
- codebind/session.py +10 -3
- codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/install.json +5 -0
- codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/package.json +37 -0
- codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/590.6544266cd73a003b.js +1 -0
- codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/remoteEntry.6f017385dfc989f1.js +1 -0
- codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/style.js +2 -0
- codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/third-party-licenses.json +3 -0
- {codebind-0.3.0.dist-info → codebind-0.4.1.dist-info}/METADATA +15 -4
- codebind-0.4.1.dist-info/RECORD +19 -0
- codebind-0.3.0.dist-info/RECORD +0 -12
- {codebind-0.3.0.dist-info → codebind-0.4.1.dist-info}/WHEEL +0 -0
- {codebind-0.3.0.dist-info → codebind-0.4.1.dist-info}/entry_points.txt +0 -0
- {codebind-0.3.0.dist-info → codebind-0.4.1.dist-info}/licenses/LICENSE +0 -0
codebind/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@ from importlib.metadata import version
|
|
|
4
4
|
|
|
5
5
|
from .execution import ExecutionReport, IPythonExecutor
|
|
6
6
|
from .extension import load_ipython_extension, unload_ipython_extension
|
|
7
|
+
from .jupyter import JupyterLabBridge
|
|
7
8
|
from .session import Session
|
|
8
9
|
|
|
9
10
|
|
|
@@ -12,6 +13,7 @@ __version__ = version("codebind")
|
|
|
12
13
|
__all__ = [
|
|
13
14
|
"ExecutionReport",
|
|
14
15
|
"IPythonExecutor",
|
|
16
|
+
"JupyterLabBridge",
|
|
15
17
|
"Session",
|
|
16
18
|
"__version__",
|
|
17
19
|
"load_ipython_extension",
|
codebind/execution.py
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import sys
|
|
6
|
+
from collections.abc import Callable, Iterator
|
|
7
|
+
from contextlib import contextmanager
|
|
5
8
|
from dataclasses import asdict, dataclass
|
|
6
|
-
from typing import Any
|
|
9
|
+
from typing import Any, cast
|
|
7
10
|
|
|
11
|
+
from IPython.core.displaypub import DisplayPublisher
|
|
8
12
|
from IPython.core.interactiveshell import InteractiveShell
|
|
9
13
|
from IPython.utils.capture import capture_output
|
|
10
14
|
|
|
11
15
|
from .display import display_cell
|
|
16
|
+
from .jupyter import JupyterLabBridge
|
|
12
17
|
|
|
13
18
|
|
|
14
19
|
@dataclass(frozen=True, slots=True)
|
|
@@ -27,21 +32,194 @@ class ExecutionReport:
|
|
|
27
32
|
return asdict(self)
|
|
28
33
|
|
|
29
34
|
|
|
35
|
+
class _ExecutionDisplayHook:
|
|
36
|
+
"""Capture an expression result while preserving IPython output history."""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
shell: InteractiveShell,
|
|
41
|
+
outputs: list[dict[str, Any]],
|
|
42
|
+
on_output: Callable[[dict[str, Any]], None] | None = None,
|
|
43
|
+
) -> None:
|
|
44
|
+
self.shell = shell
|
|
45
|
+
self.outputs = outputs
|
|
46
|
+
self.on_output = on_output
|
|
47
|
+
|
|
48
|
+
def __call__(self, value: Any = None) -> None:
|
|
49
|
+
if value is None:
|
|
50
|
+
return
|
|
51
|
+
displayhook = self.shell.displayhook
|
|
52
|
+
displayhook.check_for_underscore()
|
|
53
|
+
if displayhook.quiet():
|
|
54
|
+
return
|
|
55
|
+
data, metadata = displayhook.compute_format_data(value)
|
|
56
|
+
displayhook.update_user_ns(value)
|
|
57
|
+
displayhook.fill_exec_result(value)
|
|
58
|
+
if data:
|
|
59
|
+
displayhook.log_output(data)
|
|
60
|
+
self.outputs.append({"data": data, "metadata": metadata})
|
|
61
|
+
if self.on_output is not None:
|
|
62
|
+
self.on_output(
|
|
63
|
+
{
|
|
64
|
+
"output_type": "execute_result",
|
|
65
|
+
"execution_count": displayhook.prompt_count,
|
|
66
|
+
"data": data,
|
|
67
|
+
"metadata": metadata,
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class _StreamingTextIO:
|
|
73
|
+
"""Write to IPython's capture buffer while forwarding live stream output."""
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
stream: Any,
|
|
78
|
+
name: str,
|
|
79
|
+
on_output: Callable[[dict[str, Any]], None],
|
|
80
|
+
) -> None:
|
|
81
|
+
self.stream = stream
|
|
82
|
+
self.name = name
|
|
83
|
+
self.on_output = on_output
|
|
84
|
+
|
|
85
|
+
def write(self, text: str) -> int:
|
|
86
|
+
written = self.stream.write(text)
|
|
87
|
+
if text:
|
|
88
|
+
self.on_output({"output_type": "stream", "name": self.name, "text": text})
|
|
89
|
+
return written
|
|
90
|
+
|
|
91
|
+
def flush(self) -> None:
|
|
92
|
+
self.stream.flush()
|
|
93
|
+
|
|
94
|
+
def __getattr__(self, name: str) -> Any:
|
|
95
|
+
return getattr(self.stream, name)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class _StreamingDisplayPublisher:
|
|
99
|
+
"""Capture rich displays while forwarding them to the notebook cell."""
|
|
100
|
+
|
|
101
|
+
def __init__(
|
|
102
|
+
self,
|
|
103
|
+
publisher: Any,
|
|
104
|
+
on_output: Callable[[dict[str, Any]], None],
|
|
105
|
+
on_clear: Callable[[bool], None],
|
|
106
|
+
) -> None:
|
|
107
|
+
self.publisher = publisher
|
|
108
|
+
self.on_output = on_output
|
|
109
|
+
self.on_clear = on_clear
|
|
110
|
+
|
|
111
|
+
def publish(
|
|
112
|
+
self,
|
|
113
|
+
data: dict[str, Any],
|
|
114
|
+
metadata: dict[str, Any] | None = None,
|
|
115
|
+
source: str | None = None,
|
|
116
|
+
*,
|
|
117
|
+
transient: dict[str, Any] | None = None,
|
|
118
|
+
update: bool = False,
|
|
119
|
+
) -> None:
|
|
120
|
+
self.publisher.publish(
|
|
121
|
+
data,
|
|
122
|
+
metadata=metadata,
|
|
123
|
+
source=source,
|
|
124
|
+
transient=transient,
|
|
125
|
+
update=update,
|
|
126
|
+
)
|
|
127
|
+
self.on_output(
|
|
128
|
+
{
|
|
129
|
+
"output_type": "display_data",
|
|
130
|
+
"data": data,
|
|
131
|
+
"metadata": metadata or {},
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def clear_output(self, wait: bool = False) -> None:
|
|
136
|
+
self.publisher.clear_output(wait)
|
|
137
|
+
self.on_clear(wait)
|
|
138
|
+
|
|
139
|
+
def __getattr__(self, name: str) -> Any:
|
|
140
|
+
return getattr(self.publisher, name)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@contextmanager
|
|
144
|
+
def _captured_execution(
|
|
145
|
+
shell: InteractiveShell,
|
|
146
|
+
expression_outputs: list[dict[str, Any]],
|
|
147
|
+
*,
|
|
148
|
+
bridged: bool,
|
|
149
|
+
on_output: Callable[[dict[str, Any]], None] | None = None,
|
|
150
|
+
on_clear: Callable[[bool], None] | None = None,
|
|
151
|
+
) -> Iterator[Any]:
|
|
152
|
+
"""Capture one nested execution without leaking its output to the parent cell."""
|
|
153
|
+
with capture_output() as captured:
|
|
154
|
+
if not bridged:
|
|
155
|
+
yield captured
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
previous_displayhook = sys.displayhook
|
|
159
|
+
previous_showtraceback = shell.showtraceback
|
|
160
|
+
previous_showsyntaxerror = shell.showsyntaxerror
|
|
161
|
+
if on_output is not None:
|
|
162
|
+
sys.stdout = _StreamingTextIO(sys.stdout, "stdout", on_output)
|
|
163
|
+
sys.stderr = _StreamingTextIO(sys.stderr, "stderr", on_output)
|
|
164
|
+
shell.display_pub = cast(
|
|
165
|
+
DisplayPublisher,
|
|
166
|
+
_StreamingDisplayPublisher(
|
|
167
|
+
shell.display_pub,
|
|
168
|
+
on_output,
|
|
169
|
+
on_clear or (lambda wait: None),
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
sys.displayhook = _ExecutionDisplayHook(shell, expression_outputs, on_output)
|
|
173
|
+
shell.showtraceback = lambda *args, **kwargs: None
|
|
174
|
+
shell.showsyntaxerror = lambda *args, **kwargs: None
|
|
175
|
+
try:
|
|
176
|
+
yield captured
|
|
177
|
+
finally:
|
|
178
|
+
sys.displayhook = previous_displayhook
|
|
179
|
+
shell.showtraceback = previous_showtraceback
|
|
180
|
+
shell.showsyntaxerror = previous_showsyntaxerror
|
|
181
|
+
|
|
182
|
+
|
|
30
183
|
class IPythonExecutor:
|
|
31
184
|
"""Run cells through one existing IPython shell."""
|
|
32
185
|
|
|
33
|
-
def __init__(
|
|
186
|
+
def __init__(
|
|
187
|
+
self,
|
|
188
|
+
shell: InteractiveShell,
|
|
189
|
+
bridge: JupyterLabBridge | None = None,
|
|
190
|
+
) -> None:
|
|
34
191
|
self.shell = shell
|
|
192
|
+
self.bridge = bridge
|
|
35
193
|
|
|
36
194
|
def execute(self, cell: str) -> ExecutionReport:
|
|
37
195
|
"""Execute a cell through IPython and replay its native rich output."""
|
|
38
196
|
if not isinstance(cell, str) or not cell.strip():
|
|
39
197
|
raise ValueError("cell must be a non-empty string")
|
|
40
198
|
|
|
41
|
-
|
|
42
|
-
|
|
199
|
+
bridge = self.bridge
|
|
200
|
+
cell_id = bridge.start_code_cell(cell) if bridge is not None else None
|
|
201
|
+
bridged = cell_id is not None
|
|
202
|
+
on_output, on_clear = self._stream_callbacks(cell_id)
|
|
203
|
+
if not bridged:
|
|
204
|
+
display_cell(cell)
|
|
205
|
+
expression_outputs: list[dict[str, Any]] = []
|
|
206
|
+
with _captured_execution(
|
|
207
|
+
self.shell,
|
|
208
|
+
expression_outputs,
|
|
209
|
+
bridged=bridged,
|
|
210
|
+
on_output=on_output,
|
|
211
|
+
on_clear=on_clear,
|
|
212
|
+
) as captured:
|
|
43
213
|
result = self.shell.run_cell(cell, store_history=True)
|
|
44
|
-
|
|
214
|
+
if bridged:
|
|
215
|
+
assert bridge is not None and cell_id is not None
|
|
216
|
+
bridge.finish_code_cell(
|
|
217
|
+
cell_id,
|
|
218
|
+
result.execution_count,
|
|
219
|
+
self._notebook_outputs(result, captured, expression_outputs),
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
captured.show()
|
|
45
223
|
|
|
46
224
|
return self._report(result, captured)
|
|
47
225
|
|
|
@@ -50,18 +228,57 @@ class IPythonExecutor:
|
|
|
50
228
|
if not isinstance(cell, str) or not cell.strip():
|
|
51
229
|
raise ValueError("cell must be a non-empty string")
|
|
52
230
|
|
|
53
|
-
|
|
231
|
+
bridge = self.bridge
|
|
232
|
+
cell_id = bridge.start_code_cell(cell) if bridge is not None else None
|
|
233
|
+
bridged = cell_id is not None
|
|
234
|
+
on_output, on_clear = self._stream_callbacks(cell_id)
|
|
235
|
+
if not bridged:
|
|
236
|
+
display_cell(cell)
|
|
54
237
|
transformed = self.shell.transform_cell(cell)
|
|
55
|
-
|
|
238
|
+
expression_outputs: list[dict[str, Any]] = []
|
|
239
|
+
with _captured_execution(
|
|
240
|
+
self.shell,
|
|
241
|
+
expression_outputs,
|
|
242
|
+
bridged=bridged,
|
|
243
|
+
on_output=on_output,
|
|
244
|
+
on_clear=on_clear,
|
|
245
|
+
) as captured:
|
|
56
246
|
result = await self.shell.run_cell_async(
|
|
57
247
|
cell,
|
|
58
248
|
store_history=True,
|
|
59
249
|
transformed_cell=transformed,
|
|
60
250
|
)
|
|
61
|
-
|
|
251
|
+
if bridged:
|
|
252
|
+
assert bridge is not None and cell_id is not None
|
|
253
|
+
bridge.finish_code_cell(
|
|
254
|
+
cell_id,
|
|
255
|
+
result.execution_count,
|
|
256
|
+
self._notebook_outputs(result, captured, expression_outputs),
|
|
257
|
+
)
|
|
258
|
+
else:
|
|
259
|
+
captured.show()
|
|
62
260
|
|
|
63
261
|
return self._report(result, captured)
|
|
64
262
|
|
|
263
|
+
def _stream_callbacks(
|
|
264
|
+
self,
|
|
265
|
+
cell_id: str | None,
|
|
266
|
+
) -> tuple[
|
|
267
|
+
Callable[[dict[str, Any]], None] | None,
|
|
268
|
+
Callable[[bool], None] | None,
|
|
269
|
+
]:
|
|
270
|
+
bridge = self.bridge
|
|
271
|
+
if bridge is None or cell_id is None:
|
|
272
|
+
return None, None
|
|
273
|
+
|
|
274
|
+
def on_output(output: dict[str, Any]) -> None:
|
|
275
|
+
bridge.append_code_output(cell_id, output)
|
|
276
|
+
|
|
277
|
+
def on_clear(wait: bool) -> None:
|
|
278
|
+
bridge.clear_code_output(cell_id, wait=wait)
|
|
279
|
+
|
|
280
|
+
return on_output, on_clear
|
|
281
|
+
|
|
65
282
|
@staticmethod
|
|
66
283
|
def _report(result: Any, captured: Any) -> ExecutionReport:
|
|
67
284
|
"""Build the model-facing text projection of an IPython execution."""
|
|
@@ -86,3 +303,61 @@ class IPythonExecutor:
|
|
|
86
303
|
displays=tuple(displays),
|
|
87
304
|
error=error,
|
|
88
305
|
)
|
|
306
|
+
|
|
307
|
+
def _notebook_outputs(
|
|
308
|
+
self,
|
|
309
|
+
result: Any,
|
|
310
|
+
captured: Any,
|
|
311
|
+
expression_outputs: list[dict[str, Any]],
|
|
312
|
+
) -> list[dict[str, Any]]:
|
|
313
|
+
"""Build standard nbformat outputs for a JupyterLab code cell."""
|
|
314
|
+
outputs: list[dict[str, Any]] = []
|
|
315
|
+
if captured.stdout:
|
|
316
|
+
outputs.append({"output_type": "stream", "name": "stdout", "text": captured.stdout})
|
|
317
|
+
if captured.stderr:
|
|
318
|
+
outputs.append({"output_type": "stream", "name": "stderr", "text": captured.stderr})
|
|
319
|
+
|
|
320
|
+
for output in captured.outputs:
|
|
321
|
+
data = getattr(output, "data", None)
|
|
322
|
+
if not isinstance(data, dict):
|
|
323
|
+
continue
|
|
324
|
+
outputs.append(
|
|
325
|
+
{
|
|
326
|
+
"output_type": "display_data",
|
|
327
|
+
"data": data,
|
|
328
|
+
"metadata": getattr(output, "metadata", {}) or {},
|
|
329
|
+
}
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
for expression in expression_outputs:
|
|
333
|
+
outputs.append(
|
|
334
|
+
{
|
|
335
|
+
"output_type": "execute_result",
|
|
336
|
+
"execution_count": result.execution_count,
|
|
337
|
+
"data": expression["data"],
|
|
338
|
+
"metadata": expression["metadata"],
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
exception = result.error_before_exec or result.error_in_exec
|
|
343
|
+
if exception is not None:
|
|
344
|
+
if isinstance(exception, SyntaxError):
|
|
345
|
+
traceback = self.shell.SyntaxTB.structured_traceback(
|
|
346
|
+
type(exception),
|
|
347
|
+
exception,
|
|
348
|
+
)
|
|
349
|
+
else:
|
|
350
|
+
traceback = self.shell.InteractiveTB.structured_traceback(
|
|
351
|
+
type(exception),
|
|
352
|
+
exception,
|
|
353
|
+
exception.__traceback__,
|
|
354
|
+
)
|
|
355
|
+
outputs.append(
|
|
356
|
+
{
|
|
357
|
+
"output_type": "error",
|
|
358
|
+
"ename": type(exception).__name__,
|
|
359
|
+
"evalue": str(exception),
|
|
360
|
+
"traceback": traceback,
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
return outputs
|
codebind/extension.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import Any
|
|
|
7
7
|
from IPython.core.interactiveshell import InteractiveShell
|
|
8
8
|
from models_provider import Models
|
|
9
9
|
|
|
10
|
+
from .jupyter import JupyterLabBridge
|
|
10
11
|
from .session import Session
|
|
11
12
|
|
|
12
13
|
|
|
@@ -17,9 +18,13 @@ def load_ipython_extension(ipython: InteractiveShell) -> None:
|
|
|
17
18
|
"""Load Codebind into the active IPython user namespace."""
|
|
18
19
|
previous = getattr(ipython, _NAMESPACE_ATTRIBUTE, None)
|
|
19
20
|
if isinstance(previous, dict):
|
|
21
|
+
previous_chat = previous.get("chat")
|
|
22
|
+
if isinstance(previous_chat, Session) and previous_chat.bridge is not None:
|
|
23
|
+
previous_chat.bridge.close()
|
|
20
24
|
ipython.drop_by_id(previous)
|
|
25
|
+
bridge = JupyterLabBridge.connect(ipython)
|
|
21
26
|
namespace: dict[str, Any] = {
|
|
22
|
-
"chat": Session(shell=ipython),
|
|
27
|
+
"chat": Session(shell=ipython, bridge=bridge),
|
|
23
28
|
"Models": Models,
|
|
24
29
|
}
|
|
25
30
|
ipython.push(namespace)
|
|
@@ -30,5 +35,8 @@ def unload_ipython_extension(ipython: InteractiveShell) -> None:
|
|
|
30
35
|
"""Remove names added by Codebind without touching user replacements."""
|
|
31
36
|
namespace = getattr(ipython, _NAMESPACE_ATTRIBUTE, None)
|
|
32
37
|
if isinstance(namespace, dict):
|
|
38
|
+
chat = namespace.get("chat")
|
|
39
|
+
if isinstance(chat, Session) and chat.bridge is not None:
|
|
40
|
+
chat.bridge.close()
|
|
33
41
|
ipython.drop_by_id(namespace)
|
|
34
42
|
delattr(ipython, _NAMESPACE_ATTRIBUTE)
|
codebind/jupyter.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Optional bridge from an IPython kernel to the Codebind JupyterLab extension."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
from uuid import uuid4
|
|
7
|
+
|
|
8
|
+
from IPython.core.interactiveshell import InteractiveShell
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_TARGET_NAME = "codebind"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JupyterLabBridge:
|
|
15
|
+
"""Send native notebook cells to a connected JupyterLab frontend."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, comm: Any) -> None:
|
|
18
|
+
self._comm = comm
|
|
19
|
+
self.ready = False
|
|
20
|
+
comm.on_msg(self._on_message)
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def connect(cls, shell: InteractiveShell) -> JupyterLabBridge | None:
|
|
24
|
+
"""Open a frontend comm when running inside an IPython kernel."""
|
|
25
|
+
if not hasattr(shell, "kernel"):
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
from comm import create_comm # pyright: ignore[reportMissingImports]
|
|
30
|
+
|
|
31
|
+
return cls(create_comm(target_name=_TARGET_NAME))
|
|
32
|
+
except (ImportError, RuntimeError):
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
def close(self) -> None:
|
|
36
|
+
"""Close the frontend connection."""
|
|
37
|
+
self._comm.close()
|
|
38
|
+
self.ready = False
|
|
39
|
+
|
|
40
|
+
def start_code_cell(self, source: str) -> str | None:
|
|
41
|
+
"""Insert a running code cell before its execution begins."""
|
|
42
|
+
if not self.ready:
|
|
43
|
+
return None
|
|
44
|
+
cell_id = str(uuid4())
|
|
45
|
+
self._comm.send(
|
|
46
|
+
{
|
|
47
|
+
"type": "code_cell_started",
|
|
48
|
+
"cell_id": cell_id,
|
|
49
|
+
"source": source,
|
|
50
|
+
}
|
|
51
|
+
)
|
|
52
|
+
return cell_id
|
|
53
|
+
|
|
54
|
+
def finish_code_cell(
|
|
55
|
+
self,
|
|
56
|
+
cell_id: str,
|
|
57
|
+
execution_count: int | None,
|
|
58
|
+
outputs: list[dict[str, Any]],
|
|
59
|
+
) -> bool:
|
|
60
|
+
"""Finish a previously inserted code cell with its native outputs."""
|
|
61
|
+
if not self.ready:
|
|
62
|
+
return False
|
|
63
|
+
self._comm.send(
|
|
64
|
+
{
|
|
65
|
+
"type": "code_cell_finished",
|
|
66
|
+
"cell_id": cell_id,
|
|
67
|
+
"execution_count": execution_count,
|
|
68
|
+
"outputs": outputs,
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
return True
|
|
72
|
+
|
|
73
|
+
def append_code_output(self, cell_id: str, output: dict[str, Any]) -> bool:
|
|
74
|
+
"""Append one live output to a running code cell."""
|
|
75
|
+
if not self.ready:
|
|
76
|
+
return False
|
|
77
|
+
self._comm.send(
|
|
78
|
+
{
|
|
79
|
+
"type": "code_cell_output",
|
|
80
|
+
"cell_id": cell_id,
|
|
81
|
+
"output": output,
|
|
82
|
+
}
|
|
83
|
+
)
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
def clear_code_output(self, cell_id: str, *, wait: bool = False) -> bool:
|
|
87
|
+
"""Clear a running code cell's outputs."""
|
|
88
|
+
if not self.ready:
|
|
89
|
+
return False
|
|
90
|
+
self._comm.send(
|
|
91
|
+
{
|
|
92
|
+
"type": "code_cell_clear",
|
|
93
|
+
"cell_id": cell_id,
|
|
94
|
+
"wait": wait,
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
def insert_markdown_cell(self, source: str) -> bool:
|
|
100
|
+
"""Insert one rendered Markdown cell through JupyterLab."""
|
|
101
|
+
if not self.ready:
|
|
102
|
+
return False
|
|
103
|
+
self._comm.send({"type": "markdown_cell", "source": source})
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
def _on_message(self, message: dict[str, Any]) -> None:
|
|
107
|
+
data = message.get("content", {}).get("data", {})
|
|
108
|
+
if isinstance(data, dict) and data.get("type") == "ready":
|
|
109
|
+
self.ready = True
|
codebind/session.py
CHANGED
|
@@ -15,6 +15,7 @@ from langchain_core.runnables import Runnable
|
|
|
15
15
|
|
|
16
16
|
from .display import display_assistant
|
|
17
17
|
from .execution import ExecutionReport, IPythonExecutor
|
|
18
|
+
from .jupyter import JupyterLabBridge
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
IPYTHON_TOOL = {
|
|
@@ -66,13 +67,15 @@ class Session:
|
|
|
66
67
|
*,
|
|
67
68
|
shell: InteractiveShell | None = None,
|
|
68
69
|
instructions: str | None = None,
|
|
70
|
+
bridge: JupyterLabBridge | None = None,
|
|
69
71
|
) -> None:
|
|
70
72
|
resolved_shell = shell or get_ipython()
|
|
71
73
|
if resolved_shell is None:
|
|
72
74
|
raise RuntimeError("Session must be created inside IPython or given an IPython shell.")
|
|
73
75
|
|
|
74
76
|
self.shell = resolved_shell
|
|
75
|
-
self.
|
|
77
|
+
self.bridge = bridge
|
|
78
|
+
self.executor = IPythonExecutor(resolved_shell, bridge)
|
|
76
79
|
self.instructions = instructions.strip() if instructions else None
|
|
77
80
|
self.messages: list[BaseMessage] = []
|
|
78
81
|
self.last_response: AIMessage | None = None
|
|
@@ -112,7 +115,7 @@ class Session:
|
|
|
112
115
|
if not response.tool_calls:
|
|
113
116
|
answer = _message_text(response)
|
|
114
117
|
if answer:
|
|
115
|
-
|
|
118
|
+
self._display_assistant(answer)
|
|
116
119
|
return
|
|
117
120
|
|
|
118
121
|
for call in response.tool_calls:
|
|
@@ -144,7 +147,7 @@ class Session:
|
|
|
144
147
|
if not response.tool_calls:
|
|
145
148
|
answer = _message_text(response)
|
|
146
149
|
if answer:
|
|
147
|
-
|
|
150
|
+
self._display_assistant(answer)
|
|
148
151
|
return
|
|
149
152
|
|
|
150
153
|
for call in response.tool_calls:
|
|
@@ -164,6 +167,10 @@ class Session:
|
|
|
164
167
|
except NotImplementedError:
|
|
165
168
|
return model.bind(tools=[IPYTHON_TOOL], parallel_tool_calls=False)
|
|
166
169
|
|
|
170
|
+
def _display_assistant(self, answer: str) -> None:
|
|
171
|
+
if self.bridge is None or not self.bridge.insert_markdown_cell(answer):
|
|
172
|
+
display_assistant(answer)
|
|
173
|
+
|
|
167
174
|
def _execute_call(self, call: Mapping[str, Any]) -> ExecutionReport:
|
|
168
175
|
if call.get("name") != "ipython":
|
|
169
176
|
return _tool_error("UnknownTool", f"unknown tool: {call.get('name')!r}")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codebind-jupyterlab",
|
|
3
|
+
"version": "0.4.1",
|
|
4
|
+
"description": "Native JupyterLab cells for Codebind model executions.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "lib/index.js",
|
|
7
|
+
"types": "lib/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"lib/**/*"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "jlpm run clean && jlpm run build:lib && jupyter-builder build && jlpm run build:install",
|
|
13
|
+
"build:install": "node -e \"require('node:fs').copyFileSync('install.json', '../data/share/jupyter/labextensions/codebind-jupyterlab/install.json')\"",
|
|
14
|
+
"build:lib": "tsc",
|
|
15
|
+
"clean": "rimraf lib tsconfig.tsbuildinfo"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@jupyterlab/application": "^4.0.0",
|
|
19
|
+
"@jupyterlab/cells": "^4.0.0",
|
|
20
|
+
"@jupyterlab/nbformat": "^4.0.0",
|
|
21
|
+
"@jupyterlab/notebook": "^4.0.0",
|
|
22
|
+
"@jupyterlab/services": "^7.0.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@jupyter/builder": "^1.2.0",
|
|
26
|
+
"rimraf": "^6.0.0",
|
|
27
|
+
"typescript": "~5.7.0"
|
|
28
|
+
},
|
|
29
|
+
"jupyterlab": {
|
|
30
|
+
"extension": true,
|
|
31
|
+
"outputDir": "../data/share/jupyter/labextensions/codebind-jupyterlab",
|
|
32
|
+
"_build": {
|
|
33
|
+
"load": "static/remoteEntry.6f017385dfc989f1.js",
|
|
34
|
+
"extension": "./extension"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.rspackChunkcodebind_jupyterlab=self.rspackChunkcodebind_jupyterlab||[]).push([[590],{509(e,t,n){n.r(t);var o=n(171);function l(e){let t=e.sessionContext.session?.kernel;if(!t)return;let n=null,l=new Map;t.registerCommTarget("codebind",(u,c)=>{u.onMsg=u=>{var c;let d,i,r=u.content.data;if(!("object"==typeof r&&null!==r&&("markdown_cell"===r.type?"string"==typeof r.source:"code_cell_started"===r.type?"string"==typeof r.cell_id&&"string"==typeof r.source:"code_cell_output"===r.type?"string"==typeof r.cell_id&&"object"==typeof r.output&&null!==r.output:"code_cell_clear"===r.type?"string"==typeof r.cell_id&&"boolean"==typeof r.wait:"code_cell_finished"===r.type&&"string"==typeof r.cell_id&&("number"==typeof r.execution_count||null===r.execution_count)&&Array.isArray(r.outputs))))return;if("code_cell_output"===r.type)return void l.get(r.cell_id)?.outputs.add(r.output);if("code_cell_clear"===r.type)return void l.get(r.cell_id)?.outputs.clear(r.wait);if("code_cell_finished"===r.type){let e=l.get(r.cell_id);if(!e)return;e.outputs.fromJSON(r.outputs),e.executionCount=r.execution_count,e.executionState="idle",n&&null===n.parentExecutionCount&&null!==r.execution_count&&(n.parentExecutionCount=r.execution_count-1);return}let s=(()=>{if(n)return n;let o=e.content,l=o.widgets.findIndex(e=>"code"===e.model.type&&"running"===e.model.executionState);n={parentModel:l>=0?o.widgets[l].model:null,parentExecutionCount:null,resumeModel:o.activeCell?.model??null,nextIndex:l>=0?l+1:o.activeCell?o.activeCellIndex+1:0};let u=(o,l)=>{"idle"===l&&(t.statusChanged.disconnect(u),(()=>{if(!n)return;let t=e.content,o=n;if(n=null,o.parentModel&&null!==o.parentExecutionCount&&(o.parentModel.executionCount=o.parentExecutionCount),o.resumeModel){let e=t.widgets.findIndex(e=>e.model===o.resumeModel);e>=0&&(t.activeCellIndex=e)}})())};return t.statusChanged.connect(u),n})(),a=(c=s.nextIndex,(i=(d=e.content).model)?("code_cell_started"===r.type?i.sharedModel.insertCell(c,{cell_type:"code",source:r.source,metadata:{trusted:!0},execution_count:null,outputs:[]}):i.sharedModel.insertCell(c,{cell_type:"markdown",source:r.source,metadata:{}}),d.activeCellIndex=c,d.deselectAll(),"markdown_cell"===r.type&&o.NotebookActions.run(d),d.scrollToItem(c),d.widgets[c]?.model??null):null);"code_cell_started"===r.type&&a?.type==="code"&&(a.executionState="running",l.set(r.cell_id,a)),s.nextIndex+=1},u.send({type:"ready"})})}function u(e){e.sessionContext.ready.then(()=>l(e)),e.sessionContext.kernelChanged.connect(()=>l(e))}let c={id:"codebind-jupyterlab:plugin",description:"Insert Codebind executions as native notebook cells.",autoStart:!0,requires:[o.INotebookTracker],activate:(e,t)=>{t.forEach(u),t.widgetAdded.connect((e,t)=>u(t))}};n.d(t,{},{default:c})}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var _JUPYTERLAB;(()=>{"use strict";var e,n,r,t,o,i,u,a,c,l,f,s,p,d,h,v,g,m,y,b,S,j,k,w,E,x,T,P,M,A,C,O,D,_,L,N,$,q={457(e,n,r){var t={"./index":()=>r.e(590).then(()=>()=>r(509)),"./extension":()=>r.e(590).then(()=>()=>r(509))},o=function(e,n){return r.R=n,n=r.o(t,e)?t[e]():Promise.resolve().then(()=>{throw Error('Module "'+e+'" does not exist in container.')}),r.R=void 0,n},i=function(e,n){if(r.S){var t="default",o=r.S[t];if(o&&o!==e)throw Error("Container initialization failed as it has already been initialized with a different share scope");return r.S[t]=e,r.I(t,n)}};r.d(n,{get:()=>o,init:()=>i})}},z={};function I(e){var n=z[e];if(void 0!==n)return n.exports;var r=z[e]={exports:{}};return q[e](r,r.exports,I),r.exports}I.m=q,I.c=z,I.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return I.d(n,{a:n}),n},I.d=(e,n,r)=>{var t=(n,r)=>{for(var t in n)I.o(n,t)&&!I.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,[r]:n[t]})};t(n,"get"),t(r,"value")},I.f={},I.e=e=>Promise.all(Object.keys(I.f).reduce((n,r)=>(I.f[r](e,n),n),[])),I.u=e=>""+e+".6544266cd73a003b.js?v=6544266cd73a003b",I.g=(()=>{if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}})(),I.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),R={},I.l=function(e,n,r,t){if(R[e])return void R[e].push(n);if(void 0!==r)for(var o,i,u=document.getElementsByTagName("script"),a=0;a<u.length;a++){var c=u[a];if(c.getAttribute("src")==e||c.getAttribute("data-rspack")=="codebind-jupyterlab:"+r){o=c;break}}o||(i=!0,(o=document.createElement("script")).timeout=120,I.nc&&o.setAttribute("nonce",I.nc),o.setAttribute("data-rspack","codebind-jupyterlab:"+r),o.src=e),R[e]=[n];var l=function(n,r){o.onerror=o.onload=null,clearTimeout(f);var t=R[e];if(delete R[e],o.parentNode&&o.parentNode.removeChild(o),t&&t.forEach(function(e){return e(r)}),n)return n(r)},f=setTimeout(l.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=l.bind(null,o.onerror),o.onload=l.bind(null,o.onload),i&&document.head.appendChild(o)},I.r=e=>{"u">typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},I.S={},I.initializeSharingData={scopeToSharingDataMapping:{default:[{name:"codebind-jupyterlab",version:"0.4.1",factory:()=>I.e(590).then(()=>()=>I(509)),eager:0,treeShakingMode:null}]},uniqueName:"codebind-jupyterlab"},U={},B={},I.I=function(e,n){n||(n=[]);var r=B[e];if(r||(r=B[e]={}),!(n.indexOf(r)>=0)){if(n.push(r),U[e])return U[e];I.o(I.S,e)||(I.S[e]={});var t=I.S[e],o=function(e){"u">typeof console&&console.warn&&console.warn(e)},i=I.initializeSharingData.uniqueName,u=function(e,n,r,o){var u=t[e]=t[e]||{},a=u[n];(!a||!a.loaded&&(!o!=!a.eager?o:i>a.from))&&(u[n]={get:r,from:i,eager:!!o})},a=function(r){var t=function(e){o("Initialization of sharing external failed: "+e)};try{var i=I(r);if(!i)return;var u=function(r){return r&&r.init&&r.init(I.S[e],n)};if(i.then)return c.push(i.then(u,t));var a=u(i);if(a&&a.then)return c.push(a.catch(t))}catch(e){t(e)}},c=[],l=I.initializeSharingData.scopeToSharingDataMapping;return(l[e]&&l[e].forEach(function(e){"object"==typeof e?u(e.name,e.version,e.factory,e.eager):a(e)}),c.length)?U[e]=Promise.all(c).then(function(){return U[e]=1}):U[e]=1}},I.g.importScripts&&(V=I.g.location+"");var R,U,B,V,J=I.g.document;if(!V&&J&&(J.currentScript&&"SCRIPT"===J.currentScript.tagName.toUpperCase()&&(V=J.currentScript.src),!V)){var Y=J.getElementsByTagName("script");if(Y.length)for(var K=Y.length-1;K>-1&&(!V||!/^http(s?):/.test(V));)V=Y[K--].src}if(!V)throw Error("Automatic publicPath is not supported in this browser");I.p=V=V.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),I.consumesLoadingData={chunkMapping:{590:["171"]},moduleIdToConsumeDataMapping:{171:{shareScope:"default",shareKey:"@jupyterlab/notebook",import:null,requiredVersion:"^4.6.3",strictVersion:!1,singleton:!0,eager:!1,fallback:void 0,treeShakingMode:null}},initialConsumes:[]},e=function(e){return e.split(".").map(function(e){return+e==e?+e:e})},n=function(n){var r=function(n){var r=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(n),t=r[1]?[0].concat(e(r[1])):[0];r[2]&&(t.length++,t.push.apply(t,e(r[2])));let o=t[t.length-1];for(;t.length&&(void 0===o||/^[*xX]$/.test(o));)t.pop(),o=t[t.length-1];return t},t=function(e){return 1===e.length?[0]:2===e.length?[1].concat(e.slice(1)):3===e.length?[2].concat(e.slice(1)):[e.length].concat(e.slice(1))},o=function(e){return[-e[0]-1].concat(e.slice(1))},i=function(e){let n=/^(\^|~|<=|<|>=|>|=|v|!)/.exec(e),i=n?n[0]:"",u=r(i.length?e.slice(i.length).trim():e.trim());switch(i){case"^":if(u.length>1&&0===u[1]){if(u.length>2&&0===u[2])return[3].concat(u.slice(1));return[2].concat(u.slice(1))}return[1].concat(u.slice(1));case"~":return[2].concat(u.slice(1));case">=":return u;case"=":case"v":case"":return t(u);case"<":return o(u);case">":return[,t(u),0,u,2];case"<=":return[,t(u),o(u),1];case"!":return[,t(u),0];default:throw Error("Unexpected start value")}},u=function(e,n){if(1===e.length)return e[0];let r=[];for(let n of e.slice().reverse())0 in n?r.push(n):r.push.apply(r,n.slice(1));return[,].concat(r,e.slice(1).map(()=>n))};return u(n.split(/\s*\|\|\s*/).map(function(e){let n=e.split(/\s+-\s+/);if(1===n.length){e=e.trim();let n=[],r=/[-0-9A-Za-z]\s+/g;for(var a,c=0;a=r.exec(e);){let r=a.index+1;n.push(i(e.slice(c,r).trim())),c=r}return n.push(i(e.slice(c).trim())),u(n,2)}let l=r(n[0]),f=r(n[1]);return[,t(f),o(f),1,l,2]}),1)},r=function(n){var r=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(n),t=r[1]?e(r[1]):[];return r[2]&&(t.length++,t.push.apply(t,e(r[2]))),r[3]&&(t.push([]),t.push.apply(t,e(r[3]))),t},t=function(e,n){e=r(e),n=r(n);for(var t=0;;){if(t>=e.length)return t<n.length&&"u"!=(typeof n[t])[0];var o=e[t],i=(typeof o)[0];if(t>=n.length)return"u"==i;var u=n[t],a=(typeof u)[0];if(i==a){if("o"!=i&&"u"!=i&&o!=u)return o<u;t++}else{if("o"==i&&"n"==a)return!0;return"s"==a||"u"==i}}},o=function(e){var n=e[0],r="";if(1===e.length)return"*";if(n+.5){r+=0==n?">=":-1==n?"<":1==n?"^":2==n?"~":n>0?"=":"!=";for(var t=1,i=1;i<e.length;i++){var u=e[i],a=(typeof u)[0];t--,r+="u"==a?"-":(t>0?".":"")+(t=2,u)}return r}for(var c=[],i=1;i<e.length;i++){var u=e[i];c.push(0===u?"not("+l()+")":1===u?"("+l()+" || "+l()+")":2===u?c.pop()+" "+c.pop():o(u))}return l();function l(){return c.pop().replace(/^\((.+)\)$/,"$1")}},i=function(e,n){if(0 in e){n=r(n);var t=e[0],o=t<0;o&&(t=-t-1);for(var u=0,a=1,c=!0;;a++,u++){var l,f,s=a<e.length?(typeof e[a])[0]:"";if(u>=n.length||"o"==(f=(typeof(l=n[u]))[0])){if(!c)return!0;if("u"==s)return a>t&&!o;return""==s!=o}if("u"==f){if(!c||"u"!=s)return!1}else if(c)if(s==f)if(a<=t){if(l!=e[a])return!1}else{if(o?l>e[a]:l<e[a])return!1;l!=e[a]&&(c=!1)}else if("s"!=s&&"n"!=s){if(o||a<=t)return!1;c=!1,a--}else{if(a<=t||f<s!=o)return!1;c=!1}else"s"!=s&&"n"!=s&&(c=!1,a--)}}for(var p=[],d=p.pop.bind(p),u=1;u<e.length;u++){var h=e[u];p.push(1==h?d()|d():2==h?d()&d():h?i(h,n):!d())}return!!d()},u=function(e,n){var r=I.S[e];if(!r||!I.o(r,n))throw Error("Shared module "+n+" doesn't exist in shared scope "+e);return r},a=function(e,n){var r=e[n],n=Object.keys(r).reduce(function(e,n){return!e||t(e,n)?n:e},0);return n&&r[n]},c=function(e,n){var r=e[n];return Object.keys(r).reduce(function(e,n){return!e||!r[e].loaded&&t(e,n)?n:e},0)},l=function(e,n,r,t){return"Unsatisfied version "+r+" from "+(r&&e[n][r].from)+" of shared singleton module "+n+" (required "+o(t)+")"},f=function(e,n,r,t){var o=c(e,r);return y(e[r][o])},s=function(e,n,r,t){var o=c(e,r);return i(t,o)||g(l(e,r,o,t)),y(e[r][o])},p=function(e,n,r,t){var o=c(e,r);if(!i(t,o))throw Error(l(e,r,o,t));return y(e[r][o])},d=function(e,n,r){var o=e[n],n=Object.keys(o).reduce(function(e,n){return i(r,n)&&(!e||t(e,n))?n:e},0);return n&&o[n]},h=function(e,n,r,t){var i=e[r];return"No satisfying version ("+o(t)+") of shared module "+r+" found in shared scope "+n+".\nAvailable versions: "+Object.keys(i).map(function(e){return e+" from "+i[e].from}).join(", ")},v=function(e,n,r,t){var o=d(e,r,t);if(o)return y(o);throw Error(h(e,n,r,t))},g=function(e){"u">typeof console&&console.warn&&console.warn(e)},m=function(e,n,r,t){g(h(e,n,r,t))},y=function(e){return e.loaded=1,e.get()},S=(b=function(e){return function(n,r,t,o){var i=I.I(n);return i&&i.then?i.then(e.bind(e,n,I.S[n],r,t,o)):e(n,I.S[n],r,t,o)}})(function(e,n,r){return u(e,r),y(a(n,r))}),j=b(function(e,n,r,t){return n&&I.o(n,r)?y(a(n,r)):t()}),k=b(function(e,n,r,t){return u(e,r),y(d(n,r,t)||m(n,e,r,t)||a(n,r))}),w=b(function(e,n,r){return u(e,r),f(n,e,r)}),E=b(function(e,n,r,t){return u(e,r),s(n,e,r,t)}),x=b(function(e,n,r,t){return u(e,r),v(n,e,r,t)}),T=b(function(e,n,r,t){return u(e,r),p(n,e,r,t)}),P=b(function(e,n,r,t,o){return n&&I.o(n,r)?y(d(n,r,t)||m(n,e,r,t)||a(n,r)):o()}),M=b(function(e,n,r,t){return n&&I.o(n,r)?f(n,e,r):t()}),A=b(function(e,n,r,t,o){return n&&I.o(n,r)?s(n,e,r,t):o()}),C=b(function(e,n,r,t,o){var i=n&&I.o(n,r)&&d(n,r,t);return i?y(i):o()}),O=b(function(e,n,r,t,o){return n&&I.o(n,r)?p(n,e,r,t):o()}),D=function(e){var r=!1,t=!1,o=!1,i=!1,u=[e.shareScope,e.shareKey];return(e.requiredVersion?(e.strictVersion&&(r=!0),e.singleton&&(t=!0),u.push(n(e.requiredVersion)),o=!0):e.singleton&&(t=!0),e.fallback&&(i=!0,u.push(e.fallback)),r&&t&&o&&i)?function(){return O.apply(null,u)}:r&&o&&i?function(){return C.apply(null,u)}:t&&o&&i?function(){return A.apply(null,u)}:r&&t&&o?function(){return T.apply(null,u)}:t&&i?function(){return M.apply(null,u)}:o&&i?function(){return P.apply(null,u)}:r&&o?function(){return x.apply(null,u)}:t&&o?function(){return E.apply(null,u)}:t?function(){return w.apply(null,u)}:o?function(){return k.apply(null,u)}:i?function(){return j.apply(null,u)}:function(){return S.apply(null,u)}},_={},I.f.consumes=function(e,n){var r=I.consumesLoadingData.moduleIdToConsumeDataMapping,t=I.consumesLoadingData.chunkMapping;I.o(t,e)&&t[e].forEach(function(e){if(I.o(_,e))return n.push(_[e]);var t=function(n){_[e]=0,I.m[e]=function(r){delete I.c[e],r.exports=n()}},o=function(n){delete _[e],I.m[e]=function(r){throw delete I.c[e],n}};try{var i=D(r[e])();i.then?n.push(_[e]=i.then(t).catch(o)):t(i)}catch(e){o(e)}})},L={871:0},I.f.j=function(e,n){var r=I.o(L,e)?L[e]:void 0;if(0!==r)if(r)n.push(r[2]);else{var t=new Promise((n,t)=>r=L[e]=[n,t]);n.push(r[2]=t);var o=I.p+I.u(e),i=Error();I.l(o,function(n){if(I.o(L,e)&&(0!==(r=L[e])&&(L[e]=void 0),r)){var t=n&&("load"===n.type?"missing":n.type),o=n&&n.target&&n.target.src;i.message="Loading chunk "+e+" failed.\n("+t+": "+o+")",i.name="ChunkLoadError",i.type=t,i.request=o,r[1](i)}},"chunk-"+e,e)}},N=(e,n)=>{var r,t,[o,i,u]=n,a=0;if(o.some(e=>0!==L[e])){for(r in i)I.o(i,r)&&(I.m[r]=i[r]);u&&u(I)}for(e&&e(n);a<o.length;a++)t=o[a],I.o(L,t)&&L[t]&&L[t][0](),L[t]=0},($=self.rspackChunkcodebind_jupyterlab=self.rspackChunkcodebind_jupyterlab||[]).forEach(N.bind(null,0)),$.push=N.bind(null,$.push.bind($));var F=I(457);(_JUPYTERLAB=void 0===_JUPYTERLAB?{}:_JUPYTERLAB)["codebind-jupyterlab"]=F})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebind
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: A frontend-neutral model loop for persistent IPython sessions.
|
|
5
5
|
Keywords: ai,ipython,llm,repl,agents
|
|
6
6
|
Author: Giovanni Gravili
|
|
@@ -8,6 +8,7 @@ Author-email: Giovanni Gravili <ghovax@users.noreply.github.com>
|
|
|
8
8
|
License-Expression: MIT
|
|
9
9
|
License-File: LICENSE
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: Operating System :: OS Independent
|
|
13
14
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -61,7 +62,13 @@ Codebind does not load files or construct a project prompt automatically. The us
|
|
|
61
62
|
|
|
62
63
|
## Jupyter
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
Start JupyterLab with Codebind from any directory without a permanent installation:
|
|
66
|
+
|
|
67
|
+
```console
|
|
68
|
+
uvx --from jupyterlab --with codebind jupyter lab
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or install both packages into the same environment, then start JupyterLab normally:
|
|
65
72
|
|
|
66
73
|
```console
|
|
67
74
|
pip install codebind jupyterlab
|
|
@@ -72,15 +79,19 @@ Load Codebind in a notebook:
|
|
|
72
79
|
|
|
73
80
|
```python
|
|
74
81
|
%load_ext codebind
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Run that cell once so JupyterLab can connect to the extension, then use Codebind in later cells:
|
|
75
85
|
|
|
86
|
+
```python
|
|
76
87
|
models = Models({"openai": "OPENAI_API_KEY"})
|
|
77
88
|
model = models.chat("openai/gpt-5")
|
|
78
89
|
await chat.asend("Inspect the current notebook state.", model)
|
|
79
90
|
```
|
|
80
91
|
|
|
81
|
-
Codebind
|
|
92
|
+
The Codebind package includes a prebuilt JupyterLab extension. In JupyterLab, each model-authored IPython execution becomes a genuine code cell with its native execution count and outputs, and the assistant response becomes a rendered Markdown cell. The cells are ordinary notebook content and are saved with the notebook.
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
Other IPython frontends use the standard MIME display protocol instead. They still receive syntax-highlighted code, assistant Markdown, stdout, tracebacks, rich results, and native IPython history without Codebind depending on their UI.
|
|
84
95
|
|
|
85
96
|
## ChatGPT account login
|
|
86
97
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
codebind/__init__.py,sha256=GNO_K5EPAdgiK2CWsPVVY-3bAmH2TnFJ9ReAWgSbnqM,514
|
|
2
|
+
codebind/cli.py,sha256=9LOR1L3P8sPsOOgsnpbOsA1PNUZdAj1Z9ROFKeo3EV8,423
|
|
3
|
+
codebind/display.py,sha256=IpxcElyNcr5olRIatEduN_d-sRwpsMlaq1UtrOXocQA,494
|
|
4
|
+
codebind/execution.py,sha256=b1U0iT0e9fDpvGZclGGihod_iLFjJ7wLUkXhmawgeRs,11968
|
|
5
|
+
codebind/extension.py,sha256=elIDW_zl5o3uyil1tEnJ0A5jVkADalqPWI_qcDw4xhY,1460
|
|
6
|
+
codebind/jupyter.py,sha256=xuoiV2-pqtcb_dWXvaaVrlSvqcYfqRYtbX4B-Zh_pBA,3272
|
|
7
|
+
codebind/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
codebind/session.py,sha256=6KK2d55XsKaq5IVRQmUp5Pzdg-O76bAZvCYgxBU2BN0,7455
|
|
9
|
+
codebind-0.4.1.dist-info/licenses/LICENSE,sha256=ujX46e0LFBIBTnCDY0u3Q7LV363B7CiI7QJZxIkEXlk,1073
|
|
10
|
+
codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/install.json,sha256=78rEThkfrk1pXtekX5WrG9_Y0lJYi5XBEm8n9athpFE,164
|
|
11
|
+
codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/package.json,sha256=HYr80rRV4byoPUvhf5r9swwnssr7eGxIay227Cusyqo,1152
|
|
12
|
+
codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/590.6544266cd73a003b.js,sha256=zrQdGY0320_MGN8KRf8dzMXCjXI_6jtZoAnW4UhDquo,2771
|
|
13
|
+
codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/remoteEntry.6f017385dfc989f1.js,sha256=Cwm6yVfy68shv0OTb6yIbHwV5FxozSz-jO9gf9WE0vc,11087
|
|
14
|
+
codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/style.js,sha256=Opy15wyuR_ABqsrk-cOcOj2yZtvtq0r36aDkErnloD0,113
|
|
15
|
+
codebind-0.4.1.data/data/share/jupyter/labextensions/codebind-jupyterlab/static/third-party-licenses.json,sha256=MNToQfru1YnHAie-2Virp23yWsc5co3qG0IzrO6SEfw,20
|
|
16
|
+
codebind-0.4.1.dist-info/WHEEL,sha256=R1d3uUTbmXM1FHXH_itQashbrqrOSVj-hvBCpmkIIGE,81
|
|
17
|
+
codebind-0.4.1.dist-info/entry_points.txt,sha256=GiPcU63sULDlywJSKigAS4M3vaziYIrubXOb3fAg2_c,48
|
|
18
|
+
codebind-0.4.1.dist-info/METADATA,sha256=AwmxcH7e9fosl2xI6gJhdbyzKhGbLE5yv1JBva4iX_k,4062
|
|
19
|
+
codebind-0.4.1.dist-info/RECORD,,
|
codebind-0.3.0.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
codebind/__init__.py,sha256=ZA475Vomau6BqvxTwcXUeFKXiOlrjTWK6XjD8_Ez6os,452
|
|
2
|
-
codebind/cli.py,sha256=9LOR1L3P8sPsOOgsnpbOsA1PNUZdAj1Z9ROFKeo3EV8,423
|
|
3
|
-
codebind/display.py,sha256=IpxcElyNcr5olRIatEduN_d-sRwpsMlaq1UtrOXocQA,494
|
|
4
|
-
codebind/execution.py,sha256=Md2bv_oIp4rwjMhf8W4b4azA6a0aj-4b6J64cErPP1A,2876
|
|
5
|
-
codebind/extension.py,sha256=zt30o59tynJhat5OMi_u2rci1VDhq8ZOm202hZRwFp8,1055
|
|
6
|
-
codebind/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
codebind/session.py,sha256=Q7HabKcjHUmawLKhoNc2Rdx2-pkTPM0sh1A-tApXQsU,7146
|
|
8
|
-
codebind-0.3.0.dist-info/licenses/LICENSE,sha256=ujX46e0LFBIBTnCDY0u3Q7LV363B7CiI7QJZxIkEXlk,1073
|
|
9
|
-
codebind-0.3.0.dist-info/WHEEL,sha256=R1d3uUTbmXM1FHXH_itQashbrqrOSVj-hvBCpmkIIGE,81
|
|
10
|
-
codebind-0.3.0.dist-info/entry_points.txt,sha256=GiPcU63sULDlywJSKigAS4M3vaziYIrubXOb3fAg2_c,48
|
|
11
|
-
codebind-0.3.0.dist-info/METADATA,sha256=wPZj3RGdkOU_mJLuUGsq3Du_C76P_t0jGMdtLwugeMw,3688
|
|
12
|
-
codebind-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|