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.
@@ -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
- # Extract just the basename to check if it starts with a dot
162
- return os.path.basename(path).startswith(".")
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
- diff: str,
379
- search_marker: str = "<<<<<< SEARCH",
380
- meta_marker: str = "------",
381
- separator: str = "======",
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 diff to a file.
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
- diff (str): Search/replace block defining changes (see format example below).
388
- search_marker (str): Marker for start of search block. Defaults to "<<<<<< SEARCH".
389
- meta_marker (str): Marker for start of content to search for. Defaults to "------".
390
- separator (str): Marker separating search/replace content. Defaults to "======".
391
- replace_marker (str): Marker for end of replacement block.
392
- Defaults to ">>>>>> REPLACE".
393
- SEARCH block must exactly match file content including whitespace/indentation.
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 are a highly capable AI assistant with access to tools. Your primary goal is to
10
- provide accurate, reliable, and helpful responses.
11
-
12
- Key Instructions:
13
- 1. **Prioritize Tool Use:** Always attempt to use available tools to find
14
- information or perform actions before asking the user.
15
- 2. **Correct Tool Invocation:** Use tools precisely as defined. Provide arguments
16
- in valid JSON where required. Do not pass arguments to tools that don't accept
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. Your task is to synthesize the provided
32
- conversation history and the existing context (which might contain a
33
- previous 'history_summary') into a comprehensive, updated summary
34
- Carefully review the '# Current Context' which includes any previous summary
35
- ('history_summary').
36
- Then, review the '# Conversation History to Summarize'.
37
- Combine the information from both the existing context/summary and the new
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 such as:
54
- - user_name
55
- - user_roles
56
- - preferences
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", "false").lower() == "true"
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
- system_prompt = (
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
- persona = (
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
- if persona is not None:
157
- return f"{persona}\n{system_prompt}"
158
- return system_prompt
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
 
@@ -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 run_async(execute_root_tasks(task, session))
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 get_system_prompt(
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, rendering if configured and handling defaults."""
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, # Default to 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 # Use the new BaseModel
84
- | Callable[
85
- [AnySharedContext], ConversationHistoryData | dict | list
86
- ] # Allow returning raw dict/list too
87
- | dict # Allow raw dict
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, # Default to None
101
+ summarize_history: BoolAttr | None = None,
103
102
  render_summarize_history: bool = True,
104
- history_summarization_threshold: IntAttr | None = None, # Default to 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
- system_prompt = get_system_prompt(
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: Xcom = ctx.xcom[self.queue_name]
79
+ xcom = self.get_exchange_xcom(ctx.session)
81
80
  xcom.push(now)
82
81
  await asyncio.sleep(60)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.5.9
3
+ Version: 1.5.11
4
4
  Summary: Your Automation Powerhouse
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later
@@ -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=ecFBmbMaqCIfqNGTmvd-Yt3vLtKuQ15KbjXlnIMWdho,19269
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=Zfmv1sv1KbfUxB_jOBRYKbbsajenEwQ1jsB3-Fk5DUM,9173
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=3p3mDxE97oizmh9PnF54ud9eoI_PmCXI_VsikIs-VQ8,7293
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=jgo9WKMMppSWvXiWCaPnm42KH6NgbFZKYfJfzRCZKeA,8222
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=f1OWajOBmdtx2QcXBr_8s6o82Fp4UTLqCXJqp2gxwzU,10750
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=PmzeSeUBoqZqqHox1Kq8FkikvmB2lNA3hus-pybXAQg,2719
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=ZfIRryhpS3NsczIUVZBl-f2XWzJkCcvBFmU95v0q4t0,13808
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=aSDF_WtDbmwrYXaVo3BesCG8w21UNV8XLtn-5x9GWzY,3137
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.9.dist-info/METADATA,sha256=jAqyRUhFM5nG5wTG_YW1QaL6nIlo1g2N95EoVACbWaU,8468
366
- zrb-1.5.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
367
- zrb-1.5.9.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
368
- zrb-1.5.9.dist-info/RECORD,,
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,,
@@ -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