codebind 0.6.17__tar.gz → 0.6.18__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.17 → codebind-0.6.18}/PKG-INFO +2 -2
- {codebind-0.6.17 → codebind-0.6.18}/README.md +1 -1
- {codebind-0.6.17 → codebind-0.6.18}/pyproject.toml +1 -1
- {codebind-0.6.17 → codebind-0.6.18}/pyproject.toml.orig +1 -1
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/conversation.py +21 -2
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/execution.py +15 -6
- codebind-0.6.18/src/codebind/model_text.py +16 -0
- {codebind-0.6.17 → codebind-0.6.18}/LICENSE +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/data/share/jupyter/labextensions/codebind-jupyterlab/install.json +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/data/share/jupyter/labextensions/codebind-jupyterlab/package.json +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/data/share/jupyter/labextensions/codebind-jupyterlab/static/590.98c719a309e23852.js +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/data/share/jupyter/labextensions/codebind-jupyterlab/static/remoteEntry.24258c9500a0850a.js +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/data/share/jupyter/labextensions/codebind-jupyterlab/static/style.js +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/data/share/jupyter/labextensions/codebind-jupyterlab/static/third-party-licenses.json +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/jupyterlab/.yarnrc.yml +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/jupyterlab/install.json +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/jupyterlab/package.json +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/jupyterlab/src/index.ts +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/jupyterlab/tsconfig.json +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/jupyterlab/yarn.lock +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/__init__.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/async_runtime.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/cli.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/configuration.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/context.md +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/context.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/display.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/extension.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/images.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/instructions.md +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/jupyter.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/py.typed +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/session.py +0 -0
- {codebind-0.6.17 → codebind-0.6.18}/src/codebind/terminal.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codebind
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.18
|
|
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
|
|
@@ -80,7 +80,7 @@ Inspect this project and tell me what to implement first.
|
|
|
80
80
|
Codebind keeps its terminal prompt and internal bookkeeping usable when a tool cell leaves an asyncio loop active on the terminal thread. This also supports sync clients that manage their own loop across tool cells, without changing IPython’s normal top-level `await` behavior.
|
|
81
81
|
|
|
82
82
|
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.
|
|
83
|
-
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 previews at most three gray output lines, shortening each line after 80 characters with `...`. Further lines appear as `… N more output lines.`
|
|
83
|
+
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 previews at most three gray output lines, shortening each line after 80 characters with `...`. Further lines appear as `… N more output lines.` Full output remains in IPython history, while model-facing text is bounded and displayed images stay available as image blocks; `%output 8` opens the full text of cell 8 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.
|
|
84
84
|
|
|
85
85
|
## JupyterLab
|
|
86
86
|
|
|
@@ -51,7 +51,7 @@ Inspect this project and tell me what to implement first.
|
|
|
51
51
|
Codebind keeps its terminal prompt and internal bookkeeping usable when a tool cell leaves an asyncio loop active on the terminal thread. This also supports sync clients that manage their own loop across tool cells, without changing IPython’s normal top-level `await` behavior.
|
|
52
52
|
|
|
53
53
|
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.
|
|
54
|
-
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 previews at most three gray output lines, shortening each line after 80 characters with `...`. Further lines appear as `… N more output lines.`
|
|
54
|
+
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 previews at most three gray output lines, shortening each line after 80 characters with `...`. Further lines appear as `… N more output lines.` Full output remains in IPython history, while model-facing text is bounded and displayed images stay available as image blocks; `%output 8` opens the full text of cell 8 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.
|
|
55
55
|
|
|
56
56
|
## JupyterLab
|
|
57
57
|
|
|
@@ -19,6 +19,7 @@ from langchain_core.messages import (
|
|
|
19
19
|
)
|
|
20
20
|
|
|
21
21
|
from .images import PreparedImage, prepare_mime_image
|
|
22
|
+
from .model_text import bounded_text
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
class ConversationStore(Protocol):
|
|
@@ -319,7 +320,19 @@ def _canonical_mime_bundle(
|
|
|
319
320
|
label: str,
|
|
320
321
|
images: list[tuple[str, PreparedImage]] | None,
|
|
321
322
|
) -> dict[str, Any]:
|
|
322
|
-
|
|
323
|
+
def bounded_item(key: str, item: Any) -> Any:
|
|
324
|
+
if key.startswith("text/"):
|
|
325
|
+
if isinstance(item, str):
|
|
326
|
+
return bounded_text(item)
|
|
327
|
+
if isinstance(item, list) and all(isinstance(part, str) for part in item):
|
|
328
|
+
return bounded_text("".join(item))
|
|
329
|
+
return deepcopy(item)
|
|
330
|
+
|
|
331
|
+
data = {
|
|
332
|
+
key: bounded_item(key, item)
|
|
333
|
+
for key, item in value.items()
|
|
334
|
+
if not key.startswith("image/")
|
|
335
|
+
}
|
|
323
336
|
try:
|
|
324
337
|
image = prepare_mime_image(value)
|
|
325
338
|
except ValueError as error:
|
|
@@ -370,7 +383,7 @@ def _normalize_notebook(
|
|
|
370
383
|
):
|
|
371
384
|
raise ValueError("Invalid Codebind notebook cell")
|
|
372
385
|
identifiers.add(identifier)
|
|
373
|
-
cell: dict[str, Any] = {"id": identifier, "type": cell_type, "source": source}
|
|
386
|
+
cell: dict[str, Any] = {"id": identifier, "type": cell_type, "source": bounded_text(source)}
|
|
374
387
|
if cell_type == "code":
|
|
375
388
|
execution_count = raw_cell.get("execution_count")
|
|
376
389
|
outputs = raw_cell.get("outputs", [])
|
|
@@ -384,6 +397,12 @@ def _normalize_notebook(
|
|
|
384
397
|
if not isinstance(output, dict):
|
|
385
398
|
raise ValueError("Invalid Codebind cell output")
|
|
386
399
|
normalized = deepcopy(output)
|
|
400
|
+
if output.get("output_type") == "stream":
|
|
401
|
+
text = output.get("text")
|
|
402
|
+
if isinstance(text, str):
|
|
403
|
+
normalized["text"] = bounded_text(text)
|
|
404
|
+
elif isinstance(text, list) and all(isinstance(part, str) for part in text):
|
|
405
|
+
normalized["text"] = bounded_text("".join(text))
|
|
387
406
|
data = output.get("data")
|
|
388
407
|
if isinstance(data, dict):
|
|
389
408
|
normalized["data"] = _canonical_mime_bundle(
|
|
@@ -16,6 +16,7 @@ from IPython.utils.capture import capture_output
|
|
|
16
16
|
|
|
17
17
|
from .display import display_cell
|
|
18
18
|
from .images import PreparedImage, prepare_mime_image
|
|
19
|
+
from .model_text import bounded_text
|
|
19
20
|
from .jupyter import JupyterLabBridge
|
|
20
21
|
from .terminal import preview_tool_output, record_tool_output, show_input
|
|
21
22
|
|
|
@@ -388,9 +389,10 @@ class IPythonExecutor:
|
|
|
388
389
|
displays: list[str] = []
|
|
389
390
|
images: list[PreparedImage] = []
|
|
390
391
|
omissions: list[str] = []
|
|
392
|
+
seen_images: set[str] = set()
|
|
391
393
|
for index, data in enumerate(bundles):
|
|
392
394
|
if isinstance(data, dict) and "text/plain" in data:
|
|
393
|
-
displays.append(str(data["text/plain"]))
|
|
395
|
+
displays.append(bounded_text(str(data["text/plain"])))
|
|
394
396
|
if not isinstance(data, dict):
|
|
395
397
|
continue
|
|
396
398
|
if len(images) >= _MAXIMUM_IMAGES_PER_REPORT:
|
|
@@ -402,20 +404,27 @@ class IPythonExecutor:
|
|
|
402
404
|
except ValueError as error:
|
|
403
405
|
omissions.append(f"Display {index + 1}: {error}")
|
|
404
406
|
else:
|
|
405
|
-
if image is not None:
|
|
407
|
+
if image is not None and image.source_sha256 not in seen_images:
|
|
406
408
|
images.append(image)
|
|
409
|
+
seen_images.add(image.source_sha256)
|
|
407
410
|
|
|
408
411
|
exception = result.error_before_exec or result.error_in_exec
|
|
409
412
|
error = (
|
|
410
|
-
{"type": type(exception).__name__, "message": str(exception)}
|
|
413
|
+
{"type": type(exception).__name__, "message": bounded_text(str(exception))}
|
|
411
414
|
if exception is not None
|
|
412
415
|
else None
|
|
413
416
|
)
|
|
417
|
+
value = result.result
|
|
418
|
+
rendered_result = (
|
|
419
|
+
f"<{type(value).__name__}: {len(value):,} bytes>"
|
|
420
|
+
if isinstance(value, (bytes, bytearray, memoryview))
|
|
421
|
+
else bounded_text(repr(value)) if value is not None else None
|
|
422
|
+
)
|
|
414
423
|
return ExecutionReport(
|
|
415
424
|
ok=result.success,
|
|
416
|
-
stdout=stdout,
|
|
417
|
-
stderr=stderr,
|
|
418
|
-
result=
|
|
425
|
+
stdout=bounded_text(stdout),
|
|
426
|
+
stderr=bounded_text(stderr),
|
|
427
|
+
result=rendered_result,
|
|
419
428
|
displays=tuple(displays),
|
|
420
429
|
error=error,
|
|
421
430
|
images=tuple(images),
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Keep model-facing text bounded without changing native IPython history."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
MAXIMUM_MODEL_TEXT_CHARACTERS = 16_384
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def bounded_text(value: str, limit: int = MAXIMUM_MODEL_TEXT_CHARACTERS) -> str:
|
|
9
|
+
"""Keep useful head and tail text; do not send enormous binary representations."""
|
|
10
|
+
if len(value) <= limit:
|
|
11
|
+
return value
|
|
12
|
+
if value.startswith(("b'", 'b"', "bytearray(b'", 'bytearray(b"')):
|
|
13
|
+
return f"<binary representation omitted: {len(value):,} characters>"
|
|
14
|
+
head, tail = limit // 2, limit // 4
|
|
15
|
+
omitted = len(value) - head - tail
|
|
16
|
+
return value[:head] + f"\n… {omitted:,} characters omitted …\n" + value[-tail:]
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|