zrb 1.11.0__py3-none-any.whl → 1.13.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/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,
@@ -33,9 +31,9 @@ from zrb.xcom.xcom import Xcom
33
31
 
34
32
  if TYPE_CHECKING:
35
33
  from pydantic_ai import Agent, Tool
36
- from pydantic_ai.mcp import MCPServer
37
34
  from pydantic_ai.models import Model
38
35
  from pydantic_ai.settings import ModelSettings
36
+ from pydantic_ai.toolsets import AbstractToolset
39
37
 
40
38
  ToolOrCallable = Tool | Callable
41
39
  else:
@@ -65,22 +63,21 @@ 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"]]
81
78
  ) = [],
82
- mcp_servers: (
83
- list["MCPServer"] | Callable[[AnySharedContext], list["MCPServer"]]
79
+ toolsets: (
80
+ list["AbstractToolset[Agent]"] | Callable[[AnySharedContext], list["Tool"]]
84
81
  ) = [],
85
82
  conversation_history: (
86
83
  ConversationHistory
@@ -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,24 +148,22 @@ 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"] = []
169
- self._mcp_servers = mcp_servers
170
- self._additional_mcp_servers: list["MCPServer"] = []
165
+ self._toolsets = toolsets
166
+ self._additional_toolsets: list["AbstractToolset[Agent]"] = []
171
167
  self._conversation_history = conversation_history
172
168
  self._conversation_history_reader = conversation_history_reader
173
169
  self._conversation_history_writer = conversation_history_writer
@@ -191,18 +187,12 @@ class LLMTask(BaseTask):
191
187
  for single_tool in tool:
192
188
  self._additional_tools.append(single_tool)
193
189
 
194
- def add_mcp_server(self, *mcp_server: "MCPServer"):
195
- self.append_mcp_server(*mcp_server)
196
-
197
- def append_mcp_server(self, *mcp_server: "MCPServer"):
198
- for single_mcp_server in mcp_server:
199
- self._additional_mcp_servers.append(single_mcp_server)
200
-
201
- def set_should_enrich_context(self, enrich_context: bool):
202
- self._should_enrich_context = enrich_context
190
+ def add_toolset(self, *toolset: "AbstractToolset[Agent]"):
191
+ self.append_toolset(*toolset)
203
192
 
204
- def set_context_enrichment_token_threshold(self, enrichment_token_threshold: int):
205
- self._context_enrichment_token_threshold = enrichment_token_threshold
193
+ def append_toolset(self, *toolset: "AbstractToolset[Agent]"):
194
+ for single_toolset in toolset:
195
+ self._additional_toolsets.append(single_toolset)
206
196
 
207
197
  def set_should_summarize_history(self, summarize_history: bool):
208
198
  self._should_summarize_history = summarize_history
@@ -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
@@ -256,8 +252,8 @@ class LLMTask(BaseTask):
256
252
  model_settings=model_settings,
257
253
  tools_attr=self._tools,
258
254
  additional_tools=self._additional_tools,
259
- mcp_servers_attr=self._mcp_servers,
260
- additional_mcp_servers=self._additional_mcp_servers,
255
+ toolsets_attr=self._toolsets,
256
+ additional_toolsets=self._additional_toolsets,
261
257
  )
262
258
  # 4. Run the agent iteration and save the results/history
263
259
  result = await self._execute_agent(
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
@@ -1,7 +1,7 @@
1
1
  import re
2
2
 
3
3
 
4
- def _demote_markdown_headers(md: str) -> str:
4
+ def _adjust_markdown_headers(md: str, level_change: int) -> str:
5
5
  lines = md.split("\n")
6
6
  new_lines = []
7
7
  fence_stack = []
@@ -11,7 +11,6 @@ def _demote_markdown_headers(md: str) -> str:
11
11
 
12
12
  if fence_match:
13
13
  current_fence = fence_match.group(1)
14
- # If stack is not empty and we found a closing fence
15
14
  if (
16
15
  fence_stack
17
16
  and fence_stack[-1][0] == current_fence[0]
@@ -21,18 +20,28 @@ def _demote_markdown_headers(md: str) -> str:
21
20
  else:
22
21
  fence_stack.append(current_fence)
23
22
  new_lines.append(line)
23
+ elif fence_stack:
24
+ new_lines.append(line)
24
25
  else:
25
- if fence_stack: # If we are inside a code block
26
- new_lines.append(line)
26
+ match = re.match(r"^(#{1,6})(\s)", line)
27
+ if match:
28
+ current_level = len(match.group(1))
29
+ new_level = max(1, current_level + level_change)
30
+ new_header = "#" * new_level + line[current_level:]
31
+ new_lines.append(new_header)
27
32
  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
+ new_lines.append(line)
33
34
  return "\n".join(new_lines)
34
35
 
35
36
 
37
+ def demote_markdown_headers(md: str) -> str:
38
+ return _adjust_markdown_headers(md, level_change=1)
39
+
40
+
41
+ def promote_markdown_headers(md: str) -> str:
42
+ return _adjust_markdown_headers(md, level_change=-1)
43
+
44
+
36
45
  def make_prompt_section(header: str, content: str, as_code: bool = False) -> str:
37
46
  if content.strip() == "":
38
47
  return ""
@@ -51,4 +60,4 @@ def make_prompt_section(header: str, content: str, as_code: bool = False) -> str
51
60
  fence_len = longest_backtick_sequence + 1
52
61
  fence = "`" * fence_len
53
62
  return f"# {header}\n{fence}\n{content.strip()}\n{fence}\n"
54
- return f"# {header}\n{_demote_markdown_headers(content.strip())}\n"
63
+ 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.11.0
3
+ Version: 1.13.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
@@ -19,7 +19,7 @@ Provides-Extra: rag
19
19
  Requires-Dist: beautifulsoup4 (>=4.13.3,<5.0.0)
20
20
  Requires-Dist: black (>=25.1.0,<25.2.0)
21
21
  Requires-Dist: chromadb (>=0.6.3,<0.7.0) ; extra == "rag" or extra == "all"
22
- Requires-Dist: fastapi[standard] (>=0.115.14,<0.116.0)
22
+ Requires-Dist: fastapi[standard] (>=0.116.1,<0.117.0)
23
23
  Requires-Dist: isort (>=6.0.1,<6.1.0)
24
24
  Requires-Dist: libcst (>=1.7.0,<2.0.0)
25
25
  Requires-Dist: openai (>=1.86.0,<2.0.0) ; extra == "rag" or extra == "all"
@@ -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.4.4,<0.5.0)
30
+ Requires-Dist: pydantic-ai (>=0.4.5,<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,10 +9,10 @@ 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=8Ux5xfgNsmTVFS9XVfSjz_AVkl3jn5gYY-P2m5ZAB5c,8466
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
@@ -20,7 +20,7 @@ zrb/builtin/llm/tool/cli.py,sha256=dUWZrW2X5J_lONuzR__6-SbewSdi28E3RRuksjd4mWo,1
20
20
  zrb/builtin/llm/tool/code.py,sha256=GRP_IZAkeL6RIlUm407BQRF992ES57pdzPaQdC5UsJU,8218
21
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
- zrb/builtin/llm/tool/sub_agent.py,sha256=UWBLiuCK6FT8Ku0yPfSxd_k67h_Pme1K7d2VSABacjQ,4855
23
+ zrb/builtin/llm/tool/sub_agent.py,sha256=9Su64FpNTVeE6O2qgNzo-eo4pcmv8qi_sd_QWLQBXYw,4870
24
24
  zrb/builtin/llm/tool/web.py,sha256=gQlUsmYCJOFJtNjwpjK-xk13LMvrMSpSaFHXUTnIayQ,7090
25
25
  zrb/builtin/md5.py,sha256=690RV2LbW7wQeTFxY-lmmqTSVEEZv3XZbjEUW1Q3XpE,1480
26
26
  zrb/builtin/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -217,20 +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=srXMnSQ9rjIYnX4lG1QmvIM0cM-oa92n4zqMnD6E_FM,12187
220
+ zrb/config/config.py,sha256=d_F-hdPLADjeVRHtnpOxtOkfUBu5huSLclyD53uxO4U,12306
221
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
222
+ zrb/config/default_prompt/interactive_system_prompt.md,sha256=ZFPeDEV2vlcksHiVG2o-TCehmqkFolDjtH0_Fzo1gGI,3566
223
223
  zrb/config/default_prompt/persona.md,sha256=WU4JKp-p7qJePDA6NZ_CYdBggo2B3PEq8IEnNVblIHU,41
224
224
  zrb/config/default_prompt/repo_extractor_system_prompt.md,sha256=EGZ-zj78RlMEg2jduRBs8WzO4VJTkXHR96IpBepZMsY,3881
225
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/default_workflow/code.md,sha256=aFVBjPwVcpRkWGHKdwDEYvRxfIQyLWaVYfleO68Aj8s,2533
229
- zrb/config/default_workflow/content.md,sha256=Kz_ufOApkPXhhr2R8eyt06b8gVegLc5LDkBlsN6BYV4,2057
230
- zrb/config/default_workflow/research.md,sha256=CSWP-uh3a-pI9DF9sJkzODWESD2wLCGhr1nrMfUjAlw,2205
231
- zrb/config/llm_config.py,sha256=ET2ehVdQQv-dOTXIVx81sdvybF9rprL6VUhAxE6K-Ho,9215
232
- zrb/config/llm_context/config.py,sha256=swc3hUaEIoL2MjKtbati13iP0MxveNG_y_6K3nszRAw,2571
233
- zrb/config/llm_context/config_handler.py,sha256=oQesfigIM0qMw_A3jUCN0UDJujRjuJ3jr5mXHBiLgB0,8866
226
+ zrb/config/default_prompt/summarization_prompt.md,sha256=hRXH5E78TugSze_Hgp-KTbIhCeyrMcJg-pSXvXH3C9E,1629
227
+ zrb/config/default_prompt/system_prompt.md,sha256=Jkne5n9HJcBCgfeENwxvqH-kbDO2CaiUzqR4VoWMRHY,3054
228
+ zrb/config/llm_config.py,sha256=bNLxorctwtVW1F9hA-hEYpDBe7FLSZHC25Nx8NlR4-M,8597
229
+ zrb/config/llm_context/config.py,sha256=zeqSVOKK5yyApvqTbcO3ayGxtyoag22qlWWaXp1nINs,4950
230
+ zrb/config/llm_context/config_parser.py,sha256=h95FbOjvVobhrsfGtG_BY3hxS-OLzQj-9F5vGZuehkY,1473
234
231
  zrb/config/llm_rate_limitter.py,sha256=P4vR7qxwiGwjlKx2kHcfdIxwGbJB98vdN-UQEH-Q2WU,4894
235
232
  zrb/config/web_auth_config.py,sha256=_PXatQTYh2mX9H3HSYSQKp13zm1RlLyVIoeIr6KYMQ8,6279
236
233
  zrb/content_transformer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -240,7 +237,7 @@ zrb/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
240
237
  zrb/context/any_context.py,sha256=2hgVKbbDwmwrEl1h1L1FaTUjuUYaDd_b7YRGkaorW6Q,6362
241
238
  zrb/context/any_shared_context.py,sha256=wJawL1jGgApcKPRcpw3js7W4-MhJRA3GMbR5zTsJmt0,1929
242
239
  zrb/context/context.py,sha256=ErGhXJgjgNaAqi6iPMejWxFZ3YvWnysC6mHEU-wodKk,6884
243
- zrb/context/shared_context.py,sha256=Pn0LHEYikiB3LLGnfpJVzOFgxyosQ_NYvFtKFMK_X8w,3008
240
+ zrb/context/shared_context.py,sha256=Jaa7AYCeCksOiEAwOnY3xD6Y2Yy2wJAkpehAkbKQ-Wc,3076
244
241
  zrb/dot_dict/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
245
242
  zrb/dot_dict/dot_dict.py,sha256=ubw_x8I7AOJ59xxtFVJ00VGmq_IYdZP3mUhNlO4nEK0,556
246
243
  zrb/env/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -349,17 +346,20 @@ zrb/task/base_trigger.py,sha256=WSGcmBcGAZw8EzUXfmCjqJQkz8GEmi1RzogpF6A1V4s,6902
349
346
  zrb/task/cmd_task.py,sha256=myM8WZm6NrUD-Wv0Vb5sTOrutrAVZLt5LVsSBKwX6SM,10860
350
347
  zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
351
348
  zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
352
- zrb/task/llm/agent.py,sha256=A5UoHY-l8WqyptKrf42eHVW_VhMhuYsygs2Z8XNnCzk,6681
349
+ zrb/task/llm/agent.py,sha256=aZvtcL5HmZQvD3c79R9sDIOMawO0rUMcRiq2wZ1FNas,7457
353
350
  zrb/task/llm/config.py,sha256=TlyH925_fboIlK2Ixf34tynmenqs9s9rfsnPs4jff78,3490
354
351
  zrb/task/llm/conversation_history.py,sha256=B_PDWYL_q66s0xwWBzMSomqPN6u3gkXlIeXBD5A0Apg,4416
355
- zrb/task/llm/conversation_history_model.py,sha256=AU5-M4Ky3X4wII1PMT75VU5OUEG0FjqdHrrpCSl-u6M,10771
352
+ zrb/task/llm/conversation_history_model.py,sha256=DJ0KDBB0BriQuE5ugC_q0aSHhjNIBcfjUk1f0S_3I9U,9245
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
356
356
  zrb/task/llm/error.py,sha256=QR-nIohS6pBpC_16cWR-fw7Mevo1sNYAiXMBsh_CJDE,4157
357
- zrb/task/llm/history_summarization.py,sha256=BUwBOS51Jzp4psliD_h1jWq-5oHezNbjF1fkn7vbh7o,8109
358
- zrb/task/llm/print_node.py,sha256=zocTKi9gZDxl2I6KNu095TmMc13Yip6SNuWYnswS680,4060
359
- zrb/task/llm/prompt.py,sha256=OXoN3ttXmQR8DxoJnKh3vgjaVzl532i3Y567xaXdQs8,7708
360
- zrb/task/llm/tool_wrapper.py,sha256=8_bL8m_WpRf-pVKSrvQIVqT-m2sUA87a1RBQG13lhp4,6457
357
+ zrb/task/llm/history_summarization.py,sha256=_0RmzIeJdJA3KvtdTdKnd2Ga7_7x8C1J2PM0oSn-IYw,8000
358
+ zrb/task/llm/print_node.py,sha256=mwdqsO2IVf5rDz-jdH9HXz6MFGCWrZ4Pv2xbUBtoNgc,4179
359
+ zrb/task/llm/prompt.py,sha256=sMipP-NJmq4ZmCtQYEG2mcHWUD79yJRwH7nH-iw-7Z4,9661
360
+ zrb/task/llm/tool_wrapper.py,sha256=jfKMAtTzm--HnF6TppOrbkDVsuTOIFRpowQqgwqd-7s,6756
361
361
  zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
362
- zrb/task/llm_task.py,sha256=TTYb9FYqZX_OIgDE6q5Z9IVuM6NcsKFeCVIi6ovQDE8,13712
362
+ zrb/task/llm_task.py,sha256=jVuVeN2ylcPkycUiaTpavkkd1tBLbvcVsjMNN5FMHnk,13536
363
363
  zrb/task/make_task.py,sha256=PD3b_aYazthS8LHeJsLAhwKDEgdurQZpymJDKeN60u0,2265
364
364
  zrb/task/rsync_task.py,sha256=WfqNSaicJgYWpunNU34eYxXDqHDHOftuDHyWJKjqwg0,6365
365
365
  zrb/task/scaffolder.py,sha256=rME18w1HJUHXgi9eTYXx_T2G4JdqDYzBoNOkdOOo5-o,6806
@@ -370,6 +370,7 @@ zrb/task_status/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
370
370
  zrb/task_status/task_status.py,sha256=blZ8dxg9g_8MuViq-t7yJRLoE7yGUf5srgHf-PCsXNc,3069
371
371
  zrb/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
372
372
  zrb/util/attr.py,sha256=5GlYSmVAzbcSFjNDXiqqHqNMR6NWjJ6bUHZXdE35mj8,5359
373
+ zrb/util/callable.py,sha256=b6OFXbCXp2twow3wh2E_h5hNHLs2pXaLfGQz4iVyiQc,771
373
374
  zrb/util/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
374
375
  zrb/util/cli/style.py,sha256=D_548KG1gXEirQGdkAVTc81vBdCeInXtnG1gV1yabBA,6655
375
376
  zrb/util/cli/subcommand.py,sha256=umTZIlrL-9g-qc_eRRgdaQgK-whvXK1roFfvnbuY7NQ,1753
@@ -394,7 +395,7 @@ zrb/util/git_subtree.py,sha256=AyQWCWEi2EIzEpYXRnYN55157KMUql0WHj70QNw5PHU,4612
394
395
  zrb/util/git_subtree_model.py,sha256=P_gJ0zhOAc3gFM6sYcjc0Ack9dFBt75TI5fXdE0q320,871
395
396
  zrb/util/group.py,sha256=T82yr3qg9I5k10VPXkMyrIRIqyfzadSH813bqzwKEPI,4718
396
397
  zrb/util/init_path.py,sha256=9eN7CkWNGhDBpjTQs2j9YHVMzui7Y8DEb1WP4aTPzeo,659
397
- zrb/util/llm/prompt.py,sha256=AqDcBi2IkPISCVNZ_Ccz9Q2zFHjowPMReGHZtNndD_k,1921
398
+ zrb/util/llm/prompt.py,sha256=HMpKby27DE8lJWpytYKylp7Iw9ENwsYQI0nMMKCCi54,2190
398
399
  zrb/util/load.py,sha256=DK0KYSlu48HCoGPqnW1IxnE3pHrZSPCstfz8Fjyqqv8,2140
399
400
  zrb/util/run.py,sha256=vu-mcSWDP_WuuvIKqM_--Gk3WkABO1oTXiHmBRTvVQk,546
400
401
  zrb/util/string/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -405,7 +406,7 @@ zrb/util/todo.py,sha256=r9_KYF2-hLKMNjsp6AFK9zivykMrywd-kJ4bCwfdafI,19323
405
406
  zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
406
407
  zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
407
408
  zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
408
- zrb-1.11.0.dist-info/METADATA,sha256=pcdwF1Z7DIfcE3qZxe7vnYY1gbNRKjiKaLdw9oSbkuA,9778
409
- zrb-1.11.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
410
- zrb-1.11.0.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
411
- zrb-1.11.0.dist-info/RECORD,,
409
+ zrb-1.13.0.dist-info/METADATA,sha256=L9X70AkHGlYTjK0tdgjlwA_Sj4U0Ao9FtPuk-g0UmJ0,9777
410
+ zrb-1.13.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
411
+ zrb-1.13.0.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
412
+ zrb-1.13.0.dist-info/RECORD,,
@@ -1,238 +0,0 @@
1
- import os
2
- import re
3
- from typing import Callable, Generator, NamedTuple
4
-
5
-
6
- class Section(NamedTuple):
7
- name: str
8
- key: str
9
- content: str
10
- config_file: str
11
-
12
-
13
- def _parse_config_file(
14
- config_file: str, lines: list[str]
15
- ) -> Generator[Section, None, None]:
16
- """
17
- Parses a config file's lines, yielding sections.
18
- It correctly handles markdown code fences.
19
- """
20
- any_header_pattern = re.compile(r"^# (\w+):\s*(.*)")
21
- fence_pattern = re.compile(r"^([`~]{3,})")
22
- fence_stack = []
23
- active_section_name = None
24
- active_section_key = None
25
- active_section_content = []
26
-
27
- for line in lines:
28
- stripped_line = line.strip()
29
- fence_match = fence_pattern.match(stripped_line)
30
-
31
- if fence_match:
32
- current_fence = fence_match.group(1)
33
- if (
34
- fence_stack
35
- and fence_stack[-1][0] == current_fence[0]
36
- and len(current_fence) >= len(fence_stack[-1])
37
- ):
38
- fence_stack.pop()
39
- else:
40
- fence_stack.append(current_fence)
41
-
42
- if fence_stack:
43
- if active_section_key is not None:
44
- active_section_content.append(line)
45
- continue
46
-
47
- match = any_header_pattern.match(line)
48
- if match:
49
- if active_section_key is not None:
50
- content = "".join(active_section_content).strip()
51
- if content:
52
- yield Section(
53
- name=active_section_name,
54
- key=active_section_key,
55
- content=content,
56
- config_file=config_file,
57
- )
58
-
59
- active_section_name = match.group(1)
60
- active_section_key = match.group(2).strip()
61
- active_section_content = []
62
- elif active_section_key is not None:
63
- active_section_content.append(line)
64
-
65
- if active_section_key is not None:
66
- content = "".join(active_section_content).strip()
67
- if content:
68
- yield Section(
69
- name=active_section_name,
70
- key=active_section_key,
71
- content=content,
72
- config_file=config_file,
73
- )
74
-
75
-
76
- def _get_config_file_hierarchy(path: str, config_file_name: str) -> list[str]:
77
- """Finds all config files from a given path up to the home directory."""
78
- config_files = []
79
- home_dir = os.path.expanduser("~")
80
- current_path = os.path.abspath(path)
81
- while True:
82
- config_path = os.path.join(current_path, config_file_name)
83
- if os.path.exists(config_path):
84
- config_files.append(config_path)
85
- if current_path == home_dir:
86
- break
87
- parent = os.path.dirname(current_path)
88
- if parent == current_path: # Reached root
89
- break
90
- current_path = parent
91
- return config_files
92
-
93
-
94
- class LLMContextConfigHandler:
95
- """Handles the logic for a specific section of the config."""
96
-
97
- def __init__(
98
- self,
99
- section_name: str,
100
- config_file_name: str = "ZRB.md",
101
- filter_section_func: Callable[[str, str], bool] | None = None,
102
- resolve_section_path: bool = True,
103
- ):
104
- self._section_name = section_name
105
- self._config_file_name = config_file_name
106
- self._filter_func = filter_section_func
107
- self._resolve_section_path = resolve_section_path
108
-
109
- def _include_section(self, section_path: str, base_path: str) -> bool:
110
- if self._filter_func:
111
- return self._filter_func(section_path, base_path)
112
- return True
113
-
114
- def get_section(self, cwd: str) -> dict[str, str]:
115
- """Gathers all relevant sections for a given path."""
116
- abs_path = os.path.abspath(cwd)
117
- all_sections = {}
118
- config_files = _get_config_file_hierarchy(abs_path, self._config_file_name)
119
-
120
- for config_file in reversed(config_files):
121
- if not os.path.exists(config_file):
122
- continue
123
- with open(config_file, "r") as f:
124
- lines = f.readlines()
125
-
126
- for section in _parse_config_file(config_file, lines):
127
- if section.name != self._section_name:
128
- continue
129
-
130
- config_dir = os.path.dirname(section.config_file)
131
- key = (
132
- os.path.abspath(os.path.join(config_dir, section.key))
133
- if self._resolve_section_path
134
- else section.key
135
- )
136
-
137
- if self._include_section(key, abs_path):
138
- if key in all_sections:
139
- all_sections[key] = f"{all_sections[key]}\n{section.content}"
140
- else:
141
- all_sections[key] = section.content
142
-
143
- return all_sections
144
-
145
- def add_to_section(self, content: str, key: str, cwd: str):
146
- """Adds content to a section block in the nearest configuration file."""
147
- abs_search_path = os.path.abspath(cwd)
148
- config_files = _get_config_file_hierarchy(
149
- abs_search_path, self._config_file_name
150
- )
151
- closest_config_file = (
152
- config_files[0]
153
- if config_files
154
- else os.path.join(os.path.expanduser("~"), self._config_file_name)
155
- )
156
-
157
- config_dir = os.path.dirname(closest_config_file)
158
- header_key = key
159
- if self._resolve_section_path and os.path.isabs(key):
160
- if key == config_dir:
161
- header_key = "."
162
- elif key.startswith(config_dir):
163
- header_key = f"./{os.path.relpath(key, config_dir)}"
164
- header = f"# {self._section_name}: {header_key}"
165
- new_content = content.strip()
166
- lines = []
167
- if os.path.exists(closest_config_file):
168
- with open(closest_config_file, "r") as f:
169
- lines = f.readlines()
170
- header_index = next(
171
- (i for i, line in enumerate(lines) if line.strip() == header), -1
172
- )
173
- if header_index != -1:
174
- insert_index = len(lines)
175
- for i in range(header_index + 1, len(lines)):
176
- if re.match(r"^# \w+:", lines[i].strip()):
177
- insert_index = i
178
- break
179
- if insert_index > 0 and lines[insert_index - 1].strip():
180
- lines.insert(insert_index, f"\n{new_content}\n")
181
- else:
182
- lines.insert(insert_index, f"{new_content}\n")
183
- else:
184
- if lines and lines[-1].strip():
185
- lines.append("\n\n")
186
- lines.append(f"{header}\n")
187
- lines.append(f"{new_content}\n")
188
- with open(closest_config_file, "w") as f:
189
- f.writelines(lines)
190
-
191
- def remove_from_section(self, content: str, key: str, cwd: str) -> bool:
192
- """Removes content from a section block in all relevant config files."""
193
- abs_search_path = os.path.abspath(cwd)
194
- config_files = _get_config_file_hierarchy(
195
- abs_search_path, self._config_file_name
196
- )
197
- content_to_remove = content.strip()
198
- was_removed = False
199
- for config_file_path in config_files:
200
- if not os.path.exists(config_file_path):
201
- continue
202
- with open(config_file_path, "r") as f:
203
- file_content = f.read()
204
- config_dir = os.path.dirname(config_file_path)
205
- header_key = key
206
- if self._resolve_section_path and os.path.isabs(key):
207
- if key == config_dir:
208
- header_key = "."
209
- elif key.startswith(config_dir):
210
- header_key = f"./{os.path.relpath(key, config_dir)}"
211
- header = f"# {self._section_name}: {header_key}"
212
- # Use regex to find the section content
213
- section_pattern = re.compile(
214
- rf"^{re.escape(header)}\n(.*?)(?=\n# \w+:|\Z)",
215
- re.DOTALL | re.MULTILINE,
216
- )
217
- match = section_pattern.search(file_content)
218
- if not match:
219
- continue
220
-
221
- section_content = match.group(1)
222
- # Remove the target content and handle surrounding newlines
223
- new_section_content = section_content.replace(content_to_remove, "")
224
- new_section_content = "\n".join(
225
- line for line in new_section_content.splitlines() if line.strip()
226
- )
227
-
228
- if new_section_content != section_content.strip():
229
- was_removed = True
230
- # Reconstruct the file content
231
- start = match.start(1)
232
- end = match.end(1)
233
- new_file_content = (
234
- file_content[:start] + new_section_content + file_content[end:]
235
- )
236
- with open(config_file_path, "w") as f:
237
- f.write(new_file_content)
238
- return was_removed
File without changes