zrb 1.14.5__py3-none-any.whl → 1.14.7__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.
@@ -15,8 +15,6 @@ if TYPE_CHECKING:
15
15
  from pydantic_ai.toolsets import AbstractToolset
16
16
 
17
17
  ToolOrCallable = Tool | Callable
18
- else:
19
- ToolOrCallable = Any
20
18
 
21
19
 
22
20
  def create_sub_agent_tool(
@@ -25,7 +23,7 @@ def create_sub_agent_tool(
25
23
  system_prompt: str | None = None,
26
24
  model: "str | Model | None" = None,
27
25
  model_settings: "ModelSettings | None" = None,
28
- tools: list[ToolOrCallable] = [],
26
+ tools: "list[ToolOrCallable]" = [],
29
27
  toolsets: list["AbstractToolset[Agent]"] = [],
30
28
  is_yolo_mode: bool | None = None,
31
29
  ) -> Callable[[AnyContext, str], Coroutine[Any, Any, str]]:
@@ -97,7 +95,8 @@ def create_sub_agent_tool(
97
95
  ctx=ctx,
98
96
  agent=sub_agent_agent,
99
97
  user_prompt=query,
100
- history_list=[], # Start with empty history for the sub-agent
98
+ attachments=[],
99
+ history_list=[],
101
100
  )
102
101
 
103
102
  # Return the sub-agent's final message content
@@ -1,64 +1,23 @@
1
1
  import json
2
2
  from collections.abc import Callable
3
+ from urllib.parse import urljoin
3
4
 
4
5
 
5
6
  async def open_web_page(url: str) -> str:
6
7
  """
7
- Fetches and parses the textual content of a given web page URL.
8
+ Fetches, parses, and converts the content of a web page to Markdown.
8
9
 
9
- Use this tool to "read" a web page. It strips away HTML tags, scripts, and other non-textual elements to provide the clean text content. It also extracts any hyperlinks found on the page. This is useful when you need to understand the content of a specific URL that you have discovered through a search or from another source.
10
+ This tool "reads" a web page by fetching its content, stripping away non-essential elements like scripts and styles, and then converting the cleaned HTML into Markdown format. This preserves the semantic structure of the content (headings, lists, etc.) while removing clutter. It also extracts all hyperlinks and resolves them to absolute URLs.
10
11
 
11
12
  Args:
12
13
  url (str): The full URL of the web page to open (e.g., "https://example.com/article").
13
14
 
14
15
  Returns:
15
- str: A JSON object containing the cleaned text `content` of the page and a list of `links_on_page`.
16
+ str: A JSON object containing the page's content in Markdown format and a list of all absolute links found on the page.
16
17
  """
17
-
18
- async def get_page_content(page_url: str):
19
- user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
20
- try:
21
- from playwright.async_api import async_playwright
22
-
23
- async with async_playwright() as p:
24
- browser = await p.chromium.launch(headless=True)
25
- page = await browser.new_page()
26
- await page.set_extra_http_headers({"User-Agent": user_agent})
27
- try:
28
- # Navigate to the URL with a timeout of 30 seconds
29
- await page.goto(page_url, wait_until="networkidle", timeout=30000)
30
- # Wait for the content to load
31
- await page.wait_for_load_state("domcontentloaded")
32
- # Get the page content
33
- content = await page.content()
34
- # Extract all links from the page
35
- links = await page.eval_on_selector_all(
36
- "a[href]",
37
- """
38
- (elements) => elements.map(el => {
39
- const href = el.getAttribute('href');
40
- if (href && !href.startsWith('#') && !href.startsWith('/')) {
41
- return href;
42
- }
43
- return null;
44
- }).filter(href => href !== null)
45
- """,
46
- )
47
- return {"content": content, "links_on_page": links}
48
- finally:
49
- await browser.close()
50
- except BaseException:
51
- import requests
52
-
53
- response = requests.get(url, headers={"User-Agent": user_agent})
54
- if response.status_code != 200:
55
- msg = f"Unable to retrieve search results. Status code: {response.status_code}"
56
- raise Exception(msg)
57
- return {"content": response.text, "links_on_page": []}
58
-
59
- result = await get_page_content(url)
60
- # Parse the HTML content
61
- return json.dumps(parse_html_text(result["content"]))
18
+ html_content, links = await _fetch_page_content(url)
19
+ markdown_content = _convert_html_to_markdown(html_content)
20
+ return json.dumps({"content": markdown_content, "links_on_page": links})
62
21
 
63
22
 
64
23
  def create_search_internet_tool(serp_api_key: str) -> Callable[[str, int], str]:
@@ -85,14 +44,14 @@ def create_search_internet_tool(serp_api_key: str) -> Callable[[str, int], str]:
85
44
  num_results (int, optional): The desired number of search results. Defaults to 10.
86
45
 
87
46
  Returns:
88
- str: A JSON object containing the parsed text content from the search results page.
47
+ str: A formatted string summarizing the search results, including titles, links, and snippets.
89
48
  """
90
49
  import requests
91
50
 
92
51
  response = requests.get(
93
52
  "https://serpapi.com/search",
94
53
  headers={
95
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" # noqa
54
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
96
55
  },
97
56
  params={
98
57
  "q": query,
@@ -104,9 +63,9 @@ def create_search_internet_tool(serp_api_key: str) -> Callable[[str, int], str]:
104
63
  )
105
64
  if response.status_code != 200:
106
65
  raise Exception(
107
- f"Error: Unable to retrieve search results (status code: {response.status_code})" # noqa
66
+ f"Error: Unable to retrieve search results (status code: {response.status_code})"
108
67
  )
109
- return json.dumps(parse_html_text(response.text))
68
+ return response.json()
110
69
 
111
70
  return search_internet
112
71
 
@@ -150,30 +109,69 @@ def search_arxiv(query: str, num_results: int = 10) -> str:
150
109
  return response.content
151
110
 
152
111
 
153
- def parse_html_text(html_text: str) -> dict[str, str]:
112
+ async def _fetch_page_content(url: str) -> tuple[str, list[str]]:
113
+ """Fetches the HTML content and all absolute links from a URL."""
114
+ user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
115
+ try:
116
+ from playwright.async_api import async_playwright
117
+
118
+ async with async_playwright() as p:
119
+ browser = await p.chromium.launch(headless=True)
120
+ page = await browser.new_page()
121
+ await page.set_extra_http_headers({"User-Agent": user_agent})
122
+ try:
123
+ await page.goto(url, wait_until="networkidle", timeout=30000)
124
+ await page.wait_for_load_state("domcontentloaded")
125
+ content = await page.content()
126
+ links = await page.eval_on_selector_all(
127
+ "a[href]",
128
+ """
129
+ (elements, baseUrl) => elements.map(el => {
130
+ const href = el.getAttribute('href');
131
+ if (!href || href.startsWith('#')) return null;
132
+ try {
133
+ return new URL(href, baseUrl).href;
134
+ } catch (e) {
135
+ return null;
136
+ }
137
+ }).filter(href => href !== null)
138
+ """,
139
+ url,
140
+ )
141
+ return content, links
142
+ # return json.dumps({"content": content, "links": links})
143
+ finally:
144
+ await browser.close()
145
+ except Exception:
146
+ import requests
147
+ from bs4 import BeautifulSoup
148
+
149
+ response = requests.get(url, headers={"User-Agent": user_agent})
150
+ if response.status_code != 200:
151
+ raise Exception(
152
+ f"Unable to retrieve page content. Status code: {response.status_code}"
153
+ )
154
+ content = response.text
155
+ soup = BeautifulSoup(content, "html.parser")
156
+ links = [
157
+ urljoin(url, a["href"])
158
+ for a in soup.find_all("a", href=True)
159
+ if not a["href"].startswith("#")
160
+ ]
161
+ return content, links
162
+ # return json.dumps({"content": content, "links": links})
163
+
164
+
165
+ def _convert_html_to_markdown(html_text: str) -> str:
166
+ """Converts HTML content to a clean Markdown representation."""
154
167
  from bs4 import BeautifulSoup
168
+ from markdownify import markdownify as md
155
169
 
156
- ignored_tags = [
157
- "script",
158
- "link",
159
- "meta",
160
- "style",
161
- "code",
162
- "footer",
163
- "nav",
164
- "header",
165
- "aside",
166
- ]
167
170
  soup = BeautifulSoup(html_text, "html.parser")
168
- links = []
169
- for anchor in soup.find_all("a"):
170
- if not anchor or "href" not in anchor.attrs:
171
- continue
172
- link: str = anchor["href"]
173
- if link.startswith("#") or link.startswith("/"):
174
- continue
175
- links.append(link)
176
- for tag in soup(ignored_tags):
171
+ # Remove non-content tags
172
+ for tag in soup(
173
+ ["script", "link", "meta", "style", "header", "footer", "nav", "aside"]
174
+ ):
177
175
  tag.decompose()
178
- html_text = soup.get_text(separator=" ", strip=True)
179
- return {"content": html_text, "links_on_page": links}
176
+ # Convert the cleaned HTML to Markdown
177
+ return md(str(soup))
@@ -12,6 +12,7 @@ setup_latex_on_ubuntu = setup_latex_group.add_task(
12
12
  "texlive-fonts-extra texlive-latex-extra",
13
13
  ],
14
14
  render_cmd=False,
15
+ is_interactive=True,
15
16
  ),
16
17
  alias="ubuntu",
17
18
  )
@@ -22,7 +22,7 @@ setup_ubuntu = setup_group.add_task(
22
22
  description="🐧 Setup ubuntu",
23
23
  cmd=[
24
24
  "sudo apt install -y \\",
25
- "build-essential python3-distutils libssl-dev zlib1g-dev \\"
25
+ "build-essential libssl-dev zlib1g-dev \\"
26
26
  "libbz2-dev libreadline-dev libsqlite3-dev libpq-dev python3-dev \\",
27
27
  "llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev \\",
28
28
  "liblzma-dev python3-openssl libblas-dev liblapack-dev rustc \\",
zrb/config/config.py CHANGED
@@ -24,6 +24,13 @@ class Config:
24
24
  def __init__(self):
25
25
  self.__internal_default_prompt: dict[str, str] = {}
26
26
 
27
+ @property
28
+ def ENV_PREFIX(self) -> str:
29
+ return os.getenv("_ZRB_ENV_PREFIX", "ZRB")
30
+
31
+ def _getenv(self, env_name: str, default: str = "") -> str:
32
+ return os.getenv(f"{self.ENV_PREFIX}_{env_name}", default)
33
+
27
34
  def _get_internal_default_prompt(self, name: str) -> str:
28
35
  if name not in self.__internal_default_prompt:
29
36
  file_path = os.path.join(
@@ -39,7 +46,7 @@ class Config:
39
46
 
40
47
  @property
41
48
  def DEFAULT_SHELL(self) -> str:
42
- return os.getenv("ZRB_SHELL", self._get_current_shell())
49
+ return self._getenv("SHELL", self._get_current_shell())
43
50
 
44
51
  def _get_current_shell(self) -> str:
45
52
  if platform.system() == "Windows":
@@ -51,11 +58,11 @@ class Config:
51
58
 
52
59
  @property
53
60
  def DEFAULT_EDITOR(self) -> str:
54
- return os.getenv("ZRB_EDITOR", "nano")
61
+ return self._getenv("EDITOR", "nano")
55
62
 
56
63
  @property
57
64
  def INIT_MODULES(self) -> list[str]:
58
- init_modules_str = os.getenv("ZRB_INIT_MODULES", "")
65
+ init_modules_str = self._getenv("INIT_MODULES", "")
59
66
  if init_modules_str != "":
60
67
  return [
61
68
  module.strip()
@@ -66,15 +73,15 @@ class Config:
66
73
 
67
74
  @property
68
75
  def ROOT_GROUP_NAME(self) -> str:
69
- return os.getenv("ZRB_ROOT_GROUP_NAME", "zrb")
76
+ return self._getenv("ROOT_GROUP_NAME", "zrb")
70
77
 
71
78
  @property
72
79
  def ROOT_GROUP_DESCRIPTION(self) -> str:
73
- return os.getenv("ZRB_ROOT_GROUP_DESCRIPTION", "Your Automation Powerhouse")
80
+ return self._getenv("ROOT_GROUP_DESCRIPTION", "Your Automation Powerhouse")
74
81
 
75
82
  @property
76
83
  def INIT_SCRIPTS(self) -> list[str]:
77
- init_scripts_str = os.getenv("ZRB_INIT_SCRIPTS", "")
84
+ init_scripts_str = self._getenv("INIT_SCRIPTS", "")
78
85
  if init_scripts_str != "":
79
86
  return [
80
87
  script.strip()
@@ -85,11 +92,11 @@ class Config:
85
92
 
86
93
  @property
87
94
  def INIT_FILE_NAME(self) -> str:
88
- return os.getenv("ZRB_INIT_FILE_NAME", "zrb_init.py")
95
+ return self._getenv("INIT_FILE_NAME", "zrb_init.py")
89
96
 
90
97
  @property
91
98
  def LOGGING_LEVEL(self) -> int:
92
- return self._get_log_level(os.getenv("ZRB_LOGGING_LEVEL", "WARNING"))
99
+ return self._get_log_level(self._getenv("LOGGING_LEVEL", "WARNING"))
93
100
 
94
101
  def _get_log_level(self, level: str) -> int:
95
102
  level = level.upper()
@@ -108,11 +115,11 @@ class Config:
108
115
 
109
116
  @property
110
117
  def LOAD_BUILTIN(self) -> bool:
111
- return to_boolean(os.getenv("ZRB_LOAD_BUILTIN", "1"))
118
+ return to_boolean(self._getenv("LOAD_BUILTIN", "1"))
112
119
 
113
120
  @property
114
121
  def WARN_UNRECOMMENDED_COMMAND(self) -> bool:
115
- return to_boolean(os.getenv("ZRB_WARN_UNRECOMMENDED_COMMAND", "1"))
122
+ return to_boolean(self._getenv("WARN_UNRECOMMENDED_COMMAND", "1"))
116
123
 
117
124
  @property
118
125
  def SESSION_LOG_DIR(self) -> str:
@@ -122,15 +129,15 @@ class Config:
122
129
 
123
130
  @property
124
131
  def TODO_DIR(self) -> str:
125
- return os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
132
+ return self._getenv("TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
126
133
 
127
134
  @property
128
135
  def TODO_VISUAL_FILTER(self) -> str:
129
- return os.getenv("ZRB_TODO_FILTER", "")
136
+ return self._getenv("TODO_FILTER", "")
130
137
 
131
138
  @property
132
139
  def TODO_RETENTION(self) -> str:
133
- return os.getenv("ZRB_TODO_RETENTION", "2w")
140
+ return self._getenv("TODO_RETENTION", "2w")
134
141
 
135
142
  @property
136
143
  def VERSION(self) -> str:
@@ -141,7 +148,7 @@ class Config:
141
148
 
142
149
  @property
143
150
  def WEB_CSS_PATH(self) -> list[str]:
144
- web_css_path_str = os.getenv("ZRB_WEB_CSS_PATH", "")
151
+ web_css_path_str = self._getenv("WEB_CSS_PATH", "")
145
152
  if web_css_path_str != "":
146
153
  return [
147
154
  path.strip()
@@ -152,7 +159,7 @@ class Config:
152
159
 
153
160
  @property
154
161
  def WEB_JS_PATH(self) -> list[str]:
155
- web_js_path_str = os.getenv("ZRB_WEB_JS_PATH", "")
162
+ web_js_path_str = self._getenv("WEB_JS_PATH", "")
156
163
  if web_js_path_str != "":
157
164
  return [
158
165
  path.strip()
@@ -163,103 +170,111 @@ class Config:
163
170
 
164
171
  @property
165
172
  def WEB_FAVICON_PATH(self) -> str:
166
- return os.getenv("ZRB_WEB_FAVICON_PATH", "/static/favicon-32x32.png")
173
+ return self._getenv("WEB_FAVICON_PATH", "/static/favicon-32x32.png")
167
174
 
168
175
  @property
169
176
  def WEB_COLOR(self) -> str:
170
- return os.getenv("ZRB_WEB_COLOR", "")
177
+ return self._getenv("WEB_COLOR", "")
171
178
 
172
179
  @property
173
180
  def WEB_HTTP_PORT(self) -> int:
174
- return int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
181
+ return int(self._getenv("WEB_HTTP_PORT", "21213"))
175
182
 
176
183
  @property
177
184
  def WEB_GUEST_USERNAME(self) -> str:
178
- return os.getenv("ZRB_WEB_GUEST_USERNAME", "user")
185
+ return self._getenv("WEB_GUEST_USERNAME", "user")
179
186
 
180
187
  @property
181
188
  def WEB_SUPER_ADMIN_USERNAME(self) -> str:
182
- return os.getenv("ZRB_WEB_SUPER_ADMIN_USERNAME", "admin")
189
+ return self._getenv("WEB_SUPER_ADMIN_USERNAME", "admin")
183
190
 
184
191
  @property
185
192
  def WEB_SUPER_ADMIN_PASSWORD(self) -> str:
186
- return os.getenv("ZRB_WEB_SUPER_ADMIN_PASSWORD", "admin")
193
+ return self._getenv("WEB_SUPER_ADMIN_PASSWORD", "admin")
187
194
 
188
195
  @property
189
196
  def WEB_ACCESS_TOKEN_COOKIE_NAME(self) -> str:
190
- return os.getenv("ZRB_WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token")
197
+ return self._getenv("WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token")
191
198
 
192
199
  @property
193
200
  def WEB_REFRESH_TOKEN_COOKIE_NAME(self) -> str:
194
- return os.getenv("ZRB_WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token")
201
+ return self._getenv("WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token")
195
202
 
196
203
  @property
197
204
  def WEB_SECRET_KEY(self) -> str:
198
- return os.getenv("ZRB_WEB_SECRET", "zrb")
205
+ return self._getenv("WEB_SECRET", "zrb")
199
206
 
200
207
  @property
201
208
  def WEB_ENABLE_AUTH(self) -> bool:
202
- return to_boolean(os.getenv("ZRB_WEB_ENABLE_AUTH", "0"))
209
+ return to_boolean(self._getenv("WEB_ENABLE_AUTH", "0"))
203
210
 
204
211
  @property
205
212
  def WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES(self) -> int:
206
- return int(os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
213
+ return int(self._getenv("WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
207
214
 
208
215
  @property
209
216
  def WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES(self) -> int:
210
- return int(os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60"))
217
+ return int(self._getenv("WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60"))
211
218
 
212
219
  @property
213
220
  def WEB_TITLE(self) -> str:
214
- return os.getenv("ZRB_WEB_TITLE", "Zrb")
221
+ return self._getenv("WEB_TITLE", "Zrb")
215
222
 
216
223
  @property
217
224
  def WEB_JARGON(self) -> str:
218
- return os.getenv("ZRB_WEB_JARGON", "Your Automation PowerHouse")
225
+ return self._getenv("WEB_JARGON", "Your Automation PowerHouse")
219
226
 
220
227
  @property
221
228
  def WEB_HOMEPAGE_INTRO(self) -> str:
222
- return os.getenv("ZRB_WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface")
229
+ return self._getenv("WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface")
223
230
 
224
231
  @property
225
232
  def LLM_MODEL(self) -> str | None:
226
- return os.getenv("ZRB_LLM_MODEL", None)
233
+ value = self._getenv("LLM_MODEL")
234
+ return None if value == "" else value
227
235
 
228
236
  @property
229
237
  def LLM_BASE_URL(self) -> str | None:
230
- return os.getenv("ZRB_LLM_BASE_URL", None)
238
+ value = self._getenv("LLM_BASE_URL")
239
+ return None if value == "" else value
231
240
 
232
241
  @property
233
242
  def LLM_API_KEY(self) -> str | None:
234
- return os.getenv("ZRB_LLM_API_KEY", None)
243
+ value = self._getenv("LLM_API_KEY")
244
+ return None if value == "" else value
235
245
 
236
246
  @property
237
247
  def LLM_SYSTEM_PROMPT(self) -> str | None:
238
- return os.getenv("ZRB_LLM_SYSTEM_PROMPT", None)
248
+ value = self._getenv("LLM_SYSTEM_PROMPT")
249
+ return None if value == "" else value
239
250
 
240
251
  @property
241
252
  def LLM_INTERACTIVE_SYSTEM_PROMPT(self) -> str | None:
242
- return os.getenv("ZRB_LLM_INTERACTIVE_SYSTEM_PROMPT", None)
253
+ value = self._getenv("LLM_INTERACTIVE_SYSTEM_PROMPT")
254
+ return None if value == "" else value
243
255
 
244
256
  @property
245
257
  def LLM_PERSONA(self) -> str | None:
246
- return os.getenv("ZRB_LLM_PERSONA", None)
258
+ value = self._getenv("LLM_PERSONA")
259
+ return None if value == "" else value
247
260
 
248
261
  @property
249
262
  def LLM_MODES(self) -> list[str]:
250
263
  return [
251
264
  mode.strip()
252
- for mode in os.getenv("ZRB_LLM_MODES", "coding").split(",")
265
+ for mode in self._getenv("LLM_MODES", "coding").split(",")
253
266
  if mode.strip() != ""
254
267
  ]
255
268
 
256
269
  @property
257
270
  def LLM_SPECIAL_INSTRUCTION_PROMPT(self) -> str | None:
258
- return os.getenv("ZRB_LLM_SPECIAL_INSTRUCTION_PROMPT", None)
271
+ value = self._getenv("LLM_SPECIAL_INSTRUCTION_PROMPT")
272
+ return None if value == "" else value
259
273
 
260
274
  @property
261
275
  def LLM_SUMMARIZATION_PROMPT(self) -> str | None:
262
- return os.getenv("ZRB_LLM_SUMMARIZATION_PROMPT", None)
276
+ value = self._getenv("LLM_SUMMARIZATION_PROMPT")
277
+ return None if value == "" else value
263
278
 
264
279
  @property
265
280
  def LLM_MAX_REQUESTS_PER_MINUTE(self) -> int:
@@ -267,7 +282,7 @@ class Config:
267
282
  Maximum number of LLM requests allowed per minute.
268
283
  Default is conservative to accommodate free-tier LLM providers.
269
284
  """
270
- return int(os.getenv("LLM_MAX_REQUESTS_PER_MINUTE", "15"))
285
+ return int(self._getenv("LLM_MAX_REQUESTS_PER_MINUTE", "15"))
271
286
 
272
287
  @property
273
288
  def LLM_MAX_TOKENS_PER_MINUTE(self) -> int:
@@ -275,127 +290,127 @@ class Config:
275
290
  Maximum number of LLM tokens allowed per minute.
276
291
  Default is conservative to accommodate free-tier LLM providers.
277
292
  """
278
- return int(os.getenv("ZRB_LLM_MAX_TOKENS_PER_MINUTE", "100000"))
293
+ return int(self._getenv("LLM_MAX_TOKENS_PER_MINUTE", "100000"))
279
294
 
280
295
  @property
281
296
  def LLM_MAX_TOKENS_PER_REQUEST(self) -> int:
282
297
  """Maximum number of tokens allowed per individual LLM request."""
283
- return int(os.getenv("ZRB_LLM_MAX_TOKENS_PER_REQUEST", "50000"))
298
+ return int(self._getenv("LLM_MAX_TOKENS_PER_REQUEST", "50000"))
284
299
 
285
300
  @property
286
301
  def LLM_THROTTLE_SLEEP(self) -> float:
287
302
  """Number of seconds to sleep when throttling is required."""
288
- return float(os.getenv("ZRB_LLM_THROTTLE_SLEEP", "1.0"))
303
+ return float(self._getenv("LLM_THROTTLE_SLEEP", "1.0"))
289
304
 
290
305
  @property
291
306
  def LLM_YOLO_MODE(self) -> bool:
292
- return to_boolean(os.getenv("ZRB_LLM_YOLO_MODE", "false"))
307
+ return to_boolean(self._getenv("LLM_YOLO_MODE", "false"))
293
308
 
294
309
  @property
295
310
  def LLM_SUMMARIZE_HISTORY(self) -> bool:
296
- return to_boolean(os.getenv("ZRB_LLM_SUMMARIZE_HISTORY", "true"))
311
+ return to_boolean(self._getenv("LLM_SUMMARIZE_HISTORY", "true"))
297
312
 
298
313
  @property
299
314
  def LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD(self) -> int:
300
- return int(os.getenv("ZRB_LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD", "20000"))
315
+ return int(self._getenv("LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD", "20000"))
301
316
 
302
317
  @property
303
318
  def LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD(self) -> int:
304
- return int(os.getenv("ZRB_LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_LIMIT", "35000"))
319
+ return int(self._getenv("LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_LIMIT", "35000"))
305
320
 
306
321
  @property
307
322
  def LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD(self) -> int:
308
- return int(
309
- os.getenv("ZRB_LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_LIMIT", "35000")
310
- )
323
+ return int(self._getenv("LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_LIMIT", "35000"))
311
324
 
312
325
  @property
313
326
  def LLM_FILE_ANALYSIS_TOKEN_LIMIT(self) -> int:
314
- return int(os.getenv("ZRB_LLM_FILE_ANALYSIS_TOKEN_LIMIT", "35000"))
327
+ return int(self._getenv("LLM_FILE_ANALYSIS_TOKEN_LIMIT", "35000"))
315
328
 
316
329
  @property
317
330
  def LLM_FILE_EXTRACTOR_SYSTEM_PROMPT(self) -> str:
318
- return os.getenv(
319
- "ZRB_LLM_FILE_EXTRACTOR_SYSTEM_PROMPT",
331
+ return self._getenv(
332
+ "LLM_FILE_EXTRACTOR_SYSTEM_PROMPT",
320
333
  self._get_internal_default_prompt("file_extractor_system_prompt"),
321
334
  )
322
335
 
323
336
  @property
324
337
  def LLM_REPO_EXTRACTOR_SYSTEM_PROMPT(self) -> str:
325
- return os.getenv(
326
- "ZRB_LLM_REPO_EXTRACTOR_SYSTEM_PROMPT",
338
+ return self._getenv(
339
+ "LLM_REPO_EXTRACTOR_SYSTEM_PROMPT",
327
340
  self._get_internal_default_prompt("repo_extractor_system_prompt"),
328
341
  )
329
342
 
330
343
  @property
331
344
  def LLM_REPO_SUMMARIZER_SYSTEM_PROMPT(self) -> str:
332
- return os.getenv(
333
- "ZRB_LLM_REPO_SUMMARIZER_SYSTEM_PROMPT",
345
+ return self._getenv(
346
+ "LLM_REPO_SUMMARIZER_SYSTEM_PROMPT",
334
347
  self._get_internal_default_prompt("repo_summarizer_system_prompt"),
335
348
  )
336
349
 
337
350
  @property
338
351
  def LLM_HISTORY_DIR(self) -> str:
339
- return os.getenv(
340
- "ZRB_LLM_HISTORY_DIR",
352
+ return self._getenv(
353
+ "LLM_HISTORY_DIR",
341
354
  os.path.expanduser(os.path.join("~", ".zrb-llm-history")),
342
355
  )
343
356
 
344
357
  @property
345
358
  def LLM_ALLOW_ACCESS_LOCAL_FILE(self) -> bool:
346
- return to_boolean(os.getenv("ZRB_LLM_ACCESS_LOCAL_FILE", "1"))
359
+ return to_boolean(self._getenv("LLM_ACCESS_LOCAL_FILE", "1"))
347
360
 
348
361
  @property
349
362
  def LLM_ALLOW_ACCESS_SHELL(self) -> bool:
350
- return to_boolean(os.getenv("ZRB_LLM_ACCESS_SHELL", "1"))
363
+ return to_boolean(self._getenv("LLM_ACCESS_SHELL", "1"))
351
364
 
352
365
  @property
353
366
  def LLM_ALLOW_OPEN_WEB_PAGE(self) -> bool:
354
- return to_boolean(os.getenv("ZRB_LLM_ALLOW_OPEN_WEB_PAGE", "1"))
367
+ return to_boolean(self._getenv("LLM_ALLOW_OPEN_WEB_PAGE", "1"))
355
368
 
356
369
  @property
357
370
  def LLM_ALLOW_SEARCH_INTERNET(self) -> bool:
358
- return to_boolean(os.getenv("ZRB_LLM_ALLOW_SEARCH_INTERNET", "1"))
371
+ return to_boolean(self._getenv("LLM_ALLOW_SEARCH_INTERNET", "1"))
359
372
 
360
373
  @property
361
374
  def LLM_ALLOW_SEARCH_ARXIV(self) -> bool:
362
- return to_boolean(os.getenv("ZRB_LLM_ALLOW_SEARCH_ARXIV", "1"))
375
+ return to_boolean(self._getenv("LLM_ALLOW_SEARCH_ARXIV", "1"))
363
376
 
364
377
  @property
365
378
  def LLM_ALLOW_SEARCH_WIKIPEDIA(self) -> bool:
366
- return to_boolean(os.getenv("ZRB_LLM_ALLOW_SEARCH_WIKIPEDIA", "1"))
379
+ return to_boolean(self._getenv("LLM_ALLOW_SEARCH_WIKIPEDIA", "1"))
367
380
 
368
381
  @property
369
382
  def LLM_ALLOW_GET_CURRENT_LOCATION(self) -> bool:
370
- return to_boolean(os.getenv("ZRB_LLM_ALLOW_GET_CURRENT_LOCATION", "1"))
383
+ return to_boolean(self._getenv("LLM_ALLOW_GET_CURRENT_LOCATION", "1"))
371
384
 
372
385
  @property
373
386
  def LLM_ALLOW_GET_CURRENT_WEATHER(self) -> bool:
374
- return to_boolean(os.getenv("ZRB_LLM_ALLOW_GET_CURRENT_WEATHER", "1"))
387
+ return to_boolean(self._getenv("LLM_ALLOW_GET_CURRENT_WEATHER", "1"))
375
388
 
376
389
  @property
377
- def RAG_EMBEDDING_API_KEY(self) -> str:
378
- return os.getenv("ZRB_RAG_EMBEDDING_API_KEY", None)
390
+ def RAG_EMBEDDING_API_KEY(self) -> str | None:
391
+ value = self._getenv("RAG_EMBEDDING_API_KEY")
392
+ return None if value == "" else value
379
393
 
380
394
  @property
381
- def RAG_EMBEDDING_BASE_URL(self) -> str:
382
- return os.getenv("ZRB_RAG_EMBEDDING_BASE_URL", None)
395
+ def RAG_EMBEDDING_BASE_URL(self) -> str | None:
396
+ value = self._getenv("RAG_EMBEDDING_BASE_URL")
397
+ return None if value == "" else value
383
398
 
384
399
  @property
385
400
  def RAG_EMBEDDING_MODEL(self) -> str:
386
- return os.getenv("ZRB_RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
401
+ return self._getenv("RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
387
402
 
388
403
  @property
389
404
  def RAG_CHUNK_SIZE(self) -> int:
390
- return int(os.getenv("ZRB_RAG_CHUNK_SIZE", "1024"))
405
+ return int(self._getenv("RAG_CHUNK_SIZE", "1024"))
391
406
 
392
407
  @property
393
408
  def RAG_OVERLAP(self) -> int:
394
- return int(os.getenv("ZRB_RAG_OVERLAP", "128"))
409
+ return int(self._getenv("RAG_OVERLAP", "128"))
395
410
 
396
411
  @property
397
412
  def RAG_MAX_RESULT_COUNT(self) -> int:
398
- return int(os.getenv("ZRB_RAG_MAX_RESULT_COUNT", "5"))
413
+ return int(self._getenv("RAG_MAX_RESULT_COUNT", "5"))
399
414
 
400
415
  @property
401
416
  def SERPAPI_KEY(self) -> str:
@@ -404,13 +419,13 @@ class Config:
404
419
  @property
405
420
  def BANNER(self) -> str:
406
421
  return fstring_format(
407
- os.getenv("ZRB_BANNER", _DEFAULT_BANNER),
422
+ self._getenv("BANNER", _DEFAULT_BANNER),
408
423
  {"VERSION": self.VERSION},
409
424
  )
410
425
 
411
426
  @property
412
427
  def LLM_CONTEXT_FILE(self) -> str:
413
- return os.getenv("LLM_CONTEXT_FILE", "ZRB.md")
428
+ return self._getenv("LLM_CONTEXT_FILE", "ZRB.md")
414
429
 
415
430
 
416
431
  CFG = Config()
zrb/task/llm/agent.py CHANGED
@@ -13,13 +13,12 @@ from zrb.task.llm.typing import ListOfDict
13
13
  if TYPE_CHECKING:
14
14
  from pydantic_ai import Agent, Tool
15
15
  from pydantic_ai.agent import AgentRun
16
+ from pydantic_ai.messages import UserContent
16
17
  from pydantic_ai.models import Model
17
18
  from pydantic_ai.settings import ModelSettings
18
19
  from pydantic_ai.toolsets import AbstractToolset
19
20
 
20
21
  ToolOrCallable = Tool | Callable
21
- else:
22
- ToolOrCallable = Any
23
22
 
24
23
 
25
24
  def create_agent_instance(
@@ -27,7 +26,7 @@ def create_agent_instance(
27
26
  model: "str | Model | None" = None,
28
27
  system_prompt: str = "",
29
28
  model_settings: "ModelSettings | None" = None,
30
- tools: list[ToolOrCallable] = [],
29
+ tools: "list[ToolOrCallable]" = [],
31
30
  toolsets: list["AbstractToolset[Agent]"] = [],
32
31
  retries: int = 3,
33
32
  is_yolo_mode: bool | None = None,
@@ -80,9 +79,9 @@ def get_agent(
80
79
  system_prompt: str,
81
80
  model_settings: "ModelSettings | None",
82
81
  tools_attr: (
83
- list[ToolOrCallable] | Callable[[AnySharedContext], list[ToolOrCallable]]
82
+ "list[ToolOrCallable] | Callable[[AnySharedContext], list[ToolOrCallable]]"
84
83
  ),
85
- additional_tools: list[ToolOrCallable],
84
+ additional_tools: "list[ToolOrCallable]",
86
85
  toolsets_attr: "list[AbstractToolset[Agent]] | Callable[[AnySharedContext], list[AbstractToolset[Agent]]]", # noqa
87
86
  additional_toolsets: "list[AbstractToolset[Agent]]",
88
87
  retries: int = 3,
@@ -126,7 +125,8 @@ async def run_agent_iteration(
126
125
  ctx: AnyContext,
127
126
  agent: "Agent",
128
127
  user_prompt: str,
129
- history_list: ListOfDict,
128
+ attachments: "list[UserContent] | None" = None,
129
+ history_list: ListOfDict | None = None,
130
130
  rate_limitter: LLMRateLimiter | None = None,
131
131
  max_retry: int = 2,
132
132
  ) -> "AgentRun":
@@ -154,8 +154,11 @@ async def run_agent_iteration(
154
154
  ctx=ctx,
155
155
  agent=agent,
156
156
  user_prompt=user_prompt,
157
- history_list=history_list,
158
- rate_limitter=rate_limitter,
157
+ attachments=[] if attachments is None else attachments,
158
+ history_list=[] if history_list is None else history_list,
159
+ rate_limitter=(
160
+ llm_rate_limitter if rate_limitter is None else rate_limitter
161
+ ),
159
162
  )
160
163
  except BaseException:
161
164
  attempt += 1
@@ -168,21 +171,25 @@ async def _run_single_agent_iteration(
168
171
  ctx: AnyContext,
169
172
  agent: "Agent",
170
173
  user_prompt: str,
174
+ attachments: "list[UserContent]",
171
175
  history_list: ListOfDict,
172
- rate_limitter: LLMRateLimiter | None = None,
176
+ rate_limitter: LLMRateLimiter,
173
177
  ) -> "AgentRun":
174
178
  from openai import APIError
175
179
  from pydantic_ai.messages import ModelMessagesTypeAdapter
176
180
 
177
- agent_payload = estimate_request_payload(agent, user_prompt, history_list)
181
+ agent_payload = _estimate_request_payload(
182
+ agent, user_prompt, attachments, history_list
183
+ )
178
184
  if rate_limitter:
179
185
  await rate_limitter.throttle(agent_payload)
180
186
  else:
181
187
  await llm_rate_limitter.throttle(agent_payload)
182
188
 
189
+ user_prompt_with_attachments = [user_prompt] + attachments
183
190
  async with agent:
184
191
  async with agent.iter(
185
- user_prompt=user_prompt,
192
+ user_prompt=user_prompt_with_attachments,
186
193
  message_history=ModelMessagesTypeAdapter.validate_python(history_list),
187
194
  ) as agent_run:
188
195
  async for node in agent_run:
@@ -202,8 +209,11 @@ async def _run_single_agent_iteration(
202
209
  return agent_run
203
210
 
204
211
 
205
- def estimate_request_payload(
206
- agent: "Agent", user_prompt: str, history_list: ListOfDict
212
+ def _estimate_request_payload(
213
+ agent: "Agent",
214
+ user_prompt: str,
215
+ attachments: "list[UserContent]",
216
+ history_list: ListOfDict,
207
217
  ) -> str:
208
218
  system_prompts = agent._system_prompts if hasattr(agent, "_system_prompts") else ()
209
219
  return json.dumps(
@@ -211,10 +221,19 @@ def estimate_request_payload(
211
221
  {"role": "system", "content": "\n".join(system_prompts)},
212
222
  *history_list,
213
223
  {"role": "user", "content": user_prompt},
224
+ *[_estimate_attachment_payload(attachment) for attachment in attachments],
214
225
  ]
215
226
  )
216
227
 
217
228
 
229
+ def _estimate_attachment_payload(attachment: "UserContent") -> Any:
230
+ if hasattr(attachment, "url"):
231
+ return {"role": "user", "content": attachment.url}
232
+ if hasattr(attachment, "data"):
233
+ return {"role": "user", "content": "x" * len(attachment.data)}
234
+ return ""
235
+
236
+
218
237
  def _get_plain_printer(ctx: AnyContext):
219
238
  def printer(*args, **kwargs):
220
239
  if "plain" not in kwargs:
@@ -157,6 +157,7 @@ async def summarize_history(
157
157
  ctx=ctx,
158
158
  agent=summarization_agent,
159
159
  user_prompt=user_prompt,
160
+ attachments=[],
160
161
  history_list=[],
161
162
  rate_limitter=rate_limitter,
162
163
  )
@@ -23,6 +23,7 @@ async def print_node(print_func: Callable, agent_run: Any, node: Any):
23
23
  elif Agent.is_model_request_node(node):
24
24
  # A model request node => We can stream tokens from the model's request
25
25
  print_func(stylize_faint("🧠 Processing..."))
26
+ # Reference: https://ai.pydantic.dev/agents/#streaming
26
27
  async with node.stream(agent_run.ctx) as request_stream:
27
28
  is_streaming = False
28
29
  async for event in request_stream:
zrb/task/llm/prompt.py CHANGED
@@ -2,16 +2,21 @@ import os
2
2
  import platform
3
3
  import re
4
4
  from datetime import datetime, timezone
5
+ from typing import TYPE_CHECKING, Callable
5
6
 
6
7
  from zrb.attr.type import StrAttr, StrListAttr
7
8
  from zrb.config.llm_config import llm_config as llm_config
8
9
  from zrb.config.llm_context.config import llm_context_config
9
10
  from zrb.context.any_context import AnyContext
11
+ from zrb.context.any_shared_context import AnySharedContext
10
12
  from zrb.task.llm.conversation_history_model import ConversationHistory
11
13
  from zrb.util.attr import get_attr, get_str_attr, get_str_list_attr
12
14
  from zrb.util.file import read_dir, read_file_with_line_numbers
13
15
  from zrb.util.llm.prompt import make_prompt_section
14
16
 
17
+ if TYPE_CHECKING:
18
+ from pydantic_ai.messages import UserContent
19
+
15
20
 
16
21
  def get_persona(
17
22
  ctx: AnyContext,
@@ -193,7 +198,7 @@ def get_system_and_user_prompt(
193
198
  def extract_conversation_context(user_message: str) -> tuple[str, str]:
194
199
  modified_user_message = user_message
195
200
  # Match “@” + any non-space/comma sequence that contains at least one “/”
196
- pattern = r"(?<!\w)@(?=[^,\s]*\/)([^,\s]+)"
201
+ pattern = r"(?<!\w)@(?=[^,\s]*\/)([^,\?\!\s]+)"
197
202
  potential_resource_path = re.findall(pattern, user_message)
198
203
  apendixes = []
199
204
  for i, ref in enumerate(potential_resource_path):
@@ -274,3 +279,21 @@ def get_summarization_system_prompt(
274
279
  if summarization_prompt is not None:
275
280
  return summarization_prompt
276
281
  return llm_config.default_summarization_prompt
282
+
283
+
284
+ def get_attachments(
285
+ ctx: AnyContext,
286
+ attachment: "UserContent | list[UserContent] | Callable[[AnySharedContext], UserContent | list[UserContent]] | None" = None, # noqa
287
+ ) -> "list[UserContent]":
288
+ if attachment is None:
289
+ return []
290
+ if callable(attachment):
291
+ result = attachment(ctx)
292
+ if result is None:
293
+ return []
294
+ if isinstance(result, list):
295
+ return result
296
+ return [result]
297
+ if isinstance(attachment, list):
298
+ return attachment
299
+ return [attachment]
zrb/task/llm_task.py CHANGED
@@ -23,6 +23,7 @@ from zrb.task.llm.conversation_history import (
23
23
  from zrb.task.llm.conversation_history_model import ConversationHistory
24
24
  from zrb.task.llm.history_summarization import maybe_summarize_history
25
25
  from zrb.task.llm.prompt import (
26
+ get_attachments,
26
27
  get_summarization_system_prompt,
27
28
  get_system_and_user_prompt,
28
29
  get_user_message,
@@ -32,13 +33,12 @@ from zrb.xcom.xcom import Xcom
32
33
 
33
34
  if TYPE_CHECKING:
34
35
  from pydantic_ai import Agent, Tool
36
+ from pydantic_ai.messages import UserContent
35
37
  from pydantic_ai.models import Model
36
38
  from pydantic_ai.settings import ModelSettings
37
39
  from pydantic_ai.toolsets import AbstractToolset
38
40
 
39
41
  ToolOrCallable = Tool | Callable
40
- else:
41
- ToolOrCallable = Any
42
42
 
43
43
 
44
44
  class LLMTask(BaseTask):
@@ -72,6 +72,7 @@ class LLMTask(BaseTask):
72
72
  modes: StrListAttr | None = None,
73
73
  render_modes: bool = True,
74
74
  message: StrAttr | None = None,
75
+ attachment: "UserContent | list[UserContent] | Callable[[AnySharedContext], UserContent | list[UserContent]] | None" = None, # noqa
75
76
  render_message: bool = True,
76
77
  tools: (
77
78
  list["ToolOrCallable"]
@@ -184,11 +185,12 @@ class LLMTask(BaseTask):
184
185
  self._conversation_context = conversation_context
185
186
  self._is_yolo_mode = is_yolo_mode
186
187
  self._render_yolo_mode = render_yolo_mode
188
+ self._attachment = attachment
187
189
 
188
- def add_tool(self, *tool: ToolOrCallable):
190
+ def add_tool(self, *tool: "ToolOrCallable"):
189
191
  self.append_tool(*tool)
190
192
 
191
- def append_tool(self, *tool: ToolOrCallable):
193
+ def append_tool(self, *tool: "ToolOrCallable"):
192
194
  for single_tool in tool:
193
195
  self._additional_tools.append(single_tool)
194
196
 
@@ -230,6 +232,7 @@ class LLMTask(BaseTask):
230
232
  render_summarization_prompt=self._render_summarization_prompt,
231
233
  )
232
234
  user_message = get_user_message(ctx, self._message, self._render_message)
235
+ attachments = get_attachments(ctx, self._attachment)
233
236
  # 1. Prepare initial state (read history from previous session)
234
237
  conversation_history = await read_conversation_history(
235
238
  ctx=ctx,
@@ -268,10 +271,11 @@ class LLMTask(BaseTask):
268
271
  )
269
272
  # 4. Run the agent iteration and save the results/history
270
273
  result = await self._execute_agent(
271
- ctx,
272
- agent,
273
- user_message,
274
- conversation_history,
274
+ ctx=ctx,
275
+ agent=agent,
276
+ user_prompt=user_message,
277
+ attachments=attachments,
278
+ conversation_history=conversation_history,
275
279
  )
276
280
  # 5. Summarize
277
281
  conversation_history = await maybe_summarize_history(
@@ -305,6 +309,7 @@ class LLMTask(BaseTask):
305
309
  ctx: AnyContext,
306
310
  agent: "Agent",
307
311
  user_prompt: str,
312
+ attachments: "list[UserContent]",
308
313
  conversation_history: ConversationHistory,
309
314
  ) -> Any:
310
315
  """Executes the agent, processes results, and saves history."""
@@ -313,6 +318,7 @@ class LLMTask(BaseTask):
313
318
  ctx=ctx,
314
319
  agent=agent,
315
320
  user_prompt=user_prompt,
321
+ attachments=attachments,
316
322
  history_list=conversation_history.history,
317
323
  rate_limitter=self._rate_limitter,
318
324
  )
zrb/util/file.py CHANGED
@@ -14,14 +14,21 @@ def read_file(file_path: str, replace_map: dict[str, str] = {}) -> str:
14
14
  """
15
15
  abs_file_path = os.path.abspath(os.path.expanduser(file_path))
16
16
  is_pdf = abs_file_path.lower().endswith(".pdf")
17
- content = (
18
- _read_pdf_file_content(abs_file_path)
19
- if is_pdf
20
- else _read_text_file_content(abs_file_path)
21
- )
22
- for key, val in replace_map.items():
23
- content = content.replace(key, val)
24
- return content
17
+ try:
18
+ content = (
19
+ _read_pdf_file_content(abs_file_path)
20
+ if is_pdf
21
+ else _read_text_file_content(abs_file_path)
22
+ )
23
+ for key, val in replace_map.items():
24
+ content = content.replace(key, val)
25
+ return content
26
+ except Exception:
27
+ import base64
28
+ from pathlib import Path
29
+
30
+ data = Path(abs_file_path).read_bytes()
31
+ return base64.b64encode(data).decode("ascii")
25
32
 
26
33
 
27
34
  def _read_text_file_content(file_path: str) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zrb
3
- Version: 1.14.5
3
+ Version: 1.14.7
4
4
  Summary: Your Automation Powerhouse
5
5
  Home-page: https://github.com/state-alchemists/zrb
6
6
  License: AGPL-3.0-or-later
@@ -22,17 +22,15 @@ Requires-Dist: chromadb (>=0.6.3,<0.7.0) ; extra == "rag" or extra == "all"
22
22
  Requires-Dist: fastapi[standard] (>=0.116.1,<0.117.0)
23
23
  Requires-Dist: isort (>=6.0.1,<6.1.0)
24
24
  Requires-Dist: libcst (>=1.7.0,<2.0.0)
25
- Requires-Dist: openai (>=1.86.0,<2.0.0) ; extra == "rag" or extra == "all"
26
- Requires-Dist: pdfplumber (>=0.11.6,<0.12.0) ; extra == "rag" or extra == "all"
25
+ Requires-Dist: openai (>=1.92.0,<=1.99.1)
26
+ Requires-Dist: pdfplumber (>=0.11.6,<0.12.0)
27
27
  Requires-Dist: playwright (>=1.53.0,<2.0.0) ; extra == "playwright" or extra == "all"
28
- Requires-Dist: prompt-toolkit (>=3.0.51,<4.0.0)
29
28
  Requires-Dist: psutil (>=7.0.0,<8.0.0)
30
- Requires-Dist: pydantic-ai (>=0.4.5,<0.5.0)
29
+ Requires-Dist: pydantic-ai-slim[anthropic,bedrock,cli,cohere,google,groq,huggingface,mcp,mistral,openai,vertexai] (>=0.6.2,<0.7.0)
31
30
  Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
32
31
  Requires-Dist: python-dotenv (>=1.1.1,<2.0.0)
33
32
  Requires-Dist: python-jose[cryptography] (>=3.4.0,<4.0.0)
34
33
  Requires-Dist: requests (>=2.32.4,<3.0.0)
35
- Requires-Dist: rich (>=14.0.0,<15.0.0)
36
34
  Requires-Dist: tiktoken (>=0.8.0,<0.9.0)
37
35
  Requires-Dist: ulid-py (>=1.1.0,<2.0.0)
38
36
  Project-URL: Documentation, https://github.com/state-alchemists/zrb
@@ -20,8 +20,8 @@ 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=hKqT-r4S9FTMv3CmNWuIL5y6q13hp9Ggo0vwUGg3HxI,22585
22
22
  zrb/builtin/llm/tool/rag.py,sha256=wB74JV7bxs0ec77b_09Z2lPjoR1WzPUvZbuXOdb9Q9g,9675
23
- zrb/builtin/llm/tool/sub_agent.py,sha256=yYqTIQWfG5mLL1nItc4jox-QK8vPOb_ZGvQ6sH0BcKw,4947
24
- zrb/builtin/llm/tool/web.py,sha256=gQlUsmYCJOFJtNjwpjK-xk13LMvrMSpSaFHXUTnIayQ,7090
23
+ zrb/builtin/llm/tool/sub_agent.py,sha256=0YA6OTLHa-w4VKsxboGDLCrZV2k3XnLQDW0HZev_W2M,4900
24
+ zrb/builtin/llm/tool/web.py,sha256=oyTkzoLBA3dfvwn93y70Rg1cYxT0AsXZsW30mHMawrY,7133
25
25
  zrb/builtin/md5.py,sha256=690RV2LbW7wQeTFxY-lmmqTSVEEZv3XZbjEUW1Q3XpE,1480
26
26
  zrb/builtin/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  zrb/builtin/project/add/fastapp/fastapp_input.py,sha256=MKlWR_LxWhM_DcULCtLfL_IjTxpDnDBkn9KIqNmajFs,310
@@ -196,11 +196,11 @@ zrb/builtin/random.py,sha256=p9kCvosSiSJyuGQrlrXuIQT8TRDGxXhfiUbsm3GFPd0,1793
196
196
  zrb/builtin/setup/asdf/asdf.py,sha256=_k60yiRiKbRPh_eJVI4Nx_ZmmClOlOb9G0b0KhSGo1M,2444
197
197
  zrb/builtin/setup/asdf/asdf_helper.py,sha256=6jARtyIAE1H82HKVQ84D25RrMAsAip_gD28X9ZlaTCk,1205
198
198
  zrb/builtin/setup/common_input.py,sha256=zIVVsZsNlSoV2Fw8kyt7g5B8XAU8cWBsT865NPoddwo,848
199
- zrb/builtin/setup/latex/ubuntu.py,sha256=er9wJAT4CpmghIaiIPFb3FvgqAn1aqU5UgX7GHL3FjA,577
199
+ zrb/builtin/setup/latex/ubuntu.py,sha256=Wc6k2OJckXH11hqD9idJf6jWiJ_VoBEMPwDb_k5hssU,606
200
200
  zrb/builtin/setup/tmux/tmux.py,sha256=cV-azDzLtNYtwxucmYqpPqoeSVz3lxP2GOc1-I1yhKs,1428
201
201
  zrb/builtin/setup/tmux/tmux_config.sh,sha256=wQCb4Q-mNkxIPOcvpN84X9RUWkGY16u3Vd-pOhVidgg,416
202
202
  zrb/builtin/setup/tmux/tmux_helper.py,sha256=M03l0wfL25TzGGp6lnVfX40ayT_x7N2lz-nz2chO7PU,396
203
- zrb/builtin/setup/ubuntu.py,sha256=N4F55xzWKy0Hm115Qr19BADO29thDN7HB5myUaHZDBQ,1122
203
+ zrb/builtin/setup/ubuntu.py,sha256=hNvkwYzUrjKcYUzjPhR1mncGUx57XU5hR7BGjVH2X1Q,1104
204
204
  zrb/builtin/setup/zsh/zsh.py,sha256=wy7WSYIie6GprCqH4J2ygcTjWdOeRnxmOZcz8fRykcg,1845
205
205
  zrb/builtin/setup/zsh/zsh_config.sh,sha256=SRkcXvVT3tdfS1UDT0-dSj2PKXPLohhyakY6tUEQPjc,4764
206
206
  zrb/builtin/setup/zsh/zsh_helper.py,sha256=1zF1FH0oEPVAVhMA20tsdk1H0RPMCkLusYX8twsTbGI,393
@@ -217,7 +217,7 @@ zrb/callback/callback.py,sha256=PFhCqzfxdk6IAthmXcZ13DokT62xtBzJr_ciLw6I8Zg,4030
217
217
  zrb/cmd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
218
218
  zrb/cmd/cmd_result.py,sha256=L8bQJzWCpcYexIxHBNsXj2pT3BtLmWex0iJSMkvimOA,597
219
219
  zrb/cmd/cmd_val.py,sha256=7Doowyg6BK3ISSGBLt-PmlhzaEkBjWWm51cED6fAUOQ,1014
220
- zrb/config/config.py,sha256=9yl4LtPpROvjYMqdDy3hXjlD-LA8zQXFrcsO5XWXmSw,13017
220
+ zrb/config/config.py,sha256=YKw6DGD9oJDhKFjEdxuJlOXovtqD1dGw99At3z-mOW8,13589
221
221
  zrb/config/default_prompt/file_extractor_system_prompt.md,sha256=tmeZMPzF9MGExsZZw7M2PZN6V0oFVRp1nIjiqUPvQ9M,1013
222
222
  zrb/config/default_prompt/interactive_system_prompt.md,sha256=ZFPeDEV2vlcksHiVG2o-TCehmqkFolDjtH0_Fzo1gGI,3566
223
223
  zrb/config/default_prompt/persona.md,sha256=WU4JKp-p7qJePDA6NZ_CYdBggo2B3PEq8IEnNVblIHU,41
@@ -346,7 +346,7 @@ zrb/task/base_trigger.py,sha256=WSGcmBcGAZw8EzUXfmCjqJQkz8GEmi1RzogpF6A1V4s,6902
346
346
  zrb/task/cmd_task.py,sha256=myM8WZm6NrUD-Wv0Vb5sTOrutrAVZLt5LVsSBKwX6SM,10860
347
347
  zrb/task/http_check.py,sha256=Gf5rOB2Se2EdizuN9rp65HpGmfZkGc-clIAlHmPVehs,2565
348
348
  zrb/task/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
349
- zrb/task/llm/agent.py,sha256=bdrsmozPxSrUQXhv4FUs_BhIeSppQcVn2dOm40rySW4,7659
349
+ zrb/task/llm/agent.py,sha256=KJKmlLqXsDlIxauQYbTpH_qmgI0KUCkrJwEnRN2YZ3w,8493
350
350
  zrb/task/llm/config.py,sha256=UwTsC3YqQPaNfl75T8wMRZWhBjGQce-xaselRqs37Lc,3788
351
351
  zrb/task/llm/conversation_history.py,sha256=B_PDWYL_q66s0xwWBzMSomqPN6u3gkXlIeXBD5A0Apg,4416
352
352
  zrb/task/llm/conversation_history_model.py,sha256=DJ0KDBB0BriQuE5ugC_q0aSHhjNIBcfjUk1f0S_3I9U,9245
@@ -354,12 +354,12 @@ zrb/task/llm/default_workflow/coding.md,sha256=2uythvPsnBpYfIhiIH1cCinQXX0i0yUqs
354
354
  zrb/task/llm/default_workflow/copywriting.md,sha256=xSO7GeDolwGxiuz6kXsK2GKGpwp8UgtG0yRqTmill_s,1999
355
355
  zrb/task/llm/default_workflow/researching.md,sha256=KD-aYHFHir6Ti-4FsBBtGwiI0seSVgleYbKJZi_POXA,2139
356
356
  zrb/task/llm/error.py,sha256=QR-nIohS6pBpC_16cWR-fw7Mevo1sNYAiXMBsh_CJDE,4157
357
- zrb/task/llm/history_summarization.py,sha256=_0RmzIeJdJA3KvtdTdKnd2Ga7_7x8C1J2PM0oSn-IYw,8000
358
- zrb/task/llm/print_node.py,sha256=mwdqsO2IVf5rDz-jdH9HXz6MFGCWrZ4Pv2xbUBtoNgc,4179
359
- zrb/task/llm/prompt.py,sha256=pHYuo4cLzuH9VYk2-PAyxng_l1ItXVZl0llZDQcbnWA,9748
357
+ zrb/task/llm/history_summarization.py,sha256=YaK3BR7gJVi9f8G5loosNAaO4y7K3ck_4bJ9GuOChXk,8028
358
+ zrb/task/llm/print_node.py,sha256=Z6J1n024MkYIeO4xGnDHmUHk9yTjJRVZRxAC5schE7w,4242
359
+ zrb/task/llm/prompt.py,sha256=p_SWitTj6twftae0c_GM7mBdlF9kO9TpmmdKaXGJPAY,10460
360
360
  zrb/task/llm/tool_wrapper.py,sha256=6PSxX466u4TraJgd9PdZBSOpo2Xjn_FPw-E-LT6V4DE,8257
361
361
  zrb/task/llm/typing.py,sha256=c8VAuPBw_4A3DxfYdydkgedaP-LU61W9_wj3m3CAX1E,58
362
- zrb/task/llm_task.py,sha256=88yc0SbU2ocWK6VBGoqBy-Lo49ebBTI_KKjsLxDp3sM,13945
362
+ zrb/task/llm_task.py,sha256=O1WJigcpvkZM3-Cu-ru-i9wgiJ4oCqc8V0IPFctH_fQ,14391
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
@@ -388,7 +388,7 @@ zrb/util/codemod/modify_function_call.py,sha256=wbyoRRsM4V9fPYkT5kHN0zpBetpRDq2S
388
388
  zrb/util/codemod/modify_method.py,sha256=5fioXjqNQmrf4CV2qlTZHpViF9PMnNer4FvuKam7byo,6344
389
389
  zrb/util/codemod/modify_module.py,sha256=2mzi_NxJ-kNFo5G0U_Rqb3JoYQl1s6Izt7b_wAl10F0,715
390
390
  zrb/util/cron.py,sha256=UWqqhhM7DDTPx6wjCIdBndnVh3NRu-sdKazp8aZkXh8,3833
391
- zrb/util/file.py,sha256=tm_8qn0vEM8Hz46yXUcvFHfsLtQNqidQaEuB85xqhFE,2806
391
+ zrb/util/file.py,sha256=FBuLpyMLvJMflK2shssct2TA5lBGaIIX2SJKHSZ7DXQ,3027
392
392
  zrb/util/git.py,sha256=GS08OqE-gs6mN-_VqACN82DbI81nzG3FiXeim_-jVAU,8193
393
393
  zrb/util/git_diff_model.py,sha256=Tg2q6oxQ5chryThzjs0cLcKFGM03CnqvusTo_Ug_oX4,369
394
394
  zrb/util/git_subtree.py,sha256=AyQWCWEi2EIzEpYXRnYN55157KMUql0WHj70QNw5PHU,4612
@@ -406,7 +406,7 @@ zrb/util/todo.py,sha256=r9_KYF2-hLKMNjsp6AFK9zivykMrywd-kJ4bCwfdafI,19323
406
406
  zrb/util/todo_model.py,sha256=hhzAX-uFl5rsg7iVX1ULlJOfBtblwQ_ieNUxBWfc-Os,1670
407
407
  zrb/xcom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
408
408
  zrb/xcom/xcom.py,sha256=o79rxR9wphnShrcIushA0Qt71d_p3ZTxjNf7x9hJB78,1571
409
- zrb-1.14.5.dist-info/METADATA,sha256=ZzCGmwveGNaJZ5XMyWK4PlNGJK0hJrgHKkW6MhjUnSs,9777
410
- zrb-1.14.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
411
- zrb-1.14.5.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
412
- zrb-1.14.5.dist-info/RECORD,,
409
+ zrb-1.14.7.dist-info/METADATA,sha256=yfoND3r81K4RGQuI6r3WK_11LXfbQromJetn2qij5eM,9709
410
+ zrb-1.14.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
411
+ zrb-1.14.7.dist-info/entry_points.txt,sha256=-Pg3ElWPfnaSM-XvXqCxEAa-wfVI6BEgcs386s8C8v8,46
412
+ zrb-1.14.7.dist-info/RECORD,,
File without changes