zrb 1.13.1__py3-none-any.whl → 1.21.33__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/__init__.py +2 -6
- zrb/attr/type.py +10 -7
- zrb/builtin/__init__.py +2 -0
- zrb/builtin/git.py +12 -1
- zrb/builtin/group.py +31 -15
- zrb/builtin/http.py +7 -8
- zrb/builtin/llm/attachment.py +40 -0
- zrb/builtin/llm/chat_completion.py +287 -0
- zrb/builtin/llm/chat_session.py +130 -144
- zrb/builtin/llm/chat_session_cmd.py +288 -0
- zrb/builtin/llm/chat_trigger.py +78 -0
- zrb/builtin/llm/history.py +4 -4
- zrb/builtin/llm/llm_ask.py +218 -110
- zrb/builtin/llm/tool/api.py +74 -62
- zrb/builtin/llm/tool/cli.py +56 -21
- zrb/builtin/llm/tool/code.py +57 -47
- zrb/builtin/llm/tool/file.py +292 -255
- zrb/builtin/llm/tool/note.py +84 -0
- zrb/builtin/llm/tool/rag.py +25 -18
- zrb/builtin/llm/tool/search/__init__.py +1 -0
- zrb/builtin/llm/tool/search/brave.py +66 -0
- zrb/builtin/llm/tool/search/searxng.py +61 -0
- zrb/builtin/llm/tool/search/serpapi.py +61 -0
- zrb/builtin/llm/tool/sub_agent.py +53 -26
- zrb/builtin/llm/tool/web.py +94 -157
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
- zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
- zrb/builtin/searxng/config/settings.yml +5671 -0
- zrb/builtin/searxng/start.py +21 -0
- zrb/builtin/setup/latex/ubuntu.py +1 -0
- zrb/builtin/setup/ubuntu.py +1 -1
- zrb/builtin/shell/autocomplete/bash.py +4 -3
- zrb/builtin/shell/autocomplete/zsh.py +4 -3
- zrb/config/config.py +297 -79
- zrb/config/default_prompt/file_extractor_system_prompt.md +109 -9
- zrb/config/default_prompt/interactive_system_prompt.md +25 -28
- zrb/config/default_prompt/persona.md +1 -1
- zrb/config/default_prompt/repo_extractor_system_prompt.md +31 -31
- zrb/config/default_prompt/repo_summarizer_system_prompt.md +27 -8
- zrb/config/default_prompt/summarization_prompt.md +57 -16
- zrb/config/default_prompt/system_prompt.md +29 -25
- zrb/config/llm_config.py +129 -24
- zrb/config/llm_context/config.py +127 -90
- zrb/config/llm_context/config_parser.py +1 -7
- zrb/config/llm_context/workflow.py +81 -0
- zrb/config/llm_rate_limitter.py +100 -47
- zrb/context/any_shared_context.py +7 -1
- zrb/context/context.py +8 -2
- zrb/context/shared_context.py +6 -8
- zrb/group/any_group.py +12 -5
- zrb/group/group.py +67 -3
- zrb/input/any_input.py +5 -1
- zrb/input/base_input.py +18 -6
- zrb/input/option_input.py +13 -1
- zrb/input/text_input.py +7 -24
- zrb/runner/cli.py +21 -20
- zrb/runner/common_util.py +24 -19
- zrb/runner/web_route/task_input_api_route.py +5 -5
- zrb/runner/web_route/task_session_api_route.py +1 -4
- zrb/runner/web_util/user.py +7 -3
- zrb/session/any_session.py +12 -6
- zrb/session/session.py +39 -18
- zrb/task/any_task.py +24 -3
- zrb/task/base/context.py +17 -9
- zrb/task/base/execution.py +15 -8
- zrb/task/base/lifecycle.py +8 -4
- zrb/task/base/monitoring.py +12 -7
- zrb/task/base_task.py +69 -5
- zrb/task/base_trigger.py +12 -5
- zrb/task/llm/agent.py +130 -145
- zrb/task/llm/agent_runner.py +152 -0
- zrb/task/llm/config.py +45 -13
- zrb/task/llm/conversation_history.py +110 -29
- zrb/task/llm/conversation_history_model.py +4 -179
- zrb/task/llm/default_workflow/coding/workflow.md +41 -0
- zrb/task/llm/default_workflow/copywriting/workflow.md +68 -0
- zrb/task/llm/default_workflow/git/workflow.md +118 -0
- zrb/task/llm/default_workflow/golang/workflow.md +128 -0
- zrb/task/llm/default_workflow/html-css/workflow.md +135 -0
- zrb/task/llm/default_workflow/java/workflow.md +146 -0
- zrb/task/llm/default_workflow/javascript/workflow.md +158 -0
- zrb/task/llm/default_workflow/python/workflow.md +160 -0
- zrb/task/llm/default_workflow/researching/workflow.md +153 -0
- zrb/task/llm/default_workflow/rust/workflow.md +162 -0
- zrb/task/llm/default_workflow/shell/workflow.md +299 -0
- zrb/task/llm/file_replacement.py +206 -0
- zrb/task/llm/file_tool_model.py +57 -0
- zrb/task/llm/history_processor.py +206 -0
- zrb/task/llm/history_summarization.py +2 -192
- zrb/task/llm/print_node.py +192 -64
- zrb/task/llm/prompt.py +198 -153
- zrb/task/llm/subagent_conversation_history.py +41 -0
- zrb/task/llm/tool_confirmation_completer.py +41 -0
- zrb/task/llm/tool_wrapper.py +216 -55
- zrb/task/llm/workflow.py +76 -0
- zrb/task/llm_task.py +122 -70
- zrb/task/make_task.py +2 -3
- zrb/task/rsync_task.py +25 -10
- zrb/task/scheduler.py +4 -4
- zrb/util/attr.py +54 -39
- zrb/util/cli/markdown.py +12 -0
- zrb/util/cli/text.py +30 -0
- zrb/util/file.py +27 -11
- zrb/util/git.py +2 -2
- zrb/util/{llm/prompt.py → markdown.py} +2 -3
- zrb/util/string/conversion.py +1 -1
- zrb/util/truncate.py +23 -0
- zrb/util/yaml.py +204 -0
- zrb/xcom/xcom.py +10 -0
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/METADATA +40 -20
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/RECORD +114 -83
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/WHEEL +1 -1
- zrb/task/llm/default_workflow/coding.md +0 -24
- zrb/task/llm/default_workflow/copywriting.md +0 -17
- zrb/task/llm/default_workflow/researching.md +0 -18
- {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/entry_points.txt +0 -0
zrb/builtin/llm/tool/cli.py
CHANGED
|
@@ -1,33 +1,68 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import subprocess
|
|
2
|
+
import sys
|
|
3
3
|
|
|
4
|
+
if sys.version_info >= (3, 12):
|
|
5
|
+
from typing import TypedDict
|
|
6
|
+
else:
|
|
7
|
+
from typing_extensions import TypedDict
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
|
|
10
|
+
class ShellCommandResult(TypedDict):
|
|
11
|
+
"""
|
|
12
|
+
Result of shell command execution
|
|
13
|
+
|
|
14
|
+
Attributes:
|
|
15
|
+
return_code: The return code, 0 indicating no error
|
|
16
|
+
stdout: Standard output
|
|
17
|
+
stderr: Standard error
|
|
6
18
|
"""
|
|
7
|
-
Executes a shell command on the user's local machine and returns the output.
|
|
8
19
|
|
|
9
|
-
|
|
20
|
+
return_code: int
|
|
21
|
+
stdout: str
|
|
22
|
+
stderr: str
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
|
|
25
|
+
def run_shell_command(command: str, timeout: int = 30) -> ShellCommandResult:
|
|
26
|
+
"""
|
|
27
|
+
Executes a non-interactive shell command on the user's machine.
|
|
28
|
+
|
|
29
|
+
**EFFICIENCY TIP:**
|
|
30
|
+
Combine multiple shell commands into a single call using `&&` or `;` to save steps.
|
|
31
|
+
Example: `mkdir new_dir && cd new_dir && touch file.txt`
|
|
32
|
+
|
|
33
|
+
CRITICAL: This tool runs with user-level permissions. Explain commands that modify
|
|
34
|
+
the system (e.g., `git`, `pip`) and ask for confirmation.
|
|
35
|
+
IMPORTANT: Long-running processes should be run in the background (e.g., `command &`).
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
run_shell_command(command='ls -l', timeout=30)
|
|
12
39
|
|
|
13
40
|
Args:
|
|
14
|
-
command (str): The exact shell command to
|
|
41
|
+
command (str): The exact shell command to be executed.
|
|
42
|
+
timeout (int): The maximum time in seconds to wait for the command to finish.
|
|
43
|
+
Defaults to 30.
|
|
15
44
|
|
|
16
45
|
Returns:
|
|
17
|
-
|
|
18
|
-
and standard error (stderr) from the command.
|
|
19
|
-
Example: {"return_code": 0, "stdout": "ok", "stderr": ""}
|
|
46
|
+
dict: return_code, stdout, and stderr.
|
|
20
47
|
"""
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
48
|
+
try:
|
|
49
|
+
result = subprocess.run(
|
|
50
|
+
command,
|
|
51
|
+
shell=True,
|
|
52
|
+
capture_output=True,
|
|
53
|
+
text=True,
|
|
54
|
+
timeout=timeout,
|
|
55
|
+
)
|
|
56
|
+
return {
|
|
57
|
+
"return_code": int(result.returncode),
|
|
58
|
+
"stdout": str(result.stdout or ""),
|
|
59
|
+
"stderr": str(result.stderr or ""),
|
|
60
|
+
}
|
|
61
|
+
except subprocess.TimeoutExpired as e:
|
|
62
|
+
stdout = e.stdout.decode() if isinstance(e.stdout, bytes) else (e.stdout or "")
|
|
63
|
+
stderr = e.stderr.decode() if isinstance(e.stderr, bytes) else (e.stderr or "")
|
|
64
|
+
return {
|
|
65
|
+
"return_code": 124,
|
|
66
|
+
"stdout": str(stdout),
|
|
67
|
+
"stderr": f"{stderr}\nError: Command timed out after {timeout} seconds".strip(),
|
|
32
68
|
}
|
|
33
|
-
)
|
zrb/builtin/llm/tool/code.py
CHANGED
|
@@ -6,6 +6,7 @@ from zrb.builtin.llm.tool.sub_agent import create_sub_agent_tool
|
|
|
6
6
|
from zrb.config.config import CFG
|
|
7
7
|
from zrb.config.llm_rate_limitter import llm_rate_limitter
|
|
8
8
|
from zrb.context.any_context import AnyContext
|
|
9
|
+
from zrb.util.cli.style import stylize_faint
|
|
9
10
|
|
|
10
11
|
_DEFAULT_EXTENSIONS = [
|
|
11
12
|
"py",
|
|
@@ -49,36 +50,41 @@ _DEFAULT_EXTENSIONS = [
|
|
|
49
50
|
async def analyze_repo(
|
|
50
51
|
ctx: AnyContext,
|
|
51
52
|
path: str,
|
|
52
|
-
|
|
53
|
+
query: str,
|
|
53
54
|
extensions: list[str] = _DEFAULT_EXTENSIONS,
|
|
54
55
|
exclude_patterns: list[str] = DEFAULT_EXCLUDED_PATTERNS,
|
|
55
56
|
extraction_token_threshold: int | None = None,
|
|
56
57
|
summarization_token_threshold: int | None = None,
|
|
57
58
|
) -> str:
|
|
58
59
|
"""
|
|
59
|
-
|
|
60
|
+
Analyzes a code repository or directory to answer a specific query.
|
|
60
61
|
|
|
61
|
-
|
|
62
|
+
CRITICAL: The query must contain ALL necessary context, instructions, and information.
|
|
63
|
+
The sub-agent performing the analysis does NOT share your current conversation
|
|
64
|
+
history, memory, or global context.
|
|
65
|
+
The quality of analysis depends entirely on the query. Vague queries yield poor
|
|
66
|
+
results.
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
IMPORTANT: This tool can be slow and expensive on large repositories. Use judiciously.
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
analyze_repo(
|
|
72
|
+
path='src/my_project',
|
|
73
|
+
query='Summarize the main functionalities by analyzing Python files.',
|
|
74
|
+
extensions=['py']
|
|
75
|
+
)
|
|
69
76
|
|
|
70
77
|
Args:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
ctx (AnyContext): The execution context.
|
|
79
|
+
path (str): Path to the directory or repository.
|
|
80
|
+
query (str): Clear and specific analysis question or goal.
|
|
81
|
+
extensions (list[str], optional): File extensions to include.
|
|
82
|
+
exclude_patterns (list[str], optional): Glob patterns to exclude.
|
|
83
|
+
extraction_token_threshold (int, optional): Token limit for extraction sub-agent.
|
|
84
|
+
summarization_token_threshold (int, optional): Token limit for summarization sub-agent.
|
|
77
85
|
|
|
78
86
|
Returns:
|
|
79
|
-
str:
|
|
80
|
-
Raises:
|
|
81
|
-
Exception: If an error occurs during the analysis.
|
|
87
|
+
str: Detailed, markdown-formatted analysis and summary.
|
|
82
88
|
"""
|
|
83
89
|
if extraction_token_threshold is None:
|
|
84
90
|
extraction_token_threshold = CFG.LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD
|
|
@@ -88,23 +94,26 @@ async def analyze_repo(
|
|
|
88
94
|
)
|
|
89
95
|
abs_path = os.path.abspath(os.path.expanduser(path))
|
|
90
96
|
file_metadatas = _get_file_metadatas(abs_path, extensions, exclude_patterns)
|
|
91
|
-
ctx.print("Extraction")
|
|
97
|
+
ctx.print(stylize_faint(" 📝 Extraction"), plain=True)
|
|
92
98
|
extracted_infos = await _extract_info(
|
|
93
99
|
ctx,
|
|
94
100
|
file_metadatas=file_metadatas,
|
|
95
|
-
|
|
101
|
+
query=query,
|
|
96
102
|
token_limit=extraction_token_threshold,
|
|
97
103
|
)
|
|
104
|
+
if len(extracted_infos) == 0:
|
|
105
|
+
raise RuntimeError(
|
|
106
|
+
"No info can be extracted, adjust extensions or exclude_patterns."
|
|
107
|
+
)
|
|
98
108
|
if len(extracted_infos) == 1:
|
|
99
109
|
return extracted_infos[0]
|
|
100
|
-
ctx.print("Summarization")
|
|
101
110
|
summarized_infos = extracted_infos
|
|
102
111
|
while len(summarized_infos) > 1:
|
|
103
|
-
ctx.print("Summarization")
|
|
112
|
+
ctx.print(stylize_faint(" 📝 Summarization"), plain=True)
|
|
104
113
|
summarized_infos = await _summarize_info(
|
|
105
114
|
ctx,
|
|
106
115
|
extracted_infos=summarized_infos,
|
|
107
|
-
|
|
116
|
+
query=query,
|
|
108
117
|
token_limit=summarization_token_threshold,
|
|
109
118
|
)
|
|
110
119
|
return summarized_infos[0]
|
|
@@ -122,11 +131,11 @@ def _get_file_metadatas(
|
|
|
122
131
|
if not any(file.endswith(f".{ext}") for ext in extensions):
|
|
123
132
|
continue
|
|
124
133
|
file_path = os.path.join(root, file)
|
|
125
|
-
if is_excluded(file_path, exclude_patterns):
|
|
126
|
-
continue
|
|
127
134
|
try:
|
|
135
|
+
rel_path = os.path.relpath(file_path, dir_path)
|
|
136
|
+
if is_excluded(rel_path, exclude_patterns):
|
|
137
|
+
continue
|
|
128
138
|
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
|
129
|
-
rel_path = os.path.relpath(file_path, dir_path)
|
|
130
139
|
metadata_list.append({"path": rel_path, "content": f.read()})
|
|
131
140
|
except Exception as e:
|
|
132
141
|
print(f"Error reading file {file_path}: {e}")
|
|
@@ -137,13 +146,15 @@ def _get_file_metadatas(
|
|
|
137
146
|
async def _extract_info(
|
|
138
147
|
ctx: AnyContext,
|
|
139
148
|
file_metadatas: list[dict[str, str]],
|
|
140
|
-
|
|
149
|
+
query: str,
|
|
141
150
|
token_limit: int,
|
|
142
151
|
) -> list[str]:
|
|
143
152
|
extract = create_sub_agent_tool(
|
|
144
153
|
tool_name="extract",
|
|
145
154
|
tool_description="extract",
|
|
146
155
|
system_prompt=CFG.LLM_REPO_EXTRACTOR_SYSTEM_PROMPT,
|
|
156
|
+
auto_summarize=False,
|
|
157
|
+
remember_history=False,
|
|
147
158
|
)
|
|
148
159
|
extracted_infos = []
|
|
149
160
|
content_buffer = []
|
|
@@ -155,7 +166,7 @@ async def _extract_info(
|
|
|
155
166
|
file_str = json.dumps(file_obj)
|
|
156
167
|
if current_token_count + llm_rate_limitter.count_token(file_str) > token_limit:
|
|
157
168
|
if content_buffer:
|
|
158
|
-
prompt = _create_extract_info_prompt(
|
|
169
|
+
prompt = json.dumps(_create_extract_info_prompt(query, content_buffer))
|
|
159
170
|
extracted_info = await extract(
|
|
160
171
|
ctx, llm_rate_limitter.clip_prompt(prompt, token_limit)
|
|
161
172
|
)
|
|
@@ -165,10 +176,9 @@ async def _extract_info(
|
|
|
165
176
|
else:
|
|
166
177
|
content_buffer.append(file_obj)
|
|
167
178
|
current_token_count += llm_rate_limitter.count_token(file_str)
|
|
168
|
-
|
|
169
179
|
# Process any remaining content in the buffer
|
|
170
180
|
if content_buffer:
|
|
171
|
-
prompt = _create_extract_info_prompt(
|
|
181
|
+
prompt = json.dumps(_create_extract_info_prompt(query, content_buffer))
|
|
172
182
|
extracted_info = await extract(
|
|
173
183
|
ctx, llm_rate_limitter.clip_prompt(prompt, token_limit)
|
|
174
184
|
)
|
|
@@ -176,25 +186,25 @@ async def _extract_info(
|
|
|
176
186
|
return extracted_infos
|
|
177
187
|
|
|
178
188
|
|
|
179
|
-
def _create_extract_info_prompt(
|
|
180
|
-
return
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
)
|
|
189
|
+
def _create_extract_info_prompt(query: str, content_buffer: list[dict]) -> dict:
|
|
190
|
+
return {
|
|
191
|
+
"main_assistant_query": query,
|
|
192
|
+
"files": content_buffer,
|
|
193
|
+
}
|
|
186
194
|
|
|
187
195
|
|
|
188
196
|
async def _summarize_info(
|
|
189
197
|
ctx: AnyContext,
|
|
190
198
|
extracted_infos: list[str],
|
|
191
|
-
|
|
199
|
+
query: str,
|
|
192
200
|
token_limit: int,
|
|
193
201
|
) -> list[str]:
|
|
194
202
|
summarize = create_sub_agent_tool(
|
|
195
203
|
tool_name="extract",
|
|
196
204
|
tool_description="extract",
|
|
197
205
|
system_prompt=CFG.LLM_REPO_SUMMARIZER_SYSTEM_PROMPT,
|
|
206
|
+
auto_summarize=False,
|
|
207
|
+
remember_history=False,
|
|
198
208
|
)
|
|
199
209
|
summarized_infos = []
|
|
200
210
|
content_buffer = ""
|
|
@@ -202,7 +212,9 @@ async def _summarize_info(
|
|
|
202
212
|
new_prompt = content_buffer + extracted_info
|
|
203
213
|
if llm_rate_limitter.count_token(new_prompt) > token_limit:
|
|
204
214
|
if content_buffer:
|
|
205
|
-
prompt =
|
|
215
|
+
prompt = json.dumps(
|
|
216
|
+
_create_summarize_info_prompt(query, content_buffer)
|
|
217
|
+
)
|
|
206
218
|
summarized_info = await summarize(
|
|
207
219
|
ctx, llm_rate_limitter.clip_prompt(prompt, token_limit)
|
|
208
220
|
)
|
|
@@ -213,7 +225,7 @@ async def _summarize_info(
|
|
|
213
225
|
|
|
214
226
|
# Process any remaining content in the buffer
|
|
215
227
|
if content_buffer:
|
|
216
|
-
prompt = _create_summarize_info_prompt(
|
|
228
|
+
prompt = json.dumps(_create_summarize_info_prompt(query, content_buffer))
|
|
217
229
|
summarized_info = await summarize(
|
|
218
230
|
ctx, llm_rate_limitter.clip_prompt(prompt, token_limit)
|
|
219
231
|
)
|
|
@@ -221,10 +233,8 @@ async def _summarize_info(
|
|
|
221
233
|
return summarized_infos
|
|
222
234
|
|
|
223
235
|
|
|
224
|
-
def _create_summarize_info_prompt(
|
|
225
|
-
return
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
)
|
|
236
|
+
def _create_summarize_info_prompt(query: str, content_buffer: str) -> dict:
|
|
237
|
+
return {
|
|
238
|
+
"main_assistant_query": query,
|
|
239
|
+
"extracted_info": content_buffer,
|
|
240
|
+
}
|