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.
- zrb/__init__.py +118 -129
- zrb/builtin/__init__.py +54 -2
- zrb/builtin/llm/chat.py +147 -0
- zrb/callback/callback.py +8 -1
- zrb/cmd/cmd_result.py +2 -1
- zrb/config/config.py +491 -280
- zrb/config/helper.py +84 -0
- zrb/config/web_auth_config.py +50 -35
- zrb/context/any_shared_context.py +13 -2
- zrb/context/context.py +31 -3
- zrb/context/print_fn.py +13 -0
- zrb/context/shared_context.py +14 -1
- zrb/input/option_input.py +30 -2
- zrb/llm/agent/__init__.py +9 -0
- zrb/llm/agent/agent.py +215 -0
- zrb/llm/agent/summarizer.py +20 -0
- zrb/llm/app/__init__.py +10 -0
- zrb/llm/app/completion.py +281 -0
- zrb/llm/app/confirmation/allow_tool.py +66 -0
- zrb/llm/app/confirmation/handler.py +178 -0
- zrb/llm/app/confirmation/replace_confirmation.py +77 -0
- zrb/llm/app/keybinding.py +34 -0
- zrb/llm/app/layout.py +117 -0
- zrb/llm/app/lexer.py +155 -0
- zrb/llm/app/redirection.py +28 -0
- zrb/llm/app/style.py +16 -0
- zrb/llm/app/ui.py +733 -0
- zrb/llm/config/__init__.py +4 -0
- zrb/llm/config/config.py +122 -0
- zrb/llm/config/limiter.py +247 -0
- zrb/llm/history_manager/__init__.py +4 -0
- zrb/llm/history_manager/any_history_manager.py +23 -0
- zrb/llm/history_manager/file_history_manager.py +91 -0
- zrb/llm/history_processor/summarizer.py +108 -0
- zrb/llm/note/__init__.py +3 -0
- zrb/llm/note/manager.py +122 -0
- zrb/llm/prompt/__init__.py +29 -0
- zrb/llm/prompt/claude_compatibility.py +92 -0
- zrb/llm/prompt/compose.py +55 -0
- zrb/llm/prompt/default.py +51 -0
- zrb/llm/prompt/markdown/mandate.md +23 -0
- zrb/llm/prompt/markdown/persona.md +3 -0
- zrb/llm/prompt/markdown/summarizer.md +21 -0
- zrb/llm/prompt/note.py +41 -0
- zrb/llm/prompt/system_context.py +46 -0
- zrb/llm/prompt/zrb.py +41 -0
- zrb/llm/skill/__init__.py +3 -0
- zrb/llm/skill/manager.py +86 -0
- zrb/llm/task/__init__.py +4 -0
- zrb/llm/task/llm_chat_task.py +316 -0
- zrb/llm/task/llm_task.py +245 -0
- zrb/llm/tool/__init__.py +39 -0
- zrb/llm/tool/bash.py +75 -0
- zrb/llm/tool/code.py +266 -0
- zrb/llm/tool/file.py +419 -0
- zrb/llm/tool/note.py +70 -0
- zrb/{builtin/llm ā llm}/tool/rag.py +8 -5
- zrb/llm/tool/search/brave.py +53 -0
- zrb/llm/tool/search/searxng.py +47 -0
- zrb/llm/tool/search/serpapi.py +47 -0
- zrb/llm/tool/skill.py +19 -0
- zrb/llm/tool/sub_agent.py +70 -0
- zrb/llm/tool/web.py +97 -0
- zrb/llm/tool/zrb_task.py +66 -0
- zrb/llm/util/attachment.py +101 -0
- zrb/llm/util/prompt.py +104 -0
- zrb/llm/util/stream_response.py +178 -0
- zrb/session/any_session.py +0 -3
- zrb/session/session.py +1 -1
- zrb/task/base/context.py +25 -13
- zrb/task/base/execution.py +52 -47
- zrb/task/base/lifecycle.py +7 -4
- zrb/task/base_task.py +48 -49
- zrb/task/base_trigger.py +4 -1
- zrb/task/cmd_task.py +6 -0
- zrb/task/http_check.py +11 -5
- zrb/task/make_task.py +3 -0
- zrb/task/rsync_task.py +5 -0
- zrb/task/scaffolder.py +7 -4
- zrb/task/scheduler.py +3 -0
- zrb/task/tcp_check.py +6 -4
- zrb/util/ascii_art/art/bee.txt +17 -0
- zrb/util/ascii_art/art/cat.txt +9 -0
- zrb/util/ascii_art/art/ghost.txt +16 -0
- zrb/util/ascii_art/art/panda.txt +17 -0
- zrb/util/ascii_art/art/rose.txt +14 -0
- zrb/util/ascii_art/art/unicorn.txt +15 -0
- zrb/util/ascii_art/banner.py +92 -0
- zrb/util/cli/markdown.py +22 -2
- zrb/util/cmd/command.py +33 -10
- zrb/util/file.py +51 -32
- zrb/util/match.py +78 -0
- zrb/util/run.py +3 -3
- {zrb-1.21.29.dist-info ā zrb-2.0.0a4.dist-info}/METADATA +9 -15
- {zrb-1.21.29.dist-info ā zrb-2.0.0a4.dist-info}/RECORD +100 -128
- zrb/attr/__init__.py +0 -0
- zrb/builtin/llm/attachment.py +0 -40
- zrb/builtin/llm/chat_completion.py +0 -274
- zrb/builtin/llm/chat_session.py +0 -270
- zrb/builtin/llm/chat_session_cmd.py +0 -288
- zrb/builtin/llm/chat_trigger.py +0 -79
- zrb/builtin/llm/history.py +0 -71
- zrb/builtin/llm/input.py +0 -27
- zrb/builtin/llm/llm_ask.py +0 -269
- zrb/builtin/llm/previous-session.js +0 -21
- zrb/builtin/llm/tool/__init__.py +0 -0
- zrb/builtin/llm/tool/api.py +0 -75
- zrb/builtin/llm/tool/cli.py +0 -52
- zrb/builtin/llm/tool/code.py +0 -236
- zrb/builtin/llm/tool/file.py +0 -560
- zrb/builtin/llm/tool/note.py +0 -84
- zrb/builtin/llm/tool/sub_agent.py +0 -150
- zrb/builtin/llm/tool/web.py +0 -171
- zrb/builtin/project/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
- zrb/builtin/project/create/__init__.py +0 -0
- zrb/builtin/shell/__init__.py +0 -0
- zrb/builtin/shell/autocomplete/__init__.py +0 -0
- zrb/callback/__init__.py +0 -0
- zrb/cmd/__init__.py +0 -0
- zrb/config/default_prompt/interactive_system_prompt.md +0 -29
- zrb/config/default_prompt/persona.md +0 -1
- zrb/config/default_prompt/summarization_prompt.md +0 -57
- zrb/config/default_prompt/system_prompt.md +0 -38
- zrb/config/llm_config.py +0 -339
- zrb/config/llm_context/config.py +0 -166
- zrb/config/llm_context/config_parser.py +0 -40
- zrb/config/llm_context/workflow.py +0 -81
- zrb/config/llm_rate_limitter.py +0 -190
- zrb/content_transformer/__init__.py +0 -0
- zrb/context/__init__.py +0 -0
- zrb/dot_dict/__init__.py +0 -0
- zrb/env/__init__.py +0 -0
- zrb/group/__init__.py +0 -0
- zrb/input/__init__.py +0 -0
- zrb/runner/__init__.py +0 -0
- zrb/runner/web_route/__init__.py +0 -0
- zrb/runner/web_route/home_page/__init__.py +0 -0
- zrb/session/__init__.py +0 -0
- zrb/session_state_log/__init__.py +0 -0
- zrb/session_state_logger/__init__.py +0 -0
- zrb/task/__init__.py +0 -0
- zrb/task/base/__init__.py +0 -0
- zrb/task/llm/__init__.py +0 -0
- zrb/task/llm/agent.py +0 -204
- zrb/task/llm/agent_runner.py +0 -152
- zrb/task/llm/config.py +0 -122
- zrb/task/llm/conversation_history.py +0 -209
- zrb/task/llm/conversation_history_model.py +0 -67
- zrb/task/llm/default_workflow/coding/workflow.md +0 -41
- zrb/task/llm/default_workflow/copywriting/workflow.md +0 -68
- zrb/task/llm/default_workflow/git/workflow.md +0 -118
- zrb/task/llm/default_workflow/golang/workflow.md +0 -128
- zrb/task/llm/default_workflow/html-css/workflow.md +0 -135
- zrb/task/llm/default_workflow/java/workflow.md +0 -146
- zrb/task/llm/default_workflow/javascript/workflow.md +0 -158
- zrb/task/llm/default_workflow/python/workflow.md +0 -160
- zrb/task/llm/default_workflow/researching/workflow.md +0 -153
- zrb/task/llm/default_workflow/rust/workflow.md +0 -162
- zrb/task/llm/default_workflow/shell/workflow.md +0 -299
- zrb/task/llm/error.py +0 -95
- zrb/task/llm/file_replacement.py +0 -206
- zrb/task/llm/file_tool_model.py +0 -57
- zrb/task/llm/history_processor.py +0 -206
- zrb/task/llm/history_summarization.py +0 -25
- zrb/task/llm/print_node.py +0 -221
- zrb/task/llm/prompt.py +0 -321
- zrb/task/llm/subagent_conversation_history.py +0 -41
- zrb/task/llm/tool_wrapper.py +0 -361
- zrb/task/llm/typing.py +0 -3
- zrb/task/llm/workflow.py +0 -76
- zrb/task/llm_task.py +0 -379
- zrb/task_status/__init__.py +0 -0
- zrb/util/__init__.py +0 -0
- zrb/util/cli/__init__.py +0 -0
- zrb/util/cmd/__init__.py +0 -0
- zrb/util/codemod/__init__.py +0 -0
- zrb/util/string/__init__.py +0 -0
- zrb/xcom/__init__.py +0 -0
- /zrb/{config/default_prompt/file_extractor_system_prompt.md ā llm/prompt/markdown/file_extractor.md} +0 -0
- /zrb/{config/default_prompt/repo_extractor_system_prompt.md ā llm/prompt/markdown/repo_extractor.md} +0 -0
- /zrb/{config/default_prompt/repo_summarizer_system_prompt.md ā llm/prompt/markdown/repo_summarizer.md} +0 -0
- {zrb-1.21.29.dist-info ā zrb-2.0.0a4.dist-info}/WHEEL +0 -0
- {zrb-1.21.29.dist-info ā zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from prompt_toolkit.completion import CompleteEvent, Completer, Completion
|
|
4
|
-
from prompt_toolkit.document import Document
|
|
5
|
-
|
|
6
|
-
from zrb.builtin.llm.chat_session_cmd import (
|
|
7
|
-
ADD_SUB_CMD,
|
|
8
|
-
ATTACHMENT_ADD_SUB_CMD_DESC,
|
|
9
|
-
ATTACHMENT_CLEAR_SUB_CMD_DESC,
|
|
10
|
-
ATTACHMENT_CMD,
|
|
11
|
-
ATTACHMENT_CMD_DESC,
|
|
12
|
-
ATTACHMENT_SET_SUB_CMD_DESC,
|
|
13
|
-
CLEAR_SUB_CMD,
|
|
14
|
-
HELP_CMD,
|
|
15
|
-
HELP_CMD_DESC,
|
|
16
|
-
MULTILINE_END_CMD,
|
|
17
|
-
MULTILINE_END_CMD_DESC,
|
|
18
|
-
MULTILINE_START_CMD,
|
|
19
|
-
MULTILINE_START_CMD_DESC,
|
|
20
|
-
QUIT_CMD,
|
|
21
|
-
QUIT_CMD_DESC,
|
|
22
|
-
RUN_CLI_CMD,
|
|
23
|
-
RUN_CLI_CMD_DESC,
|
|
24
|
-
SAVE_CMD,
|
|
25
|
-
SAVE_CMD_DESC,
|
|
26
|
-
SET_SUB_CMD,
|
|
27
|
-
WORKFLOW_ADD_SUB_CMD_DESC,
|
|
28
|
-
WORKFLOW_CLEAR_SUB_CMD_DESC,
|
|
29
|
-
WORKFLOW_CMD,
|
|
30
|
-
WORKFLOW_CMD_DESC,
|
|
31
|
-
WORKFLOW_SET_SUB_CMD_DESC,
|
|
32
|
-
YOLO_CMD,
|
|
33
|
-
YOLO_CMD_DESC,
|
|
34
|
-
YOLO_SET_CMD_DESC,
|
|
35
|
-
YOLO_SET_FALSE_CMD_DESC,
|
|
36
|
-
YOLO_SET_TRUE_CMD_DESC,
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class ChatCompleter(Completer):
|
|
41
|
-
|
|
42
|
-
def get_completions(self, document: Document, complete_event: CompleteEvent):
|
|
43
|
-
# Slash command
|
|
44
|
-
for completion in self._complete_slash_command(document):
|
|
45
|
-
yield completion
|
|
46
|
-
for completion in self._complete_slash_file_command(document):
|
|
47
|
-
yield completion
|
|
48
|
-
# Appendix
|
|
49
|
-
for completion in self._complete_appendix(document):
|
|
50
|
-
yield completion
|
|
51
|
-
|
|
52
|
-
def _complete_slash_file_command(self, document: Document):
|
|
53
|
-
text = document.text_before_cursor
|
|
54
|
-
prefixes = []
|
|
55
|
-
for cmd in ATTACHMENT_CMD:
|
|
56
|
-
for subcmd in ADD_SUB_CMD:
|
|
57
|
-
prefixes.append(f"{cmd} {subcmd} ")
|
|
58
|
-
for prefix in prefixes:
|
|
59
|
-
if text.startswith(prefix):
|
|
60
|
-
pattern = text[len(prefix) :]
|
|
61
|
-
potential_options = self._fuzzy_path_search(pattern, dirs=False)
|
|
62
|
-
for prefixed_option in [
|
|
63
|
-
f"{prefix}{option}" for option in potential_options
|
|
64
|
-
]:
|
|
65
|
-
yield Completion(
|
|
66
|
-
prefixed_option,
|
|
67
|
-
start_position=-len(text),
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
def _complete_slash_command(self, document: Document):
|
|
71
|
-
text = document.text_before_cursor
|
|
72
|
-
if not text.startswith("/"):
|
|
73
|
-
return
|
|
74
|
-
for command, description in self._get_cmd_options().items():
|
|
75
|
-
if command.lower().startswith(text.lower()):
|
|
76
|
-
yield Completion(
|
|
77
|
-
command,
|
|
78
|
-
start_position=-len(text),
|
|
79
|
-
display_meta=description,
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
def _complete_appendix(self, document: Document):
|
|
83
|
-
token = document.get_word_before_cursor(WORD=True)
|
|
84
|
-
prefix = "@"
|
|
85
|
-
if not token.startswith(prefix):
|
|
86
|
-
return
|
|
87
|
-
pattern = token[len(prefix) :]
|
|
88
|
-
potential_options = self._fuzzy_path_search(pattern, dirs=False)
|
|
89
|
-
for prefixed_option in [f"{prefix}{option}" for option in potential_options]:
|
|
90
|
-
yield Completion(
|
|
91
|
-
prefixed_option,
|
|
92
|
-
start_position=-len(token),
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
def _get_cmd_options(self):
|
|
96
|
-
cmd_options = {}
|
|
97
|
-
# Add all commands with their descriptions
|
|
98
|
-
for cmd in MULTILINE_START_CMD:
|
|
99
|
-
cmd_options[cmd] = MULTILINE_START_CMD_DESC
|
|
100
|
-
for cmd in MULTILINE_END_CMD:
|
|
101
|
-
cmd_options[cmd] = MULTILINE_END_CMD_DESC
|
|
102
|
-
for cmd in QUIT_CMD:
|
|
103
|
-
cmd_options[cmd] = QUIT_CMD_DESC
|
|
104
|
-
for cmd in WORKFLOW_CMD:
|
|
105
|
-
cmd_options[cmd] = WORKFLOW_CMD_DESC
|
|
106
|
-
for subcmd in ADD_SUB_CMD:
|
|
107
|
-
cmd_options[f"{cmd} {subcmd}"] = WORKFLOW_ADD_SUB_CMD_DESC
|
|
108
|
-
for subcmd in CLEAR_SUB_CMD:
|
|
109
|
-
cmd_options[f"{cmd} {subcmd}"] = WORKFLOW_CLEAR_SUB_CMD_DESC
|
|
110
|
-
for subcmd in SET_SUB_CMD:
|
|
111
|
-
cmd_options[f"{cmd} {subcmd}"] = WORKFLOW_SET_SUB_CMD_DESC
|
|
112
|
-
for cmd in SAVE_CMD:
|
|
113
|
-
cmd_options[cmd] = SAVE_CMD_DESC
|
|
114
|
-
for cmd in ATTACHMENT_CMD:
|
|
115
|
-
cmd_options[cmd] = ATTACHMENT_CMD_DESC
|
|
116
|
-
for subcmd in ADD_SUB_CMD:
|
|
117
|
-
cmd_options[f"{cmd} {subcmd}"] = ATTACHMENT_ADD_SUB_CMD_DESC
|
|
118
|
-
for subcmd in CLEAR_SUB_CMD:
|
|
119
|
-
cmd_options[f"{cmd} {subcmd}"] = ATTACHMENT_CLEAR_SUB_CMD_DESC
|
|
120
|
-
for subcmd in SET_SUB_CMD:
|
|
121
|
-
cmd_options[f"{cmd} {subcmd}"] = ATTACHMENT_SET_SUB_CMD_DESC
|
|
122
|
-
for cmd in YOLO_CMD:
|
|
123
|
-
cmd_options[cmd] = YOLO_CMD_DESC
|
|
124
|
-
for subcmd in SET_SUB_CMD:
|
|
125
|
-
cmd_options[f"{cmd} {subcmd} true"] = YOLO_SET_TRUE_CMD_DESC
|
|
126
|
-
cmd_options[f"{cmd} {subcmd} false"] = YOLO_SET_FALSE_CMD_DESC
|
|
127
|
-
cmd_options[f"{cmd} {subcmd}"] = YOLO_SET_CMD_DESC
|
|
128
|
-
for cmd in HELP_CMD:
|
|
129
|
-
cmd_options[cmd] = HELP_CMD_DESC
|
|
130
|
-
for cmd in RUN_CLI_CMD:
|
|
131
|
-
cmd_options[cmd] = RUN_CLI_CMD_DESC
|
|
132
|
-
return dict(sorted(cmd_options.items()))
|
|
133
|
-
|
|
134
|
-
def _fuzzy_path_search(
|
|
135
|
-
self,
|
|
136
|
-
pattern: str,
|
|
137
|
-
root: str | None = None,
|
|
138
|
-
max_results: int = 20,
|
|
139
|
-
include_hidden: bool = False,
|
|
140
|
-
case_sensitive: bool = False,
|
|
141
|
-
dirs: bool = True,
|
|
142
|
-
files: bool = True,
|
|
143
|
-
) -> list[str]:
|
|
144
|
-
"""
|
|
145
|
-
Return a list of filesystem paths under `root` that fuzzy-match `pattern`.
|
|
146
|
-
- pattern: e.g. "./some/x" or "proj util/io"
|
|
147
|
-
- include_hidden: if False skip files/dirs starting with '.'
|
|
148
|
-
- dirs/files booleans let you restrict results
|
|
149
|
-
- returns list of relative paths (from root), sorted best-first
|
|
150
|
-
"""
|
|
151
|
-
search_pattern = pattern
|
|
152
|
-
if root is None:
|
|
153
|
-
# Determine root and adjust pattern if necessary
|
|
154
|
-
expanded_pattern = os.path.expanduser(pattern)
|
|
155
|
-
if os.path.isabs(expanded_pattern) or pattern.startswith("~"):
|
|
156
|
-
# For absolute paths, find the deepest existing directory
|
|
157
|
-
if os.path.isdir(expanded_pattern):
|
|
158
|
-
root = expanded_pattern
|
|
159
|
-
search_pattern = ""
|
|
160
|
-
else:
|
|
161
|
-
root = os.path.dirname(expanded_pattern)
|
|
162
|
-
while root and not os.path.isdir(root) and len(root) > 1:
|
|
163
|
-
root = os.path.dirname(root)
|
|
164
|
-
if not os.path.isdir(root):
|
|
165
|
-
root = "." # Fallback
|
|
166
|
-
search_pattern = pattern
|
|
167
|
-
else:
|
|
168
|
-
try:
|
|
169
|
-
search_pattern = os.path.relpath(expanded_pattern, root)
|
|
170
|
-
if search_pattern == ".":
|
|
171
|
-
search_pattern = ""
|
|
172
|
-
except ValueError:
|
|
173
|
-
search_pattern = os.path.basename(pattern)
|
|
174
|
-
else:
|
|
175
|
-
root = "."
|
|
176
|
-
search_pattern = pattern
|
|
177
|
-
# Normalize pattern -> tokens split on path separators or whitespace
|
|
178
|
-
search_pattern = search_pattern.strip()
|
|
179
|
-
if search_pattern:
|
|
180
|
-
raw_tokens = [t for t in search_pattern.split(os.path.sep) if t]
|
|
181
|
-
else:
|
|
182
|
-
raw_tokens = []
|
|
183
|
-
# prepare tokens (case)
|
|
184
|
-
if not case_sensitive:
|
|
185
|
-
tokens = [t.lower() for t in raw_tokens]
|
|
186
|
-
else:
|
|
187
|
-
tokens = raw_tokens
|
|
188
|
-
# specific ignore list
|
|
189
|
-
try:
|
|
190
|
-
is_recursive = os.path.abspath(os.path.expanduser(root)).startswith(
|
|
191
|
-
os.path.abspath(os.getcwd())
|
|
192
|
-
)
|
|
193
|
-
except Exception:
|
|
194
|
-
is_recursive = False
|
|
195
|
-
# walk filesystem
|
|
196
|
-
candidates: list[tuple[float, str]] = []
|
|
197
|
-
for dirpath, dirnames, filenames in os.walk(root):
|
|
198
|
-
# Filter directories
|
|
199
|
-
if not include_hidden:
|
|
200
|
-
dirnames[:] = [d for d in dirnames if not d.startswith(".")]
|
|
201
|
-
rel_dir = os.path.relpath(dirpath, root)
|
|
202
|
-
# treat '.' as empty prefix
|
|
203
|
-
if rel_dir == ".":
|
|
204
|
-
rel_dir = ""
|
|
205
|
-
# build list of entries to test depending on files/dirs flags
|
|
206
|
-
entries = []
|
|
207
|
-
if dirs:
|
|
208
|
-
entries.extend([os.path.join(rel_dir, d) for d in dirnames])
|
|
209
|
-
if files:
|
|
210
|
-
entries.extend([os.path.join(rel_dir, f) for f in filenames])
|
|
211
|
-
if not is_recursive:
|
|
212
|
-
dirnames[:] = []
|
|
213
|
-
for ent in entries:
|
|
214
|
-
# Normalize presentation: use ./ prefix for relative paths
|
|
215
|
-
display_path = ent if ent else "."
|
|
216
|
-
# Skip hidden entries unless requested (double check for rel path segments)
|
|
217
|
-
if not include_hidden:
|
|
218
|
-
if any(
|
|
219
|
-
seg.startswith(".") for seg in display_path.split(os.sep) if seg
|
|
220
|
-
):
|
|
221
|
-
continue
|
|
222
|
-
cand = display_path.replace(os.sep, "/") # unify separator
|
|
223
|
-
cand_cmp = cand if case_sensitive else cand.lower()
|
|
224
|
-
last_pos = 0
|
|
225
|
-
score = 0.0
|
|
226
|
-
matched_all = True
|
|
227
|
-
for token in tokens:
|
|
228
|
-
# try contiguous substring search first
|
|
229
|
-
idx = cand_cmp.find(token, last_pos)
|
|
230
|
-
if idx != -1:
|
|
231
|
-
# good match: reward contiguous early matches
|
|
232
|
-
score += idx # smaller idx preferred
|
|
233
|
-
last_pos = idx + len(token)
|
|
234
|
-
else:
|
|
235
|
-
# fallback to subsequence matching
|
|
236
|
-
pos = self._find_subsequence_pos(cand_cmp, token, last_pos)
|
|
237
|
-
if pos is None:
|
|
238
|
-
matched_all = False
|
|
239
|
-
break
|
|
240
|
-
# subsequence match is less preferred than contiguous substring
|
|
241
|
-
score += pos + 0.5 * len(token)
|
|
242
|
-
last_pos = pos + len(token)
|
|
243
|
-
if matched_all:
|
|
244
|
-
# prefer shorter paths when score ties, so include length as tiebreaker
|
|
245
|
-
score += 0.01 * len(cand)
|
|
246
|
-
out = (
|
|
247
|
-
cand
|
|
248
|
-
if os.path.abspath(cand) == cand
|
|
249
|
-
else os.path.join(root, cand)
|
|
250
|
-
)
|
|
251
|
-
candidates.append((score, out))
|
|
252
|
-
# sort by score then lexicographically and return top results
|
|
253
|
-
candidates.sort(key=lambda x: (x[0], x[1]))
|
|
254
|
-
return [p for _, p in candidates[:max_results]]
|
|
255
|
-
|
|
256
|
-
def _find_subsequence_pos(
|
|
257
|
-
self, hay: str, needle: str, start: int = 0
|
|
258
|
-
) -> int | None:
|
|
259
|
-
"""
|
|
260
|
-
Try to locate needle in hay as a subsequence starting at `start`.
|
|
261
|
-
Returns the index of the first matched character of the subsequence or None if not match.
|
|
262
|
-
"""
|
|
263
|
-
if not needle:
|
|
264
|
-
return start
|
|
265
|
-
i = start
|
|
266
|
-
j = 0
|
|
267
|
-
first_pos = None
|
|
268
|
-
while i < len(hay) and j < len(needle):
|
|
269
|
-
if hay[i] == needle[j]:
|
|
270
|
-
if first_pos is None:
|
|
271
|
-
first_pos = i
|
|
272
|
-
j += 1
|
|
273
|
-
i += 1
|
|
274
|
-
return first_pos if j == len(needle) else None
|
zrb/builtin/llm/chat_session.py
DELETED
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import sys
|
|
3
|
-
from typing import TYPE_CHECKING, Any
|
|
4
|
-
|
|
5
|
-
from zrb.builtin.llm.chat_session_cmd import (
|
|
6
|
-
ATTACHMENT_CMD,
|
|
7
|
-
HELP_CMD,
|
|
8
|
-
MULTILINE_END_CMD,
|
|
9
|
-
MULTILINE_START_CMD,
|
|
10
|
-
QUIT_CMD,
|
|
11
|
-
RUN_CLI_CMD,
|
|
12
|
-
SAVE_CMD,
|
|
13
|
-
WORKFLOW_CMD,
|
|
14
|
-
YOLO_CMD,
|
|
15
|
-
get_new_attachments,
|
|
16
|
-
get_new_workflows,
|
|
17
|
-
get_new_yolo_mode,
|
|
18
|
-
is_command_match,
|
|
19
|
-
print_commands,
|
|
20
|
-
print_current_attachments,
|
|
21
|
-
print_current_workflows,
|
|
22
|
-
print_current_yolo_mode,
|
|
23
|
-
run_cli_command,
|
|
24
|
-
save_final_result,
|
|
25
|
-
)
|
|
26
|
-
from zrb.builtin.llm.chat_trigger import llm_chat_trigger
|
|
27
|
-
from zrb.config.llm_config import llm_config
|
|
28
|
-
from zrb.context.any_context import AnyContext
|
|
29
|
-
from zrb.util.cli.markdown import render_markdown
|
|
30
|
-
|
|
31
|
-
if TYPE_CHECKING:
|
|
32
|
-
from asyncio import StreamReader
|
|
33
|
-
|
|
34
|
-
from prompt_toolkit import PromptSession
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
async def read_user_prompt(ctx: AnyContext) -> str:
|
|
38
|
-
"""
|
|
39
|
-
Reads user input from the CLI for an interactive chat session.
|
|
40
|
-
Orchestrates the session by calling helper functions.
|
|
41
|
-
"""
|
|
42
|
-
print_commands(ctx)
|
|
43
|
-
is_tty: bool = ctx.is_tty
|
|
44
|
-
reader: PromptSession[Any] | StreamReader = await _setup_input_reader(is_tty)
|
|
45
|
-
multiline_mode = False
|
|
46
|
-
is_first_time = True
|
|
47
|
-
current_workflows: str = ctx.input.workflows
|
|
48
|
-
current_yolo_mode: bool | str = ctx.input.yolo
|
|
49
|
-
current_attachments: str = ctx.input.attach
|
|
50
|
-
user_inputs: list[str] = []
|
|
51
|
-
final_result: str = ""
|
|
52
|
-
should_end = False
|
|
53
|
-
while not should_end:
|
|
54
|
-
await asyncio.sleep(0.01)
|
|
55
|
-
previous_session_name: str | None = (
|
|
56
|
-
ctx.input.previous_session if is_first_time else ""
|
|
57
|
-
)
|
|
58
|
-
start_new: bool = ctx.input.start_new if is_first_time else False
|
|
59
|
-
if is_first_time and ctx.input.message.strip() != "":
|
|
60
|
-
user_input = ctx.input.message
|
|
61
|
-
else:
|
|
62
|
-
# Get user input based on mode
|
|
63
|
-
if not multiline_mode:
|
|
64
|
-
ctx.print("š¬ >>", plain=True)
|
|
65
|
-
user_input = await llm_chat_trigger.wait(reader, ctx)
|
|
66
|
-
if not multiline_mode:
|
|
67
|
-
ctx.print("", plain=True)
|
|
68
|
-
# At this point, is_first_time has to be False
|
|
69
|
-
if is_first_time:
|
|
70
|
-
is_first_time = False
|
|
71
|
-
# Handle user input (including slash commands)
|
|
72
|
-
if multiline_mode:
|
|
73
|
-
if is_command_match(user_input, MULTILINE_END_CMD):
|
|
74
|
-
ctx.print("", plain=True)
|
|
75
|
-
multiline_mode = False
|
|
76
|
-
else:
|
|
77
|
-
user_inputs.append(user_input)
|
|
78
|
-
continue
|
|
79
|
-
else:
|
|
80
|
-
if is_command_match(user_input, QUIT_CMD):
|
|
81
|
-
should_end = True
|
|
82
|
-
elif is_command_match(user_input, MULTILINE_START_CMD):
|
|
83
|
-
multiline_mode = True
|
|
84
|
-
ctx.print("", plain=True)
|
|
85
|
-
continue
|
|
86
|
-
elif is_command_match(user_input, WORKFLOW_CMD):
|
|
87
|
-
current_workflows = get_new_workflows(current_workflows, user_input)
|
|
88
|
-
print_current_workflows(ctx, current_workflows)
|
|
89
|
-
continue
|
|
90
|
-
elif is_command_match(user_input, SAVE_CMD):
|
|
91
|
-
save_final_result(ctx, user_input, final_result)
|
|
92
|
-
continue
|
|
93
|
-
elif is_command_match(user_input, ATTACHMENT_CMD):
|
|
94
|
-
current_attachments = get_new_attachments(
|
|
95
|
-
current_attachments, user_input
|
|
96
|
-
)
|
|
97
|
-
print_current_attachments(ctx, current_attachments)
|
|
98
|
-
continue
|
|
99
|
-
elif is_command_match(user_input, YOLO_CMD):
|
|
100
|
-
current_yolo_mode = get_new_yolo_mode(current_yolo_mode, user_input)
|
|
101
|
-
print_current_yolo_mode(ctx, current_yolo_mode)
|
|
102
|
-
continue
|
|
103
|
-
elif is_command_match(user_input, RUN_CLI_CMD):
|
|
104
|
-
run_cli_command(ctx, user_input)
|
|
105
|
-
continue
|
|
106
|
-
elif is_command_match(user_input, HELP_CMD):
|
|
107
|
-
print_commands(ctx)
|
|
108
|
-
continue
|
|
109
|
-
else:
|
|
110
|
-
user_inputs.append(user_input)
|
|
111
|
-
# Trigger LLM
|
|
112
|
-
user_prompt = "\n".join(user_inputs)
|
|
113
|
-
user_inputs = []
|
|
114
|
-
result = await _trigger_ask_and_wait_for_result(
|
|
115
|
-
ctx=ctx,
|
|
116
|
-
user_prompt=user_prompt,
|
|
117
|
-
attach=current_attachments,
|
|
118
|
-
workflows=current_workflows,
|
|
119
|
-
yolo_mode=current_yolo_mode,
|
|
120
|
-
previous_session_name=previous_session_name,
|
|
121
|
-
start_new=start_new,
|
|
122
|
-
)
|
|
123
|
-
current_attachments = ""
|
|
124
|
-
final_result = final_result if result is None else result
|
|
125
|
-
if ctx.is_web_mode or not is_tty:
|
|
126
|
-
return final_result
|
|
127
|
-
return final_result
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
async def _setup_input_reader(
|
|
131
|
-
is_interactive: bool,
|
|
132
|
-
) -> "PromptSession[Any] | StreamReader":
|
|
133
|
-
"""Sets up and returns the appropriate asynchronous input reader."""
|
|
134
|
-
if is_interactive:
|
|
135
|
-
from prompt_toolkit import PromptSession
|
|
136
|
-
|
|
137
|
-
return PromptSession()
|
|
138
|
-
|
|
139
|
-
loop = asyncio.get_event_loop()
|
|
140
|
-
reader = asyncio.StreamReader(loop=loop)
|
|
141
|
-
protocol = asyncio.StreamReaderProtocol(reader)
|
|
142
|
-
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
|
|
143
|
-
return reader
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
async def _trigger_ask_and_wait_for_result(
|
|
147
|
-
ctx: AnyContext,
|
|
148
|
-
user_prompt: str,
|
|
149
|
-
attach: str,
|
|
150
|
-
workflows: str,
|
|
151
|
-
yolo_mode: bool | str,
|
|
152
|
-
previous_session_name: str | None = None,
|
|
153
|
-
start_new: bool = False,
|
|
154
|
-
) -> str | None:
|
|
155
|
-
"""
|
|
156
|
-
Triggers the LLM ask task and waits for the result via XCom.
|
|
157
|
-
|
|
158
|
-
Args:
|
|
159
|
-
ctx: The context object for the task.
|
|
160
|
-
user_prompt: The user's message to send to the LLM.
|
|
161
|
-
previous_session_name: The name of the previous chat session (optional).
|
|
162
|
-
start_new: Whether to start a new conversation (optional).
|
|
163
|
-
|
|
164
|
-
Returns:
|
|
165
|
-
The result from the LLM task, or None if the user prompt is empty.
|
|
166
|
-
"""
|
|
167
|
-
if user_prompt.strip() == "":
|
|
168
|
-
return None
|
|
169
|
-
await _trigger_ask(
|
|
170
|
-
ctx, user_prompt, attach, workflows, yolo_mode, previous_session_name, start_new
|
|
171
|
-
)
|
|
172
|
-
result = await _wait_ask_result(ctx)
|
|
173
|
-
md_result = render_markdown(result) if result is not None else ""
|
|
174
|
-
ctx.print("\nš¤ >>", plain=True)
|
|
175
|
-
ctx.print(md_result, plain=True)
|
|
176
|
-
ctx.print("", plain=True)
|
|
177
|
-
return result
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def get_llm_ask_input_mapping(callback_ctx: AnyContext):
|
|
181
|
-
"""
|
|
182
|
-
Generates the input mapping for the LLM ask task from the callback context.
|
|
183
|
-
|
|
184
|
-
Args:
|
|
185
|
-
callback_ctx: The context object for the callback.
|
|
186
|
-
|
|
187
|
-
Returns:
|
|
188
|
-
A dictionary containing the input mapping for the LLM ask task.
|
|
189
|
-
"""
|
|
190
|
-
data = callback_ctx.xcom.ask_trigger.pop()
|
|
191
|
-
system_prompt = callback_ctx.input.system_prompt
|
|
192
|
-
if system_prompt is None or system_prompt.strip() == "":
|
|
193
|
-
system_prompt = llm_config.default_interactive_system_prompt
|
|
194
|
-
return {
|
|
195
|
-
"model": callback_ctx.input.model,
|
|
196
|
-
"base-url": callback_ctx.input.base_url,
|
|
197
|
-
"api-key": callback_ctx.input.api_key,
|
|
198
|
-
"system-prompt": system_prompt,
|
|
199
|
-
"start-new": data.get("start_new"),
|
|
200
|
-
"previous-session": data.get("previous_session_name"),
|
|
201
|
-
"message": data.get("message"),
|
|
202
|
-
"attach": data.get("attach"),
|
|
203
|
-
"workflows": data.get("workflows"),
|
|
204
|
-
"yolo": data.get("yolo"),
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
async def _trigger_ask(
|
|
209
|
-
ctx: AnyContext,
|
|
210
|
-
user_prompt: str,
|
|
211
|
-
attach: str,
|
|
212
|
-
workflows: str,
|
|
213
|
-
yolo_mode: bool | str,
|
|
214
|
-
previous_session_name: str | None = None,
|
|
215
|
-
start_new: bool = False,
|
|
216
|
-
):
|
|
217
|
-
"""
|
|
218
|
-
Triggers the LLM ask task by pushing data to the 'ask_trigger' XCom queue.
|
|
219
|
-
|
|
220
|
-
Args:
|
|
221
|
-
ctx: The context object for the task.
|
|
222
|
-
user_prompt: The user's message to send to the LLM.
|
|
223
|
-
previous_session_name: The name of the previous chat session (optional).
|
|
224
|
-
start_new: Whether to start a new conversation (optional).
|
|
225
|
-
"""
|
|
226
|
-
if previous_session_name is None:
|
|
227
|
-
previous_session_name = await _wait_ask_session_name(ctx)
|
|
228
|
-
ctx.xcom["ask_trigger"].push(
|
|
229
|
-
{
|
|
230
|
-
"previous_session_name": previous_session_name,
|
|
231
|
-
"start_new": start_new,
|
|
232
|
-
"message": user_prompt,
|
|
233
|
-
"attach": attach,
|
|
234
|
-
"workflows": workflows,
|
|
235
|
-
"yolo": yolo_mode,
|
|
236
|
-
}
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
async def _wait_ask_result(ctx: AnyContext) -> str | None:
|
|
241
|
-
"""
|
|
242
|
-
Waits for and retrieves the LLM task result from the 'ask_result' XCom queue.
|
|
243
|
-
|
|
244
|
-
Args:
|
|
245
|
-
ctx: The context object for the task.
|
|
246
|
-
|
|
247
|
-
Returns:
|
|
248
|
-
The result string from the LLM task.
|
|
249
|
-
"""
|
|
250
|
-
while "ask_result" not in ctx.xcom or len(ctx.xcom.ask_result) == 0:
|
|
251
|
-
await asyncio.sleep(0.1)
|
|
252
|
-
if "ask_error" in ctx.xcom and len(ctx.xcom.ask_error) > 0:
|
|
253
|
-
ctx.xcom.ask_error.pop()
|
|
254
|
-
return None
|
|
255
|
-
return ctx.xcom.ask_result.pop()
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
async def _wait_ask_session_name(ctx: AnyContext) -> str:
|
|
259
|
-
"""
|
|
260
|
-
Waits for and retrieves the LLM chat session name from the 'ask_session_name' XCom queue.
|
|
261
|
-
|
|
262
|
-
Args:
|
|
263
|
-
ctx: The context object for the task.
|
|
264
|
-
|
|
265
|
-
Returns:
|
|
266
|
-
The session name string.
|
|
267
|
-
"""
|
|
268
|
-
while "ask_session_name" not in ctx.xcom or len(ctx.xcom.ask_session_name) == 0:
|
|
269
|
-
await asyncio.sleep(0.1)
|
|
270
|
-
return ctx.xcom.ask_session_name.pop()
|