zrb 1.15.21__py3-none-any.whl → 1.15.22__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.
- zrb/input/text_input.py +7 -20
- zrb/task/llm/tool_wrapper.py +84 -38
- zrb/util/cli/text.py +28 -0
- {zrb-1.15.21.dist-info → zrb-1.15.22.dist-info}/METADATA +1 -1
- {zrb-1.15.21.dist-info → zrb-1.15.22.dist-info}/RECORD +7 -6
- {zrb-1.15.21.dist-info → zrb-1.15.22.dist-info}/WHEEL +0 -0
- {zrb-1.15.21.dist-info → zrb-1.15.22.dist-info}/entry_points.txt +0 -0
zrb/input/text_input.py
CHANGED
@@ -6,6 +6,7 @@ from collections.abc import Callable
|
|
6
6
|
from zrb.config.config import CFG
|
7
7
|
from zrb.context.any_shared_context import AnySharedContext
|
8
8
|
from zrb.input.base_input import BaseInput
|
9
|
+
from zrb.util.cli.text import edit_text
|
9
10
|
from zrb.util.file import read_file
|
10
11
|
|
11
12
|
|
@@ -85,24 +86,10 @@ class TextInput(BaseInput):
|
|
85
86
|
comment_prompt_message = (
|
86
87
|
f"{self.comment_start}{prompt_message}{self.comment_end}"
|
87
88
|
)
|
88
|
-
comment_prompt_message_eol = f"{comment_prompt_message}\n"
|
89
89
|
default_value = self.get_default_str(shared_ctx)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
if default_value:
|
97
|
-
temp_file.write(default_value.encode())
|
98
|
-
temp_file.flush()
|
99
|
-
subprocess.call([self.editor_cmd, temp_file_name])
|
100
|
-
# Read the edited content
|
101
|
-
edited_content = read_file(temp_file_name)
|
102
|
-
parts = [
|
103
|
-
text.strip() for text in edited_content.split(comment_prompt_message, 1)
|
104
|
-
]
|
105
|
-
edited_content = "\n".join(parts).lstrip()
|
106
|
-
os.remove(temp_file_name)
|
107
|
-
print(f"{prompt_message}: {edited_content}")
|
108
|
-
return edited_content
|
90
|
+
return edit_text(
|
91
|
+
prompt_message=comment_prompt_message,
|
92
|
+
value=default_value,
|
93
|
+
editor=self.editor_cmd,
|
94
|
+
extension=self._extension,
|
95
|
+
)
|
zrb/task/llm/tool_wrapper.py
CHANGED
@@ -5,6 +5,7 @@ import typing
|
|
5
5
|
from collections.abc import Callable
|
6
6
|
from typing import TYPE_CHECKING, Any
|
7
7
|
|
8
|
+
from zrb.config.config import CFG
|
8
9
|
from zrb.context.any_context import AnyContext
|
9
10
|
from zrb.task.llm.error import ToolExecutionError
|
10
11
|
from zrb.util.callable import get_callable_name
|
@@ -15,6 +16,7 @@ from zrb.util.cli.style import (
|
|
15
16
|
stylize_green,
|
16
17
|
stylize_yellow,
|
17
18
|
)
|
19
|
+
from zrb.util.cli.text import edit_text
|
18
20
|
from zrb.util.run import run_async
|
19
21
|
from zrb.util.string.conversion import to_boolean
|
20
22
|
|
@@ -39,7 +41,6 @@ def wrap_tool(func: Callable, ctx: AnyContext, yolo_mode: bool | list[str]) -> "
|
|
39
41
|
def wrap_func(func: Callable, ctx: AnyContext, yolo_mode: bool | list[str]) -> Callable:
|
40
42
|
original_sig = inspect.signature(func)
|
41
43
|
needs_any_context_for_injection = _has_context_parameter(original_sig, AnyContext)
|
42
|
-
takes_no_args = len(original_sig.parameters) == 0
|
43
44
|
# Pass individual flags to the wrapper creator
|
44
45
|
wrapper = _create_wrapper(
|
45
46
|
func=func,
|
@@ -48,7 +49,7 @@ def wrap_func(func: Callable, ctx: AnyContext, yolo_mode: bool | list[str]) -> C
|
|
48
49
|
needs_any_context_for_injection=needs_any_context_for_injection,
|
49
50
|
yolo_mode=yolo_mode,
|
50
51
|
)
|
51
|
-
_adjust_signature(wrapper, original_sig
|
52
|
+
_adjust_signature(wrapper, original_sig)
|
52
53
|
return wrapper
|
53
54
|
|
54
55
|
|
@@ -113,7 +114,9 @@ def _create_wrapper(
|
|
113
114
|
if (
|
114
115
|
isinstance(yolo_mode, list) and func.__name__ not in yolo_mode
|
115
116
|
) or not yolo_mode:
|
116
|
-
approval, reason = await
|
117
|
+
approval, reason = await _handle_user_response(
|
118
|
+
ctx, func, args, kwargs
|
119
|
+
)
|
117
120
|
if not approval:
|
118
121
|
raise ToolExecutionCancelled(f"User disapproving: {reason}")
|
119
122
|
return await run_async(func(*args, **kwargs))
|
@@ -131,54 +134,97 @@ def _create_wrapper(
|
|
131
134
|
return wrapper
|
132
135
|
|
133
136
|
|
134
|
-
async def
|
135
|
-
ctx: AnyContext,
|
137
|
+
async def _handle_user_response(
|
138
|
+
ctx: AnyContext,
|
139
|
+
func: Callable,
|
140
|
+
args: list[Any] | tuple[Any],
|
141
|
+
kwargs: dict[str, Any],
|
136
142
|
) -> tuple[bool, str]:
|
137
|
-
func_call_str = _get_func_call_str(func, args, kwargs)
|
138
|
-
complete_confirmation_message = "\n".join(
|
139
|
-
[
|
140
|
-
f"\n🎰 >> {func_call_str}",
|
141
|
-
_get_detail_func_param(args, kwargs),
|
142
|
-
f"🎰 >> {_get_run_func_confirmation(func)}",
|
143
|
-
]
|
144
|
-
)
|
145
143
|
while True:
|
144
|
+
func_call_str = _get_func_call_str(func, args, kwargs)
|
145
|
+
complete_confirmation_message = "\n".join(
|
146
|
+
[
|
147
|
+
f"\n🎰 >> {func_call_str}",
|
148
|
+
_get_detail_func_param(args, kwargs),
|
149
|
+
f"🎰 >> {_get_run_func_confirmation(func)}",
|
150
|
+
]
|
151
|
+
)
|
146
152
|
ctx.print(complete_confirmation_message, plain=True)
|
147
|
-
|
153
|
+
user_response = await _read_line()
|
148
154
|
ctx.print("", plain=True)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
155
|
+
new_kwargs, is_edited = _get_edited_kwargs(ctx, user_response, kwargs)
|
156
|
+
if is_edited:
|
157
|
+
kwargs = new_kwargs
|
158
|
+
continue
|
159
|
+
approval_and_reason = _get_user_approval_and_reason(
|
160
|
+
ctx, user_response, func_call_str
|
161
|
+
)
|
162
|
+
if approval_and_reason is None:
|
163
|
+
continue
|
164
|
+
return approval_and_reason
|
165
|
+
|
166
|
+
|
167
|
+
def _get_edited_kwargs(
|
168
|
+
cx: AnyContext, user_response: str, kwargs: dict[str, Any]
|
169
|
+
) -> tuple[dict[str, Any], bool]:
|
170
|
+
user_edit_responses = [val for val in user_response.split(" ", maxsplit=2)]
|
171
|
+
if len(user_edit_responses) >= 1 and user_edit_responses[0].lower() != "edit":
|
172
|
+
return kwargs, False
|
173
|
+
while len(user_edit_responses) < 3:
|
174
|
+
user_edit_responses.append("")
|
175
|
+
key, val = user_edit_responses[1:]
|
176
|
+
if key not in kwargs:
|
177
|
+
return kwargs, True
|
178
|
+
if val != "":
|
179
|
+
kwargs[key] = val
|
180
|
+
return kwargs, True
|
181
|
+
val = edit_text(
|
182
|
+
prompt_message=f"// {key}",
|
183
|
+
value=kwargs.get(key, ""),
|
184
|
+
editor=CFG.DEFAULT_EDITOR,
|
185
|
+
)
|
186
|
+
kwargs[key] = val
|
187
|
+
return kwargs, True
|
188
|
+
|
189
|
+
|
190
|
+
def _get_user_approval_and_reason(
|
191
|
+
ctx: AnyContext, user_response: str, func_call_str: str
|
192
|
+
) -> tuple[bool, str] | None:
|
193
|
+
user_approval_responses = [
|
194
|
+
val.strip() for val in user_response.split(",", maxsplit=1)
|
195
|
+
]
|
196
|
+
while len(user_approval_responses) < 2:
|
197
|
+
user_approval_responses.append("")
|
198
|
+
approval_str, reason = user_approval_responses
|
199
|
+
try:
|
200
|
+
approved = True if approval_str.strip() == "" else to_boolean(approval_str)
|
201
|
+
if not approved and reason == "":
|
165
202
|
ctx.print(
|
166
203
|
stylize_error(
|
167
|
-
f"
|
204
|
+
f"You must specify rejection reason (i.e., No, <why>) for {func_call_str}" # noqa
|
168
205
|
),
|
169
206
|
plain=True,
|
170
207
|
)
|
171
|
-
|
208
|
+
return None
|
209
|
+
return approved, reason
|
210
|
+
except Exception:
|
211
|
+
ctx.print(
|
212
|
+
stylize_error(
|
213
|
+
f"Invalid approval value for {func_call_str}: {approval_str}"
|
214
|
+
),
|
215
|
+
plain=True,
|
216
|
+
)
|
217
|
+
return None
|
172
218
|
|
173
219
|
|
174
220
|
def _get_run_func_confirmation(func: Callable) -> str:
|
175
221
|
func_name = get_callable_name(func)
|
176
222
|
return render_markdown(
|
177
|
-
f"Allow to run `{func_name}`? (✅ `Yes` | ⛔ `No, <reason>`)"
|
223
|
+
f"Allow to run `{func_name}`? (✅ `Yes` | ⛔ `No, <reason>` | ✏️ `Edit <param> <value>`)"
|
178
224
|
).strip()
|
179
225
|
|
180
226
|
|
181
|
-
def _get_detail_func_param(args: list[Any], kwargs: dict[str, Any]) -> str:
|
227
|
+
def _get_detail_func_param(args: list[Any] | tuple[Any], kwargs: dict[str, Any]) -> str:
|
182
228
|
markdown = "\n".join(
|
183
229
|
[_get_func_param_item(key, val) for key, val in kwargs.items()]
|
184
230
|
)
|
@@ -198,7 +244,9 @@ def _get_func_param_item(key: str, val: Any) -> str:
|
|
198
244
|
return "\n".join(lines)
|
199
245
|
|
200
246
|
|
201
|
-
def _get_func_call_str(
|
247
|
+
def _get_func_call_str(
|
248
|
+
func: Callable, args: list[Any] | tuple[Any], kwargs: dict[str, Any]
|
249
|
+
) -> str:
|
202
250
|
func_name = get_callable_name(func)
|
203
251
|
normalized_args = [stylize_green(_truncate_arg(arg)) for arg in args]
|
204
252
|
normalized_kwargs = []
|
@@ -225,9 +273,7 @@ async def _read_line():
|
|
225
273
|
return await reader.prompt_async()
|
226
274
|
|
227
275
|
|
228
|
-
def _adjust_signature(
|
229
|
-
wrapper: Callable, original_sig: inspect.Signature, takes_no_args: bool
|
230
|
-
):
|
276
|
+
def _adjust_signature(wrapper: Callable, original_sig: inspect.Signature):
|
231
277
|
"""Adjusts the wrapper function's signature for schema generation."""
|
232
278
|
# The wrapper's signature should represent the arguments the *LLM* needs to provide.
|
233
279
|
# The LLM does not provide RunContext (pydantic-ai injects it) or AnyContext
|
zrb/util/cli/text.py
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
import os
|
2
|
+
import subprocess
|
3
|
+
import tempfile
|
4
|
+
|
5
|
+
from zrb.util.file import read_file
|
6
|
+
|
7
|
+
|
8
|
+
def edit_text(
|
9
|
+
prompt_message: str,
|
10
|
+
value: str,
|
11
|
+
editor: str = "vi",
|
12
|
+
extension: str = ".txt",
|
13
|
+
) -> str:
|
14
|
+
prompt_message_eol = f"{prompt_message}\n"
|
15
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=extension) as temp_file:
|
16
|
+
temp_file_name = temp_file.name
|
17
|
+
temp_file.write(prompt_message_eol.encode())
|
18
|
+
# Pre-fill with default content
|
19
|
+
if value:
|
20
|
+
temp_file.write(value.encode())
|
21
|
+
temp_file.flush()
|
22
|
+
subprocess.call([editor, temp_file_name])
|
23
|
+
# Read the edited content
|
24
|
+
edited_content = read_file(temp_file_name)
|
25
|
+
parts = [text.strip() for text in edited_content.split(prompt_message, 1)]
|
26
|
+
edited_content = "\n".join(parts).lstrip()
|
27
|
+
os.remove(temp_file_name)
|
28
|
+
return edited_content
|
@@ -257,7 +257,7 @@ zrb/input/int_input.py,sha256=UhxCFYlZdJcgUSGGEkz301zOgRVpK0KDG_IxxWpQfMU,1457
|
|
257
257
|
zrb/input/option_input.py,sha256=TQB82ko5odgzkULEizBZi0e9TIHEbIgvdP0AR3RhA74,2135
|
258
258
|
zrb/input/password_input.py,sha256=szBojWxSP9QJecgsgA87OIYwQrY2AQ3USIKdDZY6snU,1465
|
259
259
|
zrb/input/str_input.py,sha256=NevZHX9rf1g8eMatPyy-kUX3DglrVAQpzvVpKAzf7bA,81
|
260
|
-
zrb/input/text_input.py,sha256=
|
260
|
+
zrb/input/text_input.py,sha256=NRM9FSS2pUFs7_R0KsBlu_CD8WLxbfbwxRkpaRoeCSY,3049
|
261
261
|
zrb/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
262
262
|
zrb/runner/cli.py,sha256=E5GGNJjCOBpFbhgnMM_iE1TVYhxMNDJKA6WxCRETTmA,6951
|
263
263
|
zrb/runner/common_util.py,sha256=yIJm9ivM7hvJ4Kb4Nt5RRE7oqAlt9EN89w6JDGyLkFE,1570
|
@@ -358,7 +358,7 @@ zrb/task/llm/history_summarization.py,sha256=UIT8bpdT3hy1xn559waDLFWZlNtIqdIpIvR
|
|
358
358
|
zrb/task/llm/history_summarization_tool.py,sha256=Wazi4WMr3k1WJ1v7QgjAPbuY1JdBpHUsTWGt3DSTsLc,1706
|
359
359
|
zrb/task/llm/print_node.py,sha256=TG8i3MrAkIj3cLkU9_fSX-u49jlTdU8t9FpHGI_VtoM,8077
|
360
360
|
zrb/task/llm/prompt.py,sha256=FGXWYHecWtrNNkPnjg-uhnkqp7fYt8V91-AjFM_5fpA,11550
|
361
|
-
zrb/task/llm/tool_wrapper.py,sha256=
|
361
|
+
zrb/task/llm/tool_wrapper.py,sha256=v3y4FO14xStpq9K0lA3GIVv6-3dbq85I7xZqdtG-j9U,10243
|
362
362
|
zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
|
363
363
|
zrb/task/llm_task.py,sha256=OxJ9QpqjEyeOI1_zqzNZHtQlRHi0ANOvL9FYaWLzO3Y,14913
|
364
364
|
zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
|
@@ -376,6 +376,7 @@ zrb/util/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
376
376
|
zrb/util/cli/markdown.py,sha256=Uhuw8XR-jAG9AG3oNK8VHJpYOdU40Q_8yVN74uu0RJ8,384
|
377
377
|
zrb/util/cli/style.py,sha256=D_548KG1gXEirQGdkAVTc81vBdCeInXtnG1gV1yabBA,6655
|
378
378
|
zrb/util/cli/subcommand.py,sha256=umTZIlrL-9g-qc_eRRgdaQgK-whvXK1roFfvnbuY7NQ,1753
|
379
|
+
zrb/util/cli/text.py,sha256=6r1NqvtjKXt-XVVURyBqYE9tZA2Bnr6u8h9Lopr-Gag,870
|
379
380
|
zrb/util/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
380
381
|
zrb/util/cmd/command.py,sha256=WpEMWVL9hBsxptvDHmRR93_cJ2zP05BJ2h9-tP93M1Y,7473
|
381
382
|
zrb/util/cmd/remote.py,sha256=NGQq2_IrUMDoZz3qmcgtnNYVGjMHaBKQpZxImf0yfXA,1296
|
@@ -409,7 +410,7 @@ zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
|
|
409
410
|
zrb/util/truncate.py,sha256=eSzmjBpc1Qod3lM3M73snNbDOcARHukW_tq36dWdPvc,921
|
410
411
|
zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
411
412
|
zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
|
412
|
-
zrb-1.15.
|
413
|
-
zrb-1.15.
|
414
|
-
zrb-1.15.
|
415
|
-
zrb-1.15.
|
413
|
+
zrb-1.15.22.dist-info/METADATA,sha256=OHahYYqF0_2Z_Ht40qggq5Z538ITZNVtaC-UagYam6o,9892
|
414
|
+
zrb-1.15.22.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
415
|
+
zrb-1.15.22.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
|
416
|
+
zrb-1.15.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|