codebind 0.1.0__tar.gz → 0.1.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.
- {codebind-0.1.0 → codebind-0.1.2}/PKG-INFO +1 -1
- {codebind-0.1.0 → codebind-0.1.2}/pyproject.toml +1 -1
- {codebind-0.1.0 → codebind-0.1.2}/pyproject.toml.orig +1 -1
- {codebind-0.1.0 → codebind-0.1.2}/src/codebind/execution.py +11 -4
- codebind-0.1.2/src/codebind/prompts.py +65 -0
- {codebind-0.1.0 → codebind-0.1.2}/src/codebind/rendering.py +6 -3
- {codebind-0.1.0 → codebind-0.1.2}/src/codebind/session.py +5 -12
- {codebind-0.1.0 → codebind-0.1.2}/LICENSE +0 -0
- {codebind-0.1.0 → codebind-0.1.2}/README.md +0 -0
- {codebind-0.1.0 → codebind-0.1.2}/src/codebind/__init__.py +0 -0
- {codebind-0.1.0 → codebind-0.1.2}/src/codebind/cli.py +0 -0
- {codebind-0.1.0 → codebind-0.1.2}/src/codebind/py.typed +0 -0
|
@@ -8,6 +8,8 @@ from typing import Any
|
|
|
8
8
|
from IPython.core.interactiveshell import InteractiveShell
|
|
9
9
|
from IPython.utils.capture import capture_output
|
|
10
10
|
|
|
11
|
+
from .prompts import CellPrompts
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
@dataclass(frozen=True, slots=True)
|
|
13
15
|
class ExecutionReport:
|
|
@@ -31,19 +33,24 @@ class IPythonExecutor:
|
|
|
31
33
|
def __init__(self, shell: InteractiveShell) -> None:
|
|
32
34
|
self.shell = shell
|
|
33
35
|
|
|
34
|
-
def execute(self, cell: str) -> ExecutionReport:
|
|
36
|
+
def execute(self, cell: str, *, prompts: CellPrompts | None = None) -> ExecutionReport:
|
|
35
37
|
"""Execute a cell, replay its visible output, and capture a structured result."""
|
|
36
38
|
if not isinstance(cell, str) or not cell.strip():
|
|
37
39
|
raise ValueError("cell must be a non-empty string")
|
|
38
40
|
|
|
41
|
+
original_prompts = getattr(self.shell, "prompts", None)
|
|
39
42
|
displayhook = self.shell.displayhook
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
original_output_prompt = displayhook.write_output_prompt
|
|
44
|
+
if prompts is not None and original_prompts is not None:
|
|
45
|
+
self.shell.prompts = prompts
|
|
46
|
+
displayhook.write_output_prompt = lambda: None
|
|
42
47
|
try:
|
|
43
48
|
with capture_output() as captured:
|
|
44
49
|
result = self.shell.run_cell(cell, store_history=False)
|
|
45
50
|
finally:
|
|
46
|
-
|
|
51
|
+
if prompts is not None and original_prompts is not None:
|
|
52
|
+
self.shell.prompts = original_prompts
|
|
53
|
+
displayhook.write_output_prompt = original_output_prompt
|
|
47
54
|
|
|
48
55
|
displays: list[str] = []
|
|
49
56
|
for output in captured.outputs:
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Native IPython prompt rendering for model-authored cells."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from IPython.terminal.prompts import Prompts
|
|
8
|
+
from IPython.terminal.ptutils import IPythonPTLexer
|
|
9
|
+
from prompt_toolkit.document import Document
|
|
10
|
+
from prompt_toolkit.formatted_text import FormattedText, PygmentsTokens
|
|
11
|
+
from prompt_toolkit.shortcuts import print_formatted_text
|
|
12
|
+
from pygments.token import Token
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class CellPrompts(Prompts):
|
|
16
|
+
"""IPython prompts for one numbered model-authored cell."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, shell: object, number: int) -> None:
|
|
19
|
+
super().__init__(shell)
|
|
20
|
+
self.number = number
|
|
21
|
+
|
|
22
|
+
def in_prompt_tokens(self):
|
|
23
|
+
return [
|
|
24
|
+
(Token.Prompt, "Python ["),
|
|
25
|
+
(Token.PromptNum, str(self.number)),
|
|
26
|
+
(Token.Prompt, "]: "),
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
def continuation_prompt_tokens(self, width: int | None = None, **_: object):
|
|
30
|
+
width = self._width() if width is None else width
|
|
31
|
+
return [
|
|
32
|
+
(Token.Prompt.Continuation, (" " * (width - 5)) + "...:"),
|
|
33
|
+
(Token.Prompt.Padding, " "),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
def out_prompt_tokens(self):
|
|
37
|
+
return [
|
|
38
|
+
(Token.OutPrompt, "Shell ["),
|
|
39
|
+
(Token.OutPromptNum, str(self.number)),
|
|
40
|
+
(Token.OutPrompt, "]: "),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def render_cell(shell: object, cell: str, prompts: CellPrompts) -> None:
|
|
45
|
+
"""Render a cell with IPython's lexer, prompt tokens, and terminal style."""
|
|
46
|
+
lexer = IPythonPTLexer().lex_document(Document(cell))
|
|
47
|
+
pt_app = getattr(shell, "pt_app", None)
|
|
48
|
+
style = pt_app.app.style if pt_app is not None else None
|
|
49
|
+
sys.stdout.write(getattr(shell, "separate_in", "\n"))
|
|
50
|
+
|
|
51
|
+
for index, _line in enumerate(cell.split("\n")):
|
|
52
|
+
prompt_tokens = (
|
|
53
|
+
prompts.in_prompt_tokens()
|
|
54
|
+
if index == 0
|
|
55
|
+
else prompts.continuation_prompt_tokens(lineno=index - 1)
|
|
56
|
+
)
|
|
57
|
+
print_formatted_text(PygmentsTokens(prompt_tokens), style=style, end="")
|
|
58
|
+
print_formatted_text(FormattedText(lexer(index)), style=style)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def render_output_prompt(shell: object, prompts: CellPrompts) -> None:
|
|
62
|
+
"""Render an output prompt with IPython's terminal style."""
|
|
63
|
+
pt_app = getattr(shell, "pt_app", None)
|
|
64
|
+
style = pt_app.app.style if pt_app is not None else None
|
|
65
|
+
print_formatted_text(PygmentsTokens(prompts.out_prompt_tokens()), style=style, end="")
|
|
@@ -10,9 +10,10 @@ from rich.segment import Segments
|
|
|
10
10
|
from rich.text import Text
|
|
11
11
|
|
|
12
12
|
from .execution import ExecutionReport
|
|
13
|
+
from .prompts import CellPrompts, render_output_prompt
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
_MAXIMUM_OUTPUT_LINES = 10
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def _visible_output(report: ExecutionReport) -> str:
|
|
@@ -32,7 +33,7 @@ class TerminalRenderer:
|
|
|
32
33
|
def __init__(self) -> None:
|
|
33
34
|
self.console = Console()
|
|
34
35
|
|
|
35
|
-
def tool_output(self, report: ExecutionReport) -> None:
|
|
36
|
+
def tool_output(self, report: ExecutionReport, shell: object, prompts: CellPrompts) -> None:
|
|
36
37
|
"""Show a bounded preview while leaving the report itself intact for the model."""
|
|
37
38
|
visible = _visible_output(report)
|
|
38
39
|
if not visible:
|
|
@@ -40,7 +41,9 @@ class TerminalRenderer:
|
|
|
40
41
|
output = Text.from_ansi(visible)
|
|
41
42
|
options = self.console.options
|
|
42
43
|
complete = self.console.render_lines(output, options, pad=False, new_lines=True)
|
|
43
|
-
preview = complete[:
|
|
44
|
+
preview = complete[:_MAXIMUM_OUTPUT_LINES]
|
|
45
|
+
if report.ok:
|
|
46
|
+
render_output_prompt(shell, prompts)
|
|
44
47
|
self.console.print(Segments(chain.from_iterable(preview)), end="")
|
|
45
48
|
if len(complete) > len(preview):
|
|
46
49
|
self.console.print(
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
import sys
|
|
7
6
|
from collections.abc import Mapping, Sequence
|
|
8
7
|
from typing import Any
|
|
9
8
|
|
|
@@ -14,6 +13,7 @@ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, System
|
|
|
14
13
|
from langchain_core.runnables import Runnable
|
|
15
14
|
|
|
16
15
|
from .execution import ExecutionReport, IPythonExecutor
|
|
16
|
+
from .prompts import CellPrompts, render_cell
|
|
17
17
|
from .rendering import TerminalRenderer
|
|
18
18
|
|
|
19
19
|
|
|
@@ -134,18 +134,11 @@ class Session:
|
|
|
134
134
|
return _tool_error("InvalidArguments", "ipython requires a string cell argument")
|
|
135
135
|
|
|
136
136
|
cell = arguments["cell"]
|
|
137
|
-
self.
|
|
137
|
+
prompts = CellPrompts(self.shell, self.shell.execution_count - 1)
|
|
138
|
+
render_cell(self.shell, cell, prompts)
|
|
138
139
|
try:
|
|
139
|
-
report = self.executor.execute(cell)
|
|
140
|
+
report = self.executor.execute(cell, prompts=prompts)
|
|
140
141
|
except Exception as error: # The failure must be returned to the model, not end the session.
|
|
141
142
|
report = _tool_error(type(error).__name__, str(error))
|
|
142
|
-
self.renderer.tool_output(report)
|
|
143
|
+
self.renderer.tool_output(report, self.shell, prompts)
|
|
143
144
|
return report
|
|
144
|
-
|
|
145
|
-
def _show_call(self, cell: str) -> None:
|
|
146
|
-
highlighted = self.shell.pycolorize(cell.rstrip())
|
|
147
|
-
sys.stdout.write("\n")
|
|
148
|
-
sys.stdout.write(highlighted)
|
|
149
|
-
if not highlighted.endswith("\n"):
|
|
150
|
-
sys.stdout.write("\n")
|
|
151
|
-
sys.stdout.flush()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|