zrb 1.15.4__py3-none-any.whl → 1.15.6__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.
@@ -118,9 +118,8 @@ def create_sub_agent_tool(
118
118
  if sub_agent_run and sub_agent_run.result:
119
119
  # Return the final message content as a string
120
120
  return json.dumps({"result": sub_agent_run.result.output})
121
- else:
122
- ctx.log_warning("Sub-agent run did not produce a result.")
123
- return "Sub-agent failed to produce a result."
121
+ ctx.log_warning("Sub-agent run did not produce a result.")
122
+ raise ValueError(f"{tool_name} not returning any result")
124
123
 
125
124
  # Set the name and docstring for the callable function
126
125
  run_sub_agent.__name__ = tool_name
zrb/config/config.py CHANGED
@@ -102,6 +102,7 @@ class Config:
102
102
  level = level.upper()
103
103
  log_levels = {
104
104
  "CRITICAL": logging.CRITICAL, # 50
105
+ "FATAL": logging.CRITICAL, # 50
105
106
  "ERROR": logging.ERROR, # 40
106
107
  "WARN": logging.WARNING, # 30
107
108
  "WARNING": logging.WARNING, # 30
@@ -282,7 +283,7 @@ class Config:
282
283
  Maximum number of LLM requests allowed per minute.
283
284
  Default is conservative to accommodate free-tier LLM providers.
284
285
  """
285
- return int(self._getenv("LLM_MAX_REQUESTS_PER_MINUTE", "15"))
286
+ return int(self._getenv("LLM_MAX_REQUESTS_PER_MINUTE", "60"))
286
287
 
287
288
  @property
288
289
  def LLM_MAX_TOKENS_PER_MINUTE(self) -> int:
@@ -295,7 +296,7 @@ class Config:
295
296
  @property
296
297
  def LLM_MAX_TOKENS_PER_REQUEST(self) -> int:
297
298
  """Maximum number of tokens allowed per individual LLM request."""
298
- return int(self._getenv("LLM_MAX_TOKENS_PER_REQUEST", "50000"))
299
+ return int(self._getenv("LLM_MAX_TOKENS_PER_REQUEST", "100000"))
299
300
 
300
301
  @property
301
302
  def LLM_THROTTLE_SLEEP(self) -> float:
@@ -435,5 +436,13 @@ class Config:
435
436
  def LLM_CONTEXT_FILE(self) -> str:
436
437
  return self._getenv("LLM_CONTEXT_FILE", "ZRB.md")
437
438
 
439
+ @property
440
+ def USE_TIKTOKEN(self) -> bool:
441
+ return to_boolean(self._getenv("USE_TIKTOKEN", "true"))
442
+
443
+ @property
444
+ def TIKTOKEN_ENCODING_NAME(self) -> str:
445
+ return self._getenv("TIKTOKEN_ENCODING_NAME", "cl100k_base")
446
+
438
447
 
439
448
  CFG = Config()
@@ -3,27 +3,9 @@ import time
3
3
  from collections import deque
4
4
  from typing import Callable
5
5
 
6
- import tiktoken
7
-
8
6
  from zrb.config.config import CFG
9
7
 
10
8
 
11
- def _estimate_token(text: str) -> int:
12
- """
13
- Estimates the number of tokens in a given text.
14
- Tries to use the 'gpt-4o' model's tokenizer for an accurate count.
15
- If the tokenizer is unavailable (e.g., due to network issues),
16
- it falls back to a heuristic of 4 characters per token.
17
- """
18
- try:
19
- # Primary method: Use tiktoken for an accurate count
20
- enc = tiktoken.encoding_for_model("gpt-4o")
21
- return len(enc.encode(text))
22
- except Exception:
23
- # Fallback method: Heuristic (4 characters per token)
24
- return len(text) // 4
25
-
26
-
27
9
  class LLMRateLimiter:
28
10
  """
29
11
  Helper class to enforce LLM API rate limits and throttling.
@@ -36,13 +18,15 @@ class LLMRateLimiter:
36
18
  max_tokens_per_minute: int | None = None,
37
19
  max_tokens_per_request: int | None = None,
38
20
  throttle_sleep: float | None = None,
39
- token_counter_fn: Callable[[str], int] | None = None,
21
+ use_tiktoken: bool | None = None,
22
+ tiktoken_encodeing_name: str | None = None,
40
23
  ):
41
24
  self._max_requests_per_minute = max_requests_per_minute
42
25
  self._max_tokens_per_minute = max_tokens_per_minute
43
26
  self._max_tokens_per_request = max_tokens_per_request
44
27
  self._throttle_sleep = throttle_sleep
45
- self._token_counter_fn = token_counter_fn
28
+ self._use_tiktoken = use_tiktoken
29
+ self._tiktoken_encoding_name = tiktoken_encodeing_name
46
30
  self.request_times = deque()
47
31
  self.token_times = deque()
48
32
 
@@ -71,10 +55,16 @@ class LLMRateLimiter:
71
55
  return CFG.LLM_THROTTLE_SLEEP
72
56
 
73
57
  @property
74
- def count_token(self) -> Callable[[str], int]:
75
- if self._token_counter_fn is not None:
76
- return self._token_counter_fn
77
- return _estimate_token
58
+ def use_tiktoken(self) -> bool:
59
+ if self._use_tiktoken is not None:
60
+ return self._use_tiktoken
61
+ return CFG.USE_TIKTOKEN
62
+
63
+ @property
64
+ def tiktoken_encoding_name(self) -> str:
65
+ if self._tiktoken_encoding_name is not None:
66
+ return self._tiktoken_encoding_name
67
+ return CFG.TIKTOKEN_ENCODING_NAME
78
68
 
79
69
  def set_max_requests_per_minute(self, value: int):
80
70
  self._max_requests_per_minute = value
@@ -88,24 +78,43 @@ class LLMRateLimiter:
88
78
  def set_throttle_sleep(self, value: float):
89
79
  self._throttle_sleep = value
90
80
 
91
- def set_token_counter_fn(self, fn: Callable[[str], int]):
92
- self._token_counter_fn = fn
81
+ def count_token(self, prompt: str) -> int:
82
+ if not self.use_tiktoken:
83
+ return self._fallback_count_token(prompt)
84
+ try:
85
+ import tiktoken
86
+
87
+ enc = tiktoken.get_encoding(self.tiktoken_encoding_name)
88
+ return len(enc.encode(prompt))
89
+ except Exception:
90
+ return self._fallback_count_token(prompt)
91
+
92
+ def _fallback_count_token(self, prompt: str) -> int:
93
+ return len(prompt) // 4
93
94
 
94
95
  def clip_prompt(self, prompt: str, limit: int) -> str:
95
- token_count = self.count_token(prompt)
96
- if token_count <= limit:
97
- return prompt
98
- while token_count > limit:
99
- prompt_parts = prompt.split(" ")
100
- last_part_index = len(prompt_parts) - 2
101
- clipped_prompt = " ".join(prompt_parts[:last_part_index])
102
- clipped_prompt += "(Content clipped...)"
103
- token_count = self.count_token(clipped_prompt)
104
- if token_count < limit:
105
- return clipped_prompt
106
- return prompt[:limit]
107
-
108
- async def throttle(self, prompt: str):
96
+ if not self.use_tiktoken:
97
+ return self._fallback_clip_prompt(prompt, limit)
98
+ try:
99
+ import tiktoken
100
+
101
+ enc = tiktoken.get_encoding("cl100k_base")
102
+ tokens = enc.encode(prompt)
103
+ if len(tokens) <= limit:
104
+ return prompt
105
+ truncated = tokens[: limit - 3]
106
+ clipped_text = enc.decode(truncated)
107
+ return clipped_text + "..."
108
+ except Exception:
109
+ return self._fallback_clip_prompt(prompt, limit)
110
+
111
+ def _fallback_clip_prompt(self, prompt: str, limit: int) -> str:
112
+ char_limit = limit * 4 if limit * 4 <= 10 else limit * 4 - 10
113
+ return prompt[:char_limit] + "..."
114
+
115
+ async def throttle(
116
+ self, prompt: str, throttle_notif_callback: Callable | None = None
117
+ ):
109
118
  now = time.time()
110
119
  tokens = self.count_token(prompt)
111
120
  # Clean up old entries
@@ -123,6 +132,8 @@ class LLMRateLimiter:
123
132
  len(self.request_times) >= self.max_requests_per_minute
124
133
  or sum(t for _, t in self.token_times) + tokens > self.max_tokens_per_minute
125
134
  ):
