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.
@@ -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
- final_result = ""
32
- ctx.print("💬 >>", plain=True)
33
- ctx.print(ctx.input.message, plain=True)
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 user_input_session.prompt_async()
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 special input
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
- "This is an interactive CLI session. Your standard response format is\n"
17
- "GitHub-flavored Markdown. You MUST follow this thinking process:\n\n"
18
- "1. **Analyze Request, Scope & Identify Gaps:** Use the `Scratchpad`\n"
19
- " and `Narrative Summary` to fully understand the user's request.\n"
20
- " - **Determine Scope:** Critically assess if the request is a\n"
21
- " one-time command or a standing order that should affect future\n"
22
- " interactions (e.g., 'From now on...'). Resolve contextual\n"
23
- " references like 'it' or 'that' to the immediate topic.\n"
24
- " - **Identify Gaps:** Assess if you have enough information to\n"
25
- " proceed. If not, identify the missing information as an\n"
26
- " 'information gap'.\n\n"
27
- "2. **Fill Information Gaps:** Before planning, you MUST proactively use\n"
28
- " your tools to fill any identified information gaps. Be persistent.\n"
29
- " If one tool or approach fails, try another until you have the\n"
30
- " necessary information or determine it's impossible to obtain.\n\n"
31
- "3. **Plan & Verify Pre-conditions:** Create a step-by-step plan. Before\n"
32
- " executing, use read-only tools to check the current state. For\n"
33
- " example, if the plan is to create a file, check if it already\n"
34
- " exists. If pre-conditions are not as expected, inform the user.\n\n"
35
- "4. **Assess Consent & Execute:**\n"
36
- " - **You have standing consent to use any read-only tools** for\n"
37
- " information gathering, planning, and verification. You do not\n"
38
- " need to ask for permission for these actions.\n"
39
- " - If the user's last instruction was an explicit command (e.g.,\n"
40
- ' "create file X", "delete Y"), you have consent. Proceed with the\n'
41
- " action.\n"
42
- ' - If the request was general (e.g., "fix the bug") and your plan\n'
43
- " involves a potentially altering action, you MUST explain the\n"
44
- " action and ask for user approval before proceeding.\n\n"
45
- "5. **Verify Outcome:** After executing the action, use read-only tools to\n"
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
- "This is a one-shot CLI session. Your final answer MUST be in\n"
51
- "GitHub-flavored Markdown. You MUST follow this thinking process:\n\n"
52
- "1. **Analyze Request, Scope & Identify Gaps:** Use the `Scratchpad`\n"
53
- " and `Narrative Summary` to fully understand the user's request.\n"
54
- " - **Determine Scope:** Critically assess if the request is a\n"
55
- " one-time command or a standing order that should affect future\n"
56
- " interactions (e.g., 'From now on...'). Resolve contextual\n"
57
- " references like 'it' or 'that' to the immediate topic.\n"
58
- " - **Identify Gaps:** Assess if you have enough information to\n"
59
- " proceed. If not, identify the missing information as an\n"
60
- " 'information gap'.\n\n"
61
- "2. **Fill Information Gaps:** Before planning, you MUST proactively use\n"
62
- " your tools to fill any identified information gaps. Be persistent.\n"
63
- " If one tool or approach fails, try another until you have the\n"
64
- " necessary information or determine it's impossible to obtain.\n\n"
65
- "3. **Plan & Verify Pre-conditions:** Create a step-by-step plan. Before\n"
66
- " executing, use read-only tools to check the current state. For\n"
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
- "## Software Engineering Tasks\n"
85
- "When requested to perform tasks like fixing bugs, adding features,\n"
86
- "refactoring, or explaining code, follow this sequence:\n"
87
- "1. **Understand:** Think about the user's request and the relevant\n"
88
- "codebase context. Use your tools to understand file structures,\n"
89
- "existing code patterns, and conventions.\n"
90
- "2. **Plan:** Build a coherent and grounded plan. Share an extremely\n"
91
- "concise yet clear plan with the user.\n"
92
- "3. **Implement:** Use the available tools to act on the plan, strictly\n"
93
- "adhering to the project's established conventions.\n"
94
- "4. **Verify (Tests):** If applicable and feasible, verify the changes\n"
95
- "using the project's testing procedures. Identify the correct test\n"
96
- "commands and frameworks by examining 'README' files, build/package\n"
97
- "configuration, or existing test execution patterns. NEVER assume\n"
98
- "standard test commands.\n"
99
- "5. **Verify (Standards):** After making code changes, execute the\n"
100
- "project-specific build, linting and type-checking commands. This\n"
101
- "ensures code quality and adherence to standards.\n\n"
102
- "## Shell Command Guidelines\n"
103
- "NEVER use backticks (`` ` ``) for command substitution; use `$(...)` "
104
- "instead. Always enclose literal strings and paths in single quotes (`'`) "
105
- "to prevent unintended interpretation of special characters.\n\n"
106
- "## New Applications\n"
107
- "When asked to create a new application, follow this workflow:\n"
108
- "1. **Understand Requirements:** Analyze the user's request to identify\n"
109
- "core features, application type, and constraints.\n"
110
- "2. **Propose Plan:** Formulate a development plan. Present a clear,\n"
111
- "concise, high-level summary to the user, including technologies to be\n"
112
- "used.\n"
113
- "3. **User Approval:** Obtain user approval for the proposed plan.\n"
114
- "4. **Implementation:** Autonomously implement each feature and design\n"
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 distill the\n"
142
- "conversation history into a dense, structured snapshot for the main\n"
143
- "assistant. This snapshot is CRITICAL, as it will become the agent's\n"
144
- "primary short-term memory.\n\n"
145
- "## Historian Protocol\n"
146
- "You will receive a `Previous Summary` and the `Recent Conversation\n"
147
- "History`. Your job is to create a new, updated summary.\n\n"
148
- "### 1. Update the Narrative Summary\n"
149
- "- **Integrate:** Weave the key events from the `Recent Conversation\n"
150
- " History` into the `Narrative Summary`.\n"
151
- "- **Condense and Prune:** As you add new information, you MUST condense\n"
152
- " older parts of the narrative. Be incredibly dense with information.\n"
153
- " Omit any irrelevant conversational filler. The summary should be a\n"
154
- " rolling, high-level overview, not an ever-expanding log.\n\n"
155
- "### 2. Update the Scratchpad\n"
156
- "- **Purpose:** The Scratchpad is the assistant's working memory. It must\n"
157
- " contain the last few turns of the conversation in full, non-truncated\n"
158
- " detail.\n"
159
- "- **ABSOLUTE REQUIREMENT: The assistant's response MUST be COPIED\n"
160
- " VERBATIM into the Scratchpad. It is CRITICAL that you DO NOT\n"
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 process a\n"
175
- "conversation and produce a concise, up-to-date Markdown block of\n"
176
- "long-term context for the main assistant.\n\n"
177
- "You will be given the previous 'Long-Term Context' and the 'Recent\n"
178
- "Conversation History'. Your job is to return a NEW, UPDATED version of\n"
179
- "the 'Long-Term Context'.\n\n"
180
- "**Your Curation Process:**\n"
181
- "1. **Review:** Analyze the existing 'Long-Term Context'.\n"
182
- "2. **Update:** Read the 'Recent Conversation History' to identify\n"
183
- " new facts, changed goals, or completed tasks.\n"
184
- "3. **Re-write:** Create the new 'Long-Term Context' by applying these\n"
185
- " changes.\n\n"
186
- "**CRITICAL CURATION RULES:**\n"
187
- "- **Do Not Assume Permanence:** A one-time request (e.g., 'Write it in\n"
188
- " JSON') is NOT a permanent preference. Only save preferences that are\n"
189
- " explicitly stated as long-term (e.g., 'From now on, always...').\n"
190
- "- **The context MUST NOT grow indefinitely.** Your primary goal is to\n"
191
- " keep it concise and relevant to the *current* state of the\n"
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(BaseModel):
12
- tool_name: str
13
- error_type: str
14
- message: str
15
- details: Optional[str] = None
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
- auto_render_remote_port=render_remote_port,
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,
@@ -1,7 +1,10 @@
1
- from pydantic import BaseModel
2
-
3
-
4
- class DiffResult(BaseModel):
5
- created: list[str]
6
- removed: list[str]
7
- updated: list[str]
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 []
@@ -1,11 +1,32 @@
1
- from pydantic import BaseModel
1
+ import json
2
+ from typing import Any
2
3
 
