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.
Files changed (33) hide show
  1. zrb/builtin/llm/chat_session.py +42 -14
  2. zrb/builtin/llm/llm_ask.py +11 -0
  3. zrb/builtin/llm/tool/file.py +2 -2
  4. zrb/config/config.py +31 -80
  5. zrb/config/default_prompt/file_extractor_system_prompt.md +12 -0
  6. zrb/config/default_prompt/interactive_system_prompt.md +31 -0
  7. zrb/config/default_prompt/persona.md +1 -0
  8. zrb/config/default_prompt/repo_extractor_system_prompt.md +112 -0
  9. zrb/config/default_prompt/repo_summarizer_system_prompt.md +10 -0
  10. zrb/config/default_prompt/summarization_prompt.md +42 -0
  11. zrb/config/default_prompt/system_prompt.md +28 -0
  12. zrb/config/llm_config.py +89 -279
  13. zrb/config/llm_context/config.py +74 -0
  14. zrb/config/llm_context/config_handler.py +238 -0
  15. zrb/context/any_shared_context.py +10 -0
  16. zrb/context/context.py +8 -0
  17. zrb/context/shared_context.py +9 -0
  18. zrb/runner/web_route/task_session_api_route.py +1 -1
  19. zrb/task/llm/agent.py +2 -2
  20. zrb/task/llm/conversation_history_model.py +78 -226
  21. zrb/task/llm/default_workflow/coding.md +24 -0
  22. zrb/task/llm/default_workflow/copywriting.md +17 -0
  23. zrb/task/llm/default_workflow/researching.md +18 -0
  24. zrb/task/llm/history_summarization.py +6 -6
  25. zrb/task/llm/prompt.py +92 -41
  26. zrb/task/llm/tool_wrapper.py +20 -14
  27. zrb/task/llm_task.py +19 -23
  28. zrb/util/callable.py +23 -0
  29. zrb/util/llm/prompt.py +42 -6
  30. {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/METADATA +2 -2
  31. {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/RECORD +33 -20
  32. {zrb-1.10.2.dist-info → zrb-1.12.0.dist-info}/WHEEL +0 -0
  33. {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=False,
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=False,
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=False,
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(ctx, system_prompt_attr)
74
- special_instruction = get_special_instruction_prompt(
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", 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]*/)([^,\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
- print("RESOURCE PATH", resource_path)
199
+ content = ""
200
+ ref_type = ""
134
201
  if os.path.isfile(resource_path):
135
202
  content = read_file_with_line_numbers(resource_path)
136
- apendixes.append(
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"`{ref}` (directory path: `{resource_path}`)",
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=False,
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
@@ -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
- # Call the original function.
99
- # pydantic-ai is responsible for injecting RunContext if takes_ctx is True.
100
- # Our wrapper injects AnyContext if needed.
101
- # The arguments received by the wrapper (*args, **kwargs) are those
102
- # provided by the LLM, potentially with RunContext already injected by
103
- # pydantic-ai if takes_ctx is True. We just need to ensure AnyContext
104
- # is injected if required by the original function.
105
- # The dummy argument handling is moved to _adjust_signature's logic
106
- # for schema generation, it's not needed here before calling the actual
107
- # function.
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._should_enrich_context = enrich_context
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
- def demote(match):
6
- hashes = match.group(1)
7
- return "#" + hashes + match.group(2) # add one `#`
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
- # Replace headers at the beginning of a line
10
- return re.sub(r"^(#{1,6})(\s)", demote, md, flags=re.MULTILINE)
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
- return f"# {header}\n````\n{content.strip()}\n````\n"
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.10.2
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.3.4,<0.4.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=0R04DpBr_LGfNJbXIQ_4XQSxL7kY2M3U-bbu5lsXZ54,8542
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=oozfQwa1i2PnXV4qWbn60Pmd3fS0kgmhYCbfKlhr25o,4549
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=vUpkHPJHszdFKWjsh5Ma8_WGFwZMcm1nlJ-rGCIA_tI,22290
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=vUmJWQHRgmMVL1FmoukjQe9J6vCuyzqBIg4FncREGmw,15108
221
- zrb/config/llm_config.py,sha256=2UO6a9DxN-WkZ6rwZX8hNEhK_UZjjDeCNCFvADbQaws,20814
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=p1i9af_CUDz5Mf1h1kBZMAa2AEhf17I3O5IgAcjRLoY,1768
230
- zrb/context/context.py,sha256=3p43LTTqkcaX00wSBwLFVTyoEdlPnMYMASqnXUxdU_U,6706
231
- zrb/context/shared_context.py,sha256=TMQF_KH5rw5M2ybjJBSMI872yRPExJWfr0mIe1sRjE8,2808
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=4U6VDzQ3rU_VjhH3LZ3NAy0fQjHFjohyZLlYpxmckxo,6258
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=BZHbz-YXgSdm1tTwGMR_maqcd3yMFGSdzLyDjuxT_XI,6702
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=xsKo2QtAie_1f7rVIQ6ruaClv1NdkhLbK4Fa9WUGtnM,16379
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=vY2_iLULgSNTaqW1xJqOhI8oOH3vNEsZn_yNcx6jYX8,8104
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=TZYoI5MXddhHYnlVNsIjRLdhGwA9eEBVSeGz_Wi31JQ,7833
348
- zrb/task/llm/tool_wrapper.py,sha256=8_bL8m_WpRf-pVKSrvQIVqT-m2sUA87a1RBQG13lhp4,6457
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=TTYb9FYqZX_OIgDE6q5Z9IVuM6NcsKFeCVIi6ovQDE8,13712
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=tJEGV2X7v13b1PXUzRXzu1e1HnY6d9JLtqbUGiZqHoo,573
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.10.2.dist-info/METADATA,sha256=0UVwKjfnakRF8kPcW_37rGPJ5sEZwCcEywEOrg5l_D4,9778
397
- zrb-1.10.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
398
- zrb-1.10.2.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
399
- zrb-1.10.2.dist-info/RECORD,,
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