135
+ if throttle_notif_callback is not None:
136
+ throttle_notif_callback()
126
137
  await asyncio.sleep(self.throttle_sleep)
127
138
  now = time.time()
128
139
  while self.request_times and now - self.request_times[0] > 60:
zrb/context/context.py CHANGED
@@ -63,7 +63,7 @@ class Context(AnyContext):
63
63
 
64
64
  @property
65
65
  def session(self) -> AnySession | None:
66
- return self._shared_ctx._session
66
+ return self._shared_ctx.session
67
67
 
68
68
  def update_task_env(self, task_env: dict[str, str]):
69
69
  self._env.update(task_env)
@@ -40,11 +40,7 @@ class SharedContext(AnySharedContext):
40
40
 
41
41
  def __repr__(self):
42
42
  class_name = self.__class__.__name__
43
- input = self._input
44
- args = self._args
45
- env = self._env
46
- xcom = self._xcom
47
- return f"<{class_name} input={input} args={args} xcom={xcom} env={env}>"
43
+ return f"<{class_name}>"
48
44
 
49
45
  @property
50
46
  def is_web_mode(self) -> bool:
zrb/input/any_input.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
+ from typing import Any
2
3
 
3
4
  from zrb.context.any_shared_context import AnySharedContext
4
5
 
@@ -35,7 +36,10 @@ class AnyInput(ABC):
35
36
 
36
37
  @abstractmethod
37
38
  def update_shared_context(
38
- self, shared_ctx: AnySharedContext, str_value: str | None = None
39
+ self,
40
+ shared_ctx: AnySharedContext,
41
+ str_value: str | None = None,
42
+ value: Any = None,
39
43
  ):
40
44
  pass
41
45
 
zrb/input/base_input.py CHANGED
@@ -58,11 +58,15 @@ class BaseInput(AnyInput):
58
58
  return f'<input name="{name}" placeholder="{description}" value="{default}" />'
59
59
 
60
60
  def update_shared_context(
61
- self, shared_ctx: AnySharedContext, str_value: str | None = None
61
+ self,
62
+ shared_ctx: AnySharedContext,
63
+ str_value: str | None = None,
64
+ value: Any = None,
62
65
  ):
63
- if str_value is None:
64
- str_value = self.get_default_str(shared_ctx)
65
- value = self._parse_str_value(str_value)
66
+ if value is None:
67
+ if str_value is None:
68
+ str_value = self.get_default_str(shared_ctx)
69
+ value = self._parse_str_value(str_value)
66
70
  if self.name in shared_ctx.input:
67
71
  raise ValueError(f"Input already defined in the context: {self.name}")
68
72
  shared_ctx.input[self.name] = value
zrb/runner/cli.py CHANGED
@@ -7,7 +7,7 @@ from zrb.context.any_context import AnyContext
7
7
  from zrb.context.shared_context import SharedContext
8
8
  from zrb.group.any_group import AnyGroup
9
9
  from zrb.group.group import Group
10
- from zrb.runner.common_util import get_run_kwargs
10
+ from zrb.runner.common_util import get_task_str_kwargs
11
11
  from zrb.session.session import Session
12
12
  from zrb.session_state_logger.session_state_logger_factory import session_state_logger
13
13
  from zrb.task.any_task import AnyTask
@@ -38,23 +38,25 @@ class Cli(Group):
38
38
  def banner(self) -> str:
39
39
  return CFG.BANNER
40
40
 
41
- def run(self, args: list[str] = []):
42
- kwargs, args = self._extract_kwargs_from_args(args)
43
- node, node_path, args = extract_node_from_args(self, args)
41
+ def run(self, str_args: list[str] = []):
42
+ str_kwargs, str_args = self._extract_kwargs_from_args(str_args)
43
+ node, node_path, str_args = extract_node_from_args(self, str_args)
44
44
  if isinstance(node, AnyGroup):
45
45
  self._show_group_info(node)
46
46
  return
47
- if "h" in kwargs or "help" in kwargs:
47
+ if "h" in str_kwargs or "help" in str_kwargs:
48
48
  self._show_task_info(node)
49
49
  return
50
- run_kwargs = get_run_kwargs(task=node, args=args, kwargs=kwargs, cli_mode=True)
50
+ task_str_kwargs = get_task_str_kwargs(
51
+ task=node, str_args=str_args, str_kwargs=str_kwargs, cli_mode=True
52
+ )
51
53
  try:
52
- result = self._run_task(node, args, run_kwargs)
54
+ result = self._run_task(node, str_args, task_str_kwargs)
53
55
  if result is not None:
54
56
  print(result)
55
57
  return result
56
58
  finally:
57
- run_command = self._get_run_command(node_path, run_kwargs)
59
+ run_command = self._get_run_command(node_path, task_str_kwargs)
58
60
  self._print_run_command(run_command)
59
61
 
60
62
  def _print_run_command(self, run_command: str):
@@ -64,11 +66,14 @@ class Cli(Group):
64
66
  file=sys.stderr,
65
67
  )
66
68
 
67
- def _get_run_command(self, node_path: list[str], run_kwargs: dict[str, str]) -> str:
69
+ def _get_run_command(
70
+ self, node_path: list[str], task_str_kwargs: dict[str, str]
71
+ ) -> str:
68
72
  parts = [self.name] + node_path
69
- if len(run_kwargs) > 0:
73
+ if len(task_str_kwargs) > 0:
70
74
  parts += [
71
- self._get_run_command_param(key, val) for key, val in run_kwargs.items()
75
+ self._get_run_command_param(key, val)
76
+ for key, val in task_str_kwargs.items()
72
77
  ]