3
4
 
4
- class SingleSubTreeConfig(BaseModel):
5
- repo_url: str
6
- branch: str
7
- prefix: str
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
- class SubTreeConfig(BaseModel):
11
- data: dict[str, SingleSubTreeConfig]
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)
@@ -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
- class TodoTaskModel(BaseModel):
7
- priority: str | None = Field("D", pattern=r"^[A-Z]$") # Priority like A, B, ...
8
- completed: bool = False # True if completed, False otherwise
9
- description: str # Main task description
10
- projects: list[str] = [] # List of projects (e.g., +Project)
11
- contexts: list[str] = [] # List of contexts (e.g., @Context)
12
- keyval: dict[str, str] = {} # Special key (e.g., due:2016-05-30)
13
- creation_date: datetime.date | None = None # Creation date
14
- completion_date: datetime.date | None = None # Completion date
15
-
16
- @model_validator(mode="before")
17
- def validate_dates(cls, values):
18
- completion_date = values.get("completion_date")
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
- return values
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
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.9.12
3
+ Version: 1.9.14
4
4
  Summary: Your Automation Powerhouse
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later
@@ -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=1DMy6j24jfTVkQfsZAHvazniXrWfUW8E0ZwTAxi7WAU,7593
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=-eQCD6A3rCpIrR6XtcEA-aeTX73FcyFTacXa_T0A-4o,21524
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=s5PSK3ibGupMzOM0P81nstHWrMr3205H0FSDwfhWUDA,3662
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=GSL9144bmp6F0EckT6m-2a1xG25AzrrWYzH4k3SVUKM,6370
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=Fthl2nyBz3D5fIfbMCPEFBWfXUB6qA4rQWApKaimaI8,131
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=mDMUG6mju1_-XXpIE41EqFdu_gmDeeJ3OAxqcJunY7g,196
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=sMmstzbrNgLvWAQukqoXz45JtsNpJrniudAtzJaQlYw,6240
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=0SJ8aLYfJAscDOk5JsH7pXP3h1rAG91VMCS20-c2Y6A,1576
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.12.dist-info/METADATA,sha256=-DI8RzdASD_NeRQmc2UTza0e2DoLwD-CFQm4YqENtBk,9778
397
- zrb-1.9.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
398
- zrb-1.9.12.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
399
- zrb-1.9.12.dist-info/RECORD,,
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