zrb 1.9.12__py3-none-any.whl → 1.9.14__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/builtin/llm/chat_session.py +71 -46
- zrb/config/llm_config.py +117 -185
- zrb/task/llm/error.py +24 -8
- zrb/task/rsync_task.py +1 -1
- zrb/util/git_diff_model.py +10 -7
- zrb/util/git_subtree_model.py +28 -7
- zrb/util/string/conversion.py +1 -1
- zrb/util/todo_model.py +23 -17
- {zrb-1.9.12.dist-info → zrb-1.9.14.dist-info}/METADATA +1 -1
- {zrb-1.9.12.dist-info → zrb-1.9.14.dist-info}/RECORD +12 -12
- {zrb-1.9.12.dist-info → zrb-1.9.14.dist-info}/WHEEL +0 -0
- {zrb-1.9.12.dist-info → zrb-1.9.14.dist-info}/entry_points.txt +0 -0
zrb/builtin/llm/chat_session.py
CHANGED
@@ -6,6 +6,7 @@ conversation flow via XCom.
|
|
6
6
|
"""
|
7
7
|
|
8
8
|
import asyncio
|
9
|
+
import sys
|
9
10
|
|
10
11
|
from zrb.config.llm_config import llm_config
|
11
12
|
from zrb.context.any_context import AnyContext
|
@@ -15,45 +16,26 @@ from zrb.util.cli.style import stylize_bold_yellow, stylize_faint
|
|
15
16
|
async def read_user_prompt(ctx: AnyContext) -> str:
|
16
17
|
"""
|
17
18
|
Reads user input from the CLI for an interactive chat session.
|
18
|
-
|
19
|
-
Handles special commands like /bye, /multi, /end, and /help.
|
20
|
-
Triggers the LLM task and waits for the result.
|
21
|
-
|
22
|
-
Args:
|
23
|
-
ctx: The context object for the task.
|
24
|
-
|
25
|
-
Returns:
|
26
|
-
The final result from the LLM session.
|
19
|
+
Orchestrates the session by calling helper functions.
|
27
20
|
"""
|
28
|
-
from prompt_toolkit import PromptSession
|
29
|
-
|
30
21
|
_show_info(ctx)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
ctx.print("", plain=True)
|
35
|
-
result = await _trigger_ask_and_wait_for_result(
|
36
|
-
ctx,
|
37
|
-
user_prompt=ctx.input.message,
|
38
|
-
previous_session_name=ctx.input.previous_session,
|
39
|
-
start_new=ctx.input.start_new,
|
40
|
-
)
|
41
|
-
if result is not None:
|
42
|
-
final_result = result
|
43
|
-
if ctx.env.get("_ZRB_WEB_ENV", "0") != "0":
|
44
|
-
# On web environment this won't be interactive
|
22
|
+
is_web = ctx.env.get("_ZRB_WEB_ENV", "0") == "1"
|
23
|
+
final_result = await _handle_initial_message(ctx)
|
24
|
+
if is_web:
|
45
25
|
return final_result
|
26
|
+
is_interactive = sys.stdin.isatty()
|
27
|
+
reader = await _setup_input_reader(is_interactive)
|
46
28
|
multiline_mode = False
|
47
29
|
user_inputs = []
|
48
|
-
user_input_session = PromptSession()
|
49
30
|
while True:
|
50
31
|
await asyncio.sleep(0.01)
|
32
|
+
# Get user input based on mode
|
51
33
|
if not multiline_mode:
|
52
34
|
ctx.print("💬 >>", plain=True)
|
53
|
-
user_input = await
|
35
|
+
user_input = await _read_next_line(is_interactive, reader, ctx)
|
54
36
|
if not multiline_mode:
|
55
37
|
ctx.print("", plain=True)
|
56
|
-
# Handle
|
38
|
+
# Handle user input
|
57
39
|
if user_input.strip().lower() in ("/bye", "/quit", "/q", "/exit"):
|
58
40
|
user_prompt = "\n".join(user_inputs)
|
59
41
|
user_inputs = []
|
@@ -86,6 +68,67 @@ async def read_user_prompt(ctx: AnyContext) -> str:
|
|
86
68
|
return final_result
|
87
69
|
|
88
70
|
|
71
|
+
def _show_info(ctx: AnyContext):
|
72
|
+
"""
|
73
|
+
Displays the available chat session commands to the user.
|
74
|
+
Args:
|
75
|
+
ctx: The context object for the task.
|
76
|
+
"""
|
77
|
+
ctx.print(
|
78
|
+
(
|
79
|
+
f" {stylize_bold_yellow('/bye')} {stylize_faint('Quit from chat session')}\n"
|
80
|
+
f" {stylize_bold_yellow('/multi')} {stylize_faint('Start multiline input')}\n"
|
81
|
+
f" {stylize_bold_yellow('/end')} {stylize_faint('End multiline input')}\n"
|
82
|
+
f" {stylize_bold_yellow('/help')} {stylize_faint('Show this message')}\n"
|
83
|
+
),
|
84
|
+
plain=True,
|
85
|
+
)
|
86
|
+
|
87
|
+
|
88
|
+
async def _handle_initial_message(ctx: AnyContext) -> str:
|
89
|
+
"""Processes the initial message from the command line."""
|
90
|
+
if not ctx.input.message or ctx.input.message.strip() == "":
|
91
|
+
return ""
|
92
|
+
ctx.print("💬 >>", plain=True)
|
93
|
+
ctx.print(ctx.input.message, plain=True)
|
94
|
+
ctx.print("", plain=True)
|
95
|
+
result = await _trigger_ask_and_wait_for_result(
|
96
|
+
ctx,
|
97
|
+
user_prompt=ctx.input.message,
|
98
|
+
previous_session_name=ctx.input.previous_session,
|
99
|
+
start_new=ctx.input.start_new,
|
100
|
+
)
|
101
|
+
return result if result is not None else ""
|
102
|
+
|
103
|
+
|
104
|
+
async def _setup_input_reader(is_interactive: bool):
|
105
|
+
"""Sets up and returns the appropriate asynchronous input reader."""
|
106
|
+
if is_interactive:
|
107
|
+
from prompt_toolkit import PromptSession
|
108
|
+
|
109
|
+
return PromptSession()
|
110
|
+
|
111
|
+
loop = asyncio.get_event_loop()
|
112
|
+
reader = asyncio.StreamReader(loop=loop)
|
113
|
+
protocol = asyncio.StreamReaderProtocol(reader)
|
114
|
+
await loop.connect_read_pipe(lambda: protocol, sys.stdin)
|
115
|
+
return reader
|
116
|
+
|
117
|
+
|
118
|
+
async def _read_next_line(is_interactive: bool, reader, ctx: AnyContext) -> str:
|
119
|
+
"""Reads one line of input using the provided reader."""
|
120
|
+
if is_interactive:
|
121
|
+
return await reader.prompt_async()
|
122
|
+
|
123
|
+
line_bytes = await reader.readline()
|
124
|
+
if not line_bytes:
|
125
|
+
return "/bye" # Signal to exit
|
126
|
+
|
127
|
+
user_input = line_bytes.decode().strip()
|
128
|
+
ctx.print(user_input, plain=True)
|
129
|
+
return user_input
|
130
|
+
|
131
|
+
|
89
132
|
async def _trigger_ask_and_wait_for_result(
|
90
133
|
ctx: AnyContext,
|
91
134
|
user_prompt: str,
|
@@ -211,21 +254,3 @@ async def _wait_ask_session_name(ctx: AnyContext) -> str:
|
|
211
254
|
while "ask_session_name" not in ctx.xcom or len(ctx.xcom.ask_session_name) == 0:
|
212
255
|
await asyncio.sleep(0.1)
|
213
256
|
return ctx.xcom.ask_session_name.pop()
|
214
|
-
|
215
|
-
|
216
|
-
def _show_info(ctx: AnyContext):
|
217
|
-
"""
|
218
|
-
Displays the available chat session commands to the user.
|
219
|
-
|
220
|
-
Args:
|
221
|
-
ctx: The context object for the task.
|
222
|
-
"""
|
223
|
-
ctx.print(
|
224
|
-
(
|
225
|
-
f" {stylize_bold_yellow('/bye')} {stylize_faint('Quit from chat session')}\n"
|
226
|
-
f" {stylize_bold_yellow('/multi')} {stylize_faint('Start multiline input')}\n"
|
227
|
-
f" {stylize_bold_yellow('/end')} {stylize_faint('End multiline input')}\n"
|
228
|
-
f" {stylize_bold_yellow('/help')} {stylize_faint('Show this message')}\n"
|
229
|
-
),
|
230
|
-
plain=True,
|
231
|
-
)
|
zrb/config/llm_config.py
CHANGED
@@ -8,207 +8,139 @@ if TYPE_CHECKING:
|
|
8
8
|
from pydantic_ai.settings import ModelSettings
|
9
9
|
|
10
10
|
|
11
|
-
_DEFAULT_PERSONA =
|
12
|
-
"You are a helpful and efficient AI agent specializing in CLI " "interaction."
|
13
|
-
)
|
11
|
+
_DEFAULT_PERSONA = "You are a helpful and efficient AI agent."
|
14
12
|
|
15
13
|
_DEFAULT_INTERACTIVE_SYSTEM_PROMPT = (
|
16
|
-
"
|
17
|
-
"
|
18
|
-
"1. **Analyze
|
19
|
-
"
|
20
|
-
"
|
21
|
-
"
|
22
|
-
"
|
23
|
-
"
|
24
|
-
"
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
31
|
-
"
|
32
|
-
"
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"
|
39
|
-
"
|
40
|
-
|
41
|
-
"
|
42
|
-
'
|
43
|
-
"
|
44
|
-
"
|
45
|
-
"
|
46
|
-
" confirm it was successful. Report the outcome to the user.\n\n"
|
14
|
+
"You are an expert AI agent in a CLI. You MUST follow this workflow for "
|
15
|
+
"this interactive session. Respond in GitHub-flavored Markdown.\n\n"
|
16
|
+
"1. **Analyze and Clarify:** Understand the user's goal. If the request "
|
17
|
+
"is ambiguous, ask clarifying questions. Use your tools to gather "
|
18
|
+
"necessary information before proceeding.\n\n"
|
19
|
+
"2. **Assess and Decide:**\n"
|
20
|
+
" * For **read-only** actions, proceed directly.\n"
|
21
|
+
" * For **destructive** actions (modifying or deleting data), you "
|
22
|
+
"MUST evaluate the risk. Consider the command's nature, the target's "
|
23
|
+
"importance (e.g., temp file vs. project file vs. system file), and the "
|
24
|
+
"user's specificity. Based on your assessment, decide the appropriate "
|
25
|
+
"course of action:\n"
|
26
|
+
" * **Low Risk:** Proceed directly.\n"
|
27
|
+
" * **Moderate Risk:** Proceed, but issue a warning.\n"
|
28
|
+
" * **High Risk or Vague Request:** Formulate a plan and ask "
|
29
|
+
"for approval.\n"
|
30
|
+
" * **Extreme Risk (e.g., operating on critical system "
|
31
|
+
"files):** Refuse and explain the danger.\n\n"
|
32
|
+
"3. **Execute and Verify (The E+V Loop):**\n"
|
33
|
+
" * Execute the action.\n"
|
34
|
+
" * **CRITICAL:** Immediately after execution, you MUST use a tool "
|
35
|
+
"to verify the outcome (e.g., after `write_file`, use `read_file`; "
|
36
|
+
"after `rm`, use `ls` to confirm absence).\n\n"
|
37
|
+
"4. **Report Results:**\n"
|
38
|
+
" * Provide a concise summary of the action taken.\n"
|
39
|
+
" * **You MUST explicitly state how you verified the action** (e.g., "
|
40
|
+
"'I have deleted the file and verified its removal by listing the "
|
41
|
+
"directory.').\n"
|
42
|
+
" * If an error occurs, report the error and the failed verification "
|
43
|
+
"step."
|
47
44
|
).strip()
|
48
45
|
|
49
46
|
_DEFAULT_SYSTEM_PROMPT = (
|
50
|
-
"
|
51
|
-
"
|
52
|
-
"
|
53
|
-
"
|
54
|
-
"
|
55
|
-
"
|
56
|
-
"
|
57
|
-
"
|
58
|
-
"
|
59
|
-
"
|
60
|
-
"
|
61
|
-
"
|
62
|
-
"
|
63
|
-
"
|
64
|
-
"
|
65
|
-
"
|
66
|
-
"
|
67
|
-
" example, if the plan is to create a file, check if it already\n"
|
68
|
-
" exists. If pre-conditions are not as expected, state that and stop.\n\n"
|
69
|
-
"4. **Assess Consent & Execute:**\n"
|
70
|
-
" - **You have standing consent to use any read-only tools** for\n"
|
71
|
-
" information gathering, planning, and verification. You do not\n"
|
72
|
-
" need to ask for permission for these actions.\n"
|
73
|
-
" - If the user's last instruction was an explicit command (e.g.,\n"
|
74
|
-
' "create file X", "delete Y"), you have consent. Proceed with the\n'
|
75
|
-
" action.\n"
|
76
|
-
' - If the request was general (e.g., "fix the bug") and your plan\n'
|
77
|
-
" involves a potentially altering action, you MUST explain the\n"
|
78
|
-
" action and ask for user approval before proceeding.\n\n"
|
79
|
-
"5. **Verify Outcome:** After executing the action, use read-only tools to\n"
|
80
|
-
" confirm it was successful. Report the outcome to the user.\n\n"
|
47
|
+
"You are an expert AI agent executing a one-shot CLI command. You MUST "
|
48
|
+
"follow this workflow. Your final output MUST be in GitHub-flavored "
|
49
|
+
"Markdown.\n\n"
|
50
|
+
"1. **Plan:** Internally devise a step-by-step plan. This plan MUST "
|
51
|
+
"include verification steps for each action.\n\n"
|
52
|
+
"2. **Execute and Verify (The E+V Loop):**\n"
|
53
|
+
" * Execute each step of your plan.\n"
|
54
|
+
" * **CRITICAL:** After each step, you MUST use a tool to verify "
|
55
|
+
"the outcome (e.g., check exit codes, read back file contents, check "
|
56
|
+
"system state).\n\n"
|
57
|
+
"3. **Report Final Outcome:**\n"
|
58
|
+
" * Provide a concise summary of the result.\n"
|
59
|
+
" * **You MUST explicitly state how you verified the final state** "
|
60
|
+
"(e.g., 'The script was executed, and I verified its success by checking "
|
61
|
+
"for the presence of the output file `results.txt`.').\n"
|
62
|
+
" * If an error occurred, report the error and the failed "
|
63
|
+
"verification step."
|
81
64
|
).strip()
|
82
65
|
|
83
66
|
_DEFAULT_SPECIAL_INSTRUCTION_PROMPT = (
|
84
|
-
"##
|
85
|
-
"
|
86
|
-
"
|
87
|
-
"
|
88
|
-
"
|
89
|
-
"existing code
|
90
|
-
"
|
91
|
-
"
|
92
|
-
"
|
93
|
-
"
|
94
|
-
"
|
95
|
-
"
|
96
|
-
"
|
97
|
-
"
|
98
|
-
"
|
99
|
-
"
|
100
|
-
"
|
101
|
-
"
|
102
|
-
"
|
103
|
-
"
|
104
|
-
"
|
105
|
-
"
|
106
|
-
"
|
107
|
-
"
|
108
|
-
"
|
109
|
-
"
|
110
|
-
"
|
111
|
-
"
|
112
|
-
"
|
113
|
-
"3.
|
114
|
-
"
|
115
|
-
"element per the approved plan.\n"
|
116
|
-
"5. **Verify:** Review work against the original request and the approved\n"
|
117
|
-
"plan. Ensure the application builds and runs without errors.\n"
|
118
|
-
"6. **Solicit Feedback:** Provide instructions on how to start the\n"
|
119
|
-
"application and request user feedback.\n\n"
|
120
|
-
"## Git Repository\n"
|
121
|
-
"If you are in a git repository, you can be asked to commit changes:\n"
|
122
|
-
"- Use `git status` to ensure all relevant files are tracked and staged.\n"
|
123
|
-
"- Use `git diff HEAD` to review all changes.\n"
|
124
|
-
"- Use `git log -n 3` to review recent commit messages and match their\n"
|
125
|
-
"style.\n"
|
126
|
-
"- Propose a draft commit message. Never just ask the user to give you\n"
|
127
|
-
"the full commit message.\n\n"
|
128
|
-
"## Researching\n"
|
129
|
-
"When asked to research a topic, follow this workflow:\n"
|
130
|
-
"1. **Understand:** Clarify the research question and the desired output\n"
|
131
|
-
"format (e.g., summary, list of key points).\n"
|
132
|
-
"2. **Search:** Use your tools to gather information from multiple reputable \n"
|
133
|
-
"sources.\n"
|
134
|
-
"3. **Synthesize & Cite:** Present the information in the requested\n"
|
135
|
-
"format. For every piece of information, you MUST provide a citation\n"
|
136
|
-
"with the source URL."
|
67
|
+
"## Guiding Principles\n"
|
68
|
+
"- **Safety First:** Never run commands that are destructive or could "
|
69
|
+
"compromise the system without explicit user confirmation. When in "
|
70
|
+
"doubt, ask.\n"
|
71
|
+
"- **Adhere to Conventions:** When working within a project, analyze "
|
72
|
+
"existing code, files, and configuration to match its style and "
|
73
|
+
"conventions.\n"
|
74
|
+
"- **Efficiency:** Use your tools to get the job done with the minimum "
|
75
|
+
"number of steps. Combine commands where possible.\n\n"
|
76
|
+
"## Common Task Workflows\n\n"
|
77
|
+
"**File System Operations:**\n"
|
78
|
+
"1. **Analyze:** Before modifying, read the file or list the "
|
79
|
+
"directory.\n"
|
80
|
+
"2. **Execute:** Perform the write, delete, or move operation.\n"
|
81
|
+
"3. **Verify:** Check that the file/directory now exists (or doesn't) in "
|
82
|
+
"its expected state.\n\n"
|
83
|
+
"**Code & Software Development:**\n"
|
84
|
+
"1. **Understand:** Use tools to analyze the codebase, dependencies, "
|
85
|
+
"and existing patterns.\n"
|
86
|
+
"2. **Plan:** For non-trivial changes, formulate a plan.\n"
|
87
|
+
"3. **Implement:** Make the changes, strictly adhering to project "
|
88
|
+
"conventions.\n"
|
89
|
+
"4. **Verify:** Run all relevant tests, linters, and build commands to "
|
90
|
+
"ensure your changes are correct and meet quality standards.\n\n"
|
91
|
+
"**Research & Analysis:**\n"
|
92
|
+
"1. **Clarify:** Understand the core question and the desired output "
|
93
|
+
"format.\n"
|
94
|
+
"2. **Search:** Use web search tools to gather information from multiple "
|
95
|
+
"reputable sources.\n"
|
96
|
+
"3. **Synthesize & Cite:** Present the information clearly. For factual "
|
97
|
+
"claims, cite the source URL."
|
137
98
|
).strip()
|
138
99
|
|
139
100
|
|
140
101
|
_DEFAULT_SUMMARIZATION_PROMPT = (
|
141
|
-
"You are a Conversation Historian. Your task is to
|
142
|
-
"
|
143
|
-
"
|
144
|
-
"
|
145
|
-
"
|
146
|
-
"
|
147
|
-
"
|
148
|
-
"
|
149
|
-
"
|
150
|
-
"
|
151
|
-
"
|
152
|
-
"
|
153
|
-
"
|
154
|
-
"
|
155
|
-
"
|
156
|
-
"
|
157
|
-
"
|
158
|
-
"
|
159
|
-
"- **
|
160
|
-
"
|
161
|
-
" truncate, summarize, use placeholders, or alter the assistant's\n"
|
162
|
-
" response in any way. The entire, full response must be preserved.**\n"
|
163
|
-
"- **Format:** Present the assistant's turn as: `assistant (thought:\n"
|
164
|
-
" brief summary of action) final response`.\n\n"
|
165
|
-
"### 3. Output Specification\n"
|
166
|
-
"Your entire output MUST be a single block of text with these two\n"
|
167
|
-
"sections:\n"
|
168
|
-
"1. `## Narrative Summary` (The updated and condensed narrative)\n"
|
169
|
-
"2. `## Scratchpad` (The new, non-truncated recent history)"
|
102
|
+
"You are a Conversation Historian. Your task is to create a dense, "
|
103
|
+
"structured snapshot of the conversation for the main assistant.\n\n"
|
104
|
+
"You will receive a `Previous Summary` and the `Recent Conversation "
|
105
|
+
"History`. Your goal is to produce an updated, rolling summary. Your "
|
106
|
+
"output MUST be a single block of text with two sections:\n\n"
|
107
|
+
"1. `## Narrative Summary`\n"
|
108
|
+
" - **Identify Key Information:** From the `Recent Conversation "
|
109
|
+
"History`, extract critical facts, user decisions, and final outcomes "
|
110
|
+
"of tasks.\n"
|
111
|
+
" - **Integrate and Condense:** Integrate these key facts into the "
|
112
|
+
"`Previous Summary`. Discard conversational filler and intermediate "
|
113
|
+
"steps of completed tasks (e.g., 'User asked to see file X, I showed "
|
114
|
+
"them' can be discarded if the file was just a step towards a larger "
|
115
|
+
"goal). The summary should reflect the current state of the project or "
|
116
|
+
"conversation, not a log of every single turn.\n\n"
|
117
|
+
"2. `## Transcript`\n"
|
118
|
+
" - The Transcript is the assistant's working memory. It MUST "
|
119
|
+
"contain the last few turns of the conversation in full detail.\n"
|
120
|
+
" - **CRITICAL REQUIREMENT:** The assistant's last response MUST be "
|
121
|
+
"COPIED VERBATIM into the Transcript. Do not alter it."
|
170
122
|
).strip()
|
171
123
|
|
172
124
|
|
173
125
|
_DEFAULT_CONTEXT_ENRICHMENT_PROMPT = (
|
174
|
-
"You are a Memory Curator. Your sole purpose is to
|
175
|
-
"
|
176
|
-
"
|
177
|
-
"
|
178
|
-
"
|
179
|
-
"
|
180
|
-
"**
|
181
|
-
"
|
182
|
-
"
|
183
|
-
"
|
184
|
-
"
|
185
|
-
"
|
186
|
-
"**
|
187
|
-
"
|
188
|
-
"
|
189
|
-
"
|
190
|
-
"
|
191
|
-
"
|
192
|
-
" conversation.\n"
|
193
|
-
"- **ADD** new, explicitly stable facts (e.g., long-term user\n"
|
194
|
-
" preferences).\n"
|
195
|
-
"- **UPDATE** existing facts if the user provides new information or if\n"
|
196
|
-
" new information overrides the previous one.\n"
|
197
|
-
"- **Your primary goal is to create a concise, relevant context.** "
|
198
|
-
"Aggressively prune outdated or irrelevant information, but retain any "
|
199
|
-
"detail, fact, or nuance that is critical for understanding the user's "
|
200
|
-
"current and future goals.\n"
|
201
|
-
"- **CONDENSE** older entries that are still relevant but not the\n"
|
202
|
-
" immediate focus. For example, a completed high-level goal might be\n"
|
203
|
-
" condensed into a single 'Past Accomplishments' line item.\n"
|
204
|
-
"\n"
|
205
|
-
"**A Note on Dynamic Information:**\n"
|
206
|
-
"Be mindful that some information is temporary and highly dynamic (e.g.,\n"
|
207
|
-
"current weather, location, current working directory, project context,\n"
|
208
|
-
"or file contents). You MUST add a note to this kind of information,\n"
|
209
|
-
"for example: `(short-term, must be re-verified)`.\n"
|
210
|
-
"The main assistant MUST NOT assume this information is current and\n"
|
211
|
-
"should always use its tools to verify the latest state when needed."
|
126
|
+
"You are a Memory Curator. Your sole purpose is to produce a concise, "
|
127
|
+
"up-to-date Markdown block of long-term context.\n\n"
|
128
|
+
"You will be given the `Previous Long-Term Context` and the `Recent "
|
129
|
+
"Conversation History`. Your job is to return a NEW, UPDATED version of "
|
130
|
+
"the `Long-Term Context` by applying these rules:\n\n"
|
131
|
+
"**Curation Heuristics:**\n"
|
132
|
+
"- **Extract Stable Facts:** Identify durable information such as user "
|
133
|
+
"preferences (e.g., 'I prefer tabs over spaces'), project-level "
|
134
|
+
"decisions, or architectural choices.\n"
|
135
|
+
"- **Integrate, Don't Just Append:** Update existing facts if new "
|
136
|
+
"information overrides them. Merge related facts to keep the context "
|
137
|
+
"dense.\n"
|
138
|
+
"- **Discard Ephemeral Details:** Omit temporary states, resolved "
|
139
|
+
"errors, and one-off requests that do not reflect a permanent preference "
|
140
|
+
"or project state.\n"
|
141
|
+
"- **Mark Dynamic Info:** For temporary information that must be "
|
142
|
+
"retained (e.g., CWD, contents of a file being actively edited), you "
|
143
|
+
"MUST add a note: `(short-term, must be re-verified)`."
|
212
144
|
).strip()
|
213
145
|
|
214
146
|
|
zrb/task/llm/error.py
CHANGED
@@ -1,18 +1,34 @@
|
|
1
1
|
import json
|
2
|
-
from typing import TYPE_CHECKING, Optional
|
3
|
-
|
4
|
-
from pydantic import BaseModel
|
2
|
+
from typing import TYPE_CHECKING, Any, Optional
|
5
3
|
|
6
4
|
if TYPE_CHECKING:
|
7
5
|
from openai import APIError
|
8
6
|
|
9
7
|
|
10
8
|
# Define a structured error model for tool execution failures
|
11
|
-
class ToolExecutionError
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
class ToolExecutionError:
|
10
|
+
def __init__(
|
11
|
+
self,
|
12
|
+
tool_name: str,
|
13
|
+
error_type: str,
|
14
|
+
message: str,
|
15
|
+
details: Optional[str] = None,
|
16
|
+
):
|
17
|
+
self.tool_name = tool_name
|
18
|
+
self.error_type = error_type
|
19
|
+
self.message = message
|
20
|
+
self.details = details
|
21
|
+
|
22
|
+
def to_dict(self) -> dict[str, Any]:
|
23
|
+
return {
|
24
|
+
"tool_name": self.tool_name,
|
25
|
+
"error_type": self.error_type,
|
26
|
+
"message": self.message,
|
27
|
+
"details": self.details,
|
28
|
+
}
|
29
|
+
|
30
|
+
def model_dump_json(self, indent: int = 2) -> str:
|
31
|
+
return json.dumps(self.to_dict(), indent=indent)
|
16
32
|
|
17
33
|
|
18
34
|
def extract_api_error_details(error: "APIError") -> str:
|
zrb/task/rsync_task.py
CHANGED
@@ -65,7 +65,7 @@ class RsyncTask(CmdTask):
|
|
65
65
|
remote_host=remote_host,
|
66
66
|
render_remote_host=render_remote_host,
|
67
67
|
remote_port=remote_port,
|
68
|
-
|
68
|
+
render_remote_port=render_remote_port,
|
69
69
|
remote_user=remote_user,
|
70
70
|
render_remote_user=render_remote_user,
|
71
71
|
remote_password=remote_password,
|
zrb/util/git_diff_model.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
class DiffResult:
|
2
|
+
def __init__(
|
3
|
+
self,
|
4
|
+
created: list[str] | None = None,
|
5
|
+
removed: list[str] | None = None,
|
6
|
+
updated: list[str] | None = None,
|
7
|
+
):
|
8
|
+
self.created = created if created is not None else []
|
9
|
+
self.removed = removed if removed is not None else []
|
10
|
+
self.updated = updated if updated is not None else []
|
zrb/util/git_subtree_model.py
CHANGED
@@ -1,11 +1,32 @@
|
|
1
|
-
|
1
|
+
import json
|
2
|
+
from typing import Any
|
2
3
|
|
3
4
|
|
4
|
-
class SingleSubTreeConfig
|
5
|
-
repo_url: str
|
6
|
-
|
7
|
-
|
5
|
+
class SingleSubTreeConfig:
|
6
|
+
def __init__(self, repo_url: str, branch: str, prefix: str):
|
7
|
+
self.repo_url = repo_url
|
8
|
+
self.branch = branch
|
9
|
+
self.prefix = prefix
|
8
10
|
|
11
|
+
def to_dict(self) -> dict[str, Any]:
|
12
|
+
return {
|
13
|
+
"repo_url": self.repo_url,
|
14
|
+
"branch": self.branch,
|
15
|
+
"prefix": self.prefix,
|
16
|
+
}
|
9
17
|
|
10
|
-
|
11
|
-
|
18
|
+
def model_dump_json(self, indent: int = 2) -> str:
|
19
|
+
return json.dumps(self.to_dict(), indent=indent)
|
20
|
+
|
21
|
+
|
22
|
+
class SubTreeConfig:
|
23
|
+
def __init__(self, data: dict[str, SingleSubTreeConfig]):
|
24
|
+
self.data = data
|
25
|
+
|
26
|
+
def to_dict(self) -> dict[str, Any]:
|
27
|
+
return {
|
28
|
+
"data": {k: self.data[k].to_dict() for k in self.data},
|
29
|
+
}
|
30
|
+
|
31
|
+
def model_dump_json(self, indent: int = 2) -> str:
|
32
|
+
return json.dumps(self.to_dict(), indent=indent)
|
zrb/util/string/conversion.py
CHANGED
@@ -178,7 +178,7 @@ def pluralize(noun: str) -> str:
|
|
178
178
|
if noun.lower() in irregulars:
|
179
179
|
return irregulars[noun.lower()]
|
180
180
|
# Handle words ending in 'y' preceded by a consonant
|
181
|
-
if noun.endswith("y") and not re.match(r"[aeiou]y$", noun):
|
181
|
+
if noun.endswith("y") and not re.match(r".*[aeiou]y$", noun, re.IGNORECASE):
|
182
182
|
return re.sub(r"y$", "ies", noun)
|
183
183
|
# Handle words ending in 's', 'x', 'z', 'ch', or 'sh'
|
184
184
|
if re.search(r"(s|x|z|ch|sh)$", noun):
|
zrb/util/todo_model.py
CHANGED
@@ -1,27 +1,33 @@
|
|
1
1
|
import datetime
|
2
|
+
import re
|
2
3
|
|
3
|
-
from pydantic import BaseModel, Field, model_validator
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
creation_date = values.get("creation_date")
|
5
|
+
class TodoTaskModel:
|
6
|
+
def __init__(
|
7
|
+
self,
|
8
|
+
description: str,
|
9
|
+
priority: str | None = "D",
|
10
|
+
completed: bool = False,
|
11
|
+
projects: list[str] | None = None,
|
12
|
+
contexts: list[str] | None = None,
|
13
|
+
keyval: dict[str, str] | None = None,
|
14
|
+
creation_date: datetime.date | None = None,
|
15
|
+
completion_date: datetime.date | None = None,
|
16
|
+
):
|
17
|
+
if priority is not None and not re.match(r"^[A-Z]$", priority):
|
18
|
+
raise ValueError("Invalid priority format")
|
20
19
|
if completion_date and not creation_date:
|
21
20
|
raise ValueError(
|
22
21
|
"creation_date must be specified if completion_date is set."
|
23
22
|
)
|
24
|
-
|
23
|
+
self.priority = priority
|
24
|
+
self.completed = completed
|
25
|
+
self.description = description
|
26
|
+
self.projects = projects if projects is not None else []
|
27
|
+
self.contexts = contexts if contexts is not None else []
|
28
|
+
self.keyval = keyval if keyval is not None else {}
|
29
|
+
self.creation_date = creation_date
|
30
|
+
self.completion_date = completion_date
|
25
31
|
|
26
32
|
def get_additional_info_length(self):
|
27
33
|
"""
|
@@ -9,7 +9,7 @@ zrb/builtin/git_subtree.py,sha256=7BKwOkVTWDrR0DXXQ4iJyHqeR6sV5VYRt8y_rEB0EHg,35
|
|
9
9
|
zrb/builtin/group.py,sha256=t008xLM4_fgbjfZrPoi_fQAnSHIo6MOiQSCHBO4GDYU,2379
|
10
10
|
zrb/builtin/http.py,sha256=sLqEczuSxGYXWzyJR6frGOHkPTviu4BeyroUr3-ZuAI,4322
|
11
11
|
zrb/builtin/jwt.py,sha256=3M5uaQhJZbKQLjTUft1OwPz_JxtmK-xtkjxWjciOQho,2859
|
12
|
-
zrb/builtin/llm/chat_session.py,sha256=
|
12
|
+
zrb/builtin/llm/chat_session.py,sha256=0R04DpBr_LGfNJbXIQ_4XQSxL7kY2M3U-bbu5lsXZ54,8542
|
13
13
|
zrb/builtin/llm/history.py,sha256=jCMeRCHUsDFnQWyDoH9SOBptzceOs_wACvVpYkDOoTk,3086
|
14
14
|
zrb/builtin/llm/input.py,sha256=Nw-26uTWp2QhUgKJcP_IMHmtk-b542CCSQ_vCOjhvhM,877
|
15
15
|
zrb/builtin/llm/llm_ask.py,sha256=oozfQwa1i2PnXV4qWbn60Pmd3fS0kgmhYCbfKlhr25o,4549
|
@@ -218,7 +218,7 @@ zrb/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
218
218
|
zrb/cmd/cmd_result.py,sha256=L8bQJzWCpcYexIxHBNsXj2pT3BtLmWex0iJSMkvimOA,597
|
219
219
|
zrb/cmd/cmd_val.py,sha256=7Doowyg6BK3ISSGBLt-PmlhzaEkBjWWm51cED6fAUOQ,1014
|
220
220
|
zrb/config/config.py,sha256=UpVm_IFD_bSfGS-QJoRo86xV63eGIuIwWICMaUZgR00,15268
|
221
|
-
zrb/config/llm_config.py,sha256
|
221
|
+
zrb/config/llm_config.py,sha256=o9-RjPB50isYjbHGtvrOKnqnQbazml-z8Pbo61ZMHQ4,16497
|
222
222
|
zrb/config/llm_rate_limitter.py,sha256=0U0qm4qgCWqBjohPdwANNUzLR3joJCFYr6oW6Xpccfo,4436
|
223
223
|
zrb/config/web_auth_config.py,sha256=_PXatQTYh2mX9H3HSYSQKp13zm1RlLyVIoeIr6KYMQ8,6279
|
224
224
|
zrb/content_transformer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -341,7 +341,7 @@ zrb/task/llm/agent.py,sha256=BZHbz-YXgSdm1tTwGMR_maqcd3yMFGSdzLyDjuxT_XI,6702
|
|
341
341
|
zrb/task/llm/config.py,sha256=TlyH925_fboIlK2Ixf34tynmenqs9s9rfsnPs4jff78,3490
|
342
342
|
zrb/task/llm/context.py,sha256=LGGQ_mb1dWorfshHjGgXEW_pRweGj-6MZcIUFq3AHy4,2213
|
343
343
|
zrb/task/llm/context_enrichment.py,sha256=djY4fE9C0zUxJPrrb2xDboBXr_2kPUS_b4HjqslVpHg,6051
|
344
|
-
zrb/task/llm/error.py,sha256=
|
344
|
+
zrb/task/llm/error.py,sha256=QR-nIohS6pBpC_16cWR-fw7Mevo1sNYAiXMBsh_CJDE,4157
|
345
345
|
zrb/task/llm/history.py,sha256=dqCwJYRoMVrvyMHb4M6-KLcwAUF4tbDCP7qgD1h694s,8540
|
346
346
|
zrb/task/llm/history_summarization.py,sha256=V0G1BiISnxxmD8040PrvT0_dfqGE7zbLtk74KUpuqig,6050
|
347
347
|
zrb/task/llm/print_node.py,sha256=zocTKi9gZDxl2I6KNu095TmMc13Yip6SNuWYnswS680,4060
|
@@ -350,7 +350,7 @@ zrb/task/llm/tool_wrapper.py,sha256=8_bL8m_WpRf-pVKSrvQIVqT-m2sUA87a1RBQG13lhp4,
|
|
350
350
|
zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
|
351
351
|
zrb/task/llm_task.py,sha256=Vq2kPnE40xJZtHYHjeCBv-nNFKzSCkyMJaVUNXmEmuc,15616
|
352
352
|
zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
|
353
|
-
zrb/task/rsync_task.py,sha256=
|
353
|
+
zrb/task/rsync_task.py,sha256=WfqNSaicJgYWpunNU34eYxXDqHDHOftuDHyWJKjqwg0,6365
|
354
354
|
zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
|
355
355
|
zrb/task/scheduler.py,sha256=5xoet0bMjVq0JKwSwwivD1Jo84wwwA2PrL5WaKwJAGA,3110
|
356
356
|
zrb/task/task.py,sha256=KCrCaWYOQQvv1RJsYtHDeo9RBFmlXQ28oKyEFU4Q7pA,73
|
@@ -378,22 +378,22 @@ zrb/util/codemod/modify_module.py,sha256=2mzi_NxJ-kNFo5G0U_Rqb3JoYQl1s6Izt7b_wAl
|
|
378
378
|
zrb/util/cron.py,sha256=UWqqhhM7DDTPx6wjCIdBndnVh3NRu-sdKazp8aZkXh8,3833
|
379
379
|
zrb/util/file.py,sha256=tm_8qn0vEM8Hz46yXUcvFHfsLtQNqidQaEuB85xqhFE,2806
|
380
380
|
zrb/util/git.py,sha256=GS08OqE-gs6mN-_VqACN82DbI81nzG3FiXeim_-jVAU,8193
|
381
|
-
zrb/util/git_diff_model.py,sha256=
|
381
|
+
zrb/util/git_diff_model.py,sha256=Tg2q6oxQ5chryThzjs0cLcKFGM03CnqvusTo_Ug_oX4,369
|
382
382
|
zrb/util/git_subtree.py,sha256=AyQWCWEi2EIzEpYXRnYN55157KMUql0WHj70QNw5PHU,4612
|
383
|
-
zrb/util/git_subtree_model.py,sha256=
|
383
|
+
zrb/util/git_subtree_model.py,sha256=P_gJ0zhOAc3gFM6sYcjc0Ack9dFBt75TI5fXdE0q320,871
|
384
384
|
zrb/util/group.py,sha256=T82yr3qg9I5k10VPXkMyrIRIqyfzadSH813bqzwKEPI,4718
|
385
385
|
zrb/util/init_path.py,sha256=9eN7CkWNGhDBpjTQs2j9YHVMzui7Y8DEb1WP4aTPzeo,659
|
386
386
|
zrb/util/load.py,sha256=DK0KYSlu48HCoGPqnW1IxnE3pHrZSPCstfz8Fjyqqv8,2140
|
387
387
|
zrb/util/run.py,sha256=vu-mcSWDP_WuuvIKqM_--Gk3WkABO1oTXiHmBRTvVQk,546
|
388
388
|
zrb/util/string/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
389
|
-
zrb/util/string/conversion.py,sha256=
|
389
|
+
zrb/util/string/conversion.py,sha256=XEvN1hyziEwP9mEC_LdNaisoLLZF3l4DE40DgNEo6is,6257
|
390
390
|
zrb/util/string/format.py,sha256=MwWGAwSdtOgR_2uz-JCXlg_q-uRYUUI-G8CGkfdgqik,1198
|
391
391
|
zrb/util/string/name.py,sha256=SXEfxJ1-tDOzHqmSV8kvepRVyMqs2XdV_vyoh_9XUu0,1584
|
392
392
|
zrb/util/todo.py,sha256=r9_KYF2-hLKMNjsp6AFK9zivykMrywd-kJ4bCwfdafI,19323
|
393
|
-
zrb/util/todo_model.py,sha256=
|
393
|
+
zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
|
394
394
|
zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
395
395
|
zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
|
396
|
-
zrb-1.9.
|
397
|
-
zrb-1.9.
|
398
|
-
zrb-1.9.
|
399
|
-
zrb-1.9.
|
396
|
+
zrb-1.9.14.dist-info/METADATA,sha256=FrkKRj_OuRF25B0gsVFvK83J1okldK7ubXhQXqwUiWQ,9778
|
397
|
+
zrb-1.9.14.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
398
|
+
zrb-1.9.14.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
|
399
|
+
zrb-1.9.14.dist-info/RECORD,,
|
File without changes
|
File without changes
|