73
78
  return " ".join(parts)
74
79
 
@@ -81,13 +86,9 @@ class Cli(Group):
81
86
  self, task: AnyTask, args: list[str], run_kwargs: dict[str, str]
82
87
  ) -> tuple[Any]:
83
88
  shared_ctx = SharedContext(args=args)
84
- for task_input in task.inputs:
85
- if task_input.name in run_kwargs:
86
- task_input.update_shared_context(
87
- shared_ctx, run_kwargs[task_input.name]
88
- )
89
- continue
90
- return task.run(Session(shared_ctx=shared_ctx, root_group=self))
89
+ return task.run(
90
+ Session(shared_ctx=shared_ctx, root_group=self), str_kwargs=run_kwargs
91
+ )
91
92
 
92
93
  def _show_task_info(self, task: AnyTask):
93
94
  description = task.description
zrb/runner/common_util.py CHANGED
@@ -1,31 +1,36 @@
1
- from typing import Any
2
-
3
1
  from zrb.context.shared_context import SharedContext
4
2
  from zrb.task.any_task import AnyTask
5
3
 
6
4
 
7
- def get_run_kwargs(
8
- task: AnyTask, args: list[str], kwargs: dict[str, str], cli_mode: bool
5
+ def get_task_str_kwargs(
6
+ task: AnyTask, str_args: list[str], str_kwargs: dict[str, str], cli_mode: bool
9
7
  ) -> dict[str, str]:
10
8
  arg_index = 0
11
- str_kwargs = {key: f"{val}" for key, val in kwargs.items()}
12
- run_kwargs = {**str_kwargs}
13
- shared_ctx = SharedContext(args=args)
9
+ dummmy_shared_ctx = SharedContext()
10
+ task_str_kwargs = {}
14
11
  for task_input in task.inputs:
12
+ task_name = task_input.name
15
13
  if task_input.name in str_kwargs:
16
- # Update shared context for next input default value
17
- task_input.update_shared_context(shared_ctx, str_kwargs[task_input.name])
18
- elif arg_index < len(args) and task_input.allow_positional_parsing:
19
- run_kwargs[task_input.name] = args[arg_index]
20
- # Update shared context for next input default value
21
- task_input.update_shared_context(shared_ctx, run_kwargs[task_input.name])
14
+ task_str_kwargs[task_input.name] = str_kwargs[task_name]
15
+ # Update dummy shared context for next input default value
16
+ task_input.update_shared_context(
17
+ dummmy_shared_ctx, str_value=str_kwargs[task_name]
18
+ )
19
+ elif arg_index < len(str_args) and task_input.allow_positional_parsing:
20
+ task_str_kwargs[task_name] = str_args[arg_index]
21
+ # Update dummy shared context for next input default value
22
+ task_input.update_shared_context(
23
+ dummmy_shared_ctx, str_value=task_str_kwargs[task_name]
24
+ )
22
25
  arg_index += 1
23
26
  else:
24
27
  if cli_mode and task_input.always_prompt:
25
- str_value = task_input.prompt_cli_str(shared_ctx)
28
+ str_value = task_input.prompt_cli_str(dummmy_shared_ctx)
26
29
  else:
27
- str_value = task_input.get_default_str(shared_ctx)
28
- run_kwargs[task_input.name] = str_value
29
- # Update shared context for next input default value
30
- task_input.update_shared_context(shared_ctx, run_kwargs[task_input.name])
31
- return run_kwargs
30
+ str_value = task_input.get_default_str(dummmy_shared_ctx)
31
+ task_str_kwargs[task_name] = str_value
32
+ # Update dummy shared context for next input default value
33
+ task_input.update_shared_context(
34
+ dummmy_shared_ctx, str_value=task_str_kwargs[task_name]
35
+ )
36
+ return task_str_kwargs
@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING
3
3
 
4
4
  from zrb.config.web_auth_config import WebAuthConfig
5
5
  from zrb.group.any_group import AnyGroup
6
- from zrb.runner.common_util import get_run_kwargs
6
+ from zrb.runner.common_util import get_task_str_kwargs
7
7
  from zrb.runner.web_util.user import get_user_from_request
8
8
  from zrb.task.any_task import AnyTask
9
9
  from zrb.util.group import NodeNotFoundError, extract_node_from_args
@@ -39,9 +39,9 @@ def serve_task_input_api(
39
39
  if isinstance(task, AnyTask):
40
40
  if not user.can_access_task(task):
41
41
  return JSONResponse(content={"detail": "Forbidden"}, status_code=403)
42
- query_dict = json.loads(query)
43
- run_kwargs = get_run_kwargs(
44
- task=task, args=[], kwargs=query_dict, cli_mode=False
42
+ str_kwargs = json.loads(query)
43
+ task_str_kwargs = get_task_str_kwargs(
44
+ task=task, str_args=[], str_kwargs=str_kwargs, cli_mode=False
45
45
  )
46
- return run_kwargs
46
+ return task_str_kwargs
47
47
  return JSONResponse(content={"detail": "Not found"}, status_code=404)
zrb/task/any_task.py CHANGED
@@ -148,13 +148,17 @@ class AnyTask(ABC):
148
148
 
149
149
  @abstractmethod
150
150
  def run(
151
- self, session: "AnySession | None" = None, str_kwargs: dict[str, str] = {}
151
+ self,
152
+ session: "AnySession | None" = None,
153
+ str_kwargs: dict[str, str] | None = None,
154
+ kwargs: dict[str, Any] | None = None,
152
155
  ) -> Any:
153
156
  """Runs the task synchronously.
154
157
 
155
158
  Args:
156
159
  session (AnySession): The shared session.
157
160
  str_kwargs(dict[str, str]): The input string values.
161
+ kwargs(dict[str, Any]): The input values.
158
162
 
159
163
  Returns:
160
164
  Any: The result of the task execution.
@@ -163,13 +167,17 @@ class AnyTask(ABC):
163
167
 
164
168
  @abstractmethod
165
169
  async def async_run(
166
- self, session: "AnySession | None" = None, str_kwargs: dict[str, str] = {}
170
+ self,
171
+ session: "AnySession | None" = None,
172
+ str_kwargs: dict[str, str] | None = None,
173
+ kwargs: dict[str, Any] | None = None,
167
174
  ) -> Any:
168
175
  """Runs the task asynchronously.
169
176
 
170
177
  Args:
171
178
  session (AnySession): The shared session.
172
179
  str_kwargs(dict[str, str]): The input string values.
180
+ kwargs(dict[str, Any]): The input values.
173
181
 
174
182
  Returns:
175
183
  Any: The result of the task execution.
zrb/task/base/context.py CHANGED
@@ -26,25 +26,33 @@ def build_task_context(task: AnyTask, session: AnySession) -> AnyContext:
26
26
 
27
27
 
28
28
  def fill_shared_context_inputs(
29
- task: AnyTask, shared_context: AnySharedContext, str_kwargs: dict[str, str] = {}
29
+ shared_ctx: AnySharedContext,
30
+ task: AnyTask,
31
+ str_kwargs: dict[str, str] | None = None,
32
+ kwargs: dict[str, str] | None = None,
30
33
  ):
31
34
  """
32
- Populates the shared context with input values provided via kwargs.
35
+ Populates the shared context with input values provided via str_kwargs.
33
36
  """
37
+ str_kwarg_dict = str_kwargs if str_kwargs is not None else {}
38
+ kwarg_dict = kwargs if kwargs is not None else {}
34
39
  for task_input in task.inputs:
35
- if task_input.name not in shared_context.input:
36
- str_value = str_kwargs.get(task_input.name, None)
37
- task_input.update_shared_context(shared_context, str_value)
40
+ if task_input.name not in shared_ctx.input:
41
+ task_input.update_shared_context(
42
+ shared_ctx,
43
+ value=kwarg_dict.get(task_input.name, None),
44
+ str_value=str_kwarg_dict.get(task_input.name, None),
45
+ )
38
46
 
39
47
 
40
- def fill_shared_context_envs(shared_context: AnySharedContext):
48
+ def fill_shared_context_envs(shared_ctx: AnySharedContext):
41
49
  """
42
50
  Injects OS environment variables into the shared context if they don't already exist.
43
51
  """
44
52
  os_env_map = {
45
- key: val for key, val in os.environ.items() if key not in shared_context.env
53
+ key: val for key, val in os.environ.items() if key not in shared_ctx.env
46
54
  }
47
- shared_context.env.update(os_env_map)
55
+ shared_ctx.env.update(os_env_map)
48
56
 
49
57
 
50
58
  def combine_inputs(
@@ -12,7 +12,8 @@ from zrb.util.run import run_async
12
12
  async def run_and_cleanup(
13
13
  task: AnyTask,
14
14
  session: AnySession | None = None,
15
- str_kwargs: dict[str, str] = {},
15
+ str_kwargs: dict[str, str] | None = None,
16
+ kwargs: dict[str, Any] | None = None,
16
17
  ) -> Any:
17
18
  """
18
19
  Wrapper for async_run that ensures session termination and cleanup of
@@ -23,7 +24,9 @@ async def run_and_cleanup(
23
24
  session = Session(shared_ctx=SharedContext())
24
25
 
25
26
  # Create the main task execution coroutine
26
- main_task_coro = asyncio.create_task(run_task_async(task, session, str_kwargs))
27
+ main_task_coro = asyncio.create_task(
28
+ run_task_async(task, session, str_kwargs, kwargs)
29
+ )
27
30
 
28
31
  try:
29
32
  result = await main_task_coro
@@ -67,7 +70,8 @@ async def run_and_cleanup(
67
70
  async def run_task_async(
68
71
  task: AnyTask,
69
72
  session: AnySession | None = None,
70
- str_kwargs: dict[str, str] = {},
73
+ str_kwargs: dict[str, str] | None = None,
74
+ kwargs: dict[str, Any] | None = None,
71
75
  ) -> Any:
72
76
  """
73
77
  Asynchronous entry point for running a task (`task.async_run()`).
@@ -77,7 +81,7 @@ async def run_task_async(
77
81
  session = Session(shared_ctx=SharedContext())
78
82
 
79
83
  # Populate shared context with inputs and environment variables
80
- fill_shared_context_inputs(task, session.shared_ctx, str_kwargs)
84
+ fill_shared_context_inputs(session.shared_ctx, task, str_kwargs, kwargs)
81
85
  fill_shared_context_envs(session.shared_ctx) # Inject OS env vars
82
86
 
83
87
  # Start the execution chain from the root tasks
zrb/task/base_task.py CHANGED
@@ -216,7 +216,10 @@ class BaseTask(AnyTask):
216
216
  return build_task_context(self, session)
217
217
 
218
218
  def run(
219
- self, session: AnySession | None = None, str_kwargs: dict[str, str] = {}
219
+ self,
220
+ session: AnySession | None = None,
221
+ str_kwargs: dict[str, str] | None = None,
222
+ kwargs: dict[str, str] | None = None,
220
223
  ) -> Any:
221
224
  """
222
225
  Synchronously runs the task and its dependencies, handling async setup and cleanup.
@@ -235,12 +238,19 @@ class BaseTask(AnyTask):
235
238
  Any: The final result of the main task execution.
236
239
  """
237
240
  # Use asyncio.run() to execute the async cleanup wrapper
238
- return asyncio.run(run_and_cleanup(self, session, str_kwargs))
241
+ return asyncio.run(
242
+ run_and_cleanup(self, session=session, str_kwargs=str_kwargs, kwargs=kwargs)
243
+ )
239
244
 
240
245
  async def async_run(
241
- self, session: AnySession | None = None, str_kwargs: dict[str, str] = {}
246
+ self,
247
+ session: AnySession | None = None,
248
+ str_kwargs: dict[str, str] | None = None,
249
+ kwargs: dict[str, Any] | None = None,
242
250
  ) -> Any:
243
- return await run_task_async(self, session, str_kwargs)
251
+ return await run_task_async(
252
+ self, session=session, str_kwargs=str_kwargs, kwargs=kwargs
253
+ )
244
254
 
245
255
  async def exec_root_tasks(self, session: AnySession):
246
256
  return await execute_root_tasks(self, session)
@@ -280,3 +290,43 @@ class BaseTask(AnyTask):
280
290
  # fallback: use the __notes__ attribute directly
281
291
  e.__notes__ = getattr(e, "__notes__", []) + [additional_error_note]
282
292
  raise e
293
+
294
+ def to_fn(self):
295
+ # NOTE: Non documented feature, untested
296
+ from inspect import Parameter, Signature
297
+
298
+ from zrb.context.shared_context import SharedContext
299
+ from zrb.session.session import Session
300
+
301
+ def task_runner_fn(**kwargs):
302
+ str_kwargs = {k: str(v) for k, v in kwargs.items()}
303
+ shared_ctx = SharedContext()
304
+ session = Session(shared_ctx=shared_ctx)
305
+ return self.run(session=session, str_kwargs=str_kwargs)
306
+
307
+ # Create docstring
308
+ doc = f"{self.description}\n\n"
309
+ if len(self.inputs) > 0:
310
+ doc += "Args:\n"
311
+ for inp in self.inputs:
312
+ doc += f" {inp.name}: {inp.description}"
313
+ if inp.default is not None:
314
+ doc += f" (default: {inp.default})"
315
+ doc += "\n"
316
+ task_runner_fn.__doc__ = doc
317
+
318
+ # Create signature
319
+ params = []
320
+ for inp in self.inputs:
321
+ params.append(
322
+ Parameter(
323
+ name=inp.name,
324
+ kind=Parameter.POSITIONAL_OR_KEYWORD,
325
+ default=inp.default if inp.default is not None else Parameter.empty,
326
+ )
327
+ )
328
+ sig = Signature(params)
329
+ task_runner_fn.__signature__ = sig
330
+ task_runner_fn.__name__ = self.name
331
+
332
+ return task_runner_fn
zrb/task/llm/agent.py CHANGED
@@ -9,6 +9,7 @@ from zrb.task.llm.error import extract_api_error_details
9
9
  from zrb.task.llm.print_node import print_node
10
10
  from zrb.task.llm.tool_wrapper import wrap_func, wrap_tool
11
11
  from zrb.task.llm.typing import ListOfDict
12
+ from zrb.util.cli.style import stylize_faint
12
13
 
13
14
  if TYPE_CHECKING:
14
15
  from pydantic_ai import Agent, Tool
@@ -184,10 +185,11 @@ async def _run_single_agent_iteration(
184
185
  agent_payload = _estimate_request_payload(
185
186
  agent, user_prompt, attachments, history_list
186
187
  )
188
+ callback = lambda: _print_throttle_notif(ctx)
187
189
  if rate_limitter:
188
- await rate_limitter.throttle(agent_payload)
190
+ await rate_limitter.throttle(agent_payload, callback)
189
191
  else:
190
- await llm_rate_limitter.throttle(agent_payload)
192
+ await llm_rate_limitter.throttle(agent_payload, callback)
191
193
 
192
194
  user_prompt_with_attachments = [user_prompt] + attachments
193
195
  async with agent:
@@ -214,6 +216,10 @@ async def _run_single_agent_iteration(
214
216
  return agent_run
215
217
 
216
218
 
219
+ def _print_throttle_notif(ctx: AnyContext):
220
+ ctx.print(stylize_faint(" ⌛>> Request Throttled"), plain=True)
221
+
222
+
217
223
  def _estimate_request_payload(
218
224
  agent: "Agent",
219
225
  user_prompt: str,
@@ -16,6 +16,7 @@ from zrb.task.llm.typing import ListOfDict
16
16
  from zrb.util.attr import get_bool_attr, get_int_attr
17
17
  from zrb.util.cli.style import stylize_faint
18
18
  from zrb.util.llm.prompt import make_prompt_section
19
+ from zrb.util.truncate import truncate_str
19
20
 
20
21
  if TYPE_CHECKING:
21
22
  from pydantic_ai.models import Model
@@ -88,7 +89,7 @@ async def summarize_history(
88
89
  conversation_history: ConversationHistory,
89
90
  rate_limitter: LLMRateLimiter | None = None,
90
91
  retries: int = 3,
91
- ) -> str:
92
+ ) -> ConversationHistory:
92
93
  """Runs an LLM call to update the conversation summary."""
93
94
  from pydantic_ai import Agent
94
95
 
@@ -115,7 +116,7 @@ async def summarize_history(
115
116
  ),
116
117
  make_prompt_section(
117
118
  "Recent Conversation (JSON)",
118
- json.dumps(conversation_history.history),
119
+ json.dumps(truncate_str(conversation_history.history, 1000)),
119
120
  as_code=True,
120
121
  ),
121
122
  make_prompt_section(
@@ -111,6 +111,8 @@ def _create_wrapper(
111
111
  if not approval:
112
112
  raise ValueError(f"User disapproving: {reason}")
113
113
  return await run_async(func(*args, **kwargs))
114
+ except KeyboardInterrupt as e:
115
+ raise e
114
116
  except Exception as e:
115
117
  error_model = ToolExecutionError(
116
118
  tool_name=func.__name__,
@@ -126,14 +128,13 @@ def _create_wrapper(
126
128
  async def _ask_for_approval(
127
129
  ctx: AnyContext, func: Callable, args: list[Any], kwargs: dict[str, Any]
128
130
  ) -> tuple[bool, str]:
129
- func_name = get_callable_name(func)
130
131
  func_call_str = _get_func_call_str(func, args, kwargs)
131
- func_detail_param = _get_detail_func_param(args, kwargs)
132
- confirmation_message = render_markdown(
133
- f"Allow to run `{func_name}`? (`Yes` | `No, <reason>`)"
134
- )
135
- complete_confirmation_message = (
136
- f"\n✅ >> {func_call_str}" f"\n{func_detail_param}" f"\n{confirmation_message}"
132
+ complete_confirmation_message = "\n".join(
133
+ [
134
+ f"\n🎰 >> {func_call_str}",
135
+ _get_detail_func_param(args, kwargs),
136
+ f"✅ >> {_get_run_func_confirmation(func)}",
137
+ ]
137
138
  )
138
139
  while True:
139
140
  ctx.print(complete_confirmation_message, plain=True)
@@ -164,6 +165,13 @@ async def _ask_for_approval(
164
165
  continue
165
166
 
166
167
 
168
+ def _get_run_func_confirmation(func: Callable) -> str:
169
+ func_name = get_callable_name(func)
170
+ return render_markdown(
171
+ f"Allow to run `{func_name}`? (`Yes` | `No, <reason>`)"
172
+ ).strip()
173
+
174
+
167
175
  def _get_detail_func_param(args: list[Any], kwargs: dict[str, Any]) -> str:
168
176
  markdown = "\n".join(
169
177
  [_get_func_param_item(key, val) for key, val in kwargs.items()]
@@ -198,9 +206,10 @@ def _get_func_call_str(func: Callable, args: list[Any], kwargs: dict[str, Any])
198
206
 
199
207
 
200
208
  def _truncate_arg(arg: str, length: int = 19) -> str:
201
- if len(arg) > length:
202
- return f"{arg[:length-4]} ..."
203
- return arg
209
+ normalized_arg = arg.replace("\n", "\\n")
210
+ if len(normalized_arg) > length:
211
+ return f"{normalized_arg[:length-4]} ..."
212
+ return normalized_arg
204
213
 
205
214
 
206
215
  async def _read_line():
zrb/task/llm_task.py CHANGED
@@ -323,6 +323,7 @@ class LLMTask(BaseTask):
323
323
  history_list=conversation_history.history,
324
324
  rate_limitter=self._rate_limitter,
325
325
  )
326
+
326
327
  if agent_run and agent_run.result:
327
328
  new_history_list = json.loads(agent_run.result.all_messages_json())
328
329
  conversation_history.history = new_history_list
zrb/util/truncate.py ADDED
@@ -0,0 +1,23 @@
1
+ from collections.abc import Mapping, Sequence
2
+ from typing import Any
3
+
4
+
5
+ def truncate_str(value: Any, limit: int):
6
+ # If value is a string, truncate
7
+ if isinstance(value, str):
8
+ if len(value) > limit:
9
+ if limit < 4:
10
+ return value[:limit]
11
+ return value[: limit - 4] + " ..."
12
+ # If value is a dict, process recursively
13
+ elif isinstance(value, Mapping):
14
+ return {k: truncate_str(v, limit) for k, v in value.items()}
15
+ # If value is a list or tuple, process recursively preserving type
16
+ elif isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
17
+ t = type(value)
18
+ return t(truncate_str(v, limit) for v in value)
19
+ # If value is a set, process recursively preserving type
20
+ elif isinstance(value, set):
21
+ return {truncate_str(v, limit) for v in value}
22
+ # Other types are returned unchanged
23
+ return value
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: zrb
3
- Version: 1.15.4
3
+ Version: 1.15.6
4
4
  Summary: Your Automation Powerhouse
5
- Home-page: https://github.com/state-alchemists/zrb
6
5
  License: AGPL-3.0-or-later
7
6
  Keywords: Automation,Task Runner,Code Generator,Monorepo,Low Code
8
7
  Author: Go Frendi Gunawan
@@ -13,28 +12,29 @@ Classifier: Programming Language :: Python :: 3
13
12
  Classifier: Programming Language :: Python :: 3.10
14
13
  Classifier: Programming Language :: Python :: 3.11
15
14
  Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
16
  Provides-Extra: all
17
17
  Provides-Extra: playwright
18
18
  Provides-Extra: rag
19
- Requires-Dist: beautifulsoup4 (>=4.13.3,<5.0.0)
20
- Requires-Dist: black (>=25.1.0,<25.2.0)
19
+ Requires-Dist: beautifulsoup4 (>=4.13.4,<5.0.0)
20
+ Requires-Dist: black (>=25.1.0,<26.0.0)
21
21
  Requires-Dist: chromadb (>=0.6.3,<0.7.0) ; extra == "rag" or extra == "all"
22
22
  Requires-Dist: fastapi[standard] (>=0.116.1,<0.117.0)
23
- Requires-Dist: isort (>=6.0.1,<6.1.0)
24
- Requires-Dist: libcst (>=1.7.0,<2.0.0)
23
+ Requires-Dist: isort (>=6.0.1,<7.0.0)
24
+ Requires-Dist: libcst (>=1.8.2,<2.0.0)
25
25
  Requires-Dist: markdownify (>=1.2.0,<2.0.0)
26
- Requires-Dist: openai (>=1.99.7)
27
- Requires-Dist: pdfplumber (>=0.11.6,<0.12.0)
28
- Requires-Dist: playwright (>=1.53.0,<2.0.0) ; extra == "playwright" or extra == "all"
26
+ Requires-Dist: pdfplumber (>=0.11.7,<0.12.0)
27
+ Requires-Dist: playwright (>=1.54.0,<2.0.0) ; extra == "playwright" or extra == "all"
29
28
  Requires-Dist: psutil (>=7.0.0,<8.0.0)
30
- Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cli,cohere,google,groq,huggingface,mcp,mistral,openai,vertexai] (>=0.7.0,<0.8.0)
29
+ Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cli,cohere,google,groq,huggingface,mcp,mistral,openai,vertexai] (>=0.7.4,<0.8.0)
31
30
  Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
32
31
  Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
33
- Requires-Dist: python-jose[cryptography] (>=3.4.0,<4.0.0)
32
+ Requires-Dist: python-jose[cryptography] (>=3.5.0,<4.0.0)
34
33
  Requires-Dist: requests (>=2.32.4,<3.0.0)
35
34
  Requires-Dist: tiktoken (>=0.8.0,<0.9.0)
36
35
  Requires-Dist: ulid-py (>=1.1.0,<2.0.0)
37
36
  Project-URL: Documentation, https://github.com/state-alchemists/zrb
37
+ Project-URL: Homepage, https://github.com/state-alchemists/zrb
38
38
  Project-URL: Repository, https://github.com/state-alchemists/zrb
39
39
  Description-Content-Type: text/markdown
40
40
 
@@ -20,7 +20,7 @@ zrb/builtin/llm/tool/cli.py,sha256=8rugrKaNPEatHjr7nN4OIRLRT2TcF-oylEZGbLI9Brs,1
20
20
  zrb/builtin/llm/tool/code.py,sha256=BACeH0tGhTdDo0rZ7sTAP6oRaLi9cS7gdEigXiTd0Jg,8842
21
21
  zrb/builtin/llm/tool/file.py,sha256=eXFGGFxxpdpWGVw0svyQNQc03I5M7wotSsA_HjkXw7c,23670
22
22
  zrb/builtin/llm/tool/rag.py,sha256=Ab8_ZljnG_zfkwxPezImvorshuz3Fi4CmSzNOtU1a-g,9770
23
- zrb/builtin/llm/tool/sub_agent.py,sha256=hsR-5iozcgE0f-3J7VLP189JqsW6lE2h-x_1YW6pVAo,5086
23
+ zrb/builtin/llm/tool/sub_agent.py,sha256=dPqdFCXxJ-3hZnPjheZr-bPeKNbfSBSDKcZZR5vkWFM,5075
24
24
  zrb/builtin/llm/tool/web.py,sha256=zNQWDNOz_MyyzhncBeV5E_9XijQxTFVp6BSzz_e5tBI,7261
25
25
  zrb/builtin/md5.py,sha256=690RV2LbW7wQeTFxY-lmmqTSVEEZv3XZbjEUW1Q3XpE,1480
26
26
  zrb/builtin/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -217,7 +217,7 @@ zrb/callback/callback.py,sha256=PFhCqzfxdk6IAthmXcZ13DokT62xtBzJr_ciLw6I8Zg,4030
217
217
  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
- zrb/config/config.py,sha256=YhmXeD5FKlMXv924_iWA-k9eUBbuOKXphoW6V56NQa4,13871
220
+ zrb/config/config.py,sha256=AtJ61i_ZTi95vRV5h4EKtMWhPdU2YVEdv08mU1DePEk,14161
221
221
  zrb/config/default_prompt/file_extractor_system_prompt.md,sha256=tmeZMPzF9MGExsZZw7M2PZN6V0oFVRp1nIjiqUPvQ9M,1013
222
222
  zrb/config/default_prompt/interactive_system_prompt.md,sha256=sRFrwqnqudnXATkTwHnnQAhLKC0ep6lad8vKXaRAPzc,3505
223
223
  zrb/config/default_prompt/persona.md,sha256=WU4JKp-p7qJePDA6NZ_CYdBggo2B3PEq8IEnNVblIHU,41
@@ -228,7 +228,7 @@ zrb/config/default_prompt/system_prompt.md,sha256=Jkne5n9HJcBCgfeENwxvqH-kbDO2Ca
228
228
  zrb/config/llm_config.py,sha256=xt-Xf8ZuNoUT_GKCSFz5yy0BhbeHzxP-jrezB06WeiY,8857
229
229
  zrb/config/llm_context/config.py,sha256=PDsrKAduQfsEUMYt4jirG0F7KDkY7jqhrbsptxdMOEg,4962
230
230
  zrb/config/llm_context/config_parser.py,sha256=h95FbOjvVobhrsfGtG_BY3hxS-OLzQj-9F5vGZuehkY,1473
231
- zrb/config/llm_rate_limitter.py,sha256=P4vR7qxwiGwjlKx2kHcfdIxwGbJB98vdN-UQEH-Q2WU,4894
231
+ zrb/config/llm_rate_limitter.py,sha256=_iQRv3d6kUPeRvmUYZX_iwCE7iDSEK1oKa4bQ9GROho,5261
232
232
  zrb/config/web_auth_config.py,sha256=_PXatQTYh2mX9H3HSYSQKp13zm1RlLyVIoeIr6KYMQ8,6279
233
233
  zrb/content_transformer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
234
234
  zrb/content_transformer/any_content_transformer.py,sha256=v8ZUbcix1GGeDQwB6OKX_1TjpY__ksxWVeqibwa_iZA,850
@@ -236,8 +236,8 @@ zrb/content_transformer/content_transformer.py,sha256=STl77wW-I69QaGzCXjvkppngYF
236
236
  zrb/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
237
237
  zrb/context/any_context.py,sha256=2hgVKbbDwmwrEl1h1L1FaTUjuUYaDd_b7YRGkaorW6Q,6362
238
238
  zrb/context/any_shared_context.py,sha256=wJawL1jGgApcKPRcpw3js7W4-MhJRA3GMbR5zTsJmt0,1929
239
- zrb/context/context.py,sha256=ErGhXJgjgNaAqi6iPMejWxFZ3YvWnysC6mHEU-wodKk,6884
240
- zrb/context/shared_context.py,sha256=c87VujBGKbkEC6R8Bby0-Gp0h2UvX8ZsH_3S0ElEKSs,3124
239
+ zrb/context/context.py,sha256=pnFV7KjVMKWAkYidhZTVFpPZvvGbqGuj_TERpWOvCIw,6883
240
+ zrb/context/shared_context.py,sha256=mBbTDPfde5ZBj4EKfMEC2636bSE7GZWx7MtaQnETqgg,2972
241
241
  zrb/dot_dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
242
242
  zrb/dot_dict/dot_dict.py,sha256=ubw_x8I7AOJ59xxtFVJ00VGmq_IYdZP3mUhNlO4nEK0,556
243
243
  zrb/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -249,8 +249,8 @@ zrb/group/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
249
  zrb/group/any_group.py,sha256=sdUk82YRuJBMwDhD_b-vJ3v-Yxz8K7LFW52TM19k3HI,1242
250
250
  zrb/group/group.py,sha256=t5JnXlrLfthFfqmkVdRLKCIyms4JqsthcTODVncSmPk,4312
251
251
  zrb/input/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
252
- zrb/input/any_input.py,sha256=mmkU7WsqIK-IK8B5rNkWdy6mKS47zasnXJWE60MTRWQ,984
253
- zrb/input/base_input.py,sha256=2gwIcT9xLXjcrwF-4k6ghDs819E-DWIwijlzfQtauWs,3766
252
+ zrb/input/any_input.py,sha256=3EQgg2Qk_2t2Ege_4pHV2G95tV3XAIThLLuFZ6uV6oM,1051
253
+ zrb/input/base_input.py,sha256=Rb2m9xLJgGDph2D8L9HnLFPtQZqmPAPKrSP2K4pK97A,3848
254
254
  zrb/input/bool_input.py,sha256=9ir8aTm8zaufrJ84_EerC5vOjyn4hHZ3BiLzFqPgPFM,1839
255
255
  zrb/input/float_input.py,sha256=8G9lJKLlb_Upk9m5pBF9JwqlAAiYJLrbIItLnyzPxpg,1475
256
256
  zrb/input/int_input.py,sha256=UhxCFYlZdJcgUSGGEkz301zOgRVpK0KDG_IxxWpQfMU,1457
@@ -259,8 +259,8 @@ zrb/input/password_input.py,sha256=szBojWxSP9QJecgsgA87OIYwQrY2AQ3USIKdDZY6snU,1
259
259
  zrb/input/str_input.py,sha256=NevZHX9rf1g8eMatPyy-kUX3DglrVAQpzvVpKAzf7bA,81
260
260
  zrb/input/text_input.py,sha256=UCkC497V6L12cPjupOgIZ5XW2eBbBDydQi5IIYtknek,3702
261
261
  zrb/runner/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
262
- zrb/runner/cli.py,sha256=EjnTtEEkTDdTURh20Jie3SMoAG5NJQneCR3esgbPmuE,6996
263
- zrb/runner/common_util.py,sha256=JDMcwvQ8cxnv9kQrAoKVLA40Q1omfv-u5_d5MvvwHeE,1373
262
+ zrb/runner/cli.py,sha256=CNZoti_cNcHEkD6TO4gQie2_t6KTinjdc7oEc-wszQg,6947
263
+ zrb/runner/common_util.py,sha256=yIJm9ivM7hvJ4Kb4Nt5RRE7oqAlt9EN89w6JDGyLkFE,1570
264
264
  zrb/runner/web_app.py,sha256=n8iXtQ5DGIfRcFsHdBafm9VJisVSDD159XFPpEXQTN0,2796
265
265
  zrb/runner/web_route/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
266
266
  zrb/runner/web_route/docs_route.py,sha256=Y8zTROzIOfmJuU_E9KZljdjgkd65DcvLeX3k0xO-o_k,550
@@ -315,7 +315,7 @@ zrb/runner/web_route/static/resources/session/current-session.js,sha256=tzUdK7qJ
315
315
  zrb/runner/web_route/static/resources/session/event.js,sha256=X5OlSHefK0SDB9VkFCRyBKE_Pb7mqM319mW9jRGoDOk,4716
316
316
  zrb/runner/web_route/static/resources/session/past-session.js,sha256=RwGJYKSp75K8NZ-iZP58XppWgdzkiKFaiC5wgcMLxDo,5470
317
317
  zrb/runner/web_route/static/static_route.py,sha256=QPs5XW4O_8CuzG0Wy4sHh5wRcLbU63CLDI4YNqkUxHA,1555
318
- zrb/runner/web_route/task_input_api_route.py,sha256=6JIehRjXPhzclq9qGMYkztaKB0TzWsBBbim0m47-YmA,1767
318
+ zrb/runner/web_route/task_input_api_route.py,sha256=1uTI9efQNXJ_S9jGWSudQFIyGSkINSLLYzSWCMcOtkc,1795
319
319
  zrb/runner/web_route/task_session_api_route.py,sha256=U9fPOh_nmAzsmRnS5xe713KFUU15n0IkbbzU_Eg8YGI,6181
320
320
  zrb/runner/web_schema/session.py,sha256=NwbuS2Sv-CXO52nU-EZv8OMlD4vgCQWNeLC_dT0FK7I,92
321
321
  zrb/runner/web_schema/token.py,sha256=Y7XCPS4WzrxslTDtHeLcPTTUpmWhPOkRcl4b99zrC7c,185
@@ -334,19 +334,19 @@ zrb/session_state_logger/any_session_state_logger.py,sha256=T_-aJv63HwoTeiDOmXKH
334
334
  zrb/session_state_logger/file_session_state_logger.py,sha256=NMyj_g2ImQc6ZRM9f35EpA-CM1UO-ZgoDnPkN1DSi9U,3915
335
335
  zrb/session_state_logger/session_state_logger_factory.py,sha256=_oT1UIqm25nMwKhsUvXehnCdDvNqxCyuN8GLdv2D_U4,188
336
336
  zrb/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
337
- zrb/task/any_task.py,sha256=E4HA4Fumwv5dIsEnl2q0eE6t6LHnF6uyk0O5yQ2Mz6g,5826
337
+ zrb/task/any_task.py,sha256=SAPX1uuq4c0mxgGEQ6lyioR4eRmFovhH-Wr2xCtrHnA,6078
338
338
  zrb/task/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
339
- zrb/task/base/context.py,sha256=73k3fKwup0AJwTTLpay0f_-axJextaxgbTem4w4Bmas,3670
339
+ zrb/task/base/context.py,sha256=uj0fHPtbjiRWE4gRB8DKNJhLmEN1ZWH6FCjItlE45q4,3918
340
340
  zrb/task/base/execution.py,sha256=scDLfNYBe8Bc8Ct1LCIKmFtjpPxm7FjqZ2bJXIQAzv8,11042
341
- zrb/task/base/lifecycle.py,sha256=4tGeN9yE5sQt1lHYq-1_vOFSGk1z8JneNCbVUWFVm-Q,7507
341
+ zrb/task/base/lifecycle.py,sha256=c2v977pUm7S4EqrTMcUJKhnYOWugACVwU3qORDKiLXQ,7639
342
342
  zrb/task/base/monitoring.py,sha256=UAOEcPiYNtZR4FFxzWCosuOEFE_P3c4GT5vAhQmohqI,5663
343
343
  zrb/task/base/operators.py,sha256=uAMFqpZJsPnCrojgOl1FUDXTS15mtOa_IqiAXltyYRU,1576
344
- zrb/task/base_task.py,sha256=Y5JNzU6Y1E8RKGdj16HTlhny0v8N2I98GCA4D8h6Kmw,10962
344
+ zrb/task/base_task.py,sha256=qPtEIP3TEX7YbDadQtsUXLJ4_7Khzk_JxXDh_mRrluU,12602
345
345
  zrb/task/base_trigger.py,sha256=WSGcmBcGAZw8EzUXfmCjqJQkz8GEmi1RzogpF6A1V4s,6902
346
346
  zrb/task/cmd_task.py,sha256=myM8WZm6NrUD-Wv0Vb5sTOrutrAVZLt5LVsSBKwX6SM,10860
347
347
  zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
348
348
  zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
349
- zrb/task/llm/agent.py,sha256=miQMF7NMFsPEuAp5gTh1kAAEGxgYfLb4uoDU7FL83Go,8645
349
+ zrb/task/llm/agent.py,sha256=jfC90N8oER2z-fTmPEuWH3ASPgw5t8cZm8rk98HBBlw,8876
350
350
  zrb/task/llm/config.py,sha256=n1SPmwab09K2i1sL_OCwrEOWHI0Owx_hvWelg3Dreus,3781
351
351
  zrb/task/llm/conversation_history.py,sha256=B_PDWYL_q66s0xwWBzMSomqPN6u3gkXlIeXBD5A0Apg,4416
352
352
  zrb/task/llm/conversation_history_model.py,sha256=DJ0KDBB0BriQuE5ugC_q0aSHhjNIBcfjUk1f0S_3I9U,9245
@@ -354,12 +354,12 @@ zrb/task/llm/default_workflow/coding.md,sha256=2uythvPsnBpYfIhiIH1cCinQXX0i0yUqs
354
354
  zrb/task/llm/default_workflow/copywriting.md,sha256=xSO7GeDolwGxiuz6kXsK2GKGpwp8UgtG0yRqTmill_s,1999
355
355
  zrb/task/llm/default_workflow/researching.md,sha256=KD-aYHFHir6Ti-4FsBBtGwiI0seSVgleYbKJZi_POXA,2139
356
356
  zrb/task/llm/error.py,sha256=QR-nIohS6pBpC_16cWR-fw7Mevo1sNYAiXMBsh_CJDE,4157
357
- zrb/task/llm/history_summarization.py,sha256=xSHVoVOJ_4Da2fycWyKm8dBCIbjfAFU_rDC7u6XhKow,8093
357
+ zrb/task/llm/history_summarization.py,sha256=_eWFP4cAn3tTfyt7YPjnPwm_PgMhMChPg9g2q2Gsf0c,8172
358
358
  zrb/task/llm/print_node.py,sha256=Sd8ovTO6KKI9hllZf5TxRXog7QGCne1A1E83jnrH_kg,6526
359
359
  zrb/task/llm/prompt.py,sha256=FGXWYHecWtrNNkPnjg-uhnkqp7fYt8V91-AjFM_5fpA,11550
360
- zrb/task/llm/tool_wrapper.py,sha256=4gdBrtjdwBzhBD1y41vzujPGFEY3q5OmHb_E-y62dHI,9393
360
+ zrb/task/llm/tool_wrapper.py,sha256=2cbaSNvOMlIb3l5RXEFbH7cnox01TAr4b4Vtg0wzuYs,9609
361
361
  zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
362
- zrb/task/llm_task.py,sha256=aweruBmC09RCKNGmiwLo7WVSttCUH2h2RkSfgUGvRFo,14450
362
+ zrb/task/llm_task.py,sha256=0c9F_AjeMLxQX8VlAJHfZAkC52PhRaWbcKoqVOggcMg,14451
363
363
  zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
364
364
  zrb/task/rsync_task.py,sha256=WfqNSaicJgYWpunNU34eYxXDqHDHOftuDHyWJKjqwg0,6365
365
365
  zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
@@ -405,9 +405,10 @@ zrb/util/string/format.py,sha256=MwWGAwSdtOgR_2uz-JCXlg_q-uRYUUI-G8CGkfdgqik,119
405
405
  zrb/util/string/name.py,sha256=SXEfxJ1-tDOzHqmSV8kvepRVyMqs2XdV_vyoh_9XUu0,1584
406
406
  zrb/util/todo.py,sha256=r9_KYF2-hLKMNjsp6AFK9zivykMrywd-kJ4bCwfdafI,19323
407
407
  zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
408
+ zrb/util/truncate.py,sha256=eSzmjBpc1Qod3lM3M73snNbDOcARHukW_tq36dWdPvc,921
408
409
  zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
409
410
  zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
410
- zrb-1.15.4.dist-info/METADATA,sha256=LdpREgmZtxq9meZWfPZ6ivABI_531S6agnMVFP7jaME,9744
411
- zrb-1.15.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
412
- zrb-1.15.4.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
413
- zrb-1.15.4.dist-info/RECORD,,
411
+ zrb-1.15.6.dist-info/METADATA,sha256=octlKaqi1aMWs5qYYb5d02at13BGvqvMdOEjYrKbp4E,9774
412
+ zrb-1.15.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
413
+ zrb-1.15.6.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
414
+ zrb-1.15.6.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.0
2
+ Generator: poetry-core 2.1.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any