zrb 1.5.9__py3-none-any.whl → 1.5.11__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/tool/file.py +23 -91
- zrb/llm_config.py +45 -47
- zrb/task/base/lifecycle.py +1 -1
- zrb/task/base_task.py +16 -0
- zrb/task/cmd_task.py +2 -2
- zrb/task/llm/prompt.py +64 -3
- zrb/task/llm_task.py +24 -16
- zrb/task/scheduler.py +1 -2
- {zrb-1.5.9.dist-info → zrb-1.5.11.dist-info}/METADATA +1 -1
- {zrb-1.5.9.dist-info → zrb-1.5.11.dist-info}/RECORD +12 -13
- zrb/task/base/dependencies.py +0 -57
- {zrb-1.5.9.dist-info → zrb-1.5.11.dist-info}/WHEEL +0 -0
- {zrb-1.5.9.dist-info → zrb-1.5.11.dist-info}/entry_points.txt +0 -0
zrb/builtin/llm/tool/file.py
CHANGED
@@ -92,6 +92,10 @@ def list_files(
|
|
92
92
|
"""
|
93
93
|
all_files: list[str] = []
|
94
94
|
abs_path = os.path.abspath(os.path.expanduser(path))
|
95
|
+
# Explicitly check if path exists before proceeding
|
96
|
+
if not os.path.exists(abs_path):
|
97
|
+
# Raise FileNotFoundError, which is a subclass of OSError
|
98
|
+
raise FileNotFoundError(f"Path does not exist: {path}")
|
95
99
|
# Determine effective exclusion patterns
|
96
100
|
patterns_to_exclude = (
|
97
101
|
excluded_patterns
|
@@ -132,9 +136,7 @@ def list_files(
|
|
132
136
|
all_files.append(full_path)
|
133
137
|
# Return paths relative to the original path requested
|
134
138
|
try:
|
135
|
-
rel_files = [
|
136
|
-
os.path.relpath(f, os.path.dirname(abs_path)) for f in all_files
|
137
|
-
]
|
139
|
+
rel_files = [os.path.relpath(f, abs_path) for f in all_files]
|
138
140
|
return json.dumps({"files": sorted(rel_files)})
|
139
141
|
except (
|
140
142
|
ValueError
|
@@ -152,14 +154,17 @@ def list_files(
|
|
152
154
|
|
153
155
|
def _is_hidden(path: str) -> bool:
|
154
156
|
"""
|
155
|
-
Check if path is hidden (starts with '.').
|
157
|
+
Check if path is hidden (starts with '.') but ignore '.' and '..'.
|
156
158
|
Args:
|
157
159
|
path: File or directory path to check
|
158
160
|
Returns:
|
159
161
|
True if the path is hidden, False otherwise
|
160
162
|
"""
|
161
|
-
|
162
|
-
|
163
|
+
basename = os.path.basename(path)
|
164
|
+
# Ignore '.' and '..' as they are not typically considered hidden in listings
|
165
|
+
if basename == "." or basename == "..":
|
166
|
+
return False
|
167
|
+
return basename.startswith(".")
|
163
168
|
|
164
169
|
|
165
170
|
def _is_excluded(name: str, patterns: list[str]) -> bool:
|
@@ -375,41 +380,27 @@ def _get_file_matches(
|
|
375
380
|
|
376
381
|
def apply_diff(
|
377
382
|
path: str,
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
replace_marker: str = ">>>>>> REPLACE",
|
383
|
+
start_line: int,
|
384
|
+
end_line: int,
|
385
|
+
search_content: str,
|
386
|
+
replace_content: str,
|
383
387
|
) -> str:
|
384
|
-
"""Apply a precise search/replace
|
388
|
+
"""Apply a precise search/replace to a file based on line numbers and content.
|
385
389
|
Args:
|
386
390
|
path (str): Path to modify. Pass exactly as provided, including '~'.
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
SEARCH block should NOT contains line numbers
|
395
|
-
Format example:
|
396
|
-
[Search Marker, e.g., <<<<<< SEARCH]
|
397
|
-
:start_line:10
|
398
|
-
:end_line:15
|
399
|
-
[Meta Marker, e.g., ------]
|
400
|
-
[exact content to find including whitespace]
|
401
|
-
[Separator, e.g., ======]
|
402
|
-
[new content to replace with]
|
403
|
-
[Replace Marker, e.g., >>>>>> REPLACE]
|
391
|
+
start_line (int): The 1-based starting line number of the content to replace.
|
392
|
+
end_line (int): The 1-based ending line number (inclusive) of the content to replace.
|
393
|
+
search_content (str): The exact content expected to be found in the specified
|
394
|
+
line range. Must exactly match file content including whitespace/indentation,
|
395
|
+
excluding line numbers.
|
396
|
+
replace_content (str): The new content to replace the search_content with.
|
397
|
+
Excluding line numbers.
|
404
398
|
Returns:
|
405
399
|
str: JSON: {"success": true, "path": "f.py"} or {"success": false, "error": "..."}
|
406
400
|
Raises:
|
407
401
|
Exception: If an error occurs.
|
408
402
|
"""
|
409
403
|
try:
|
410
|
-
start_line, end_line, search_content, replace_content = _parse_diff(
|
411
|
-
diff, search_marker, meta_marker, separator, replace_marker
|
412
|
-
)
|
413
404
|
abs_path = os.path.abspath(os.path.expanduser(path))
|
414
405
|
if not os.path.exists(abs_path):
|
415
406
|
return json.dumps(
|
@@ -453,62 +444,3 @@ def apply_diff(
|
|
453
444
|
raise OSError(f"Error applying diff to {path}: {e}")
|
454
445
|
except Exception as e:
|
455
446
|
raise RuntimeError(f"Unexpected error applying diff to {path}: {e}")
|
456
|
-
|
457
|
-
|
458
|
-
def _parse_diff(
|
459
|
-
diff: str,
|
460
|
-
search_marker: str,
|
461
|
-
meta_marker: str,
|
462
|
-
separator: str,
|
463
|
-
replace_marker: str,
|
464
|
-
) -> tuple[int, int, str, str]:
|
465
|
-
"""
|
466
|
-
Parse diff content into components.
|
467
|
-
Args:
|
468
|
-
diff: The diff content to parse
|
469
|
-
search_marker: Marker indicating the start of the search block
|
470
|
-
meta_marker: Marker indicating the start of the content to search for
|
471
|
-
separator: Marker separating search content from replacement content
|
472
|
-
replace_marker: Marker indicating the end of the replacement block
|
473
|
-
Returns:
|
474
|
-
Tuple of (start_line, end_line, search_content, replace_content)
|
475
|
-
Raises:
|
476
|
-
ValueError: If diff format is invalid or missing required markers
|
477
|
-
ValueError: If start_line or end_line cannot be parsed
|
478
|
-
"""
|
479
|
-
# Find all marker positions
|
480
|
-
search_start_idx = diff.find(search_marker)
|
481
|
-
meta_start_idx = diff.find(meta_marker)
|
482
|
-
separator_idx = diff.find(separator)
|
483
|
-
replace_end_idx = diff.find(replace_marker)
|
484
|
-
# Validate all markers are present
|
485
|
-
missing_markers = []
|
486
|
-
if search_start_idx == -1:
|
487
|
-
missing_markers.append("search marker")
|
488
|
-
if meta_start_idx == -1:
|
489
|
-
missing_markers.append("meta marker")
|
490
|
-
if separator_idx == -1:
|
491
|
-
missing_markers.append("separator")
|
492
|
-
if replace_end_idx == -1:
|
493
|
-
missing_markers.append("replace marker")
|
494
|
-
if missing_markers:
|
495
|
-
raise ValueError(f"Invalid diff format - missing: {', '.join(missing_markers)}")
|
496
|
-
# Extract metadata
|
497
|
-
meta_content = diff[search_start_idx + len(search_marker) : meta_start_idx].strip()
|
498
|
-
# Parse line numbers
|
499
|
-
start_line_match = re.search(r":start_line:(\d+)", meta_content)
|
500
|
-
end_line_match = re.search(r":end_line:(\d+)", meta_content)
|
501
|
-
if not start_line_match:
|
502
|
-
raise ValueError("Missing start_line in diff metadata")
|
503
|
-
if not end_line_match:
|
504
|
-
raise ValueError("Missing end_line in diff metadata")
|
505
|
-
start_line = int(start_line_match.group(1))
|
506
|
-
end_line = int(end_line_match.group(1))
|
507
|
-
# Extract content sections
|
508
|
-
search_content = diff[meta_start_idx + len(meta_marker) : separator_idx].strip(
|
509
|
-
"\r\n"
|
510
|
-
)
|
511
|
-
replace_content = diff[separator_idx + len(separator) : replace_end_idx].strip(
|
512
|
-
"\r\n"
|
513
|
-
)
|
514
|
-
return start_line, end_line, search_content, replace_content
|
zrb/llm_config.py
CHANGED
@@ -5,62 +5,45 @@ from pydantic_ai.models.openai import OpenAIModel
|
|
5
5
|
from pydantic_ai.providers import Provider
|
6
6
|
from pydantic_ai.providers.openai import OpenAIProvider
|
7
7
|
|
8
|
+
from zrb.util.string.conversion import to_boolean
|
9
|
+
|
8
10
|
DEFAULT_SYSTEM_PROMPT = """
|
9
|
-
You
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
them. Handle tool errors gracefully and retry or adapt your strategy if necessary.
|
18
|
-
3. **Accuracy is Paramount:** Ensure all information, code, or outputs provided are
|
19
|
-
correct and reliable. Verify facts and generate executable code when requested.
|
20
|
-
4. **Clarity and Conciseness:** Respond clearly and directly to the user's query
|
21
|
-
after gathering the necessary information. Avoid unnecessary conversation.
|
22
|
-
5. **Context Awareness:** Understand the user's request fully to provide the most
|
23
|
-
relevant and effective assistance.
|
11
|
+
You have access to tools.
|
12
|
+
Your goal is to complete the user's task efficiently.
|
13
|
+
Analyze the request and use the available tools proactively to achieve the goal.
|
14
|
+
Infer parameters and actions from the context.
|
15
|
+
Do not ask for confirmation unless strictly necessary due to ambiguity or
|
16
|
+
missing critical information.
|
17
|
+
Apply relevant domain knowledge and best practices.
|
18
|
+
Respond directly and concisely upon task completion or when clarification is essential.
|
24
19
|
""".strip()
|
25
20
|
|
26
21
|
DEFAULT_PERSONA = """
|
27
22
|
You are an expert in various fields including technology, science, history, and more.
|
28
23
|
""".strip()
|
29
24
|
|
25
|
+
# Concise summarization focused on preserving critical context for continuity.
|
30
26
|
DEFAULT_SUMMARIZATION_PROMPT = """
|
31
|
-
You are a summarization assistant.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
history. Create a single, coherent summary that captures ALL essential
|
39
|
-
information, including:
|
40
|
-
- Key decisions made
|
41
|
-
- Actions taken (including tool usage and their results)
|
42
|
-
- Important facts or data points mentioned
|
43
|
-
- Outcomes of discussions or actions
|
44
|
-
- Any unresolved questions or pending items
|
45
|
-
The goal is to provide a complete yet concise background so that the main
|
46
|
-
assistant can seamlessly continue the conversation without losing critical
|
47
|
-
context from the summarized parts.
|
48
|
-
Output *only* the updated summary text."
|
27
|
+
You are a summarization assistant. Create an updated, concise summary integrating
|
28
|
+
the previous summary (if any) with the new conversation history.
|
29
|
+
Your primary goal is to preserve ALL critical context needed for the main assistant
|
30
|
+
to continue the task effectively. This includes key facts, decisions, tool usage
|
31
|
+
results, and essential background. Do not omit details that would force the main
|
32
|
+
assistant to re-gather information.
|
33
|
+
Output *only* the updated summary text.
|
49
34
|
""".strip()
|
50
35
|
|
51
36
|
DEFAULT_CONTEXT_ENRICHMENT_PROMPT = """
|
52
37
|
You are an information extraction assistant.
|
53
|
-
Analyze the conversation history and current context to extract key facts
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
- goals
|
58
|
-
- etc
|
59
|
-
Return only a JSON object containing a single key "response",
|
60
|
-
whose value is another JSON object with these details.
|
38
|
+
Analyze the conversation history and current context to extract key facts like
|
39
|
+
user_name, user_roles, preferences, goals, etc.
|
40
|
+
Return only a JSON object containing a single key "response", whose value is
|
41
|
+
another JSON object with these details.
|
61
42
|
If nothing is found, output {"response": {}}.
|
62
43
|
""".strip()
|
63
44
|
|
45
|
+
DEFAULT_SPECIAL_INSTRUCTION_PROMPT = "" # Default to empty
|
46
|
+
|
64
47
|
|
65
48
|
class LLMConfig:
|
66
49
|
|
@@ -71,6 +54,7 @@ class LLMConfig:
|
|
71
54
|
default_api_key: str | None = None,
|
72
55
|
default_persona: str | None = None,
|
73
56
|
default_system_prompt: str | None = None,
|
57
|
+
default_special_instruction_prompt: str | None = None,
|
74
58
|
default_summarization_prompt: str | None = None,
|
75
59
|
default_context_enrichment_prompt: str | None = None,
|
76
60
|
default_summarize_history: bool | None = None,
|
@@ -102,6 +86,11 @@ class LLMConfig:
|
|
102
86
|
if default_persona is not None
|
103
87
|
else os.getenv("ZRB_LLM_PERSONA", None)
|
104
88
|
)
|
89
|
+
self._default_special_instruction_prompt = (
|
90
|
+
default_special_instruction_prompt
|
91
|
+
if default_special_instruction_prompt is not None
|
92
|
+
else os.getenv("ZRB_LLM_SPECIAL_INSTRUCTION_PROMPT", None)
|
93
|
+
)
|
105
94
|
self._default_summarization_prompt = (
|
106
95
|
default_summarization_prompt
|
107
96
|
if default_summarization_prompt is not None
|
@@ -125,7 +114,7 @@ class LLMConfig:
|
|
125
114
|
self._default_enrich_context = (
|
126
115
|
default_enrich_context
|
127
116
|
if default_enrich_context is not None
|
128
|
-
else os.getenv("ZRB_LLM_ENRICH_CONTEXT", "
|
117
|
+
else to_boolean(os.getenv("ZRB_LLM_ENRICH_CONTEXT", "0"))
|
129
118
|
)
|
130
119
|
self._default_provider = None
|
131
120
|
self._default_model = None
|
@@ -145,17 +134,23 @@ class LLMConfig:
|
|
145
134
|
)
|
146
135
|
|
147
136
|
def get_default_system_prompt(self) -> str:
|
148
|
-
|
137
|
+
return (
|
149
138
|
DEFAULT_SYSTEM_PROMPT
|
150
139
|
if self._default_system_prompt is None
|
151
140
|
else self._default_system_prompt
|
152
141
|
)
|
153
|
-
|
142
|
+
|
143
|
+
def get_default_persona(self) -> str:
|
144
|
+
return (
|
154
145
|
DEFAULT_PERSONA if self._default_persona is None else self._default_persona
|
155
146
|
)
|
156
|
-
|
157
|
-
|
158
|
-
return
|
147
|
+
|
148
|
+
def get_default_special_instruction_prompt(self) -> str:
|
149
|
+
return (
|
150
|
+
DEFAULT_SPECIAL_INSTRUCTION_PROMPT
|
151
|
+
if self._default_special_instruction_prompt is None
|
152
|
+
else self._default_special_instruction_prompt
|
153
|
+
)
|
159
154
|
|
160
155
|
def get_default_summarization_prompt(self) -> str:
|
161
156
|
return (
|
@@ -197,6 +192,9 @@ class LLMConfig:
|
|
197
192
|
def set_default_system_prompt(self, system_prompt: str):
|
198
193
|
self._default_system_prompt = system_prompt
|
199
194
|
|
195
|
+
def set_default_special_instruction_prompt(self, special_instruction_prompt: str):
|
196
|
+
self._default_special_instruction_prompt = special_instruction_prompt
|
197
|
+
|
200
198
|
def set_default_summarization_prompt(self, summarization_prompt: str):
|
201
199
|
self._default_summarization_prompt = summarization_prompt
|
202
200
|
|
zrb/task/base/lifecycle.py
CHANGED
@@ -77,7 +77,7 @@ async def run_task_async(
|
|
77
77
|
fill_shared_context_envs(session.shared_ctx) # Inject OS env vars
|
78
78
|
|
79
79
|
# Start the execution chain from the root tasks
|
80
|
-
result = await
|
80
|
+
result = await task.exec_root_tasks(session)
|
81
81
|
return result
|
82
82
|
|
83
83
|
|
zrb/task/base_task.py
CHANGED
@@ -194,6 +194,22 @@ class BaseTask(AnyTask):
|
|
194
194
|
def run(
|
195
195
|
self, session: AnySession | None = None, str_kwargs: dict[str, str] = {}
|
196
196
|
) -> Any:
|
197
|
+
"""
|
198
|
+
Synchronously runs the task and its dependencies, handling async setup and cleanup.
|
199
|
+
|
200
|
+
Uses `asyncio.run()` internally, which creates a new event loop.
|
201
|
+
WARNING: Do not call this method from within an already running asyncio
|
202
|
+
event loop, as it will raise a RuntimeError. Use `async_run` instead
|
203
|
+
if you are in an async context.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
session (AnySession | None): The session to use. If None, a new one
|
207
|
+
might be created implicitly.
|
208
|
+
str_kwargs (dict[str, str]): String-based key-value arguments for inputs.
|
209
|
+
|
210
|
+
Returns:
|
211
|
+
Any: The final result of the main task execution.
|
212
|
+
"""
|
197
213
|
# Use asyncio.run() to execute the async cleanup wrapper
|
198
214
|
return asyncio.run(run_and_cleanup(self, session, str_kwargs))
|
199
215
|
|
zrb/task/cmd_task.py
CHANGED
@@ -255,14 +255,14 @@ class CmdTask(BaseTask):
|
|
255
255
|
def __render_single_cmd_val(
|
256
256
|
self, ctx: AnyContext, single_cmd_val: SingleCmdVal
|
257
257
|
) -> str | None:
|
258
|
+
if isinstance(single_cmd_val, AnyCmdVal):
|
259
|
+
return single_cmd_val.to_str(ctx)
|
258
260
|
if callable(single_cmd_val):
|
259
261
|
return single_cmd_val(ctx)
|
260
262
|
if isinstance(single_cmd_val, str):
|
261
263
|
if self._render_cmd:
|
262
264
|
return ctx.render(single_cmd_val)
|
263
265
|
return single_cmd_val
|
264
|
-
if isinstance(single_cmd_val, AnyCmdVal):
|
265
|
-
return single_cmd_val.to_str(ctx)
|
266
266
|
return None
|
267
267
|
|
268
268
|
def __get_multiline_repr(self, text: str) -> str:
|
zrb/task/llm/prompt.py
CHANGED
@@ -9,12 +9,29 @@ from zrb.task.llm.context import get_default_context # Updated import
|
|
9
9
|
from zrb.util.attr import get_attr, get_str_attr
|
10
10
|
|
11
11
|
|
12
|
-
def
|
12
|
+
def get_persona(
|
13
|
+
ctx: AnyContext,
|
14
|
+
persona_attr: StrAttr | None,
|
15
|
+
render_persona: bool,
|
16
|
+
) -> str:
|
17
|
+
"""Gets the persona, prioritizing task-specific, then default."""
|
18
|
+
persona = get_attr(
|
19
|
+
ctx,
|
20
|
+
persona_attr,
|
21
|
+
None,
|
22
|
+
auto_render=render_persona,
|
23
|
+
)
|
24
|
+
if persona is not None:
|
25
|
+
return persona
|
26
|
+
return default_llm_config.get_default_persona() or ""
|
27
|
+
|
28
|
+
|
29
|
+
def get_base_system_prompt(
|
13
30
|
ctx: AnyContext,
|
14
31
|
system_prompt_attr: StrAttr | None,
|
15
32
|
render_system_prompt: bool,
|
16
33
|
) -> str:
|
17
|
-
"""Gets the system prompt,
|
34
|
+
"""Gets the base system prompt, prioritizing task-specific, then default."""
|
18
35
|
system_prompt = get_attr(
|
19
36
|
ctx,
|
20
37
|
system_prompt_attr,
|
@@ -23,7 +40,51 @@ def get_system_prompt(
|
|
23
40
|
)
|
24
41
|
if system_prompt is not None:
|
25
42
|
return system_prompt
|
26
|
-
return default_llm_config.get_default_system_prompt()
|
43
|
+
return default_llm_config.get_default_system_prompt() or ""
|
44
|
+
|
45
|
+
|
46
|
+
def get_special_instruction_prompt(
|
47
|
+
ctx: AnyContext,
|
48
|
+
special_instruction_prompt_attr: StrAttr | None,
|
49
|
+
render_special_instruction_prompt: bool,
|
50
|
+
) -> str:
|
51
|
+
"""Gets the special instruction prompt, prioritizing task-specific, then default."""
|
52
|
+
special_instruction = get_attr(
|
53
|
+
ctx,
|
54
|
+
special_instruction_prompt_attr,
|
55
|
+
None,
|
56
|
+
auto_render=render_special_instruction_prompt,
|
57
|
+
)
|
58
|
+
if special_instruction is not None:
|
59
|
+
return special_instruction
|
60
|
+
return default_llm_config.get_default_special_instruction_prompt() or ""
|
61
|
+
|
62
|
+
|
63
|
+
def get_combined_system_prompt(
|
64
|
+
ctx: AnyContext,
|
65
|
+
persona_attr: StrAttr | None,
|
66
|
+
render_persona: bool,
|
67
|
+
system_prompt_attr: StrAttr | None,
|
68
|
+
render_system_prompt: bool,
|
69
|
+
special_instruction_prompt_attr: StrAttr | None,
|
70
|
+
render_special_instruction_prompt: bool,
|
71
|
+
) -> str:
|
72
|
+
"""Combines persona, base system prompt, and special instructions."""
|
73
|
+
persona = get_persona(ctx, persona_attr, render_persona)
|
74
|
+
base_system_prompt = get_base_system_prompt(
|
75
|
+
ctx, system_prompt_attr, render_system_prompt
|
76
|
+
)
|
77
|
+
special_instruction = get_special_instruction_prompt(
|
78
|
+
ctx, special_instruction_prompt_attr, render_special_instruction_prompt
|
79
|
+
)
|
80
|
+
parts = []
|
81
|
+
if persona:
|
82
|
+
parts.append(persona)
|
83
|
+
if base_system_prompt:
|
84
|
+
parts.append(base_system_prompt)
|
85
|
+
if special_instruction:
|
86
|
+
parts.append(special_instruction)
|
87
|
+
return "\n\n".join(parts).strip()
|
27
88
|
|
28
89
|
|
29
90
|
def get_user_message(
|
zrb/task/llm_task.py
CHANGED
@@ -32,9 +32,9 @@ from zrb.task.llm.history import (
|
|
32
32
|
from zrb.task.llm.history_summarization import maybe_summarize_history
|
33
33
|
from zrb.task.llm.prompt import (
|
34
34
|
build_user_prompt,
|
35
|
+
get_combined_system_prompt,
|
35
36
|
get_context_enrichment_prompt,
|
36
37
|
get_summarization_prompt,
|
37
|
-
get_system_prompt,
|
38
38
|
)
|
39
39
|
from zrb.util.cli.style import stylize_faint
|
40
40
|
from zrb.xcom.xcom import Xcom
|
@@ -64,12 +64,16 @@ class LLMTask(BaseTask):
|
|
64
64
|
ModelSettings | Callable[[AnySharedContext], ModelSettings] | None
|
65
65
|
) = None,
|
66
66
|
agent: Agent | Callable[[AnySharedContext], Agent] | None = None,
|
67
|
+
persona: StrAttr | None = None,
|
68
|
+
render_persona: bool = True,
|
67
69
|
system_prompt: StrAttr | None = None,
|
68
70
|
render_system_prompt: bool = True,
|
71
|
+
special_instruction_prompt: StrAttr | None = None,
|
72
|
+
render_special_instruction_prompt: bool = True,
|
69
73
|
message: StrAttr | None = None,
|
70
74
|
summarization_prompt: StrAttr | None = None,
|
71
75
|
render_summarization_prompt: bool = True,
|
72
|
-
enrich_context: BoolAttr | None = None,
|
76
|
+
enrich_context: BoolAttr | None = None,
|
73
77
|
render_enrich_context: bool = True,
|
74
78
|
context_enrichment_prompt: StrAttr | None = None,
|
75
79
|
render_context_enrichment_prompt: bool = True,
|
@@ -80,28 +84,23 @@ class LLMTask(BaseTask):
|
|
80
84
|
list[MCPServer] | Callable[[AnySharedContext], list[MCPServer]]
|
81
85
|
) = [],
|
82
86
|
conversation_history: (
|
83
|
-
ConversationHistoryData
|
84
|
-
| Callable[
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
| list # Allow raw list (old format)
|
89
|
-
) = ConversationHistoryData(), # Default to an empty model instance
|
87
|
+
ConversationHistoryData
|
88
|
+
| Callable[[AnySharedContext], ConversationHistoryData | dict | list]
|
89
|
+
| dict
|
90
|
+
| list
|
91
|
+
) = ConversationHistoryData(),
|
90
92
|
conversation_history_reader: (
|
91
93
|
Callable[[AnySharedContext], ConversationHistoryData | dict | list | None]
|
92
94
|
| None
|
93
|
-
# Allow returning raw dict/list or None
|
94
95
|
) = None,
|
95
96
|
conversation_history_writer: (
|
96
|
-
Callable[[AnySharedContext, ConversationHistoryData], None]
|
97
|
-
| None
|
98
|
-
# Writer expects the model instance
|
97
|
+
Callable[[AnySharedContext, ConversationHistoryData], None] | None
|
99
98
|
) = None,
|
100
99
|
conversation_history_file: StrAttr | None = None,
|
101
100
|
render_history_file: bool = True,
|
102
|
-
summarize_history: BoolAttr | None = None,
|
101
|
+
summarize_history: BoolAttr | None = None,
|
103
102
|
render_summarize_history: bool = True,
|
104
|
-
history_summarization_threshold: IntAttr | None = None,
|
103
|
+
history_summarization_threshold: IntAttr | None = None,
|
105
104
|
render_history_summarization_threshold: bool = True,
|
106
105
|
execute_condition: bool | str | Callable[[AnySharedContext], bool] = True,
|
107
106
|
retries: int = 2,
|
@@ -149,8 +148,12 @@ class LLMTask(BaseTask):
|
|
149
148
|
self._render_model_api_key = render_model_api_key
|
150
149
|
self._model_settings = model_settings
|
151
150
|
self._agent = agent
|
151
|
+
self._persona = persona
|
152
|
+
self._render_persona = render_persona
|
152
153
|
self._system_prompt = system_prompt
|
153
154
|
self._render_system_prompt = render_system_prompt
|
155
|
+
self._special_instruction_prompt = special_instruction_prompt
|
156
|
+
self._render_special_instruction_prompt = render_special_instruction_prompt
|
154
157
|
self._message = message
|
155
158
|
self._summarization_prompt = summarization_prompt
|
156
159
|
self._render_summarization_prompt = render_summarization_prompt
|
@@ -213,10 +216,15 @@ class LLMTask(BaseTask):
|
|
213
216
|
summarization_prompt_attr=self._summarization_prompt,
|
214
217
|
render_summarization_prompt=self._render_summarization_prompt,
|
215
218
|
)
|
216
|
-
|
219
|
+
# Get the combined system prompt using the new getter
|
220
|
+
system_prompt = get_combined_system_prompt(
|
217
221
|
ctx=ctx,
|
222
|
+
persona_attr=self._persona,
|
223
|
+
render_persona=self._render_persona,
|
218
224
|
system_prompt_attr=self._system_prompt,
|
219
225
|
render_system_prompt=self._render_system_prompt,
|
226
|
+
special_instruction_prompt_attr=self._special_instruction_prompt,
|
227
|
+
render_special_instruction_prompt=self._render_special_instruction_prompt,
|
220
228
|
)
|
221
229
|
# 1. Prepare initial state (read history, get initial context)
|
222
230
|
history_list, conversation_context = await prepare_initial_state(
|
zrb/task/scheduler.py
CHANGED
@@ -12,7 +12,6 @@ from zrb.task.any_task import AnyTask
|
|
12
12
|
from zrb.task.base_trigger import BaseTrigger
|
13
13
|
from zrb.util.attr import get_str_attr
|
14
14
|
from zrb.util.cron import match_cron
|
15
|
-
from zrb.xcom.xcom import Xcom
|
16
15
|
|
17
16
|
|
18
17
|
class Scheduler(BaseTrigger):
|
@@ -77,6 +76,6 @@ class Scheduler(BaseTrigger):
|
|
77
76
|
ctx.print(f"Current time: {now}")
|
78
77
|
if match_cron(cron_pattern, now):
|
79
78
|
ctx.print(f"Matching {now} with pattern: {cron_pattern}")
|
80
|
-
xcom
|
79
|
+
xcom = self.get_exchange_xcom(ctx.session)
|
81
80
|
xcom.push(now)
|
82
81
|
await asyncio.sleep(60)
|
@@ -13,7 +13,7 @@ zrb/builtin/llm/llm_chat.py,sha256=XL5HK_o1EejkYS0fJIBI39vApuqYYsGHFSigvXfS7CI,4
|
|
13
13
|
zrb/builtin/llm/previous-session.js,sha256=xMKZvJoAbrwiyHS0OoPrWuaKxWYLoyR5sguePIoCjTY,816
|
14
14
|
zrb/builtin/llm/tool/api.py,sha256=yR9I0ZsI96OeQl9pgwORMASVuXsAL0a89D_iPS4C8Dc,1699
|
15
15
|
zrb/builtin/llm/tool/cli.py,sha256=_CNEmEc6K2Z0i9ppYeM7jGpqaEdT3uxaWQatmxP3jKE,858
|
16
|
-
zrb/builtin/llm/tool/file.py,sha256=
|
16
|
+
zrb/builtin/llm/tool/file.py,sha256=ig4tZGYnGjE96U9KgOpbANmyAgFmTcQzym1wVAsZYRM,16620
|
17
17
|
zrb/builtin/llm/tool/rag.py,sha256=45t0o88l7F62oq2P61NnC1hsZJ4h72dZsVQfcsOIUc8,7521
|
18
18
|
zrb/builtin/llm/tool/web.py,sha256=4qzom9xX-JxztIaTWneNfyTRlgweHIxzC1uSEAxJ00A,5507
|
19
19
|
zrb/builtin/md5.py,sha256=0pNlrfZA0wlZlHvFHLgyqN0JZJWGKQIF5oXxO44_OJk,949
|
@@ -239,7 +239,7 @@ zrb/input/option_input.py,sha256=TQB82ko5odgzkULEizBZi0e9TIHEbIgvdP0AR3RhA74,213
|
|
239
239
|
zrb/input/password_input.py,sha256=szBojWxSP9QJecgsgA87OIYwQrY2AQ3USIKdDZY6snU,1465
|
240
240
|
zrb/input/str_input.py,sha256=NevZHX9rf1g8eMatPyy-kUX3DglrVAQpzvVpKAzf7bA,81
|
241
241
|
zrb/input/text_input.py,sha256=shvVbc2U8Is36h23M5lcW8IEwKc9FR-4uEPZZroj3rU,3377
|
242
|
-
zrb/llm_config.py,sha256=
|
242
|
+
zrb/llm_config.py,sha256=sf2cMjD-eSTvOywT1qKVeSftRmxLQ92K3mzho6yP2S8,8952
|
243
243
|
zrb/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
244
244
|
zrb/runner/cli.py,sha256=0mT0oO_yEhc8N4nYCJNujhgLjVykZ0B-kAOFXyAvAqM,6672
|
245
245
|
zrb/runner/common_util.py,sha256=0zhZn1Jdmr194_nsL5_L-Kn9-_NDpMTI2z6_LXUQJ-U,1369
|
@@ -300,14 +300,13 @@ zrb/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
300
300
|
zrb/task/any_task.py,sha256=zklUjkLRQ62TEvfnOUUYfXChj8Zk4igee3w8V3_rN08,5846
|
301
301
|
zrb/task/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
302
302
|
zrb/task/base/context.py,sha256=73k3fKwup0AJwTTLpay0f_-axJextaxgbTem4w4Bmas,3670
|
303
|
-
zrb/task/base/dependencies.py,sha256=Kcxhn7z4OU9Fc_y-cD1Sc96wgNXs0VDoi8r5cJMu0oY,1952
|
304
303
|
zrb/task/base/execution.py,sha256=lB6cfivk-EM6sZSaPjYs_ufb7jb-A2jLJNhBupwBFgI,11101
|
305
|
-
zrb/task/base/lifecycle.py,sha256=
|
304
|
+
zrb/task/base/lifecycle.py,sha256=YIugyqRmEKMnc9MTCDEZr9jVhie3Kwt7LTMtAVAN9Ks,7278
|
306
305
|
zrb/task/base/monitoring.py,sha256=UAOEcPiYNtZR4FFxzWCosuOEFE_P3c4GT5vAhQmohqI,5663
|
307
306
|
zrb/task/base/operators.py,sha256=uAMFqpZJsPnCrojgOl1FUDXTS15mtOa_IqiAXltyYRU,1576
|
308
|
-
zrb/task/base_task.py,sha256=
|
307
|
+
zrb/task/base_task.py,sha256=3MLPYDzNgjIJiQTxSyKLmvG_FspOl61htuRZG7l34QA,8915
|
309
308
|
zrb/task/base_trigger.py,sha256=jC722rDvodaBLeNaFghkTyv1u0QXrK6BLZUUqcmBJ7Q,4581
|
310
|
-
zrb/task/cmd_task.py,sha256=
|
309
|
+
zrb/task/cmd_task.py,sha256=xFAdOvLDK9Qaye40T_lG3K6AIKgbPAMHk_3GIAYeJAM,10750
|
311
310
|
zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
|
312
311
|
zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
313
312
|
zrb/task/llm/agent.py,sha256=2u1zlX41oBzMKozXWXD3gDEOzOsHNsFpedRcJXbUNHI,5105
|
@@ -318,14 +317,14 @@ zrb/task/llm/error.py,sha256=YOwnEdFMtqOlaiA83tDHpC6uh2_9r5NeS-inrlb5a8E,3622
|
|
318
317
|
zrb/task/llm/history.py,sha256=LnrJdXLyo2qz-bNCwLorhoqGmgSiPTUU0bzY63w67-E,9257
|
319
318
|
zrb/task/llm/history_summarization.py,sha256=UaeepcIVMTxJTwqy3V22rpeBXXN04KLvEzOsFtWmyDM,6259
|
320
319
|
zrb/task/llm/print_node.py,sha256=Dkb0xFyEXpNRKFRCM4Md0lfg6K3nI0t8yH3Abh20PjE,4430
|
321
|
-
zrb/task/llm/prompt.py,sha256=
|
320
|
+
zrb/task/llm/prompt.py,sha256=Xql45nZfkB9BXbVVxfKiawtnAVCao2QhaowVdL7qTLg,4627
|
322
321
|
zrb/task/llm/tool_wrapper.py,sha256=gZgoxcuOCgAVDPnLqfJ3ps57ZCVQi7q68z_KnS5Mx1U,3350
|
323
322
|
zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
|
324
|
-
zrb/task/llm_task.py,sha256=
|
323
|
+
zrb/task/llm_task.py,sha256=uLptMfoRawCCPo8_DSneKd8Uag9WDCXkU0oOvO5Lh0M,14218
|
325
324
|
zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
|
326
325
|
zrb/task/rsync_task.py,sha256=GSL9144bmp6F0EckT6m-2a1xG25AzrrWYzH4k3SVUKM,6370
|
327
326
|
zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
|
328
|
-
zrb/task/scheduler.py,sha256=
|
327
|
+
zrb/task/scheduler.py,sha256=5xoet0bMjVq0JKwSwwivD1Jo84wwwA2PrL5WaKwJAGA,3110
|
329
328
|
zrb/task/task.py,sha256=KCrCaWYOQQvv1RJsYtHDeo9RBFmlXQ28oKyEFU4Q7pA,73
|
330
329
|
zrb/task/tcp_check.py,sha256=P_QgGqwd5dXDaud3oQRxe_WuxyxG4s7CTY2wDk9Qcu0,2511
|
331
330
|
zrb/task_status/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -362,7 +361,7 @@ zrb/util/string/name.py,sha256=8picJfUBXNpdh64GNaHv3om23QHhUZux7DguFLrXHp8,1163
|
|
362
361
|
zrb/util/todo.py,sha256=1nDdwPc22oFoK_1ZTXyf3638Bg6sqE2yp_U4_-frHoc,16015
|
363
362
|
zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
364
363
|
zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
|
365
|
-
zrb-1.5.
|
366
|
-
zrb-1.5.
|
367
|
-
zrb-1.5.
|
368
|
-
zrb-1.5.
|
364
|
+
zrb-1.5.11.dist-info/METADATA,sha256=TAOvXdXnUwI1_y0VSDOLxYElSCv441WHSRogvF5EUYU,8469
|
365
|
+
zrb-1.5.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
366
|
+
zrb-1.5.11.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
|
367
|
+
zrb-1.5.11.dist-info/RECORD,,
|
zrb/task/base/dependencies.py
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
from typing import TypeVar
|
2
|
-
|
3
|
-
from zrb.task.any_task import AnyTask
|
4
|
-
|
5
|
-
T = TypeVar("T", bound="AnyTask")
|
6
|
-
|
7
|
-
|
8
|
-
def get_dependency_list(task: AnyTask, attr_name: str) -> list[AnyTask]:
|
9
|
-
"""
|
10
|
-
Safely retrieves a list of dependencies (upstreams, fallbacks, etc.)
|
11
|
-
from a task attribute, handling None or single-task values.
|
12
|
-
"""
|
13
|
-
dependencies = getattr(task, attr_name, None)
|
14
|
-
if dependencies is None:
|
15
|
-
return []
|
16
|
-
elif isinstance(dependencies, list):
|
17
|
-
# Ensure all elements are AnyTask (or compatible) if needed,
|
18
|
-
# but for now, assume the list contains tasks.
|
19
|
-
return dependencies
|
20
|
-
# Assume it's a single AnyTask instance
|
21
|
-
return [dependencies]
|
22
|
-
|
23
|
-
|
24
|
-
def append_dependency(
|
25
|
-
task: T, attr_name: str, dependencies_to_add: AnyTask | list[AnyTask]
|
26
|
-
) -> None:
|
27
|
-
"""
|
28
|
-
Appends one or more dependencies to the specified attribute list of a task,
|
29
|
-
ensuring the attribute becomes a list and avoiding duplicates.
|
30
|
-
|
31
|
-
Modifies the attribute on the task object directly.
|
32
|
-
"""
|
33
|
-
# Retrieve the current list, handling None or single item cases
|
34
|
-
current_deps_list = getattr(task, attr_name, None)
|
35
|
-
if current_deps_list is None:
|
36
|
-
current_deps_list = []
|
37
|
-
elif not isinstance(current_deps_list, list):
|
38
|
-
current_deps_list = [current_deps_list]
|
39
|
-
else:
|
40
|
-
# Create a copy to avoid modifying the original list if no changes occur
|
41
|
-
current_deps_list = list(current_deps_list)
|
42
|
-
|
43
|
-
if isinstance(dependencies_to_add, list):
|
44
|
-
new_deps = dependencies_to_add
|
45
|
-
else:
|
46
|
-
new_deps = [dependencies_to_add] # Ensure it's always a list
|
47
|
-
|
48
|
-
added = False
|
49
|
-
for dep in new_deps:
|
50
|
-
# Check for existence before appending
|
51
|
-
if dep not in current_deps_list:
|
52
|
-
current_deps_list.append(dep)
|
53
|
-
added = True
|
54
|
-
|
55
|
-
# Update the attribute on the task object only if changes were made
|
56
|
-
if added:
|
57
|
-
setattr(task, attr_name, current_deps_list)
|
File without changes
|
File without changes
|