zrb 1.21.29__py3-none-any.whl → 2.0.0a4__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.

Potentially problematic release.


This version of zrb might be problematic. Click here for more details.

Files changed (192) hide show
  1. zrb/__init__.py +118 -129
  2. zrb/builtin/__init__.py +54 -2
  3. zrb/builtin/llm/chat.py +147 -0
  4. zrb/callback/callback.py +8 -1
  5. zrb/cmd/cmd_result.py +2 -1
  6. zrb/config/config.py +491 -280
  7. zrb/config/helper.py +84 -0
  8. zrb/config/web_auth_config.py +50 -35
  9. zrb/context/any_shared_context.py +13 -2
  10. zrb/context/context.py +31 -3
  11. zrb/context/print_fn.py +13 -0
  12. zrb/context/shared_context.py +14 -1
  13. zrb/input/option_input.py +30 -2
  14. zrb/llm/agent/__init__.py +9 -0
  15. zrb/llm/agent/agent.py +215 -0
  16. zrb/llm/agent/summarizer.py +20 -0
  17. zrb/llm/app/__init__.py +10 -0
  18. zrb/llm/app/completion.py +281 -0
  19. zrb/llm/app/confirmation/allow_tool.py +66 -0
  20. zrb/llm/app/confirmation/handler.py +178 -0
  21. zrb/llm/app/confirmation/replace_confirmation.py +77 -0
  22. zrb/llm/app/keybinding.py +34 -0
  23. zrb/llm/app/layout.py +117 -0
  24. zrb/llm/app/lexer.py +155 -0
  25. zrb/llm/app/redirection.py +28 -0
  26. zrb/llm/app/style.py +16 -0
  27. zrb/llm/app/ui.py +733 -0
  28. zrb/llm/config/__init__.py +4 -0
  29. zrb/llm/config/config.py +122 -0
  30. zrb/llm/config/limiter.py +247 -0
  31. zrb/llm/history_manager/__init__.py +4 -0
  32. zrb/llm/history_manager/any_history_manager.py +23 -0
  33. zrb/llm/history_manager/file_history_manager.py +91 -0
  34. zrb/llm/history_processor/summarizer.py +108 -0
  35. zrb/llm/note/__init__.py +3 -0
  36. zrb/llm/note/manager.py +122 -0
  37. zrb/llm/prompt/__init__.py +29 -0
  38. zrb/llm/prompt/claude_compatibility.py +92 -0
  39. zrb/llm/prompt/compose.py +55 -0
  40. zrb/llm/prompt/default.py +51 -0
  41. zrb/llm/prompt/markdown/mandate.md +23 -0
  42. zrb/llm/prompt/markdown/persona.md +3 -0
  43. zrb/llm/prompt/markdown/summarizer.md +21 -0
  44. zrb/llm/prompt/note.py +41 -0
  45. zrb/llm/prompt/system_context.py +46 -0
  46. zrb/llm/prompt/zrb.py +41 -0
  47. zrb/llm/skill/__init__.py +3 -0
  48. zrb/llm/skill/manager.py +86 -0
  49. zrb/llm/task/__init__.py +4 -0
  50. zrb/llm/task/llm_chat_task.py +316 -0
  51. zrb/llm/task/llm_task.py +245 -0
  52. zrb/llm/tool/__init__.py +39 -0
  53. zrb/llm/tool/bash.py +75 -0
  54. zrb/llm/tool/code.py +266 -0
  55. zrb/llm/tool/file.py +419 -0
  56. zrb/llm/tool/note.py +70 -0
  57. zrb/{builtin/llm → llm}/tool/rag.py +8 -5
  58. zrb/llm/tool/search/brave.py +53 -0
  59. zrb/llm/tool/search/searxng.py +47 -0
  60. zrb/llm/tool/search/serpapi.py +47 -0
  61. zrb/llm/tool/skill.py +19 -0
  62. zrb/llm/tool/sub_agent.py +70 -0
  63. zrb/llm/tool/web.py +97 -0
  64. zrb/llm/tool/zrb_task.py +66 -0
  65. zrb/llm/util/attachment.py +101 -0
  66. zrb/llm/util/prompt.py +104 -0
  67. zrb/llm/util/stream_response.py +178 -0
  68. zrb/session/any_session.py +0 -3
  69. zrb/session/session.py +1 -1
  70. zrb/task/base/context.py +25 -13
  71. zrb/task/base/execution.py +52 -47
  72. zrb/task/base/lifecycle.py +7 -4
  73. zrb/task/base_task.py +48 -49
  74. zrb/task/base_trigger.py +4 -1
  75. zrb/task/cmd_task.py +6 -0
  76. zrb/task/http_check.py +11 -5
  77. zrb/task/make_task.py +3 -0
  78. zrb/task/rsync_task.py +5 -0
  79. zrb/task/scaffolder.py +7 -4
  80. zrb/task/scheduler.py +3 -0
  81. zrb/task/tcp_check.py +6 -4
  82. zrb/util/ascii_art/art/bee.txt +17 -0
  83. zrb/util/ascii_art/art/cat.txt +9 -0
  84. zrb/util/ascii_art/art/ghost.txt +16 -0
  85. zrb/util/ascii_art/art/panda.txt +17 -0
  86. zrb/util/ascii_art/art/rose.txt +14 -0
  87. zrb/util/ascii_art/art/unicorn.txt +15 -0
  88. zrb/util/ascii_art/banner.py +92 -0
  89. zrb/util/cli/markdown.py +22 -2
  90. zrb/util/cmd/command.py +33 -10
  91. zrb/util/file.py +51 -32
  92. zrb/util/match.py +78 -0
  93. zrb/util/run.py +3 -3
  94. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/METADATA +9 -15
  95. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/RECORD +100 -128
  96. zrb/attr/__init__.py +0 -0
  97. zrb/builtin/llm/attachment.py +0 -40
  98. zrb/builtin/llm/chat_completion.py +0 -274
  99. zrb/builtin/llm/chat_session.py +0 -270
  100. zrb/builtin/llm/chat_session_cmd.py +0 -288
  101. zrb/builtin/llm/chat_trigger.py +0 -79
  102. zrb/builtin/llm/history.py +0 -71
  103. zrb/builtin/llm/input.py +0 -27
  104. zrb/builtin/llm/llm_ask.py +0 -269
  105. zrb/builtin/llm/previous-session.js +0 -21
  106. zrb/builtin/llm/tool/__init__.py +0 -0
  107. zrb/builtin/llm/tool/api.py +0 -75
  108. zrb/builtin/llm/tool/cli.py +0 -52
  109. zrb/builtin/llm/tool/code.py +0 -236
  110. zrb/builtin/llm/tool/file.py +0 -560
  111. zrb/builtin/llm/tool/note.py +0 -84
  112. zrb/builtin/llm/tool/sub_agent.py +0 -150
  113. zrb/builtin/llm/tool/web.py +0 -171
  114. zrb/builtin/project/__init__.py +0 -0
  115. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
  116. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
  117. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
  118. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
  119. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
  120. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
  121. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
  122. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
  123. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
  124. zrb/builtin/project/create/__init__.py +0 -0
  125. zrb/builtin/shell/__init__.py +0 -0
  126. zrb/builtin/shell/autocomplete/__init__.py +0 -0
  127. zrb/callback/__init__.py +0 -0
  128. zrb/cmd/__init__.py +0 -0
  129. zrb/config/default_prompt/interactive_system_prompt.md +0 -29
  130. zrb/config/default_prompt/persona.md +0 -1
  131. zrb/config/default_prompt/summarization_prompt.md +0 -57
  132. zrb/config/default_prompt/system_prompt.md +0 -38
  133. zrb/config/llm_config.py +0 -339
  134. zrb/config/llm_context/config.py +0 -166
  135. zrb/config/llm_context/config_parser.py +0 -40
  136. zrb/config/llm_context/workflow.py +0 -81
  137. zrb/config/llm_rate_limitter.py +0 -190
  138. zrb/content_transformer/__init__.py +0 -0
  139. zrb/context/__init__.py +0 -0
  140. zrb/dot_dict/__init__.py +0 -0
  141. zrb/env/__init__.py +0 -0
  142. zrb/group/__init__.py +0 -0
  143. zrb/input/__init__.py +0 -0
  144. zrb/runner/__init__.py +0 -0
  145. zrb/runner/web_route/__init__.py +0 -0
  146. zrb/runner/web_route/home_page/__init__.py +0 -0
  147. zrb/session/__init__.py +0 -0
  148. zrb/session_state_log/__init__.py +0 -0
  149. zrb/session_state_logger/__init__.py +0 -0
  150. zrb/task/__init__.py +0 -0
  151. zrb/task/base/__init__.py +0 -0
  152. zrb/task/llm/__init__.py +0 -0
  153. zrb/task/llm/agent.py +0 -204
  154. zrb/task/llm/agent_runner.py +0 -152
  155. zrb/task/llm/config.py +0 -122
  156. zrb/task/llm/conversation_history.py +0 -209
  157. zrb/task/llm/conversation_history_model.py +0 -67
  158. zrb/task/llm/default_workflow/coding/workflow.md +0 -41
  159. zrb/task/llm/default_workflow/copywriting/workflow.md +0 -68
  160. zrb/task/llm/default_workflow/git/workflow.md +0 -118
  161. zrb/task/llm/default_workflow/golang/workflow.md +0 -128
  162. zrb/task/llm/default_workflow/html-css/workflow.md +0 -135
  163. zrb/task/llm/default_workflow/java/workflow.md +0 -146
  164. zrb/task/llm/default_workflow/javascript/workflow.md +0 -158
  165. zrb/task/llm/default_workflow/python/workflow.md +0 -160
  166. zrb/task/llm/default_workflow/researching/workflow.md +0 -153
  167. zrb/task/llm/default_workflow/rust/workflow.md +0 -162
  168. zrb/task/llm/default_workflow/shell/workflow.md +0 -299
  169. zrb/task/llm/error.py +0 -95
  170. zrb/task/llm/file_replacement.py +0 -206
  171. zrb/task/llm/file_tool_model.py +0 -57
  172. zrb/task/llm/history_processor.py +0 -206
  173. zrb/task/llm/history_summarization.py +0 -25
  174. zrb/task/llm/print_node.py +0 -221
  175. zrb/task/llm/prompt.py +0 -321
  176. zrb/task/llm/subagent_conversation_history.py +0 -41
  177. zrb/task/llm/tool_wrapper.py +0 -361
  178. zrb/task/llm/typing.py +0 -3
  179. zrb/task/llm/workflow.py +0 -76
  180. zrb/task/llm_task.py +0 -379
  181. zrb/task_status/__init__.py +0 -0
  182. zrb/util/__init__.py +0 -0
  183. zrb/util/cli/__init__.py +0 -0
  184. zrb/util/cmd/__init__.py +0 -0
  185. zrb/util/codemod/__init__.py +0 -0
  186. zrb/util/string/__init__.py +0 -0
  187. zrb/xcom/__init__.py +0 -0
  188. /zrb/{config/default_prompt/file_extractor_system_prompt.md → llm/prompt/markdown/file_extractor.md} +0 -0
  189. /zrb/{config/default_prompt/repo_extractor_system_prompt.md → llm/prompt/markdown/repo_extractor.md} +0 -0
  190. /zrb/{config/default_prompt/repo_summarizer_system_prompt.md → llm/prompt/markdown/repo_summarizer.md} +0 -0
  191. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +0 -0
  192. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -1,361 +0,0 @@
