codebind 0.6.8__tar.gz → 0.6.9__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.
- {codebind-0.6.8 → codebind-0.6.9}/PKG-INFO +2 -2
- {codebind-0.6.8 → codebind-0.6.9}/README.md +1 -1
- {codebind-0.6.8 → codebind-0.6.9}/pyproject.toml +1 -1
- {codebind-0.6.8 → codebind-0.6.9}/pyproject.toml.orig +1 -1
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/session.py +35 -9
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/terminal.py +22 -13
- {codebind-0.6.8 → codebind-0.6.9}/LICENSE +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/install.json +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/package.json +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/static/590.dae4561c5427a855.js +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/static/remoteEntry.c10098fb6e87897c.js +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/static/style.js +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/static/third-party-licenses.json +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/jupyterlab/.yarnrc.yml +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/jupyterlab/install.json +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/jupyterlab/package.json +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/jupyterlab/src/index.ts +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/jupyterlab/tsconfig.json +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/jupyterlab/yarn.lock +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/__init__.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/cli.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/configuration.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/conversation.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/display.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/execution.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/extension.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/images.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/instructions.md +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/jupyter.py +0 -0
- {codebind-0.6.8 → codebind-0.6.9}/src/codebind/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebind
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.9
|
|
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
|
|
@@ -75,7 +75,7 @@ Inspect this project and tell me what to implement first.
|
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
Terminal conversations remain live for the current IPython process. Previous terminal inputs and outputs, including Python cells you ran yourself, enter the model's context when you ask a question. Durable automatic resumption belongs to notebooks, where the notebook file provides an unambiguous conversation identity.
|
|
78
|
-
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. To keep terminal scrollback usable, Codebind shows at most 10 gray lines of each model-executed cell's output, then displays a notice such as `… 990 more output lines. Run %output 8 to view all.` The complete output remains available to the model and in IPython history; `%output 8` opens its full text in IPython's pager. User-entered Python cells keep IPython's normal display behavior. Assistant Markdown is rendered through IPython's terminal MIME renderer. A terminal is a stream rather than a notebook document, so these displays are not editable notebook cells.
|
|
78
|
+
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. Assistant Markdown appears before the following tool cell, and tool output appears as it is written. To keep terminal scrollback usable, Codebind shows at most 10 gray lines of each model-executed cell's output, then displays a notice such as `… 990 more output lines. Run %output 8 to view all.` The complete output remains available to the model and in IPython history; `%output 8` opens its full text in IPython's pager. User-entered Python cells keep IPython's normal display behavior. Assistant Markdown is rendered through IPython's terminal MIME renderer. A terminal is a stream rather than a notebook document, so these displays are not editable notebook cells.
|
|
79
79
|
|
|
80
80
|
## JupyterLab
|
|
81
81
|
|
|
@@ -47,7 +47,7 @@ Inspect this project and tell me what to implement first.
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
Terminal conversations remain live for the current IPython process. Previous terminal inputs and outputs, including Python cells you ran yourself, enter the model's context when you ask a question. Durable automatic resumption belongs to notebooks, where the notebook file provides an unambiguous conversation identity.
|
|
50
|
-
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. To keep terminal scrollback usable, Codebind shows at most 10 gray lines of each model-executed cell's output, then displays a notice such as `… 990 more output lines. Run %output 8 to view all.` The complete output remains available to the model and in IPython history; `%output 8` opens its full text in IPython's pager. User-entered Python cells keep IPython's normal display behavior. Assistant Markdown is rendered through IPython's terminal MIME renderer. A terminal is a stream rather than a notebook document, so these displays are not editable notebook cells.
|
|
50
|
+
Model-executed cells appear with IPython's native input counter, syntax highlighting, output prompts, streams, and tracebacks. Assistant Markdown appears before the following tool cell, and tool output appears as it is written. To keep terminal scrollback usable, Codebind shows at most 10 gray lines of each model-executed cell's output, then displays a notice such as `… 990 more output lines. Run %output 8 to view all.` The complete output remains available to the model and in IPython history; `%output 8` opens its full text in IPython's pager. User-entered Python cells keep IPython's normal display behavior. Assistant Markdown is rendered through IPython's terminal MIME renderer. A terminal is a stream rather than a notebook document, so these displays are not editable notebook cells.
|
|
51
51
|
|
|
52
52
|
## JupyterLab
|
|
53
53
|
|
|
@@ -274,8 +274,24 @@ class Session:
|
|
|
274
274
|
for identifier in self._pending_calls(turn_id):
|
|
275
275
|
self._append_message_sync(self._interrupted_tool_message(identifier), turn_id)
|
|
276
276
|
|
|
277
|
-
def _update_assistant_stream(
|
|
278
|
-
|
|
277
|
+
def _update_assistant_stream(
|
|
278
|
+
self,
|
|
279
|
+
cell_id: str | None,
|
|
280
|
+
text: str,
|
|
281
|
+
shown: str,
|
|
282
|
+
terminal_displayed: list[str] | None = None,
|
|
283
|
+
) -> str | None:
|
|
284
|
+
if not text:
|
|
285
|
+
return cell_id
|
|
286
|
+
if self.bridge is None:
|
|
287
|
+
previous = terminal_displayed[0] if terminal_displayed is not None else shown
|
|
288
|
+
if previous.startswith(text):
|
|
289
|
+
return cell_id
|
|
290
|
+
addition = text[len(previous) :] if text.startswith(previous) else text
|
|
291
|
+
if addition:
|
|
292
|
+
self._display_assistant(addition)
|
|
293
|
+
if terminal_displayed is not None:
|
|
294
|
+
terminal_displayed[0] = text
|
|
279
295
|
return cell_id
|
|
280
296
|
if cell_id is None:
|
|
281
297
|
return self.bridge.start_markdown_cell(text)
|
|
@@ -284,7 +300,9 @@ class Session:
|
|
|
284
300
|
|
|
285
301
|
def _finish_assistant_stream(self, cell_id: str | None, response: AIMessage) -> None:
|
|
286
302
|
answer = _message_text(response)
|
|
287
|
-
if
|
|
303
|
+
if self.bridge is None:
|
|
304
|
+
return
|
|
305
|
+
if cell_id is not None:
|
|
288
306
|
self.bridge.finish_markdown_cell(cell_id, answer)
|
|
289
307
|
elif answer:
|
|
290
308
|
self._display_assistant(answer)
|
|
@@ -302,9 +320,10 @@ class Session:
|
|
|
302
320
|
self,
|
|
303
321
|
model: BaseChatModel,
|
|
304
322
|
) -> tuple[AIMessage, str | None]:
|
|
323
|
+
terminal_displayed = [""]
|
|
305
324
|
for retry in range(len(_MODEL_RETRY_DELAYS) + 1):
|
|
306
325
|
try:
|
|
307
|
-
return self._stream_model(self._bind(model), model)
|
|
326
|
+
return self._stream_model(self._bind(model), model, terminal_displayed)
|
|
308
327
|
except TransientProviderError as error:
|
|
309
328
|
if retry == len(_MODEL_RETRY_DELAYS):
|
|
310
329
|
raise
|
|
@@ -316,9 +335,10 @@ class Session:
|
|
|
316
335
|
self,
|
|
317
336
|
model: BaseChatModel,
|
|
318
337
|
) -> tuple[AIMessage, str | None]:
|
|
338
|
+
terminal_displayed = [""]
|
|
319
339
|
for retry in range(len(_MODEL_RETRY_DELAYS) + 1):
|
|
320
340
|
try:
|
|
321
|
-
return await self._astream_model(self._bind(model), model)
|
|
341
|
+
return await self._astream_model(self._bind(model), model, terminal_displayed)
|
|
322
342
|
except TransientProviderError as error:
|
|
323
343
|
if retry == len(_MODEL_RETRY_DELAYS):
|
|
324
344
|
raise
|
|
@@ -344,6 +364,7 @@ class Session:
|
|
|
344
364
|
self,
|
|
345
365
|
bound_model: Runnable[Any, BaseMessage],
|
|
346
366
|
model: BaseChatModel,
|
|
367
|
+
terminal_displayed: list[str] | None = None,
|
|
347
368
|
) -> tuple[AIMessage, str | None]:
|
|
348
369
|
aggregate: AIMessageChunk | None = None
|
|
349
370
|
response: AIMessage | None = None
|
|
@@ -366,12 +387,14 @@ class Session:
|
|
|
366
387
|
raise TypeError("model stream must return AIMessage chunks")
|
|
367
388
|
text = _message_text(current)
|
|
368
389
|
if tool_call_started and text != shown:
|
|
369
|
-
cell_id = self._update_assistant_stream(
|
|
390
|
+
cell_id = self._update_assistant_stream(
|
|
391
|
+
cell_id, text, shown, terminal_displayed
|
|
392
|
+
)
|
|
370
393
|
shown = text
|
|
371
394
|
completed = self._completed_stream_message(aggregate, response)
|
|
372
395
|
answer = _message_text(completed)
|
|
373
396
|
if answer != shown:
|
|
374
|
-
cell_id = self._update_assistant_stream(cell_id, answer)
|
|
397
|
+
cell_id = self._update_assistant_stream(cell_id, answer, shown, terminal_displayed)
|
|
375
398
|
return completed, cell_id
|
|
376
399
|
except BaseException:
|
|
377
400
|
self._cancel_assistant_stream(cell_id)
|
|
@@ -381,6 +404,7 @@ class Session:
|
|
|
381
404
|
self,
|
|
382
405
|
bound_model: Runnable[Any, BaseMessage],
|
|
383
406
|
model: BaseChatModel,
|
|
407
|
+
terminal_displayed: list[str] | None = None,
|
|
384
408
|
) -> tuple[AIMessage, str | None]:
|
|
385
409
|
aggregate: AIMessageChunk | None = None
|
|
386
410
|
response: AIMessage | None = None
|
|
@@ -403,12 +427,14 @@ class Session:
|
|
|
403
427
|
raise TypeError("model stream must return AIMessage chunks")
|
|
404
428
|
text = _message_text(current)
|
|
405
429
|
if tool_call_started and text != shown:
|
|
406
|
-
cell_id = self._update_assistant_stream(
|
|
430
|
+
cell_id = self._update_assistant_stream(
|
|
431
|
+
cell_id, text, shown, terminal_displayed
|
|
432
|
+
)
|
|
407
433
|
shown = text
|
|
408
434
|
completed = self._completed_stream_message(aggregate, response)
|
|
409
435
|
answer = _message_text(completed)
|
|
410
436
|
if answer != shown:
|
|
411
|
-
cell_id = self._update_assistant_stream(cell_id, answer)
|
|
437
|
+
cell_id = self._update_assistant_stream(cell_id, answer, shown, terminal_displayed)
|
|
412
438
|
return completed, cell_id
|
|
413
439
|
except BaseException:
|
|
414
440
|
self._cancel_assistant_stream(cell_id)
|
|
@@ -30,6 +30,20 @@ def _plain_ansi(value: str) -> str:
|
|
|
30
30
|
return "".join(text for _, text in to_formatted_text(ANSI(value)))
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
def _split_incomplete_ansi(value: str) -> tuple[str, str]:
|
|
34
|
+
last_escape = value.rfind("\x1b")
|
|
35
|
+
if last_escape < 0:
|
|
36
|
+
return value, ""
|
|
37
|
+
tail = value[last_escape:]
|
|
38
|
+
if tail == "\x1b":
|
|
39
|
+
return value[:last_escape], tail
|
|
40
|
+
if tail.startswith("\x1b[") and not any("@" <= char <= "~" for char in tail[2:]):
|
|
41
|
+
return value[:last_escape], tail
|
|
42
|
+
if tail.startswith("\x1b]") and "\x07" not in tail and "\x1b\\" not in tail:
|
|
43
|
+
return value[:last_escape], tail
|
|
44
|
+
return value, ""
|
|
45
|
+
|
|
46
|
+
|
|
33
47
|
def _magic_input(transformed: str) -> tuple[str, str] | None:
|
|
34
48
|
"""Recognize a complete IPython magic call in transformed history."""
|
|
35
49
|
try:
|
|
@@ -220,12 +234,13 @@ class _OutputPreview:
|
|
|
220
234
|
return len(data)
|
|
221
235
|
|
|
222
236
|
def show_notice(self, number: int, error: dict[str, str] | None = None) -> None:
|
|
237
|
+
if self.visible_characters and not self.visible_ends_line:
|
|
238
|
+
sys.stdout.write("\n")
|
|
239
|
+
self.visible_ends_line = True
|
|
223
240
|
if not self.hidden_characters:
|
|
224
241
|
return
|
|
225
242
|
if sys.stdout.isatty():
|
|
226
243
|
sys.stdout.write("\x1b[0m")
|
|
227
|
-
if not self.visible_ends_line:
|
|
228
|
-
sys.stdout.write("\n")
|
|
229
244
|
hidden_lines = self.hidden_line_breaks + int(self.hidden_has_tail)
|
|
230
245
|
label = "line" if hidden_lines == 1 else "lines"
|
|
231
246
|
sys.stdout.write(
|
|
@@ -246,20 +261,12 @@ class _PreviewStream:
|
|
|
246
261
|
self.pending = ""
|
|
247
262
|
|
|
248
263
|
def write(self, data: str) -> int:
|
|
249
|
-
self.pending
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
self.pending = lines.pop()
|
|
253
|
-
else:
|
|
254
|
-
self.pending = ""
|
|
255
|
-
for line in lines:
|
|
256
|
-
self.preview.write(self.stream, _plain_ansi(line))
|
|
264
|
+
complete, self.pending = _split_incomplete_ansi(self.pending + data)
|
|
265
|
+
if complete:
|
|
266
|
+
self.preview.write(self.stream, _plain_ansi(complete))
|
|
257
267
|
return len(data)
|
|
258
268
|
|
|
259
269
|
def flush(self) -> None:
|
|
260
|
-
if self.pending:
|
|
261
|
-
self.preview.write(self.stream, _plain_ansi(self.pending))
|
|
262
|
-
self.pending = ""
|
|
263
270
|
self.stream.flush()
|
|
264
271
|
|
|
265
272
|
def __getattr__(self, name: str):
|
|
@@ -388,6 +395,7 @@ def show_input(shell: TerminalInteractiveShell, cell: str) -> None:
|
|
|
388
395
|
style=style,
|
|
389
396
|
color_depth=shell.color_depth,
|
|
390
397
|
)
|
|
398
|
+
sys.stdout.write("\n")
|
|
391
399
|
|
|
392
400
|
|
|
393
401
|
def install_markdown_renderer(shell: TerminalInteractiveShell) -> Callable[[], None]:
|
|
@@ -399,6 +407,7 @@ def install_markdown_renderer(shell: TerminalInteractiveShell) -> Callable[[], N
|
|
|
399
407
|
previous_renderer = shell.mime_renderers.get(mime, _MISSING)
|
|
400
408
|
|
|
401
409
|
def render(text: str, _metadata: object) -> None:
|
|
410
|
+
sys.stdout.write("\n")
|
|
402
411
|
Console(file=sys.stdout, soft_wrap=True, no_color=shell.colors.lower() == "nocolor").print(
|
|
403
412
|
Markdown(text)
|
|
404
413
|
)
|
|
File without changes
|
{codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/install.json
RENAMED
|
File without changes
|
{codebind-0.6.8 → codebind-0.6.9}/data/share/jupyter/labextensions/codebind-jupyterlab/package.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|