zrb 1.10.2__py3-none-any.whl → 1.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zrb/builtin/llm/chat_session.py +42 -14
- zrb/builtin/llm/llm_ask.py +11 -0
- zrb/builtin/llm/tool/file.py +2 -2
- zrb/config/config.py +31 -80
- zrb/config/default_prompt/file_extractor_system_prompt.md +12 -0
- zrb/config/default_prompt/interactive_system_prompt.md +31 -0
- zrb/config/default_prompt/persona.md +1 -0
- zrb/config/default_prompt/repo_extractor_system_prompt.md +112 -0
- zrb/config/default_prompt/repo_summarizer_system_prompt.md +10 -0
- zrb/config/default_prompt/summarization_prompt.md +42 -0
- zrb/config/default_prompt/system_prompt.md +28 -0
- zrb/config/llm_config.py +89 -279
- zrb/config/llm_context/config.py +74 -0
- zrb/config/llm_context/config_handler.py +238 -0
- zrb/context/any_shared_context.py +10 -0
- zrb/context/context.py +8 -0
- zrb/context/shared_context.py +9 -0
- zrb/runner/web_route/task_session_api_route.py +1 -1
- zrb/task/llm/agent.py +2 -2
- zrb/task/llm/conversation_history_model.py +78 -226
- zrb/task/llm/default_workflow/coding.md +24 -0
- zrb/task/llm/default_workflow/copywriting.md +17 -0
- zrb/task/llm/default_workflow/researching.md +18 -0
- zrb/task/llm/history_summarization.py +6 -6
- zrb/task/llm/prompt.py +92 -41
- zrb/task/llm/tool_wrapper.py +20 -14
- zrb/task/llm_task.py +19 -23
- zrb/util/callable.py +23 -0
- zrb/util/llm/prompt.py +42 -6
- {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/METADATA +2 -2
- {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/RECORD +33 -20
- {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/WHEEL +0 -0
- {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/entry_points.txt +0 -0
zrb/task/llm/prompt.py
CHANGED
@@ -3,11 +3,12 @@ import platform
|
|
3
3
|
import re
|
4
4
|
from datetime import datetime, timezone
|
5
5
|
|
6
|
-
from zrb.attr.type import StrAttr
|
6
|
+
from zrb.attr.type import StrAttr, StrListAttr
|
7
7
|
from zrb.config.llm_config import llm_config as llm_config
|
8
|
+
from zrb.config.llm_context.config import llm_context_config
|
8
9
|
from zrb.context.any_context import AnyContext
|
9
10
|
from zrb.task.llm.conversation_history_model import ConversationHistory
|
10
|
-
from zrb.util.attr import get_attr, get_str_attr
|
11
|
+
from zrb.util.attr import get_attr, get_str_attr, get_str_list_attr
|
11
12
|
from zrb.util.file import read_dir, read_file_with_line_numbers
|
12
13
|
from zrb.util.llm.prompt import make_prompt_section
|
13
14
|
|
@@ -15,13 +16,14 @@ from zrb.util.llm.prompt import make_prompt_section
|
|
15
16
|
def get_persona(
|
16
17
|
ctx: AnyContext,
|
17
18
|
persona_attr: StrAttr | None,
|
19
|
+
render_persona: bool,
|
18
20
|
) -> str:
|
19
21
|
"""Gets the persona, prioritizing task-specific, then default."""
|
20
22
|
persona = get_attr(
|
21
23
|
ctx,
|
22
24
|
persona_attr,
|
23
25
|
None,
|
24
|
-
auto_render=
|
26
|
+
auto_render=render_persona,
|
25
27
|
)
|
26
28
|
if persona is not None:
|
27
29
|
return persona
|
@@ -31,13 +33,14 @@ def get_persona(
|
|
31
33
|
def get_base_system_prompt(
|
32
34
|
ctx: AnyContext,
|
33
35
|
system_prompt_attr: StrAttr | None,
|
36
|
+
render_system_prompt: bool,
|
34
37
|
) -> str:
|
35
38
|
"""Gets the base system prompt, prioritizing task-specific, then default."""
|
36
39
|
system_prompt = get_attr(
|
37
40
|
ctx,
|
38
41
|
system_prompt_attr,
|
39
42
|
None,
|
40
|
-
auto_render=
|
43
|
+
auto_render=render_system_prompt,
|
41
44
|
)
|
42
45
|
if system_prompt is not None:
|
43
46
|
return system_prompt
|
@@ -47,33 +50,95 @@ def get_base_system_prompt(
|
|
47
50
|
def get_special_instruction_prompt(
|
48
51
|
ctx: AnyContext,
|
49
52
|
special_instruction_prompt_attr: StrAttr | None,
|
53
|
+
render_spcecial_instruction_prompt: bool,
|
50
54
|
) -> str:
|
51
55
|
"""Gets the special instruction prompt, prioritizing task-specific, then default."""
|
52
56
|
special_instruction = get_attr(
|
53
57
|
ctx,
|
54
58
|
special_instruction_prompt_attr,
|
55
59
|
None,
|
56
|
-
auto_render=
|
60
|
+
auto_render=render_spcecial_instruction_prompt,
|
57
61
|
)
|
58
62
|
if special_instruction is not None:
|
59
63
|
return special_instruction
|
60
64
|
return llm_config.default_special_instruction_prompt
|
61
65
|
|
62
66
|
|
67
|
+
def get_modes(
|
68
|
+
ctx: AnyContext,
|
69
|
+
modes_attr: StrAttr | None,
|
70
|
+
render_modes: bool,
|
71
|
+
) -> str:
|
72
|
+
"""Gets the modes, prioritizing task-specific, then default."""
|
73
|
+
raw_modes = get_str_list_attr(
|
74
|
+
ctx,
|
75
|
+
modes_attr,
|
76
|
+
auto_render=render_modes,
|
77
|
+
)
|
78
|
+
modes = [mode.strip() for mode in raw_modes if mode.strip() != ""]
|
79
|
+
if len(modes) > 0:
|
80
|
+
return modes
|
81
|
+
return llm_config.default_modes or []
|
82
|
+
|
83
|
+
|
84
|
+
def get_workflow_prompt(
|
85
|
+
ctx: AnyContext,
|
86
|
+
modes_attr: StrAttr | None,
|
87
|
+
render_modes: bool,
|
88
|
+
) -> str:
|
89
|
+
modes = get_modes(ctx, modes_attr, render_modes)
|
90
|
+
# Get user-defined workflows
|
91
|
+
workflows = {
|
92
|
+
workflow_name: content
|
93
|
+
for workflow_name, content in llm_context_config.get_workflows().items()
|
94
|
+
if workflow_name in modes
|
95
|
+
}
|
96
|
+
# Get requested builtin-workflow names
|
97
|
+
requested_builtin_workflow_names = [
|
98
|
+
workflow_name
|
99
|
+
for workflow_name in ("coding", "copywriting", "researching")
|
100
|
+
if workflow_name in modes and workflow_name not in workflows
|
101
|
+
]
|
102
|
+
# add builtin-workflows if requested
|
103
|
+
if len(requested_builtin_workflow_names) > 0:
|
104
|
+
dir_path = os.path.dirname(__file__)
|
105
|
+
for workflow_name in requested_builtin_workflow_names:
|
106
|
+
workflow_file_path = os.path.join(
|
107
|
+
dir_path, "default_workflow", f"{workflow_name}.md"
|
108
|
+
)
|
109
|
+
with open(workflow_file_path, "r") as f:
|
110
|
+
workflows[workflow_name] = f.read()
|
111
|
+
return "\n".join(
|
112
|
+
[
|
113
|
+
make_prompt_section(header.capitalize(), content)
|
114
|
+
for header, content in workflows.items()
|
115
|
+
if header.lower() in modes
|
116
|
+
]
|
117
|
+
)
|
118
|
+
|
119
|
+
|
63
120
|
def get_system_and_user_prompt(
|
64
121
|
ctx: AnyContext,
|
65
122
|
user_message: str,
|
66
123
|
persona_attr: StrAttr | None = None,
|
124
|
+
render_persona: bool = False,
|
67
125
|
system_prompt_attr: StrAttr | None = None,
|
126
|
+
render_system_prompt: bool = False,
|
68
127
|
special_instruction_prompt_attr: StrAttr | None = None,
|
128
|
+
render_special_instruction_prompt: bool = False,
|
129
|
+
modes_attr: StrListAttr | None = None,
|
130
|
+
render_modes: bool = False,
|
69
131
|
conversation_history: ConversationHistory | None = None,
|
70
132
|
) -> tuple[str, str]:
|
71
133
|
"""Combines persona, base system prompt, and special instructions."""
|
72
|
-
persona = get_persona(ctx, persona_attr)
|
73
|
-
base_system_prompt = get_base_system_prompt(
|
74
|
-
|
75
|
-
ctx, special_instruction_prompt_attr
|
134
|
+
persona = get_persona(ctx, persona_attr, render_persona)
|
135
|
+
base_system_prompt = get_base_system_prompt(
|
136
|
+
ctx, system_prompt_attr, render_system_prompt
|
76
137
|
)
|
138
|
+
special_instruction_prompt = get_special_instruction_prompt(
|
139
|
+
ctx, special_instruction_prompt_attr, render_special_instruction_prompt
|
140
|
+
)
|
141
|
+
workflow_prompt = get_workflow_prompt(ctx, modes_attr, render_modes)
|
77
142
|
if conversation_history is None:
|
78
143
|
conversation_history = ConversationHistory()
|
79
144
|
conversation_context, new_user_message = extract_conversation_context(user_message)
|
@@ -81,7 +146,8 @@ def get_system_and_user_prompt(
|
|
81
146
|
[
|
82
147
|
make_prompt_section("Persona", persona),
|
83
148
|
make_prompt_section("System Prompt", base_system_prompt),
|
84
|
-
make_prompt_section("Special Instruction",
|
149
|
+
make_prompt_section("Special Instruction", special_instruction_prompt),
|
150
|
+
make_prompt_section("Special Workflows", workflow_prompt),
|
85
151
|
make_prompt_section(
|
86
152
|
"Past Conversation",
|
87
153
|
"\n".join(
|
@@ -125,38 +191,37 @@ def get_system_and_user_prompt(
|
|
125
191
|
def extract_conversation_context(user_message: str) -> tuple[str, str]:
|
126
192
|
modified_user_message = user_message
|
127
193
|
# Match “@” + any non-space/comma sequence that contains at least one “/”
|
128
|
-
pattern = r"(?<!\w)@(?=[^,\s]
|
194
|
+
pattern = r"(?<!\w)@(?=[^,\s]*\/)([^,\s]+)"
|
129
195
|
potential_resource_path = re.findall(pattern, user_message)
|
130
196
|
apendixes = []
|
131
|
-
for ref in potential_resource_path:
|
197
|
+
for i, ref in enumerate(potential_resource_path):
|
132
198
|
resource_path = os.path.abspath(os.path.expanduser(ref))
|
133
|
-
|
199
|
+
content = ""
|
200
|
+
ref_type = ""
|
134
201
|
if os.path.isfile(resource_path):
|
135
202
|
content = read_file_with_line_numbers(resource_path)
|
136
|
-
|
137
|
-
make_prompt_section(
|
138
|
-
f"`{ref}` (file path: `{resource_path}`)", content, as_code=True
|
139
|
-
)
|
140
|
-
)
|
141
|
-
# Remove the '@' from the modified user message for valid file paths
|
142
|
-
modified_user_message = modified_user_message.replace(f"@{ref}", ref, 1)
|
203
|
+
ref_type = "file"
|
143
204
|
elif os.path.isdir(resource_path):
|
144
205
|
content = read_dir(resource_path)
|
206
|
+
ref_type = "directory"
|
207
|
+
if content != "":
|
208
|
+
# Replace the @-reference in the user message with the placeholder
|
209
|
+
placeholder = f"[Reference {i+1}: {os.path.basename(ref)}]"
|
210
|
+
modified_user_message = modified_user_message.replace(
|
211
|
+
f"@{ref}", placeholder, 1
|
212
|
+
)
|
145
213
|
apendixes.append(
|
146
214
|
make_prompt_section(
|
147
|
-
f"
|
215
|
+
f"{placeholder} ({ref_type} path: `{resource_path}`)",
|
148
216
|
content,
|
149
217
|
as_code=True,
|
150
218
|
)
|
151
219
|
)
|
152
|
-
# Remove the '@' from the modified user message for valid directory paths
|
153
|
-
modified_user_message = modified_user_message.replace(f"@{ref}", ref, 1)
|
154
220
|
conversation_context = "\n".join(
|
155
221
|
[
|
156
222
|
make_prompt_section("Current OS", platform.system()),
|
157
223
|
make_prompt_section("OS Version", platform.version()),
|
158
224
|
make_prompt_section("Python Version", platform.python_version()),
|
159
|
-
make_prompt_section("Apendixes", "\n".join(apendixes)),
|
160
225
|
]
|
161
226
|
)
|
162
227
|
iso_date = datetime.now(timezone.utc).astimezone().isoformat()
|
@@ -172,6 +237,7 @@ def extract_conversation_context(user_message: str) -> tuple[str, str]:
|
|
172
237
|
"Current working directory", current_directory
|
173
238
|
),
|
174
239
|
make_prompt_section("Current time", iso_date),
|
240
|
+
make_prompt_section("Apendixes", "\n".join(apendixes)),
|
175
241
|
]
|
176
242
|
),
|
177
243
|
),
|
@@ -194,30 +260,15 @@ def get_user_message(
|
|
194
260
|
def get_summarization_system_prompt(
|
195
261
|
ctx: AnyContext,
|
196
262
|
summarization_prompt_attr: StrAttr | None,
|
263
|
+
render_summarization_prompt: bool,
|
197
264
|
) -> str:
|
198
265
|
"""Gets the summarization prompt, rendering if configured and handling defaults."""
|
199
266
|
summarization_prompt = get_attr(
|
200
267
|
ctx,
|
201
268
|
summarization_prompt_attr,
|
202
269
|
None,
|
203
|
-
auto_render=
|
270
|
+
auto_render=render_summarization_prompt,
|
204
271
|
)
|
205
272
|
if summarization_prompt is not None:
|
206
273
|
return summarization_prompt
|
207
274
|
return llm_config.default_summarization_prompt
|
208
|
-
|
209
|
-
|
210
|
-
def get_context_enrichment_prompt(
|
211
|
-
ctx: AnyContext,
|
212
|
-
context_enrichment_prompt_attr: StrAttr | None,
|
213
|
-
) -> str:
|
214
|
-
"""Gets the context enrichment prompt, rendering if configured and handling defaults."""
|
215
|
-
context_enrichment_prompt = get_attr(
|
216
|
-
ctx,
|
217
|
-
context_enrichment_prompt_attr,
|
218
|
-
None,
|
219
|
-
auto_render=False,
|
220
|
-
)
|
221
|
-
if context_enrichment_prompt is not None:
|
222
|
-
return context_enrichment_prompt
|
223
|
-
return llm_config.default_context_enrichment_prompt
|
zrb/task/llm/tool_wrapper.py
CHANGED
@@ -5,9 +5,12 @@ import typing
|
|
5
5
|
from collections.abc import Callable
|
6
6
|
from typing import TYPE_CHECKING
|
7
7
|
|
8
|
+
from zrb.config.config import CFG
|
8
9
|
from zrb.context.any_context import AnyContext
|
9
10
|
from zrb.task.llm.error import ToolExecutionError
|
11
|
+
from zrb.util.callable import get_callable_name
|
10
12
|
from zrb.util.run import run_async
|
13
|
+
from zrb.util.string.conversion import to_boolean
|
11
14
|
|
12
15
|
if TYPE_CHECKING:
|
13
16
|
from pydantic_ai import Tool
|
@@ -71,13 +74,11 @@ def _create_wrapper(
|
|
71
74
|
async def wrapper(*args, **kwargs):
|
72
75
|
# Identify AnyContext parameter name from the original signature if needed
|
73
76
|
any_context_param_name = None
|
74
|
-
|
75
77
|
if needs_any_context_for_injection:
|
76
78
|
for param in original_sig.parameters.values():
|
77
79
|
if _is_annotated_with_context(param.annotation, AnyContext):
|
78
80
|
any_context_param_name = param.name
|
79
81
|
break # Found it, no need to continue
|
80
|
-
|
81
82
|
if any_context_param_name is None:
|
82
83
|
# This should not happen if needs_any_context_for_injection is True,
|
83
84
|
# but check for safety
|
@@ -87,24 +88,22 @@ def _create_wrapper(
|
|
87
88
|
# Inject the captured ctx into kwargs. This will overwrite if the LLM
|
88
89
|
# somehow provided it.
|
89
90
|
kwargs[any_context_param_name] = ctx
|
90
|
-
|
91
91
|
# If the dummy argument was added for schema generation and is present in kwargs,
|
92
92
|
# remove it before calling the original function, unless the original function
|
93
93
|
# actually expects a parameter named '_dummy'.
|
94
94
|
if "_dummy" in kwargs and "_dummy" not in original_sig.parameters:
|
95
95
|
del kwargs["_dummy"]
|
96
|
-
|
97
96
|
try:
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
97
|
+
if not CFG.LLM_YOLO_MODE and not ctx.is_web_mode and ctx.is_tty:
|
98
|
+
func_name = get_callable_name(func)
|
99
|
+
ctx.print(f"✅ >> Allow to run tool: {func_name} (Y/n)", plain=True)
|
100
|
+
user_confirmation_str = await _read_line()
|
101
|
+
user_confirmation = to_boolean(user_confirmation_str)
|
102
|
+
if not user_confirmation:
|
103
|
+
ctx.print("❌ >> Why?", plain=True)
|
104
|
+
reason = await _read_line()
|
105
|
+
ctx.print("", plain=True)
|
106
|
+
raise ValueError(f"User disapproval: {reason}")
|
108
107
|
return await run_async(func(*args, **kwargs))
|
109
108
|
except Exception as e:
|
110
109
|
error_model = ToolExecutionError(
|
@@ -118,6 +117,13 @@ def _create_wrapper(
|
|
118
117
|
return wrapper
|
119
118
|
|
120
119
|
|
120
|
+
async def _read_line():
|
121
|
+
from prompt_toolkit import PromptSession
|
122
|
+
|
123
|
+
reader = PromptSession()
|
124
|
+
return await reader.prompt_async()
|
125
|
+
|
126
|
+
|
121
127
|
def _adjust_signature(
|
122
128
|
wrapper: Callable, original_sig: inspect.Signature, takes_no_args: bool
|
123
129
|
):
|
zrb/task/llm_task.py
CHANGED
@@ -2,7 +2,7 @@ import json
|
|
2
2
|
from collections.abc import Callable
|
3
3
|
from typing import TYPE_CHECKING, Any
|
4
4
|
|
5
|
-
from zrb.attr.type import BoolAttr, IntAttr, StrAttr, fstring
|
5
|
+
from zrb.attr.type import BoolAttr, IntAttr, StrAttr, StrListAttr, fstring
|
6
6
|
from zrb.config.llm_rate_limitter import LLMRateLimiter
|
7
7
|
from zrb.context.any_context import AnyContext
|
8
8
|
from zrb.context.any_shared_context import AnySharedContext
|
@@ -16,14 +16,12 @@ from zrb.task.llm.config import (
|
|
16
16
|
get_model_settings,
|
17
17
|
)
|
18
18
|
from zrb.task.llm.conversation_history import (
|
19
|
-
ListOfDict,
|
20
19
|
read_conversation_history,
|
21
20
|
write_conversation_history,
|
22
21
|
)
|
23
22
|
from zrb.task.llm.conversation_history_model import ConversationHistory
|
24
23
|
from zrb.task.llm.history_summarization import maybe_summarize_history
|
25
24
|
from zrb.task.llm.prompt import (
|
26
|
-
get_context_enrichment_prompt,
|
27
25
|
get_summarization_system_prompt,
|
28
26
|
get_system_and_user_prompt,
|
29
27
|
get_user_message,
|
@@ -65,16 +63,15 @@ class LLMTask(BaseTask):
|
|
65
63
|
) = None,
|
66
64
|
agent: "Agent | Callable[[AnySharedContext], Agent] | None" = None,
|
67
65
|
persona: StrAttr | None = None,
|
66
|
+
render_persona: bool = False,
|
68
67
|
system_prompt: StrAttr | None = None,
|
68
|
+
render_system_prompt: bool = False,
|
69
69
|
special_instruction_prompt: StrAttr | None = None,
|
70
|
+
render_special_instruction_prompt: bool = False,
|
71
|
+
modes: StrListAttr | None = None,
|
72
|
+
render_modes: bool = True,
|
70
73
|
message: StrAttr | None = None,
|
71
74
|
render_message: bool = True,
|
72
|
-
enrich_context: BoolAttr | None = None,
|
73
|
-
render_enrich_context: bool = True,
|
74
|
-
context_enrichment_prompt: StrAttr | None = None,
|
75
|
-
render_context_enrichment_prompt: bool = True,
|
76
|
-
context_enrichment_token_threshold: IntAttr | None = None,
|
77
|
-
render_context_enrichment_token_threshold: bool = True,
|
78
75
|
tools: (
|
79
76
|
list["ToolOrCallable"]
|
80
77
|
| Callable[[AnySharedContext], list["ToolOrCallable"]]
|
@@ -100,6 +97,7 @@ class LLMTask(BaseTask):
|
|
100
97
|
summarize_history: BoolAttr | None = None,
|
101
98
|
render_summarize_history: bool = True,
|
102
99
|
summarization_prompt: StrAttr | None = None,
|
100
|
+
render_summarization_prompt: bool = False,
|
103
101
|
history_summarization_token_threshold: IntAttr | None = None,
|
104
102
|
render_history_summarization_token_threshold: bool = True,
|
105
103
|
rate_limitter: LLMRateLimiter | None = None,
|
@@ -150,19 +148,17 @@ class LLMTask(BaseTask):
|
|
150
148
|
self._model_settings = model_settings
|
151
149
|
self._agent = agent
|
152
150
|
self._persona = persona
|
151
|
+
self._render_persona = render_persona
|
153
152
|
self._system_prompt = system_prompt
|
153
|
+
self._render_system_prompt = render_system_prompt
|
154
154
|
self._special_instruction_prompt = special_instruction_prompt
|
155
|
+
self._render_special_instruction_prompt = render_special_instruction_prompt
|
156
|
+
self._modes = modes
|
157
|
+
self._render_modes = render_modes
|
155
158
|
self._message = message
|
156
159
|
self._render_message = render_message
|
157
160
|
self._summarization_prompt = summarization_prompt
|
158
|
-
self.
|
159
|
-
self._render_enrich_context = render_enrich_context
|
160
|
-
self._context_enrichment_prompt = context_enrichment_prompt
|
161
|
-
self._render_context_enrichment_prompt = render_context_enrichment_prompt
|
162
|
-
self._context_enrichment_token_threshold = context_enrichment_token_threshold
|
163
|
-
self._render_context_enrichment_token_threshold = (
|
164
|
-
render_context_enrichment_token_threshold
|
165
|
-
)
|
161
|
+
self._render_summarization_prompt = render_summarization_prompt
|
166
162
|
self._tools = tools
|
167
163
|
self._rate_limitter = rate_limitter
|
168
164
|
self._additional_tools: list["ToolOrCallable"] = []
|
@@ -198,12 +194,6 @@ class LLMTask(BaseTask):
|
|
198
194
|
for single_mcp_server in mcp_server:
|
199
195
|
self._additional_mcp_servers.append(single_mcp_server)
|
200
196
|
|
201
|
-
def set_should_enrich_context(self, enrich_context: bool):
|
202
|
-
self._should_enrich_context = enrich_context
|
203
|
-
|
204
|
-
def set_context_enrichment_token_threshold(self, enrichment_token_threshold: int):
|
205
|
-
self._context_enrichment_token_threshold = enrichment_token_threshold
|
206
|
-
|
207
197
|
def set_should_summarize_history(self, summarize_history: bool):
|
208
198
|
self._should_summarize_history = summarize_history
|
209
199
|
|
@@ -227,6 +217,7 @@ class LLMTask(BaseTask):
|
|
227
217
|
summarization_prompt = get_summarization_system_prompt(
|
228
218
|
ctx=ctx,
|
229
219
|
summarization_prompt_attr=self._summarization_prompt,
|
220
|
+
render_summarization_prompt=self._render_summarization_prompt,
|
230
221
|
)
|
231
222
|
user_message = get_user_message(ctx, self._message, self._render_message)
|
232
223
|
# 1. Prepare initial state (read history from previous session)
|
@@ -243,8 +234,13 @@ class LLMTask(BaseTask):
|
|
243
234
|
ctx=ctx,
|
244
235
|
user_message=user_message,
|
245
236
|
persona_attr=self._persona,
|
237
|
+
render_persona=self._render_persona,
|
246
238
|
system_prompt_attr=self._system_prompt,
|
239
|
+
render_system_prompt=self._render_system_prompt,
|
247
240
|
special_instruction_prompt_attr=self._special_instruction_prompt,
|
241
|
+
render_special_instruction_prompt=self._render_special_instruction_prompt,
|
242
|
+
modes_attr=self._modes,
|
243
|
+
render_modes=self._render_modes,
|
248
244
|
conversation_history=conversation_history,
|
249
245
|
)
|
250
246
|
# 3. Get the agent instance
|
zrb/util/callable.py
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
from types import BuiltinMethodType, MethodType
|
2
|
+
|
3
|
+
|
4
|
+
def get_callable_name(obj):
|
5
|
+
import functools
|
6
|
+
import inspect
|
7
|
+
|
8
|
+
# 1. Unwrap decorated functions
|
9
|
+
obj = inspect.unwrap(obj, stop=lambda f: not hasattr(f, "__wrapped__"))
|
10
|
+
# 2. functools.partial – delegate to the wrapped function
|
11
|
+
if isinstance(obj, functools.partial):
|
12
|
+
return get_callable_name(obj.func)
|
13
|
+
# 3. Plain functions, built‑ins, methods
|
14
|
+
if hasattr(obj, "__name__"):
|
15
|
+
return obj.__name__
|
16
|
+
# 4. Bound or unbound methods of a class
|
17
|
+
if isinstance(obj, (MethodType, BuiltinMethodType)):
|
18
|
+
return obj.__func__.__name__
|
19
|
+
# 5. Instances of classes defining __call__
|
20
|
+
if callable(obj):
|
21
|
+
return type(obj).__name__
|
22
|
+
# 6. Fallback
|
23
|
+
return repr(obj)
|
zrb/util/llm/prompt.py
CHANGED
@@ -2,17 +2,53 @@ import re
|
|
2
2
|
|
3
3
|
|
4
4
|
def _demote_markdown_headers(md: str) -> str:
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
lines = md.split("\n")
|
6
|
+
new_lines = []
|
7
|
+
fence_stack = []
|
8
|
+
for line in lines:
|
9
|
+
stripped_line = line.strip()
|
10
|
+
fence_match = re.match(r"^([`~]{3,})", stripped_line)
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
if fence_match:
|
13
|
+
current_fence = fence_match.group(1)
|
14
|
+
# If stack is not empty and we found a closing fence
|
15
|
+
if (
|
16
|
+
fence_stack
|
17
|
+
and fence_stack[-1][0] == current_fence[0]
|
18
|
+
and len(current_fence) >= len(fence_stack[-1])
|
19
|
+
):
|
20
|
+
fence_stack.pop()
|
21
|
+
else:
|
22
|
+
fence_stack.append(current_fence)
|
23
|
+
new_lines.append(line)
|
24
|
+
else:
|
25
|
+
if fence_stack: # If we are inside a code block
|
26
|
+
new_lines.append(line)
|
27
|
+
else:
|
28
|
+
match = re.match(r"^(#{1,6})(\s)", line)
|
29
|
+
if match:
|
30
|
+
new_lines.append("#" + line)
|
31
|
+
else:
|
32
|
+
new_lines.append(line)
|
33
|
+
return "\n".join(new_lines)
|
11
34
|
|
12
35
|
|
13
36
|
def make_prompt_section(header: str, content: str, as_code: bool = False) -> str:
|
14
37
|
if content.strip() == "":
|
15
38
|
return ""
|
16
39
|
if as_code:
|
17
|
-
|
40
|
+
# Find the longest sequence of backticks in the content
|
41
|
+
longest_backtick_sequence = 0
|
42
|
+
# Use finditer to find all occurrences of backticks
|
43
|
+
for match in re.finditer(r"`+", content):
|
44
|
+
longest_backtick_sequence = max(
|
45
|
+
longest_backtick_sequence, len(match.group(0))
|
46
|
+
)
|
47
|
+
|
48
|
+
# The fence should be one longer than the longest sequence found
|
49
|
+
fence_len = 4
|
50
|
+
if longest_backtick_sequence >= fence_len:
|
51
|
+
fence_len = longest_backtick_sequence + 1
|
52
|
+
fence = "`" * fence_len
|
53
|
+
return f"# {header}\n{fence}\n{content.strip()}\n{fence}\n"
|
18
54
|
return f"# {header}\n{_demote_markdown_headers(content.strip())}\n"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: zrb
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.12.0
|
4
4
|
Summary: Your Automation Powerhouse
|
5
5
|
Home-page: https://github.com/state-alchemists/zrb
|
6
6
|
License: AGPL-3.0-or-later
|
@@ -27,7 +27,7 @@ Requires-Dist: pdfplumber (>=0.11.6,<0.12.0) ; extra == "rag" or extra == "all"
|
|
27
27
|
Requires-Dist: playwright (>=1.53.0,<2.0.0) ; extra == "playwright" or extra == "all"
|
28
28
|
Requires-Dist: prompt-toolkit (>=3.0.51,<4.0.0)
|
29
29
|
Requires-Dist: psutil (>=7.0.0,<8.0.0)
|
30
|
-
Requires-Dist: pydantic-ai (>=0.
|
30
|
+
Requires-Dist: pydantic-ai (>=0.4.4,<0.5.0)
|
31
31
|
Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
|
32
32
|
Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
|
33
33
|
Requires-Dist: python-jose[cryptography] (>=3.4.0,<4.0.0)
|
@@ -9,16 +9,16 @@ zrb/builtin/git_subtree.py,sha256=7BKwOkVTWDrR0DXXQ4iJyHqeR6sV5VYRt8y_rEB0EHg,35
|
|
9
9
|
zrb/builtin/group.py,sha256=t008xLM4_fgbjfZrPoi_fQAnSHIo6MOiQSCHBO4GDYU,2379
|
10
10
|
zrb/builtin/http.py,sha256=sLqEczuSxGYXWzyJR6frGOHkPTviu4BeyroUr3-ZuAI,4322
|
11
11
|
zrb/builtin/jwt.py,sha256=3M5uaQhJZbKQLjTUft1OwPz_JxtmK-xtkjxWjciOQho,2859
|
12
|
-
zrb/builtin/llm/chat_session.py,sha256=
|
12
|
+
zrb/builtin/llm/chat_session.py,sha256=u8bW67uKCq22hVv4ZkOsKIZxBeOdKtJh4Bjyy552RM4,9424
|
13
13
|
zrb/builtin/llm/history.py,sha256=LDOrL0p7r_AHLa5L8Dp7bHNsOALugmJd7OguXRWGnm4,3087
|
14
14
|
zrb/builtin/llm/input.py,sha256=Nw-26uTWp2QhUgKJcP_IMHmtk-b542CCSQ_vCOjhvhM,877
|
15
|
-
zrb/builtin/llm/llm_ask.py,sha256=
|
15
|
+
zrb/builtin/llm/llm_ask.py,sha256=18XAxyPWF7daE0TZkRkRt8opmqLUjhpM3oMVdOP-qWY,4857
|
16
16
|
zrb/builtin/llm/previous-session.js,sha256=xMKZvJoAbrwiyHS0OoPrWuaKxWYLoyR5sguePIoCjTY,816
|
17
17
|
zrb/builtin/llm/tool/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
18
|
zrb/builtin/llm/tool/api.py,sha256=OhmfLc2TwWKQYIMweGelqb5s4JF4nB-YynbSO4yb_Jk,2342
|
19
19
|
zrb/builtin/llm/tool/cli.py,sha256=dUWZrW2X5J_lONuzR__6-SbewSdi28E3RRuksjd4mWo,1234
|
20
20
|
zrb/builtin/llm/tool/code.py,sha256=GRP_IZAkeL6RIlUm407BQRF992ES57pdzPaQdC5UsJU,8218
|
21
|
-
zrb/builtin/llm/tool/file.py,sha256=
|
21
|
+
zrb/builtin/llm/tool/file.py,sha256=XfTuoQOHmgiAYkfi_1ew2voxOwad5vWTe_3Ww8IeVQY,22274
|
22
22
|
zrb/builtin/llm/tool/rag.py,sha256=wB74JV7bxs0ec77b_09Z2lPjoR1WzPUvZbuXOdb9Q9g,9675
|
23
23
|
zrb/builtin/llm/tool/sub_agent.py,sha256=UWBLiuCK6FT8Ku0yPfSxd_k67h_Pme1K7d2VSABacjQ,4855
|
24
24
|
zrb/builtin/llm/tool/web.py,sha256=gQlUsmYCJOFJtNjwpjK-xk13LMvrMSpSaFHXUTnIayQ,7090
|
@@ -217,8 +217,17 @@ 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=
|
221
|
-
zrb/config/
|
220
|
+
zrb/config/config.py,sha256=d_F-hdPLADjeVRHtnpOxtOkfUBu5huSLclyD53uxO4U,12306
|
221
|
+
zrb/config/default_prompt/file_extractor_system_prompt.md,sha256=tmeZMPzF9MGExsZZw7M2PZN6V0oFVRp1nIjiqUPvQ9M,1013
|
222
|
+
zrb/config/default_prompt/interactive_system_prompt.md,sha256=NlG5cQ4imEGF9CIRwqH03UZ5XRtqLu1gIin3nBDtQlI,2795
|
223
|
+
zrb/config/default_prompt/persona.md,sha256=WU4JKp-p7qJePDA6NZ_CYdBggo2B3PEq8IEnNVblIHU,41
|
224
|
+
zrb/config/default_prompt/repo_extractor_system_prompt.md,sha256=EGZ-zj78RlMEg2jduRBs8WzO4VJTkXHR96IpBepZMsY,3881
|
225
|
+
zrb/config/default_prompt/repo_summarizer_system_prompt.md,sha256=fpG5B416OK3oE41bWPrh1M6pdH5SSadCPte_NJ_79z0,858
|
226
|
+
zrb/config/default_prompt/summarization_prompt.md,sha256=3-swyZ2m9DQFkaN68kn-AxnFHTcQYqrPSzV3qwT-vw4,2122
|
227
|
+
zrb/config/default_prompt/system_prompt.md,sha256=uRRiVSTs_4s2DYBO-1cPuOGPVkaelA_UuGClLawfw3o,2283
|
228
|
+
zrb/config/llm_config.py,sha256=bNLxorctwtVW1F9hA-hEYpDBe7FLSZHC25Nx8NlR4-M,8597
|
229
|
+
zrb/config/llm_context/config.py,sha256=swc3hUaEIoL2MjKtbati13iP0MxveNG_y_6K3nszRAw,2571
|
230
|
+
zrb/config/llm_context/config_handler.py,sha256=oQesfigIM0qMw_A3jUCN0UDJujRjuJ3jr5mXHBiLgB0,8866
|
222
231
|
zrb/config/llm_rate_limitter.py,sha256=P4vR7qxwiGwjlKx2kHcfdIxwGbJB98vdN-UQEH-Q2WU,4894
|
223
232
|
zrb/config/web_auth_config.py,sha256=_PXatQTYh2mX9H3HSYSQKp13zm1RlLyVIoeIr6KYMQ8,6279
|
224
233
|
zrb/content_transformer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -226,9 +235,9 @@ zrb/content_transformer/any_content_transformer.py,sha256=v8ZUbcix1GGeDQwB6OKX_1
|
|
226
235
|
zrb/content_transformer/content_transformer.py,sha256=STl77wW-I69QaGzCXjvkppngYFLufow8ybPLSyAvlHs,2404
|
227
236
|
zrb/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
228
237
|
zrb/context/any_context.py,sha256=2hgVKbbDwmwrEl1h1L1FaTUjuUYaDd_b7YRGkaorW6Q,6362
|
229
|
-
zrb/context/any_shared_context.py,sha256=
|
230
|
-
zrb/context/context.py,sha256=
|
231
|
-
zrb/context/shared_context.py,sha256=
|
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=Pn0LHEYikiB3LLGnfpJVzOFgxyosQ_NYvFtKFMK_X8w,3008
|
232
241
|
zrb/dot_dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
233
242
|
zrb/dot_dict/dot_dict.py,sha256=ubw_x8I7AOJ59xxtFVJ00VGmq_IYdZP3mUhNlO4nEK0,556
|
234
243
|
zrb/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -307,7 +316,7 @@ zrb/runner/web_route/static/resources/session/event.js,sha256=X5OlSHefK0SDB9VkFC
|
|
307
316
|
zrb/runner/web_route/static/resources/session/past-session.js,sha256=RwGJYKSp75K8NZ-iZP58XppWgdzkiKFaiC5wgcMLxDo,5470
|
308
317
|
zrb/runner/web_route/static/static_route.py,sha256=QPs5XW4O_8CuzG0Wy4sHh5wRcLbU63CLDI4YNqkUxHA,1555
|
309
318
|
zrb/runner/web_route/task_input_api_route.py,sha256=6JIehRjXPhzclq9qGMYkztaKB0TzWsBBbim0m47-YmA,1767
|
310
|
-
zrb/runner/web_route/task_session_api_route.py,sha256=
|
319
|
+
zrb/runner/web_route/task_session_api_route.py,sha256=N4kg7uNfxiiuF-YEpk6khuorkyv_H5aDm_l3pwxNozo,6262
|
311
320
|
zrb/runner/web_schema/session.py,sha256=NwbuS2Sv-CXO52nU-EZv8OMlD4vgCQWNeLC_dT0FK7I,92
|
312
321
|
zrb/runner/web_schema/token.py,sha256=Y7XCPS4WzrxslTDtHeLcPTTUpmWhPOkRcl4b99zrC7c,185
|
313
322
|
zrb/runner/web_schema/user.py,sha256=Kp10amg4i-f8Y-4czogv1YN7rwy0HdbePFiuovYu1ts,1018
|
@@ -337,17 +346,20 @@ zrb/task/base_trigger.py,sha256=WSGcmBcGAZw8EzUXfmCjqJQkz8GEmi1RzogpF6A1V4s,6902
|
|
337
346
|
zrb/task/cmd_task.py,sha256=myM8WZm6NrUD-Wv0Vb5sTOrutrAVZLt5LVsSBKwX6SM,10860
|
338
347
|
zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
|
339
348
|
zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
340
|
-
zrb/task/llm/agent.py,sha256=
|
349
|
+
zrb/task/llm/agent.py,sha256=A5UoHY-l8WqyptKrf42eHVW_VhMhuYsygs2Z8XNnCzk,6681
|
341
350
|
zrb/task/llm/config.py,sha256=TlyH925_fboIlK2Ixf34tynmenqs9s9rfsnPs4jff78,3490
|
342
351
|
zrb/task/llm/conversation_history.py,sha256=B_PDWYL_q66s0xwWBzMSomqPN6u3gkXlIeXBD5A0Apg,4416
|
343
|
-
zrb/task/llm/conversation_history_model.py,sha256=
|
352
|
+
zrb/task/llm/conversation_history_model.py,sha256=AU5-M4Ky3X4wII1PMT75VU5OUEG0FjqdHrrpCSl-u6M,10771
|
353
|
+
zrb/task/llm/default_workflow/coding.md,sha256=2uythvPsnBpYfIhiIH1cCinQXX0i0yUqsL474Zpemw0,2484
|
354
|
+
zrb/task/llm/default_workflow/copywriting.md,sha256=xSO7GeDolwGxiuz6kXsK2GKGpwp8UgtG0yRqTmill_s,1999
|
355
|
+
zrb/task/llm/default_workflow/researching.md,sha256=KD-aYHFHir6Ti-4FsBBtGwiI0seSVgleYbKJZi_POXA,2139
|
344
356
|
zrb/task/llm/error.py,sha256=QR-nIohS6pBpC_16cWR-fw7Mevo1sNYAiXMBsh_CJDE,4157
|
345
|
-
zrb/task/llm/history_summarization.py,sha256=
|
357
|
+
zrb/task/llm/history_summarization.py,sha256=BUwBOS51Jzp4psliD_h1jWq-5oHezNbjF1fkn7vbh7o,8109
|
346
358
|
zrb/task/llm/print_node.py,sha256=zocTKi9gZDxl2I6KNu095TmMc13Yip6SNuWYnswS680,4060
|
347
|
-
zrb/task/llm/prompt.py,sha256=
|
348
|
-
zrb/task/llm/tool_wrapper.py,sha256=
|
359
|
+
zrb/task/llm/prompt.py,sha256=sMipP-NJmq4ZmCtQYEG2mcHWUD79yJRwH7nH-iw-7Z4,9661
|
360
|
+
zrb/task/llm/tool_wrapper.py,sha256=N6IuWJXFDcGUJyMJnnWmpJLsqas1QNCEj0MNL3T2nXI,6647
|
349
361
|
zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
|
350
|
-
zrb/task/llm_task.py,sha256=
|
362
|
+
zrb/task/llm_task.py,sha256=Zxmp7c7XOz5_jAX1kzwwNfD9GJ1Tok-C4e_MfqhliNk,13532
|
351
363
|
zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
|
352
364
|
zrb/task/rsync_task.py,sha256=WfqNSaicJgYWpunNU34eYxXDqHDHOftuDHyWJKjqwg0,6365
|
353
365
|
zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
|
@@ -358,6 +370,7 @@ zrb/task_status/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
358
370
|
zrb/task_status/task_status.py,sha256=blZ8dxg9g_8MuViq-t7yJRLoE7yGUf5srgHf-PCsXNc,3069
|
359
371
|
zrb/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
360
372
|
zrb/util/attr.py,sha256=5GlYSmVAzbcSFjNDXiqqHqNMR6NWjJ6bUHZXdE35mj8,5359
|
373
|
+
zrb/util/callable.py,sha256=b6OFXbCXp2twow3wh2E_h5hNHLs2pXaLfGQz4iVyiQc,771
|
361
374
|
zrb/util/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
362
375
|
zrb/util/cli/style.py,sha256=D_548KG1gXEirQGdkAVTc81vBdCeInXtnG1gV1yabBA,6655
|
363
376
|
zrb/util/cli/subcommand.py,sha256=umTZIlrL-9g-qc_eRRgdaQgK-whvXK1roFfvnbuY7NQ,1753
|
@@ -382,7 +395,7 @@ zrb/util/git_subtree.py,sha256=AyQWCWEi2EIzEpYXRnYN55157KMUql0WHj70QNw5PHU,4612
|
|
382
395
|
zrb/util/git_subtree_model.py,sha256=P_gJ0zhOAc3gFM6sYcjc0Ack9dFBt75TI5fXdE0q320,871
|
383
396
|
zrb/util/group.py,sha256=T82yr3qg9I5k10VPXkMyrIRIqyfzadSH813bqzwKEPI,4718
|
384
397
|
zrb/util/init_path.py,sha256=9eN7CkWNGhDBpjTQs2j9YHVMzui7Y8DEb1WP4aTPzeo,659
|
385
|
-
zrb/util/llm/prompt.py,sha256=
|
398
|
+
zrb/util/llm/prompt.py,sha256=AqDcBi2IkPISCVNZ_Ccz9Q2zFHjowPMReGHZtNndD_k,1921
|
386
399
|
zrb/util/load.py,sha256=DK0KYSlu48HCoGPqnW1IxnE3pHrZSPCstfz8Fjyqqv8,2140
|
387
400
|
zrb/util/run.py,sha256=vu-mcSWDP_WuuvIKqM_--Gk3WkABO1oTXiHmBRTvVQk,546
|
388
401
|
zrb/util/string/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -393,7 +406,7 @@ zrb/util/todo.py,sha256=r9_KYF2-hLKMNjsp6AFK9zivykMrywd-kJ4bCwfdafI,19323
|
|
393
406
|
zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
|
394
407
|
zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
395
408
|
zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
|
396
|
-
zrb-1.
|
397
|
-
zrb-1.
|
398
|
-
zrb-1.
|
399
|
-
zrb-1.
|
409
|
+
zrb-1.12.0.dist-info/METADATA,sha256=ZegE-xKhBfEIGj-PXDaNKUmoQsJgWYR6_4E0V4-2Awk,9778
|
410
|
+
zrb-1.12.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
411
|
+
zrb-1.12.0.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
|
412
|
+
zrb-1.12.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|