zrb 1.16.1__py3-none-any.whl → 1.16.3__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/__init__.py +1 -0
- zrb/builtin/llm/chat_session.py +22 -27
- zrb/builtin/llm/chat_trigger.py +73 -0
- zrb/builtin/llm/llm_ask.py +96 -72
- zrb/builtin/llm/tool/code.py +7 -3
- zrb/builtin/llm/tool/file.py +2 -2
- zrb/builtin/llm/tool/sub_agent.py +2 -3
- zrb/task/base/execution.py +15 -8
- zrb/task/base/monitoring.py +12 -7
- zrb/task/llm/agent.py +27 -5
- zrb/task/llm/config.py +2 -2
- zrb/task/llm/tool_wrapper.py +20 -11
- zrb/task/llm_task.py +7 -4
- {zrb-1.16.1.dist-info → zrb-1.16.3.dist-info}/METADATA +3 -3
- {zrb-1.16.1.dist-info → zrb-1.16.3.dist-info}/RECORD +17 -16
- {zrb-1.16.1.dist-info → zrb-1.16.3.dist-info}/WHEEL +0 -0
- {zrb-1.16.1.dist-info → zrb-1.16.3.dist-info}/entry_points.txt +0 -0
zrb/builtin/__init__.py
CHANGED
@@ -9,6 +9,7 @@ from zrb.builtin.git import (
|
|
9
9
|
from zrb.builtin.git_subtree import git_add_subtree, git_pull_subtree, git_push_subtree
|
10
10
|
from zrb.builtin.http import generate_curl, http_request
|
11
11
|
from zrb.builtin.jwt import decode_jwt, encode_jwt, validate_jwt
|
12
|
+
from zrb.builtin.llm.chat_trigger import llm_chat_trigger
|
12
13
|
from zrb.builtin.llm.llm_ask import llm_ask
|
13
14
|
from zrb.builtin.md5 import hash_md5, sum_md5, validate_md5
|
14
15
|
from zrb.builtin.project.add.fastapp.fastapp_task import add_fastapp_to_project
|
zrb/builtin/llm/chat_session.py
CHANGED
@@ -7,13 +7,20 @@ conversation flow via XCom.
|
|
7
7
|
|
8
8
|
import asyncio
|
9
9
|
import sys
|
10
|
+
from typing import TYPE_CHECKING, Any
|
10
11
|
|
12
|
+
from zrb.builtin.llm.chat_trigger import llm_chat_trigger
|
11
13
|
from zrb.config.llm_config import llm_config
|
12
14
|
from zrb.context.any_context import AnyContext
|
13
15
|
from zrb.util.cli.markdown import render_markdown
|
14
16
|
from zrb.util.cli.style import stylize_blue, stylize_bold_yellow, stylize_faint
|
15
17
|
from zrb.util.string.conversion import to_boolean
|
16
18
|
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from asyncio import StreamReader
|
21
|
+
|
22
|
+
from prompt_toolkit import PromptSession
|
23
|
+
|
17
24
|
|
18
25
|
async def read_user_prompt(ctx: AnyContext) -> str:
|
19
26
|
"""
|
@@ -22,7 +29,7 @@ async def read_user_prompt(ctx: AnyContext) -> str:
|
|
22
29
|
"""
|
23
30
|
_show_info(ctx)
|
24
31
|
is_tty: bool = ctx.is_tty
|
25
|
-
reader = await _setup_input_reader(is_tty)
|
32
|
+
reader: PromptSession[Any] | StreamReader = await _setup_input_reader(is_tty)
|
26
33
|
multiline_mode = False
|
27
34
|
is_first_time = True
|
28
35
|
current_modes: str = ctx.input.modes
|
@@ -32,17 +39,19 @@ async def read_user_prompt(ctx: AnyContext) -> str:
|
|
32
39
|
while True:
|
33
40
|
await asyncio.sleep(0.01)
|
34
41
|
previous_session_name: str | None = (
|
35
|
-
ctx.input.previous_session if is_first_time else
|
42
|
+
ctx.input.previous_session if is_first_time else ""
|
36
43
|
)
|
37
44
|
start_new: bool = ctx.input.start_new if is_first_time else False
|
38
|
-
if is_first_time:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
if is_first_time and ctx.input.message.strip() != "":
|
46
|
+
user_input = ctx.input.message
|
47
|
+
else:
|
48
|
+
# Get user input based on mode
|
49
|
+
if not multiline_mode:
|
50
|
+
ctx.print("💬 >>", plain=True)
|
51
|
+
user_input = await llm_chat_trigger.wait(reader, ctx)
|
52
|
+
if not multiline_mode:
|
53
|
+
ctx.print("", plain=True)
|
54
|
+
is_first_time = False
|
46
55
|
# Handle user input
|
47
56
|
if user_input.strip().lower() in ("/bye", "/quit", "/q", "/exit"):
|
48
57
|
user_prompt = "\n".join(user_inputs)
|
@@ -151,7 +160,9 @@ def _show_subcommand(command: str, description: str) -> str:
|
|
151
160
|
return f" {styled_command} {styled_description}"
|
152
161
|
|
153
162
|
|
154
|
-
async def _setup_input_reader(
|
163
|
+
async def _setup_input_reader(
|
164
|
+
is_interactive: bool,
|
165
|
+
) -> "PromptSession[Any] | StreamReader":
|
155
166
|
"""Sets up and returns the appropriate asynchronous input reader."""
|
156
167
|
if is_interactive:
|
157
168
|
from prompt_toolkit import PromptSession
|
@@ -165,22 +176,6 @@ async def _setup_input_reader(is_interactive: bool):
|
|
165
176
|
return reader
|
166
177
|
|
167
178
|
|
168
|
-
async def _read_next_line(reader, ctx: AnyContext) -> str:
|
169
|
-
"""Reads one line of input using the provided reader."""
|
170
|
-
from prompt_toolkit import PromptSession
|
171
|
-
|
172
|
-
if isinstance(reader, PromptSession):
|
173
|
-
return await reader.prompt_async()
|
174
|
-
|
175
|
-
line_bytes = await reader.readline()
|
176
|
-
if not line_bytes:
|
177
|
-
return "/bye" # Signal to exit
|
178
|
-
|
179
|
-
user_input = line_bytes.decode().strip()
|
180
|
-
ctx.print(user_input, plain=True)
|
181
|
-
return user_input
|
182
|
-
|
183
|
-
|
184
179
|
async def _trigger_ask_and_wait_for_result(
|
185
180
|
ctx: AnyContext,
|
186
181
|
user_prompt: str,
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import asyncio
|
2
|
+
from asyncio import StreamReader
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable, Coroutine
|
4
|
+
|
5
|
+
from zrb.context.any_context import AnyContext
|
6
|
+
from zrb.util.run import run_async
|
7
|
+
|
8
|
+
if TYPE_CHECKING:
|
9
|
+
from prompt_toolkit import PromptSession
|
10
|
+
|
11
|
+
|
12
|
+
ChatTrigger = Callable[[AnyContext], Coroutine[Any, Any, str] | str]
|
13
|
+
|
14
|
+
|
15
|
+
class LLMChatTrigger:
|
16
|
+
|
17
|
+
def __init__(self):
|
18
|
+
self._triggers: list[ChatTrigger] = []
|
19
|
+
|
20
|
+
def add_trigger(self, *trigger: ChatTrigger):
|
21
|
+
self.append_trigger(*trigger)
|
22
|
+
|
23
|
+
def append_trigger(self, *trigger: ChatTrigger):
|
24
|
+
for single_trigger in trigger:
|
25
|
+
self._triggers.append(single_trigger)
|
26
|
+
|
27
|
+
async def wait(
|
28
|
+
self, reader: "PromptSession[Any] | StreamReader", ctx: AnyContext
|
29
|
+
) -> str:
|
30
|
+
trigger_tasks = [
|
31
|
+
asyncio.create_task(run_async(self._read_next_line(reader, ctx)))
|
32
|
+
] + [asyncio.create_task(run_async(trigger(ctx))) for trigger in self._triggers]
|
33
|
+
final_result: str = ""
|
34
|
+
try:
|
35
|
+
done, pending = await asyncio.wait(
|
36
|
+
trigger_tasks, return_when=asyncio.FIRST_COMPLETED
|
37
|
+
)
|
38
|
+
for task in done:
|
39
|
+
final_result = await task
|
40
|
+
if pending:
|
41
|
+
for task in pending:
|
42
|
+
task.cancel()
|
43
|
+
for task in done:
|
44
|
+
break
|
45
|
+
except asyncio.CancelledError:
|
46
|
+
ctx.print("Task cancelled.", plain=True)
|
47
|
+
final_result = "/bye"
|
48
|
+
except KeyboardInterrupt:
|
49
|
+
ctx.print("KeyboardInterrupt detected. Exiting...", plain=True)
|
50
|
+
final_result = "/bye"
|
51
|
+
return final_result
|
52
|
+
|
53
|
+
async def _read_next_line(
|
54
|
+
self, reader: "PromptSession[Any] | StreamReader", ctx: AnyContext
|
55
|
+
) -> str:
|
56
|
+
"""Reads one line of input using the provided reader."""
|
57
|
+
from prompt_toolkit import PromptSession
|
58
|
+
|
59
|
+
try:
|
60
|
+
if isinstance(reader, PromptSession):
|
61
|
+
return await reader.prompt_async()
|
62
|
+
line_bytes = await reader.readline()
|
63
|
+
if not line_bytes:
|
64
|
+
return "/bye" # Signal to exit
|
65
|
+
user_input = line_bytes.decode().strip()
|
66
|
+
ctx.print(user_input, plain=True)
|
67
|
+
return user_input
|
68
|
+
except KeyboardInterrupt:
|
69
|
+
ctx.print("KeyboardInterrupt detected. Exiting...", plain=True)
|
70
|
+
return "/bye"
|
71
|
+
|
72
|
+
|
73
|
+
llm_chat_trigger = LLMChatTrigger()
|
zrb/builtin/llm/llm_ask.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import os
|
1
2
|
from collections.abc import Callable
|
2
3
|
from typing import TYPE_CHECKING
|
3
4
|
|
@@ -28,6 +29,7 @@ from zrb.callback.callback import Callback
|
|
28
29
|
from zrb.config.config import CFG
|
29
30
|
from zrb.config.llm_config import llm_config
|
30
31
|
from zrb.context.any_context import AnyContext
|
32
|
+
from zrb.input.any_input import AnyInput
|
31
33
|
from zrb.input.bool_input import BoolInput
|
32
34
|
from zrb.input.str_input import StrInput
|
33
35
|
from zrb.input.text_input import TextInput
|
@@ -37,10 +39,23 @@ from zrb.util.string.conversion import to_boolean
|
|
37
39
|
|
38
40
|
if TYPE_CHECKING:
|
39
41
|
from pydantic_ai import Tool
|
42
|
+
from pydantic_ai.toolsets import AbstractToolset
|
40
43
|
|
41
44
|
ToolOrCallable = Tool | Callable
|
42
45
|
|
43
46
|
|
47
|
+
def _get_toolset(ctx: AnyContext) -> list["AbstractToolset[None] | str"]:
|
48
|
+
cwd = os.getcwd()
|
49
|
+
toolsets = []
|
50
|
+
for config_path in [
|
51
|
+
os.path.join(cwd, "mcp_config.json"),
|
52
|
+
os.path.join(cwd, "mcp-config.json"),
|
53
|
+
]:
|
54
|
+
if os.path.isfile(config_path):
|
55
|
+
toolsets.append(config_path)
|
56
|
+
return toolsets
|
57
|
+
|
58
|
+
|
44
59
|
def _get_tool(ctx: AnyContext) -> list["ToolOrCallable"]:
|
45
60
|
tools = []
|
46
61
|
if CFG.LLM_ALLOW_ANALYZE_REPO:
|
@@ -91,81 +106,89 @@ def _render_yolo_mode_input(ctx: AnyContext) -> list[str] | bool | None:
|
|
91
106
|
return elements
|
92
107
|
|
93
108
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
109
|
+
def _get_inputs(require_message: bool = True) -> list[AnyInput | None]:
|
110
|
+
return [
|
111
|
+
StrInput(
|
112
|
+
"model",
|
113
|
+
description="LLM Model",
|
114
|
+
prompt="LLM Model",
|
115
|
+
default="",
|
116
|
+
allow_positional_parsing=False,
|
117
|
+
always_prompt=False,
|
118
|
+
allow_empty=True,
|
119
|
+
),
|
120
|
+
StrInput(
|
121
|
+
"base-url",
|
122
|
+
description="LLM API Base URL",
|
123
|
+
prompt="LLM API Base URL",
|
124
|
+
default="",
|
125
|
+
allow_positional_parsing=False,
|
126
|
+
always_prompt=False,
|
127
|
+
allow_empty=True,
|
128
|
+
),
|
129
|
+
StrInput(
|
130
|
+
"api-key",
|
131
|
+
description="LLM API Key",
|
132
|
+
prompt="LLM API Key",
|
133
|
+
default="",
|
134
|
+
allow_positional_parsing=False,
|
135
|
+
always_prompt=False,
|
136
|
+
allow_empty=True,
|
137
|
+
),
|
138
|
+
TextInput(
|
139
|
+
"system-prompt",
|
140
|
+
description="System prompt",
|
141
|
+
prompt="System prompt",
|
142
|
+
default="",
|
143
|
+
allow_positional_parsing=False,
|
144
|
+
always_prompt=False,
|
145
|
+
),
|
146
|
+
TextInput(
|
147
|
+
"modes",
|
148
|
+
description="Modes",
|
149
|
+
prompt="Modes",
|
150
|
+
default=lambda ctx: ",".join(llm_config.default_modes),
|
151
|
+
allow_positional_parsing=False,
|
152
|
+
always_prompt=False,
|
153
|
+
),
|
154
|
+
BoolInput(
|
155
|
+
"start-new",
|
156
|
+
description="Start new session (LLM Agent will forget past conversation)",
|
157
|
+
prompt="Start new session (LLM Agent will forget past conversation)",
|
158
|
+
default=False,
|
159
|
+
allow_positional_parsing=False,
|
160
|
+
always_prompt=False,
|
161
|
+
),
|
162
|
+
StrInput(
|
163
|
+
"yolo",
|
164
|
+
description="YOLO mode (LLM Agent will start in YOLO Mode)",
|
165
|
+
prompt="YOLO mode (LLM Agent will start in YOLO Mode)",
|
166
|
+
default=_get_default_yolo_mode,
|
167
|
+
allow_positional_parsing=False,
|
168
|
+
always_prompt=False,
|
169
|
+
),
|
170
|
+
TextInput(
|
171
|
+
"message",
|
172
|
+
description="User message",
|
173
|
+
prompt="Your message",
|
174
|
+
always_prompt=require_message,
|
175
|
+
allow_empty=not require_message,
|
176
|
+
),
|
177
|
+
PreviousSessionInput(
|
178
|
+
"previous-session",
|
179
|
+
description="Previous conversation session",
|
180
|
+
prompt="Previous conversation session (can be empty)",
|
181
|
+
allow_positional_parsing=False,
|
182
|
+
allow_empty=True,
|
183
|
+
always_prompt=False,
|
184
|
+
),
|
185
|
+
]
|
186
|
+
|
164
187
|
|
165
188
|
llm_ask: LLMTask = llm_group.add_task(
|
166
189
|
LLMTask(
|
167
190
|
name="llm-ask",
|
168
|
-
input=
|
191
|
+
input=_get_inputs(True),
|
169
192
|
description="❓ Ask LLM",
|
170
193
|
model=lambda ctx: None if ctx.input.model.strip() == "" else ctx.input.model,
|
171
194
|
model_base_url=lambda ctx: (
|
@@ -184,6 +207,7 @@ llm_ask: LLMTask = llm_group.add_task(
|
|
184
207
|
),
|
185
208
|
message="{ctx.input.message}",
|
186
209
|
tools=_get_tool,
|
210
|
+
toolsets=_get_toolset,
|
187
211
|
yolo_mode=_render_yolo_mode_input,
|
188
212
|
retries=0,
|
189
213
|
),
|
@@ -193,7 +217,7 @@ llm_ask: LLMTask = llm_group.add_task(
|
|
193
217
|
llm_group.add_task(
|
194
218
|
BaseTrigger(
|
195
219
|
name="llm-chat",
|
196
|
-
input=
|
220
|
+
input=_get_inputs(False),
|
197
221
|
description="💬 Chat with LLM",
|
198
222
|
queue_name="ask_trigger",
|
199
223
|
action=read_user_prompt,
|
zrb/builtin/llm/tool/code.py
CHANGED
@@ -120,6 +120,10 @@ async def analyze_repo(
|
|
120
120
|
goal=goal,
|
121
121
|
token_limit=extraction_token_threshold,
|
122
122
|
)
|
123
|
+
if len(extracted_infos) == 0:
|
124
|
+
raise RuntimeError(
|
125
|
+
"No info can be extracted, adjust extensions or exclude_patterns."
|
126
|
+
)
|
123
127
|
if len(extracted_infos) == 1:
|
124
128
|
return extracted_infos[0]
|
125
129
|
summarized_infos = extracted_infos
|
@@ -146,11 +150,11 @@ def _get_file_metadatas(
|
|
146
150
|
if not any(file.endswith(f".{ext}") for ext in extensions):
|
147
151
|
continue
|
148
152
|
file_path = os.path.join(root, file)
|
149
|
-
if is_excluded(file_path, exclude_patterns):
|
150
|
-
continue
|
151
153
|
try:
|
154
|
+
rel_path = os.path.relpath(file_path, dir_path)
|
155
|
+
if is_excluded(rel_path, exclude_patterns):
|
156
|
+
continue
|
152
157
|
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
153
|
-
rel_path = os.path.relpath(file_path, dir_path)
|
154
158
|
metadata_list.append({"path": rel_path, "content": f.read()})
|
155
159
|
except Exception as e:
|
156
160
|
print(f"Error reading file {file_path}: {e}")
|
zrb/builtin/llm/tool/file.py
CHANGED
@@ -304,7 +304,7 @@ def write_to_file(
|
|
304
304
|
Writes content to a file, completely overwriting it if it exists or
|
305
305
|
creating it if it doesn't.
|
306
306
|
|
307
|
-
Use this tool to create new files or to
|
307
|
+
Use this tool to create new files or to overwrite the entire content of
|
308
308
|
existing files. This is a destructive operation, so be certain of your
|
309
309
|
actions. Always read the file first to understand its contents before
|
310
310
|
overwriting it, unless you are creating a new file.
|
@@ -497,7 +497,7 @@ async def analyze_file(
|
|
497
497
|
ctx: AnyContext, path: str, query: str, token_limit: int | None = None
|
498
498
|
) -> dict[str, Any]:
|
499
499
|
"""
|
500
|
-
Performs a
|
500
|
+
Performs a high level, goal-oriented analysis of a single file using a sub-agent.
|
501
501
|
|
502
502
|
This tool is ideal for complex questions about a single file that go beyond
|
503
503
|
simple reading or searching. It uses a specialized sub-agent to analyze the
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import json
|
2
1
|
from collections.abc import Callable
|
3
2
|
from textwrap import dedent
|
4
3
|
from typing import TYPE_CHECKING, Any, Coroutine
|
@@ -9,7 +8,7 @@ from zrb.task.llm.config import get_model, get_model_settings
|
|
9
8
|
from zrb.task.llm.prompt import get_system_and_user_prompt
|
10
9
|
|
11
10
|
if TYPE_CHECKING:
|
12
|
-
from pydantic_ai import
|
11
|
+
from pydantic_ai import Tool
|
13
12
|
from pydantic_ai.models import Model
|
14
13
|
from pydantic_ai.settings import ModelSettings
|
15
14
|
from pydantic_ai.toolsets import AbstractToolset
|
@@ -24,7 +23,7 @@ def create_sub_agent_tool(
|
|
24
23
|
model: "str | Model | None" = None,
|
25
24
|
model_settings: "ModelSettings | None" = None,
|
26
25
|
tools: "list[ToolOrCallable]" = [],
|
27
|
-
toolsets: list["AbstractToolset[
|
26
|
+
toolsets: list["AbstractToolset[None]"] = [],
|
28
27
|
yolo_mode: bool | list[str] | None = None,
|
29
28
|
log_indent_level: int = 2,
|
30
29
|
) -> Callable[[AnyContext, str], Coroutine[Any, Any, dict[str, Any]]]:
|
zrb/task/base/execution.py
CHANGED
@@ -53,7 +53,9 @@ def check_execute_condition(task: "BaseTask", session: AnySession) -> bool:
|
|
53
53
|
Evaluates the task's execute_condition attribute.
|
54
54
|
"""
|
55
55
|
ctx = task.get_ctx(session)
|
56
|
-
execute_condition_attr =
|
56
|
+
execute_condition_attr = (
|
57
|
+
task._execute_condition if task._execute_condition is not None else True
|
58
|
+
)
|
57
59
|
return get_bool_attr(ctx, execute_condition_attr, True, auto_render=True)
|
58
60
|
|
59
61
|
|
@@ -63,8 +65,12 @@ async def execute_action_until_ready(task: "BaseTask", session: AnySession):
|
|
63
65
|
"""
|
64
66
|
ctx = task.get_ctx(session)
|
65
67
|
readiness_checks = task.readiness_checks
|
66
|
-
readiness_check_delay =
|
67
|
-
|
68
|
+
readiness_check_delay = (
|
69
|
+
task._readiness_check_delay if task._readiness_check_delay is not None else 0.5
|
70
|
+
)
|
71
|
+
monitor_readiness = (
|
72
|
+
task._monitor_readiness if task._monitor_readiness is not None else False
|
73
|
+
)
|
68
74
|
|
69
75
|
if not readiness_checks: # Simplified check for empty list
|
70
76
|
ctx.log_info("No readiness checks")
|
@@ -140,8 +146,8 @@ async def execute_action_with_retry(task: "BaseTask", session: AnySession) -> An
|
|
140
146
|
handling success (triggering successors) and failure (triggering fallbacks).
|
141
147
|
"""
|
142
148
|
ctx = task.get_ctx(session)
|
143
|
-
retries =
|
144
|
-
retry_period =
|
149
|
+
retries = task._retries if task._retries is not None else 2
|
150
|
+
retry_period = task._retry_period if task._retry_period is not None else 0
|
145
151
|
max_attempt = retries + 1
|
146
152
|
ctx.set_max_attempt(max_attempt)
|
147
153
|
|
@@ -163,8 +169,9 @@ async def execute_action_with_retry(task: "BaseTask", session: AnySession) -> An
|
|
163
169
|
session.get_task_status(task).mark_as_completed()
|
164
170
|
|
165
171
|
# Store result in XCom
|
166
|
-
task_xcom: Xcom = ctx.xcom.get(task.name)
|
167
|
-
task_xcom
|
172
|
+
task_xcom: Xcom | None = ctx.xcom.get(task.name)
|
173
|
+
if task_xcom is not None:
|
174
|
+
task_xcom.push(result)
|
168
175
|
|
169
176
|
# Skip fallbacks and execute successors on success
|
170
177
|
skip_fallbacks(task, session)
|
@@ -201,7 +208,7 @@ async def run_default_action(task: "BaseTask", ctx: AnyContext) -> Any:
|
|
201
208
|
This is the default implementation called by BaseTask._exec_action.
|
202
209
|
Subclasses like LLMTask override _exec_action with their own logic.
|
203
210
|
"""
|
204
|
-
action =
|
211
|
+
action = task._action
|
205
212
|
if action is None:
|
206
213
|
ctx.log_debug("No action defined for this task.")
|
207
214
|
return None
|
zrb/task/base/monitoring.py
CHANGED
@@ -17,9 +17,13 @@ async def monitor_task_readiness(
|
|
17
17
|
"""
|
18
18
|
ctx = task.get_ctx(session)
|
19
19
|
readiness_checks = task.readiness_checks
|
20
|
-
readiness_check_period =
|
21
|
-
|
22
|
-
|
20
|
+
readiness_check_period = (
|
21
|
+
task._readiness_check_period if task._readiness_check_period else 5.0
|
22
|
+
)
|
23
|
+
readiness_failure_threshold = (
|
24
|
+
task._readiness_failure_threshold if task._readiness_failure_threshold else 1
|
25
|
+
)
|
26
|
+
readiness_timeout = task._readiness_timeout if task._readiness_timeout else 60
|
23
27
|
|
24
28
|
if not readiness_checks:
|
25
29
|
ctx.log_debug("No readiness checks defined, monitoring is not applicable.")
|
@@ -41,8 +45,9 @@ async def monitor_task_readiness(
|
|
41
45
|
session.get_task_status(check).reset_history()
|
42
46
|
session.get_task_status(check).reset()
|
43
47
|
# Clear previous XCom data for the check task if needed
|
44
|
-
check_xcom: Xcom = ctx.xcom.get(check.name)
|
45
|
-
check_xcom
|
48
|
+
check_xcom: Xcom | None = ctx.xcom.get(check.name)
|
49
|
+
if check_xcom is not None:
|
50
|
+
check_xcom.clear()
|
46
51
|
|
47
52
|
readiness_check_coros = [
|
48
53
|
run_async(check.exec_chain(session)) for check in readiness_checks
|
@@ -77,7 +82,7 @@ async def monitor_task_readiness(
|
|
77
82
|
)
|
78
83
|
# Ensure check tasks are marked as failed on timeout
|
79
84
|
for check in readiness_checks:
|
80
|
-
if not session.get_task_status(check).
|
85
|
+
if not session.get_task_status(check).is_ready:
|
81
86
|
session.get_task_status(check).mark_as_failed()
|
82
87
|
|
83
88
|
except (asyncio.CancelledError, KeyboardInterrupt):
|
@@ -92,7 +97,7 @@ async def monitor_task_readiness(
|
|
92
97
|
)
|
93
98
|
# Mark checks as failed
|
94
99
|
for check in readiness_checks:
|
95
|
-
if not session.get_task_status(check).
|
100
|
+
if not session.get_task_status(check).is_ready:
|
96
101
|
session.get_task_status(check).mark_as_failed()
|
97
102
|
|
98
103
|
# If failure threshold is reached
|
zrb/task/llm/agent.py
CHANGED
@@ -125,8 +125,8 @@ def get_agent(
|
|
125
125
|
"list[ToolOrCallable] | Callable[[AnySharedContext], list[ToolOrCallable]]"
|
126
126
|
) = [],
|
127
127
|
additional_tools: "list[ToolOrCallable]" = [],
|
128
|
-
toolsets_attr: "list[AbstractToolset[
|
129
|
-
additional_toolsets: "list[AbstractToolset[
|
128
|
+
toolsets_attr: "list[AbstractToolset[None] | str] | Callable[[AnySharedContext], list[AbstractToolset[None] | str]]" = [], # noqa
|
129
|
+
additional_toolsets: "list[AbstractToolset[None] | str]" = [],
|
130
130
|
retries: int = 3,
|
131
131
|
yolo_mode: bool | list[str] | None = None,
|
132
132
|
) -> "Agent":
|
@@ -149,8 +149,11 @@ def get_agent(
|
|
149
149
|
tools = list(tools_attr(ctx) if callable(tools_attr) else tools_attr)
|
150
150
|
tools.extend(additional_tools)
|
151
151
|
# Get Toolsets for agent
|
152
|
-
|
153
|
-
|
152
|
+
toolset_or_str_list = list(
|
153
|
+
toolsets_attr(ctx) if callable(toolsets_attr) else toolsets_attr
|
154
|
+
)
|
155
|
+
toolset_or_str_list.extend(additional_toolsets)
|
156
|
+
toolsets = _render_toolset_or_str_list(ctx, toolset_or_str_list)
|
154
157
|
# If no agent provided, create one using the configuration
|
155
158
|
return create_agent_instance(
|
156
159
|
ctx=ctx,
|
@@ -158,13 +161,32 @@ def get_agent(
|
|
158
161
|
output_type=output_type,
|
159
162
|
system_prompt=system_prompt,
|
160
163
|
tools=tools,
|
161
|
-
toolsets=
|
164
|
+
toolsets=toolsets,
|
162
165
|
model_settings=model_settings,
|
163
166
|
retries=retries,
|
164
167
|
yolo_mode=yolo_mode,
|
165
168
|
)
|
166
169
|
|
167
170
|
|
171
|
+
def _render_toolset_or_str_list(
|
172
|
+
ctx: AnyContext, toolset_or_str_list: list["AbstractToolset[None] | str"]
|
173
|
+
) -> list["AbstractToolset[None]"]:
|
174
|
+
from pydantic_ai.mcp import load_mcp_servers
|
175
|
+
|
176
|
+
toolsets = []
|
177
|
+
for toolset_or_str in toolset_or_str_list:
|
178
|
+
if isinstance(toolset_or_str, str):
|
179
|
+
try:
|
180
|
+
servers = load_mcp_servers(toolset_or_str)
|
181
|
+
for server in servers:
|
182
|
+
toolsets.append(server)
|
183
|
+
except Exception as e:
|
184
|
+
ctx.log_error(f"Invalid MCP Config {toolset_or_str}: {e}")
|
185
|
+
continue
|
186
|
+
toolsets.append(toolset_or_str)
|
187
|
+
return toolsets
|
188
|
+
|
189
|
+
|
168
190
|
async def run_agent_iteration(
|
169
191
|
ctx: AnyContext,
|
170
192
|
agent: "Agent[None, Any]",
|
zrb/task/llm/config.py
CHANGED
@@ -4,7 +4,7 @@ if TYPE_CHECKING:
|
|
4
4
|
from pydantic_ai.models import Model
|
5
5
|
from pydantic_ai.settings import ModelSettings
|
6
6
|
|
7
|
-
from zrb.attr.type import BoolAttr, StrAttr, fstring
|
7
|
+
from zrb.attr.type import BoolAttr, StrAttr, StrListAttr, fstring
|
8
8
|
from zrb.config.llm_config import LLMConfig, llm_config
|
9
9
|
from zrb.context.any_context import AnyContext
|
10
10
|
from zrb.context.any_shared_context import AnySharedContext
|
@@ -13,7 +13,7 @@ from zrb.util.attr import get_attr, get_bool_attr, get_str_list_attr
|
|
13
13
|
|
14
14
|
def get_yolo_mode(
|
15
15
|
ctx: AnyContext,
|
16
|
-
yolo_mode_attr: BoolAttr |
|
16
|
+
yolo_mode_attr: BoolAttr | StrListAttr | None = None,
|
17
17
|
render_yolo_mode: bool = True,
|
18
18
|
) -> bool | list[str]:
|
19
19
|
if yolo_mode_attr is None:
|
zrb/task/llm/tool_wrapper.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import functools
|
2
2
|
import inspect
|
3
|
+
import json
|
3
4
|
import traceback
|
4
5
|
import typing
|
5
6
|
from collections.abc import Callable
|
@@ -172,18 +173,17 @@ def _get_edited_kwargs(
|
|
172
173
|
return kwargs, False
|
173
174
|
while len(user_edit_responses) < 3:
|
174
175
|
user_edit_responses.append("")
|
175
|
-
key,
|
176
|
+
key, val_str = user_edit_responses[1:]
|
176
177
|
if key not in kwargs:
|
177
178
|
return kwargs, True
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
)
|
186
|
-
kwargs[key] = val
|
179
|
+
is_str_param = isinstance(kwargs[key], str)
|
180
|
+
if val_str == "":
|
181
|
+
val_str = edit_text(
|
182
|
+
prompt_message=f"// {key}",
|
183
|
+
value=_get_val_str(kwargs[key]),
|
184
|
+
editor=CFG.DEFAULT_EDITOR,
|
185
|
+
)
|
186
|
+
kwargs[key] = val_str if is_str_param else json.loads(val_str)
|
187
187
|
return kwargs, True
|
188
188
|
|
189
189
|
|
@@ -233,7 +233,7 @@ def _get_detail_func_param(args: list[Any] | tuple[Any], kwargs: dict[str, Any])
|
|
233
233
|
|
234
234
|
def _get_func_param_item(key: str, val: Any) -> str:
|
235
235
|
upper_key = key.upper()
|
236
|
-
val_str =
|
236
|
+
val_str = _get_val_str(val)
|
237
237
|
val_parts = val_str.split("\n")
|
238
238
|
if len(val_parts) == 1:
|
239
239
|
return f"- {upper_key} `{val}`"
|
@@ -244,6 +244,15 @@ def _get_func_param_item(key: str, val: Any) -> str:
|
|
244
244
|
return "\n".join(lines)
|
245
245
|
|
246
246
|
|
247
|
+
def _get_val_str(val: Any) -> str:
|
248
|
+
if isinstance(val, str):
|
249
|
+
return val
|
250
|
+
try:
|
251
|
+
return json.dumps(val, indent=4)
|
252
|
+
except Exception:
|
253
|
+
return f"{val}"
|
254
|
+
|
255
|
+
|
247
256
|
def _get_func_call_str(
|
248
257
|
func: Callable, args: list[Any] | tuple[Any], kwargs: dict[str, Any]
|
249
258
|
) -> str:
|
zrb/task/llm_task.py
CHANGED
@@ -80,7 +80,8 @@ class LLMTask(BaseTask):
|
|
80
80
|
| Callable[[AnySharedContext], list["ToolOrCallable"]]
|
81
81
|
) = [],
|
82
82
|
toolsets: (
|
83
|
-
list["AbstractToolset[
|
83
|
+
list["AbstractToolset[None] | str"]
|
84
|
+
| Callable[[AnySharedContext], list["AbstractToolset[None] | str"]]
|
84
85
|
) = [],
|
85
86
|
conversation_history: (
|
86
87
|
ConversationHistory
|
@@ -169,7 +170,7 @@ class LLMTask(BaseTask):
|
|
169
170
|
self._rate_limitter = rate_limitter
|
170
171
|
self._additional_tools: list["ToolOrCallable"] = []
|
171
172
|
self._toolsets = toolsets
|
172
|
-
self._additional_toolsets: list["AbstractToolset[
|
173
|
+
self._additional_toolsets: list["AbstractToolset[None] | str"] = []
|
173
174
|
self._conversation_history = conversation_history
|
174
175
|
self._conversation_history_reader = conversation_history_reader
|
175
176
|
self._conversation_history_writer = conversation_history_writer
|
@@ -200,10 +201,12 @@ class LLMTask(BaseTask):
|
|
200
201
|
for single_tool in tool:
|
201
202
|
self._additional_tools.append(single_tool)
|
202
203
|
|
203
|
-
def add_toolset(self, *toolset: "AbstractToolset[
|
204
|
+
def add_toolset(self, *toolset: "AbstractToolset[None] | str"):
|
204
205
|
self.append_toolset(*toolset)
|
205
206
|
|
206
|
-
def append_toolset(self, *toolset: "AbstractToolset[
|
207
|
+
def append_toolset(self, *toolset: "AbstractToolset[None] | str"):
|
208
|
+
from pydantic_ai.mcp import load_mcp_servers
|
209
|
+
|
207
210
|
for single_toolset in toolset:
|
208
211
|
self._additional_toolsets.append(single_toolset)
|
209
212
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: zrb
|
3
|
-
Version: 1.16.
|
3
|
+
Version: 1.16.3
|
4
4
|
Summary: Your Automation Powerhouse
|
5
5
|
License: AGPL-3.0-or-later
|
6
6
|
Keywords: Automation,Task Runner,Code Generator,Monorepo,Low Code
|
@@ -24,12 +24,12 @@ Requires-Dist: isort (>=6.0.1,<7.0.0)
|
|
24
24
|
Requires-Dist: libcst (>=1.8.2,<2.0.0)
|
25
25
|
Requires-Dist: markdownify (>=1.2.0,<2.0.0)
|
26
26
|
Requires-Dist: mcp (>1.12.3)
|
27
|
-
Requires-Dist: openai (>=1.
|
27
|
+
Requires-Dist: openai (>=1.107.2)
|
28
28
|
Requires-Dist: pdfplumber (>=0.11.7,<0.12.0)
|
29
29
|
Requires-Dist: playwright (>=1.54.0,<2.0.0) ; extra == "playwright" or extra == "all"
|
30
30
|
Requires-Dist: prompt-toolkit (>=3)
|
31
31
|
Requires-Dist: psutil (>=7.0.0,<8.0.0)
|
32
|
-
Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cohere,google,groq,huggingface,mistral,openai,vertexai] (>=1.0.
|
32
|
+
Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cohere,google,groq,huggingface,mistral,openai,vertexai] (>=1.0.10,<1.1.0)
|
33
33
|
Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
|
34
34
|
Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
|
35
35
|
Requires-Dist: python-jose[cryptography] (>=3.5.0,<4.0.0)
|
@@ -2,25 +2,26 @@ zrb/__init__.py,sha256=qkCV2EnAGIgvsawBHYvKgPAp0zzPcikYSmbQXATLzg4,5060
|
|
2
2
|
zrb/__main__.py,sha256=9SXH9MK4PVyU9lkEyHxiIUABbcsV2wseP94HmlqTR4M,2657
|
3
3
|
zrb/attr/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
zrb/attr/type.py,sha256=4TV5gPYMMrKh5V-yB6iRYKCbsXAH_AvGXMsjxKLHcUs,568
|
5
|
-
zrb/builtin/__init__.py,sha256=
|
5
|
+
zrb/builtin/__init__.py,sha256=qeLg_S7mRWe48AYrzNutAgAnLh2YE57-iEVK0ICd-3A,1672
|
6
6
|
zrb/builtin/base64.py,sha256=UjaFttE2oRx0T7_RpKtKfgMtWfiQXfJBAJmA16ek8Ic,1507
|
7
7
|
zrb/builtin/git.py,sha256=8_qVE_2lVQEVXQ9vhiw8Tn4Prj1VZB78ZjEJJS5Ab3M,5461
|
8
8
|
zrb/builtin/git_subtree.py,sha256=7BKwOkVTWDrR0DXXQ4iJyHqeR6sV5VYRt8y_rEB0EHg,3505
|
9
9
|
zrb/builtin/group.py,sha256=zYC5uw0VE97TXiLCr464kFJ-CJIJyeQ2RXjnVRY5ovs,2577
|
10
10
|
zrb/builtin/http.py,sha256=L6RE73c65wWwG5iHFN-tpOhyh56KsrgVskDd3c3YXtk,4246
|
11
11
|
zrb/builtin/jwt.py,sha256=3M5uaQhJZbKQLjTUft1OwPz_JxtmK-xtkjxWjciOQho,2859
|
12
|
-
zrb/builtin/llm/chat_session.py,sha256=
|
12
|
+
zrb/builtin/llm/chat_session.py,sha256=6q40xQdv56OtaTZCVSS16WDchn4l0sagZI2BGX_JyQM,10448
|
13
|
+
zrb/builtin/llm/chat_trigger.py,sha256=xaJmzrvBGz6LFPOpYnG9bMeT1dY6XqZPXamtr9e72-w,2427
|
13
14
|
zrb/builtin/llm/history.py,sha256=LDOrL0p7r_AHLa5L8Dp7bHNsOALugmJd7OguXRWGnm4,3087
|
14
15
|
zrb/builtin/llm/input.py,sha256=Nw-26uTWp2QhUgKJcP_IMHmtk-b542CCSQ_vCOjhvhM,877
|
15
|
-
zrb/builtin/llm/llm_ask.py,sha256=
|
16
|
+
zrb/builtin/llm/llm_ask.py,sha256=8V5YAShUPes5qnlxLaS9Ks4KOs7oCQFGrpfkj-6rLMU,7546
|
16
17
|
zrb/builtin/llm/previous-session.js,sha256=xMKZvJoAbrwiyHS0OoPrWuaKxWYLoyR5sguePIoCjTY,816
|
17
18
|
zrb/builtin/llm/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
19
|
zrb/builtin/llm/tool/api.py,sha256=p55Fs6QTmsM2u-vVcBJb9xYxGAkiHIKX2wYKhsGlWFE,2417
|
19
20
|
zrb/builtin/llm/tool/cli.py,sha256=sm_maE1WBB051odh1xXr8QQOWln_ewAU_7OScKAneT4,1244
|
20
|
-
zrb/builtin/llm/tool/code.py,sha256
|
21
|
-
zrb/builtin/llm/tool/file.py,sha256=
|
21
|
+
zrb/builtin/llm/tool/code.py,sha256=-MKUpXX4jkWm4rCqrUmTTzsYhjfzKle9_XsNPtq8PNM,8952
|
22
|
+
zrb/builtin/llm/tool/file.py,sha256=fDgt31CWwZtOYOk6lfBSBxX85NARLr-ZzXPvG1JP8C0,23589
|
22
23
|
zrb/builtin/llm/tool/rag.py,sha256=aN8D8ZqzGXWCP_1F1LbN0QgfyzaK9CKrjfTPorDIYjw,9824
|
23
|
-
zrb/builtin/llm/tool/sub_agent.py,sha256=
|
24
|
+
zrb/builtin/llm/tool/sub_agent.py,sha256=nYluPfc8FlSobpP_4vnBIqkPARrDHq_SwKkmlh_ATUI,5067
|
24
25
|
zrb/builtin/llm/tool/web.py,sha256=zDgxYRIQRj9A8QXb80-ZSPmGCvIOWy8bpJMGSAuTL8Y,7491
|
25
26
|
zrb/builtin/md5.py,sha256=690RV2LbW7wQeTFxY-lmmqTSVEEZv3XZbjEUW1Q3XpE,1480
|
26
27
|
zrb/builtin/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -337,17 +338,17 @@ zrb/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
337
338
|
zrb/task/any_task.py,sha256=AXcBLnDWrJsiq_VihrXeNNMsQZuHzc5k6j-mS60qbyM,6443
|
338
339
|
zrb/task/base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
339
340
|
zrb/task/base/context.py,sha256=ptz9-Am1s4yq6EHZUpnSrB0Ps3k7BgyakVbE-ZpiBzc,3923
|
340
|
-
zrb/task/base/execution.py,sha256=
|
341
|
+
zrb/task/base/execution.py,sha256=ixWSk0byHDbBvT7lxh94eD8pmnbI6LRTIIkMmh0zing,11265
|
341
342
|
zrb/task/base/lifecycle.py,sha256=c2v977pUm7S4EqrTMcUJKhnYOWugACVwU3qORDKiLXQ,7639
|
342
|
-
zrb/task/base/monitoring.py,sha256=
|
343
|
+
zrb/task/base/monitoring.py,sha256=w4_q3e7dwcJureUfcJr7vhpwQ5RCikr0VHGlGA_crrM,5815
|
343
344
|
zrb/task/base/operators.py,sha256=uAMFqpZJsPnCrojgOl1FUDXTS15mtOa_IqiAXltyYRU,1576
|
344
345
|
zrb/task/base_task.py,sha256=upwuqAfwNDXTYM-uRDJhgZqqqARI03T6ksUbFHHLEH0,13321
|
345
346
|
zrb/task/base_trigger.py,sha256=HVasUkIZc8ZdAkJCbhXeO1QTY9vF7BvENoxKRV3R_eY,7171
|
346
347
|
zrb/task/cmd_task.py,sha256=myM8WZm6NrUD-Wv0Vb5sTOrutrAVZLt5LVsSBKwX6SM,10860
|
347
348
|
zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
|
348
349
|
zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
349
|
-
zrb/task/llm/agent.py,sha256=
|
350
|
-
zrb/task/llm/config.py,sha256=
|
350
|
+
zrb/task/llm/agent.py,sha256=mAiDc5YhQ87dJX9Lfv_Q8bTyY2laZDCaAcpHs2yxvtc,11724
|
351
|
+
zrb/task/llm/config.py,sha256=2usr_FjylQrrRjjPDOIBhb7pIwVQkicA0hYGeMy9qYg,4076
|
351
352
|
zrb/task/llm/conversation_history.py,sha256=_ThBOCv4vs3V6B3P_s-Aad2sH0RqE46KzLqgwdwHMC0,6758
|
352
353
|
zrb/task/llm/conversation_history_model.py,sha256=kk-7niTl29Rm2EUIhTHzPXgZ5tp4IThMnIB3dS-1OdU,3062
|
353
354
|
zrb/task/llm/default_workflow/coding.md,sha256=2uythvPsnBpYfIhiIH1cCinQXX0i0yUqsL474Zpemw0,2484
|
@@ -358,9 +359,9 @@ zrb/task/llm/history_summarization.py,sha256=blTKfaSpgaqvORWzGL3BKrRWAsfNdZB03oJ
|
|
358
359
|
zrb/task/llm/history_summarization_tool.py,sha256=5KQUoP75udhtVRbGVxJgnQ36WtVoX--aMvAfGIMBJmw,1760
|
359
360
|
zrb/task/llm/print_node.py,sha256=Nnf4F6eDJR4PFcOqQ1jLWBTFnzNGl1Stux2DZ3SMhsY,8062
|
360
361
|
zrb/task/llm/prompt.py,sha256=Mb3hz-KAp2bL9m6ZL0gcfLveRfBDRZvDVN_HHPGuEPM,12028
|
361
|
-
zrb/task/llm/tool_wrapper.py,sha256=
|
362
|
+
zrb/task/llm/tool_wrapper.py,sha256=9HlfsOi73eGVC43iGZ1BasohT9clFD4IX9xSxu4nGU4,10520
|
362
363
|
zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
|
363
|
-
zrb/task/llm_task.py,sha256=
|
364
|
+
zrb/task/llm_task.py,sha256=uzHNAPziTeIiBr08-tg5_C3MOqJMhfbKmvkWEeS0GZM,15022
|
364
365
|
zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
|
365
366
|
zrb/task/rsync_task.py,sha256=WfqNSaicJgYWpunNU34eYxXDqHDHOftuDHyWJKjqwg0,6365
|
366
367
|
zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
|
@@ -410,7 +411,7 @@ zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
|
|
410
411
|
zrb/util/truncate.py,sha256=eSzmjBpc1Qod3lM3M73snNbDOcARHukW_tq36dWdPvc,921
|
411
412
|
zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
412
413
|
zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
|
413
|
-
zrb-1.16.
|
414
|
-
zrb-1.16.
|
415
|
-
zrb-1.16.
|
416
|
-
zrb-1.16.
|
414
|
+
zrb-1.16.3.dist-info/METADATA,sha256=XONEHMCkv5j0YPf35oaP5MYliI1CYXoPXUv-8FNKFI4,9935
|
415
|
+
zrb-1.16.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
416
|
+
zrb-1.16.3.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
|
417
|
+
zrb-1.16.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|