1
- import functools
2
- import inspect
3
- import signal
4
- import traceback
5
- import typing
6
- from collections.abc import Callable
7
- from typing import TYPE_CHECKING, Any
8
-
9
- from zrb.config.config import CFG
10
- from zrb.config.llm_rate_limitter import llm_rate_limitter
11
- from zrb.context.any_context import AnyContext
12
- from zrb.task.llm.error import ToolExecutionError
13
- from zrb.task.llm.file_replacement import edit_replacement, is_single_path_replacement
14
- from zrb.util.callable import get_callable_name
15
- from zrb.util.cli.markdown import render_markdown
16
- from zrb.util.cli.style import (
17
- stylize_blue,
18
- stylize_error,
19
- stylize_faint,
20
- stylize_green,
21
- stylize_yellow,
22
- )
23
- from zrb.util.cli.text import edit_text
24
- from zrb.util.run import run_async
25
- from zrb.util.string.conversion import to_boolean
26
- from zrb.util.yaml import edit_obj, yaml_dump
27
-
28
- if TYPE_CHECKING:
29
- from pydantic_ai import Tool
30
-
31
-
32
- class ToolExecutionCancelled(ValueError):
33
- pass
34
-
35
-
36
- def wrap_tool(func: Callable, ctx: AnyContext, yolo_mode: bool | list[str]) -> "Tool":
37
- """Wraps a tool function to handle exceptions and context propagation."""
38
- from pydantic_ai import RunContext, Tool
39
-
40
- original_sig = inspect.signature(func)
41
- needs_run_context_for_pydantic = _has_context_parameter(original_sig, RunContext)
42
- wrapper = wrap_func(func, ctx, yolo_mode)
43
- return Tool(wrapper, takes_ctx=needs_run_context_for_pydantic)
44
-
45
-
46
- def wrap_func(func: Callable, ctx: AnyContext, yolo_mode: bool | list[str]) -> Callable:
47
- original_sig = inspect.signature(func)
48
- needs_any_context_for_injection = _has_context_parameter(original_sig, AnyContext)
49
- # Pass individual flags to the wrapper creator
50
- wrapper = _create_wrapper(
51
- func=func,
52
- original_signature=original_sig,
53
- ctx=ctx,
54
- needs_any_context_for_injection=needs_any_context_for_injection,
55
- yolo_mode=yolo_mode,
56
- )
57
- _adjust_signature(wrapper, original_sig)
58
- return wrapper
59
-
60
-
61
- def _has_context_parameter(original_sig: inspect.Signature, context_type: type) -> bool:
62
- """
63
- Checks if the function signature includes a parameter with the specified
64
- context type annotation.
65
- """
66
- return any(
67
- _is_annotated_with_context(param.annotation, context_type)
68
- for param in original_sig.parameters.values()
69
- )
70
-
71
-
72
- def _is_annotated_with_context(param_annotation, context_type):
73
- """
74
- Checks if the parameter annotation is the specified context type
75
- or a generic type containing it (e.g., Optional[ContextType]).
76
- """
77
- if param_annotation is inspect.Parameter.empty:
78
- return False
79
- if param_annotation is context_type:
80
- return True
81
- # Check for generic types like Optional[ContextType] or Union[ContextType, ...]
82
- origin = typing.get_origin(param_annotation)
83
- args = typing.get_args(param_annotation)
84
- if origin is not None and args:
85
- # Check if context_type is one of the arguments of the generic type
86
- return any(arg is context_type for arg in args)
87
- return False
88
-
89
-
90
- def _create_wrapper(
91
- func: Callable,
92
- original_signature: inspect.Signature,
93
- ctx: AnyContext,
94
- needs_any_context_for_injection: bool,
95
- yolo_mode: bool | list[str],
96
- ) -> Callable:
97
- """Creates the core wrapper function."""
98
-
99
- @functools.wraps(func)
100
- async def wrapper(*args, **kwargs):
101
- # Identify AnyContext parameter name from the original signature if needed
102
- any_context_param_name = None
103
- if needs_any_context_for_injection:
104
- for param in original_signature.parameters.values():
105
- if _is_annotated_with_context(param.annotation, AnyContext):
106
- any_context_param_name = param.name
107
- break # Found it, no need to continue
108
- if any_context_param_name is None:
109
- # This should not happen if needs_any_context_for_injection is True,
110
- # but check for safety
111
- raise ValueError(
112
- "AnyContext parameter name not found in function signature."
113
- )
114
- # Inject the captured ctx into kwargs. This will overwrite if the LLM
115
- # somehow provided it.
116
- kwargs[any_context_param_name] = ctx
117
- # We will need to overwrite SIGINT handler, so that when user press ctrl + c,
118
- # the program won't immediately exit
119
- original_sigint_handler = signal.getsignal(signal.SIGINT)
120
- tool_name = get_callable_name(func)
121
- try:
122
- has_ever_edited = False
123
- if not ctx.is_web_mode and ctx.is_tty:
124
- if (
125
- isinstance(yolo_mode, list) and func.__name__ not in yolo_mode
126
- ) or not yolo_mode:
127
- approval, reason, kwargs, has_ever_edited = (
128
- await _handle_user_response(ctx, func, args, kwargs)
129
- )
130
- if not approval:
131
- raise ToolExecutionCancelled(
132
- f"Tool execution cancelled. User disapproving: {reason}"
133
- )
134
- signal.signal(signal.SIGINT, _tool_wrapper_sigint_handler)
135
- ctx.print(stylize_faint(f"Run {tool_name}"), plain=True)
136
- result = await run_async(func(*args, **kwargs))
137
- _check_tool_call_result_limit(result)
138
- if has_ever_edited:
139
- return {
140
- "tool_call_result": result,
141
- "new_tool_parameters": kwargs,
142
- "message": "User correction: Tool was called with user's parameters",
143
- }
144
- return result
145
- except BaseException as e:
146
- error_model = ToolExecutionError(
147
- tool_name=tool_name,
148
- error_type=type(e).__name__,
149
- message=str(e),
150
- details=traceback.format_exc(),
151
- )
152
- return error_model.model_dump_json()
153
- finally:
154
- signal.signal(signal.SIGINT, original_sigint_handler)
155
-
156
- return wrapper
157
-
158
-
159
- def _tool_wrapper_sigint_handler(signum, frame):
160
- raise KeyboardInterrupt("SIGINT detected while running tool")
161
-
162
-
163
- def _check_tool_call_result_limit(result: Any):
164
- if (
165
- llm_rate_limitter.count_token(result)
166
- > llm_rate_limitter.max_tokens_per_tool_call_result
167
- ):
168
- raise ValueError("Result value is too large, please adjust the parameter")
169
-
170
-
171
- async def _handle_user_response(
172
- ctx: AnyContext,
173
- func: Callable,
174
- args: list[Any] | tuple[Any],
175
- kwargs: dict[str, Any],
176
- ) -> tuple[bool, str, dict[str, Any], bool]:
177
- has_ever_edited = False
178
- while True:
179
- func_call_str = _get_func_call_str(func, args, kwargs)
180
- complete_confirmation_message = "\n".join(
181
- [
182
- f"\n🎰 >> {func_call_str}",
183
- _get_detail_func_param(args, kwargs),
184
- f"🎰 >> {_get_run_func_confirmation(func)}",
185
- ]
186
- )
187
- ctx.print(complete_confirmation_message, plain=True)
188
- user_response = await _read_line(args, kwargs)
189
- ctx.print("", plain=True)
190
- new_kwargs, is_edited = _get_edited_kwargs(ctx, user_response, kwargs)
191
- if is_edited:
192
- kwargs = new_kwargs
193
- has_ever_edited = True
194
- continue
195
- approval_and_reason = _get_user_approval_and_reason(
196
- ctx, user_response, func_call_str
197
- )
198
- if approval_and_reason is None:
199
- continue
200
- approval, reason = approval_and_reason
201
- return approval, reason, kwargs, has_ever_edited
202
-
203
-
204
- def _get_edited_kwargs(
205
- ctx: AnyContext, user_response: str, kwargs: dict[str, Any]
206
- ) -> tuple[dict[str, Any], bool]:
207
- user_edit_responses = [val for val in user_response.split(" ", maxsplit=2)]
208
- if len(user_edit_responses) >= 1 and user_edit_responses[0].lower() != "edit":
209
- return kwargs, False
210
- while len(user_edit_responses) < 3:
211
- user_edit_responses.append("")
212
- key, val_str = user_edit_responses[1:]
213
- # Make sure first segment of the key is in kwargs
214
- if key != "":
215
- key_parts = key.split(".")
216
- if len(key_parts) > 0 and key_parts[0] not in kwargs:
217
- return kwargs, True
218
- # Handle replacement edit
219
- if len(kwargs) == 1:
220
- kwarg_key = list(kwargs.keys())[0]
221
- if is_single_path_replacement(kwargs[kwarg_key]) and (
222
- key == "" or key == kwarg_key
223
- ):
224
- kwargs[kwarg_key], edited = edit_replacement(kwargs[kwarg_key])
225
- return kwargs, True
226
- # Handle other kind of edit
227
- old_val_str = yaml_dump(kwargs, key)
228
- if val_str == "":
229
- val_str = edit_text(
230
- prompt_message=f"# {key}" if key != "" else "",
231
- value=old_val_str,
232
- editor=CFG.DEFAULT_EDITOR,
233
- extension=".yaml",
234
- )
235
- if old_val_str == val_str:
236
- return kwargs, True
237
- edited_kwargs = edit_obj(kwargs, key, val_str)
238
- return edited_kwargs, True
239
-
240
-
241
- def _get_user_approval_and_reason(
242
- ctx: AnyContext, user_response: str, func_call_str: str
243
- ) -> tuple[bool, str] | None:
244
- user_approval_responses = [
245
- val.strip() for val in user_response.split(",", maxsplit=1)
246
- ]
247
- while len(user_approval_responses) < 2:
248
- user_approval_responses.append("")
249
- approval_str, reason = user_approval_responses
250
- try:
251
- approved = True if approval_str.strip() == "" else to_boolean(approval_str)
252
- if not approved and reason == "":
253
- reason = "User disapproving the tool execution"
254
- return approved, reason
255
- except Exception:
256
- return False, user_response
257
-
258
-
259
- def _get_run_func_confirmation(func: Callable) -> str:
260
- func_name = get_callable_name(func)
261
- return render_markdown(
262
- f"Allow to run `{func_name}`? (✅ `Yes` | ⛔ `No, <reason>` | 📝 `Edit <param> <value>`)"
263
- ).strip()
264
-
265
-
266
- def _get_detail_func_param(args: list[Any] | tuple[Any], kwargs: dict[str, Any]) -> str:
267
- if not kwargs:
268
- return ""
269
- yaml_str = yaml_dump(kwargs)
270
- # Create the final markdown string
271
- markdown = f"```yaml\n{yaml_str}\n```"
272
- return render_markdown(markdown)
273
-
274
-
275
- def _get_func_call_str(
276
- func: Callable, args: list[Any] | tuple[Any], kwargs: dict[str, Any]
277
- ) -> str:
278
- func_name = get_callable_name(func)
279
- normalized_args = [stylize_green(_truncate_arg(arg)) for arg in args]
280
- normalized_kwargs = []
281
- for key, val in kwargs.items():
282
- truncated_val = _truncate_arg(f"{val}")
283
- normalized_kwargs.append(
284
- f"{stylize_yellow(key)}={stylize_green(truncated_val)}"
285
- )
286
- func_param_str = ", ".join(normalized_args + normalized_kwargs)
287
- return f"{stylize_blue(func_name + '(')}{func_param_str}{stylize_blue(')')}"
288
-
289
-
290
- def _truncate_arg(arg: str, length: int = 19) -> str:
291
- normalized_arg = arg.replace("\n", "\\n")
292
- if len(normalized_arg) > length:
293
- return f"{normalized_arg[:length-4]} ..."
294
- return normalized_arg
295
-
296
-
297
- async def _read_line(args: list[Any] | tuple[Any], kwargs: dict[str, Any]):
298
- from prompt_toolkit import PromptSession
299
- from prompt_toolkit.completion import Completer, Completion
300
-
301
- class ToolConfirmationCompleter(Completer):
302
- """Custom completer for tool confirmation that doesn't auto-complete partial words."""
303
-
304
- def __init__(self, options, meta_dict):
305
- self.options = options
306
- self.meta_dict = meta_dict
307
-
308
- def get_completions(self, document, complete_event):
309
- text = document.text.strip()
310
-
311
- # Only provide completions if:
312
- # 1. Input is empty, OR
313
- # 2. Input exactly matches the beginning of an option
314
- if text == "":
315
- # Show all options when nothing is typed
316
- for option in self.options:
317
- yield Completion(
318
- option,
319
- start_position=0,
320
- display_meta=self.meta_dict.get(option, ""),
321
- )
322
- else:
323
- # Only complete if text exactly matches the beginning of an option
324
- for option in self.options:
325
- if option.startswith(text):
326
- yield Completion(
327
- option,
328
- start_position=-len(text),
329
- display_meta=self.meta_dict.get(option, ""),
330
- )
331
-
332
- options = ["yes", "no", "edit"]
333
- meta_dict = {
334
- "yes": "Approve the execution",
335
- "no": "Disapprove the execution",
336
- "edit": "Edit tool execution parameters",
337
- }
338
- for key in kwargs:
339
- options.append(f"edit {key}")
340
- meta_dict[f"edit {key}"] = f"Edit tool execution parameter: {key}"
341
- completer = ToolConfirmationCompleter(options, meta_dict)
342
- reader = PromptSession()
343
- return await reader.prompt_async(completer=completer)
344
-
345
-
346
- def _adjust_signature(wrapper: Callable, original_sig: inspect.Signature):
347
- """Adjusts the wrapper function's signature for schema generation."""
348
- # The wrapper's signature should represent the arguments the *LLM* needs to provide.
349
- # The LLM does not provide RunContext (pydantic-ai injects it) or AnyContext
350
- # (we inject it). So, the wrapper's signature should be the original signature,
351
- # minus any parameters annotated with RunContext or AnyContext.
352
-
353
- from pydantic_ai import RunContext
354
-
355
- params_for_schema = [
356
- param
357
- for param in original_sig.parameters.values()
358
- if not _is_annotated_with_context(param.annotation, RunContext)
359
- and not _is_annotated_with_context(param.annotation, AnyContext)
360
- ]
361
- wrapper.__signature__ = inspect.Signature(parameters=params_for_schema)
zrb/task/llm/typing.py DELETED
@@ -1,3 +0,0 @@
1
- from typing import Any
2
-
3
- ListOfDict = list[dict[str, Any]]
zrb/task/llm/workflow.py DELETED
@@ -1,76 +0,0 @@
1
- import os
2
-
3
- from zrb.config.config import CFG
4
- from zrb.config.llm_context.config import llm_context_config
5
- from zrb.config.llm_context.workflow import LLMWorkflow
6
-
7
-
8
- def load_workflow(workflow_name: str | list[str]) -> str:
9
- """
10
- Loads and formats one or more workflow documents for LLM consumption.
11
-
12
- Retrieves workflows by name, formats with descriptive headers for LLM context injection.
13
-
14
- Args:
15
- workflow_name: Name or list of names of the workflow(s) to load
16
-
17
- Returns:
18
- Formatted workflow content as a string with headers
19
-
20
- Raises:
21
- ValueError: If any specified workflow name is not found
22
- """
23
- names = [workflow_name] if isinstance(workflow_name, str) else workflow_name
24
- available_workflows = get_available_workflows()
25
- contents = []
26
- for name in names:
27
- workflow = available_workflows.get(name.strip().lower())
28
- if workflow is None:
29
- raise ValueError(f"Workflow not found: {name}")
30
- contents.append(
31
- "\n".join(
32
- [
33
- f"# {workflow.name}",
34
- f"> Workflow Location: `{workflow.path}`",
35
- workflow.content,
36
- ]
37
- )
38
- )
39
- return "\n".join(contents)
40
-
41
-
42
- def get_available_workflows() -> dict[str, LLMWorkflow]:
43
- available_workflows = {
44
- workflow_name.strip().lower(): workflow
45
- for workflow_name, workflow in llm_context_config.get_workflows().items()
46
- }
47
- # Define builtin workflow locations in order of precedence
48
- builtin_workflow_locations = [
49
- os.path.expanduser(additional_builtin_workflow_path)
50
- for additional_builtin_workflow_path in CFG.LLM_BUILTIN_WORKFLOW_PATHS
51
- if os.path.isdir(os.path.expanduser(additional_builtin_workflow_path))
52
- ]
53
- builtin_workflow_locations.append(
54
- os.path.join(os.path.dirname(__file__), "default_workflow")
55
- )
56
- # Load workflows from all locations
57
- for workflow_location in builtin_workflow_locations:
58
- if not os.path.isdir(workflow_location):
59
- continue
60
- for workflow_name in os.listdir(workflow_location):
61
- workflow_dir = os.path.join(workflow_location, workflow_name)
62
- workflow_file = os.path.join(workflow_dir, "workflow.md")
63
- if not os.path.isfile(workflow_file):
64
- workflow_file = os.path.join(workflow_dir, "SKILL.md")
65
- if not os.path.isfile(path=workflow_file):
66
- continue
67
- # Only add if not already defined (earlier locations have precedence)
68
- if workflow_name not in available_workflows:
69
- with open(workflow_file, "r") as f:
70
- workflow_content = f.read()
71
- available_workflows[workflow_name] = LLMWorkflow(
72
- name=workflow_name,
73
- path=workflow_dir,
74
- content=workflow_content,
75
- )
76
- return available_workflows