zrb 1.13.1__py3-none-any.whl → 1.21.33__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 (117) hide show
  1. zrb/__init__.py +2 -6
  2. zrb/attr/type.py +10 -7
  3. zrb/builtin/__init__.py +2 -0
  4. zrb/builtin/git.py +12 -1
  5. zrb/builtin/group.py +31 -15
  6. zrb/builtin/http.py +7 -8
  7. zrb/builtin/llm/attachment.py +40 -0
  8. zrb/builtin/llm/chat_completion.py +287 -0
  9. zrb/builtin/llm/chat_session.py +130 -144
  10. zrb/builtin/llm/chat_session_cmd.py +288 -0
  11. zrb/builtin/llm/chat_trigger.py +78 -0
  12. zrb/builtin/llm/history.py +4 -4
  13. zrb/builtin/llm/llm_ask.py +218 -110
  14. zrb/builtin/llm/tool/api.py +74 -62
  15. zrb/builtin/llm/tool/cli.py +56 -21
  16. zrb/builtin/llm/tool/code.py +57 -47
  17. zrb/builtin/llm/tool/file.py +292 -255
  18. zrb/builtin/llm/tool/note.py +84 -0
  19. zrb/builtin/llm/tool/rag.py +25 -18
  20. zrb/builtin/llm/tool/search/__init__.py +1 -0
  21. zrb/builtin/llm/tool/search/brave.py +66 -0
  22. zrb/builtin/llm/tool/search/searxng.py +61 -0
  23. zrb/builtin/llm/tool/search/serpapi.py +61 -0
  24. zrb/builtin/llm/tool/sub_agent.py +53 -26
  25. zrb/builtin/llm/tool/web.py +94 -157
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
  28. zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
  29. zrb/builtin/searxng/config/settings.yml +5671 -0
  30. zrb/builtin/searxng/start.py +21 -0
  31. zrb/builtin/setup/latex/ubuntu.py +1 -0
  32. zrb/builtin/setup/ubuntu.py +1 -1
  33. zrb/builtin/shell/autocomplete/bash.py +4 -3
  34. zrb/builtin/shell/autocomplete/zsh.py +4 -3
  35. zrb/config/config.py +297 -79
  36. zrb/config/default_prompt/file_extractor_system_prompt.md +109 -9
  37. zrb/config/default_prompt/interactive_system_prompt.md +25 -28
  38. zrb/config/default_prompt/persona.md +1 -1
  39. zrb/config/default_prompt/repo_extractor_system_prompt.md +31 -31
  40. zrb/config/default_prompt/repo_summarizer_system_prompt.md +27 -8
  41. zrb/config/default_prompt/summarization_prompt.md +57 -16
  42. zrb/config/default_prompt/system_prompt.md +29 -25
  43. zrb/config/llm_config.py +129 -24
  44. zrb/config/llm_context/config.py +127 -90
  45. zrb/config/llm_context/config_parser.py +1 -7
  46. zrb/config/llm_context/workflow.py +81 -0
  47. zrb/config/llm_rate_limitter.py +100 -47
  48. zrb/context/any_shared_context.py +7 -1
  49. zrb/context/context.py +8 -2
  50. zrb/context/shared_context.py +6 -8
  51. zrb/group/any_group.py +12 -5
  52. zrb/group/group.py +67 -3
  53. zrb/input/any_input.py +5 -1
  54. zrb/input/base_input.py +18 -6
  55. zrb/input/option_input.py +13 -1
  56. zrb/input/text_input.py +7 -24
  57. zrb/runner/cli.py +21 -20
  58. zrb/runner/common_util.py +24 -19
  59. zrb/runner/web_route/task_input_api_route.py +5 -5
  60. zrb/runner/web_route/task_session_api_route.py +1 -4
  61. zrb/runner/web_util/user.py +7 -3
  62. zrb/session/any_session.py +12 -6
  63. zrb/session/session.py +39 -18
  64. zrb/task/any_task.py +24 -3
  65. zrb/task/base/context.py +17 -9
  66. zrb/task/base/execution.py +15 -8
  67. zrb/task/base/lifecycle.py +8 -4
  68. zrb/task/base/monitoring.py +12 -7
  69. zrb/task/base_task.py +69 -5
  70. zrb/task/base_trigger.py +12 -5
  71. zrb/task/llm/agent.py +130 -145
  72. zrb/task/llm/agent_runner.py +152 -0
  73. zrb/task/llm/config.py +45 -13
  74. zrb/task/llm/conversation_history.py +110 -29
  75. zrb/task/llm/conversation_history_model.py +4 -179
  76. zrb/task/llm/default_workflow/coding/workflow.md +41 -0
  77. zrb/task/llm/default_workflow/copywriting/workflow.md +68 -0
  78. zrb/task/llm/default_workflow/git/workflow.md +118 -0
  79. zrb/task/llm/default_workflow/golang/workflow.md +128 -0
  80. zrb/task/llm/default_workflow/html-css/workflow.md +135 -0
  81. zrb/task/llm/default_workflow/java/workflow.md +146 -0
  82. zrb/task/llm/default_workflow/javascript/workflow.md +158 -0
  83. zrb/task/llm/default_workflow/python/workflow.md +160 -0
  84. zrb/task/llm/default_workflow/researching/workflow.md +153 -0
  85. zrb/task/llm/default_workflow/rust/workflow.md +162 -0
  86. zrb/task/llm/default_workflow/shell/workflow.md +299 -0
  87. zrb/task/llm/file_replacement.py +206 -0
  88. zrb/task/llm/file_tool_model.py +57 -0
  89. zrb/task/llm/history_processor.py +206 -0
  90. zrb/task/llm/history_summarization.py +2 -192
  91. zrb/task/llm/print_node.py +192 -64
  92. zrb/task/llm/prompt.py +198 -153
  93. zrb/task/llm/subagent_conversation_history.py +41 -0
  94. zrb/task/llm/tool_confirmation_completer.py +41 -0
  95. zrb/task/llm/tool_wrapper.py +216 -55
  96. zrb/task/llm/workflow.py +76 -0
  97. zrb/task/llm_task.py +122 -70
  98. zrb/task/make_task.py +2 -3
  99. zrb/task/rsync_task.py +25 -10
  100. zrb/task/scheduler.py +4 -4
  101. zrb/util/attr.py +54 -39
  102. zrb/util/cli/markdown.py +12 -0
  103. zrb/util/cli/text.py +30 -0
  104. zrb/util/file.py +27 -11
  105. zrb/util/git.py +2 -2
  106. zrb/util/{llm/prompt.py → markdown.py} +2 -3
  107. zrb/util/string/conversion.py +1 -1
  108. zrb/util/truncate.py +23 -0
  109. zrb/util/yaml.py +204 -0
  110. zrb/xcom/xcom.py +10 -0
  111. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/METADATA +40 -20
  112. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/RECORD +114 -83
  113. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/WHEEL +1 -1
  114. zrb/task/llm/default_workflow/coding.md +0 -24
  115. zrb/task/llm/default_workflow/copywriting.md +0 -17
  116. zrb/task/llm/default_workflow/researching.md +0 -18
  117. {zrb-1.13.1.dist-info → zrb-1.21.33.dist-info}/entry_points.txt +0 -0
zrb/config/config.py CHANGED
@@ -24,6 +24,18 @@ 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 | list[str], default: str = "") -> str:
32
+ env_name_list = env_name if isinstance(env_name, list) else [env_name]
33
+ for env_name in env_name_list:
34
+ value = os.getenv(f"{self.ENV_PREFIX}_{env_name}", None)
35
+ if value is not None:
36
+ return value
37
+ return default
38
+
27
39
  def _get_internal_default_prompt(self, name: str) -> str:
28
40
  if name not in self.__internal_default_prompt:
29
41
  file_path = os.path.join(
@@ -39,7 +51,7 @@ class Config:
39
51
 
40
52
  @property
41
53
  def DEFAULT_SHELL(self) -> str:
42
- return os.getenv("ZRB_SHELL", self._get_current_shell())
54
+ return self._getenv("SHELL", self._get_current_shell())
43
55
 
44
56
  def _get_current_shell(self) -> str:
45
57
  if platform.system() == "Windows":
@@ -51,11 +63,43 @@ class Config:
51
63
 
52
64
  @property
53
65
  def DEFAULT_EDITOR(self) -> str:
54
- return os.getenv("ZRB_EDITOR", "nano")
66
+ return self._getenv("EDITOR", "nano")
67
+
68
+ @property
69
+ def DEFAULT_DIFF_EDIT_COMMAND_TPL(self) -> str:
70
+ return self._getenv("DIFF_EDIT_COMMAND", self._get_default_diff_edit_command())
71
+
72
+ def _get_default_diff_edit_command(self) -> str:
73
+ editor = self.DEFAULT_EDITOR
74
+ if editor in [
75
+ "code",
76
+ "vscode",
77
+ "vscodium",
78
+ "windsurf",
79
+ "cursor",
80
+ "zed",
81
+ "zeditor",
82
+ "agy",
83
+ ]:
84
+ return f"{editor} --wait --diff {{old}} {{new}}"
85
+ if editor == "emacs":
86
+ return 'emacs --eval \'(ediff-files "{old}" "{new}")\''
87
+ if editor in ["nvim", "vim"]:
88
+ return (
89
+ f"{editor} -d {{old}} {{new}} "
90
+ "-i NONE "
91
+ '-c "wincmd h | set readonly | wincmd l" '
92
+ '-c "highlight DiffAdd cterm=bold ctermbg=22 guibg=#005f00 | highlight DiffChange cterm=bold ctermbg=24 guibg=#005f87 | highlight DiffText ctermbg=21 guibg=#0000af | highlight DiffDelete ctermbg=52 guibg=#5f0000" ' # noqa
93
+ '-c "set showtabline=2 | set tabline=[Instructions]\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)" ' # noqa
94
+ '-c "wincmd h | setlocal statusline=OLD\\ FILE" '
95
+ '-c "wincmd l | setlocal statusline=%#StatusBold#NEW\\ FILE\\ :wqa(save\\ &\\ quit)\\ \\|\\ i/esc(toggle\\ edit\\ mode)" ' # noqa
96
+ '-c "autocmd BufWritePost * wqa"'
97
+ )
98
+ return 'vimdiff {old} {new} +"setlocal ro" +"wincmd l" +"autocmd BufWritePost <buffer> qa"' # noqa
55
99
 
56
100
  @property
57
101
  def INIT_MODULES(self) -> list[str]:
58
- init_modules_str = os.getenv("ZRB_INIT_MODULES", "")
102
+ init_modules_str = self._getenv("INIT_MODULES", "")
59
103
  if init_modules_str != "":
60
104
  return [
61
105
  module.strip()
@@ -66,15 +110,15 @@ class Config:
66
110
 
67
111
  @property
68
112
  def ROOT_GROUP_NAME(self) -> str:
69
- return os.getenv("ZRB_ROOT_GROUP_NAME", "zrb")
113
+ return self._getenv("ROOT_GROUP_NAME", "zrb")
70
114
 
71
115
  @property
72
116
  def ROOT_GROUP_DESCRIPTION(self) -> str:
73
- return os.getenv("ZRB_ROOT_GROUP_DESCRIPTION", "Your Automation Powerhouse")
117
+ return self._getenv("ROOT_GROUP_DESCRIPTION", "Your Automation Powerhouse")
74
118
 
75
119
  @property
76
120
  def INIT_SCRIPTS(self) -> list[str]:
77
- init_scripts_str = os.getenv("ZRB_INIT_SCRIPTS", "")
121
+ init_scripts_str = self._getenv("INIT_SCRIPTS", "")
78
122
  if init_scripts_str != "":
79
123
  return [
80
124
  script.strip()
@@ -85,16 +129,17 @@ class Config:
85
129
 
86
130
  @property
87
131
  def INIT_FILE_NAME(self) -> str:
88
- return os.getenv("ZRB_INIT_FILE_NAME", "zrb_init.py")
132
+ return self._getenv("INIT_FILE_NAME", "zrb_init.py")
89
133
 
90
134
  @property
91
135
  def LOGGING_LEVEL(self) -> int:
92
- return self._get_log_level(os.getenv("ZRB_LOGGING_LEVEL", "WARNING"))
136
+ return self._get_log_level(self._getenv("LOGGING_LEVEL", "WARNING"))
93
137
 
94
138
  def _get_log_level(self, level: str) -> int:
95
139
  level = level.upper()
96
140
  log_levels = {
97
141
  "CRITICAL": logging.CRITICAL, # 50
142
+ "FATAL": logging.CRITICAL, # 50
98
143
  "ERROR": logging.ERROR, # 40
99
144
  "WARN": logging.WARNING, # 30
100
145
  "WARNING": logging.WARNING, # 30
@@ -108,11 +153,11 @@ class Config:
108
153
 
109
154
  @property
110
155
  def LOAD_BUILTIN(self) -> bool:
111
- return to_boolean(os.getenv("ZRB_LOAD_BUILTIN", "1"))
156
+ return to_boolean(self._getenv("LOAD_BUILTIN", "1"))
112
157
 
113
158
  @property
114
159
  def WARN_UNRECOMMENDED_COMMAND(self) -> bool:
115
- return to_boolean(os.getenv("ZRB_WARN_UNRECOMMENDED_COMMAND", "1"))
160
+ return to_boolean(self._getenv("WARN_UNRECOMMENDED_COMMAND", "1"))
116
161
 
117
162
  @property
118
163
  def SESSION_LOG_DIR(self) -> str:
@@ -122,15 +167,15 @@ class Config:
122
167
 
123
168
  @property
124
169
  def TODO_DIR(self) -> str:
125
- return os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
170
+ return self._getenv("TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
126
171
 
127
172
  @property
128
173
  def TODO_VISUAL_FILTER(self) -> str:
129
- return os.getenv("ZRB_TODO_FILTER", "")
174
+ return self._getenv("TODO_FILTER", "")
130
175
 
131
176
  @property
132
177
  def TODO_RETENTION(self) -> str:
133
- return os.getenv("ZRB_TODO_RETENTION", "2w")
178
+ return self._getenv("TODO_RETENTION", "2w")
134
179
 
135
180
  @property
136
181
  def VERSION(self) -> str:
@@ -141,7 +186,7 @@ class Config:
141
186
 
142
187
  @property
143
188
  def WEB_CSS_PATH(self) -> list[str]:
144
- web_css_path_str = os.getenv("ZRB_WEB_CSS_PATH", "")
189
+ web_css_path_str = self._getenv("WEB_CSS_PATH", "")
145
190
  if web_css_path_str != "":
146
191
  return [
147
192
  path.strip()
@@ -152,7 +197,7 @@ class Config:
152
197
 
153
198
  @property
154
199
  def WEB_JS_PATH(self) -> list[str]:
155
- web_js_path_str = os.getenv("ZRB_WEB_JS_PATH", "")
200
+ web_js_path_str = self._getenv("WEB_JS_PATH", "")
156
201
  if web_js_path_str != "":
157
202
  return [
158
203
  path.strip()
@@ -163,103 +208,146 @@ class Config:
163
208
 
164
209
  @property
165
210
  def WEB_FAVICON_PATH(self) -> str:
166
- return os.getenv("ZRB_WEB_FAVICON_PATH", "/static/favicon-32x32.png")
211
+ return self._getenv("WEB_FAVICON_PATH", "/static/favicon-32x32.png")
167
212
 
168
213
  @property
169
214
  def WEB_COLOR(self) -> str:
170
- return os.getenv("ZRB_WEB_COLOR", "")
215
+ return self._getenv("WEB_COLOR", "")
171
216
 
172
217
  @property
173
218
  def WEB_HTTP_PORT(self) -> int:
174
- return int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
219
+ return int(self._getenv("WEB_HTTP_PORT", "21213"))
175
220
 
176
221
  @property
177
222
  def WEB_GUEST_USERNAME(self) -> str:
178
- return os.getenv("ZRB_WEB_GUEST_USERNAME", "user")
223
+ return self._getenv("WEB_GUEST_USERNAME", "user")
179
224
 
180
225
  @property
181
226
  def WEB_SUPER_ADMIN_USERNAME(self) -> str:
182
- return os.getenv("ZRB_WEB_SUPER_ADMIN_USERNAME", "admin")
227
+ return self._getenv("WEB_SUPER_ADMIN_USERNAME", "admin")
183
228
 
184
229
  @property
185
230
  def WEB_SUPER_ADMIN_PASSWORD(self) -> str:
186
- return os.getenv("ZRB_WEB_SUPER_ADMIN_PASSWORD", "admin")
231
+ return self._getenv("WEB_SUPER_ADMIN_PASSWORD", "admin")
187
232
 
188
233
  @property
189
234
  def WEB_ACCESS_TOKEN_COOKIE_NAME(self) -> str:
190
- return os.getenv("ZRB_WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token")
235
+ return self._getenv("WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token")
191
236
 
192
237
  @property
193
238
  def WEB_REFRESH_TOKEN_COOKIE_NAME(self) -> str:
194
- return os.getenv("ZRB_WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token")
239
+ return self._getenv("WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token")
195
240
 
196
241
  @property
197
242
  def WEB_SECRET_KEY(self) -> str:
198
- return os.getenv("ZRB_WEB_SECRET", "zrb")
243
+ return self._getenv("WEB_SECRET", "zrb")
199
244
 
200
245
  @property
201
246
  def WEB_ENABLE_AUTH(self) -> bool:
202
- return to_boolean(os.getenv("ZRB_WEB_ENABLE_AUTH", "0"))
247
+ return to_boolean(self._getenv("WEB_ENABLE_AUTH", "0"))
203
248
 
204
249
  @property
205
250
  def WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES(self) -> int:
206
- return int(os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
251
+ return int(self._getenv("WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
207
252
 
208
253
  @property
209
254
  def WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES(self) -> int:
210
- return int(os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60"))
255
+ return int(self._getenv("WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60"))
211
256
 
212
257
  @property
213
258
  def WEB_TITLE(self) -> str:
214
- return os.getenv("ZRB_WEB_TITLE", "Zrb")
259
+ return self._getenv("WEB_TITLE", "Zrb")
215
260
 
216
261
  @property
217
262
  def WEB_JARGON(self) -> str:
218
- return os.getenv("ZRB_WEB_JARGON", "Your Automation PowerHouse")
263
+ return self._getenv("WEB_JARGON", "Your Automation PowerHouse")
219
264
 
220
265
  @property
221
266
  def WEB_HOMEPAGE_INTRO(self) -> str:
222
- return os.getenv("ZRB_WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface")
267
+ return self._getenv("WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface")
223
268
 
224
269
  @property
225
270
  def LLM_MODEL(self) -> str | None:
226
- return os.getenv("ZRB_LLM_MODEL", None)
271
+ value = self._getenv("LLM_MODEL")
272
+ return None if value == "" else value
227
273
 
228
274
  @property
229
275
  def LLM_BASE_URL(self) -> str | None:
230
- return os.getenv("ZRB_LLM_BASE_URL", None)
276
+ value = self._getenv("LLM_BASE_URL")
277
+ return None if value == "" else value
231
278
 
232
279
  @property
233
280
  def LLM_API_KEY(self) -> str | None:
234
- return os.getenv("ZRB_LLM_API_KEY", None)
281
+ value = self._getenv("LLM_API_KEY")
282
+ return None if value == "" else value
283
+
284
+ @property
285
+ def LLM_MODEL_SMALL(self) -> str | None:
286
+ value = self._getenv("LLM_MODEL_SMALL")
287
+ return None if value == "" else value
288
+
289
+ @property
290
+ def LLM_BASE_URL_SMALL(self) -> str | None:
291
+ value = self._getenv("LLM_BASE_URL_SMALL")
292
+ return None if value == "" else value
293
+
294
+ @property
295
+ def LLM_API_KEY_SMALL(self) -> str | None:
296
+ value = self._getenv("LLM_API_KEY_SMALL")
297
+ return None if value == "" else value
235
298
 
236
299
  @property
237
300
  def LLM_SYSTEM_PROMPT(self) -> str | None:
238
- return os.getenv("ZRB_LLM_SYSTEM_PROMPT", None)
301
+ value = self._getenv("LLM_SYSTEM_PROMPT")
302
+ return None if value == "" else value
239
303
 
240
304
  @property
241
305
  def LLM_INTERACTIVE_SYSTEM_PROMPT(self) -> str | None:
242
- return os.getenv("ZRB_LLM_INTERACTIVE_SYSTEM_PROMPT", None)
306
+ value = self._getenv("LLM_INTERACTIVE_SYSTEM_PROMPT")
307
+ return None if value == "" else value
243
308
 
244
309
  @property
245
310
  def LLM_PERSONA(self) -> str | None:
246
- return os.getenv("ZRB_LLM_PERSONA", None)
311
+ value = self._getenv("LLM_PERSONA")
312
+ return None if value == "" else value
247
313
 
248
314
  @property
249
- def LLM_MODES(self) -> list[str]:
250
- return [
251
- mode.strip()
252
- for mode in os.getenv("ZRB_LLM_MODES", "coding").split(",")
253
- if mode.strip() != ""
254
- ]
315
+ def LLM_WORKFLOWS(self) -> list[str]:
316
+ """Get a list of LLM workflows from environment variables."""
317
+ workflows = []
318
+ for workflow in self._getenv("LLM_WORKFLOWS", "").split(","):
319
+ workflow = workflow.strip()
320
+ if workflow != "":
321
+ workflows.append(workflow)
322
+ return workflows
323
+
324
+ @property
325
+ def LLM_BUILTIN_WORKFLOW_PATHS(self) -> list[str]:
326
+ """Get a list of additional builtin workflow paths from environment variables."""
327
+ builtin_workflow_paths_str = self._getenv(
328
+ ["LLM_BUILTIN_WORFKLOW_PATH", "LLM_BUILTIN_WORKFLOW_PATHS"], ""
329
+ )
330
+ if builtin_workflow_paths_str != "":
331
+ return [
332
+ path.strip()
333
+ for path in builtin_workflow_paths_str.split(":")
334
+ if path.strip() != ""
335
+ ]
336
+ return []
255
337
 
256
338
  @property
257
339
  def LLM_SPECIAL_INSTRUCTION_PROMPT(self) -> str | None:
258
- return os.getenv("ZRB_LLM_SPECIAL_INSTRUCTION_PROMPT", None)
340
+ value = self._getenv("LLM_SPECIAL_INSTRUCTION_PROMPT")
341
+ return None if value == "" else value
259
342
 
260
343
  @property
261
344
  def LLM_SUMMARIZATION_PROMPT(self) -> str | None:
262
- return os.getenv("ZRB_LLM_SUMMARIZATION_PROMPT", None)
345
+ value = self._getenv("LLM_SUMMARIZATION_PROMPT")
346
+ return None if value == "" else value
347
+
348
+ @property
349
+ def LLM_SHOW_TOOL_CALL_RESULT(self) -> bool:
350
+ return to_boolean(self._getenv("LLM_SHOW_TOOL_CALL_RESULT", "false"))
263
351
 
264
352
  @property
265
353
  def LLM_MAX_REQUESTS_PER_MINUTE(self) -> int:
@@ -267,7 +355,11 @@ class Config:
267
355
  Maximum number of LLM requests allowed per minute.
268
356
  Default is conservative to accommodate free-tier LLM providers.
269
357
  """
270
- return int(os.getenv("LLM_MAX_REQUESTS_PER_MINUTE", "15"))
358
+ return int(
359
+ self._getenv(
360
+ ["LLM_MAX_REQUEST_PER_MINUTE", "LLM_MAX_REQUESTS_PER_MINUTE"], "60"
361
+ )
362
+ )
271
363
 
272
364
  @property
273
365
  def LLM_MAX_TOKENS_PER_MINUTE(self) -> int:
@@ -275,122 +367,248 @@ class Config:
275
367
  Maximum number of LLM tokens allowed per minute.
276
368
  Default is conservative to accommodate free-tier LLM providers.
277
369
  """
278
- return int(os.getenv("ZRB_LLM_MAX_TOKENS_PER_MINUTE", "100000"))
370
+ return int(
371
+ self._getenv(
372
+ ["LLM_MAX_TOKEN_PER_MINUTE", "LLM_MAX_TOKENS_PER_MINUTE"], "100000"
373
+ )
374
+ )
279
375
 
280
376
  @property
281
377
  def LLM_MAX_TOKENS_PER_REQUEST(self) -> int:
282
378
  """Maximum number of tokens allowed per individual LLM request."""
283
- return int(os.getenv("ZRB_LLM_MAX_TOKENS_PER_REQUEST", "50000"))
379
+ return int(
380
+ self._getenv(
381
+ ["LLM_MAX_TOKEN_PER_REQUEST", "LLM_MAX_TOKENS_PER_REQUEST"], "120000"
382
+ )
383
+ )
384
+
385
+ @property
386
+ def LLM_MAX_TOKENS_PER_TOOL_CALL_RESULT(self) -> int:
387
+ """Maximum number of tokens allowed per tool call result."""
388
+ return int(
389
+ self._getenv(
390
+ [
391
+ "LLM_MAX_TOKEN_PER_TOOL_CALL_RESULT",
392
+ "LLM_MAX_TOKENS_PER_TOOL_CALL_RESULT",
393
+ ],
394
+ str(self._get_max_threshold(0.4)),
395
+ )
396
+ )
284
397
 
285
398
  @property
286
399
  def LLM_THROTTLE_SLEEP(self) -> float:
287
400
  """Number of seconds to sleep when throttling is required."""
288
- return float(os.getenv("ZRB_LLM_THROTTLE_SLEEP", "1.0"))
401
+ return float(self._getenv("LLM_THROTTLE_SLEEP", "5.0"))
289
402
 
290
403
  @property
291
- def LLM_YOLO_MODE(self) -> bool:
292
- return to_boolean(os.getenv("ZRB_LLM_YOLO_MODE", "false"))
404
+ def LLM_YOLO_MODE(self) -> bool | list[str]:
405
+ str_val = self._getenv("LLM_YOLO_MODE", "false")
406
+ try:
407
+ return to_boolean(str_val)
408
+ except Exception:
409
+ return [val.strip() for val in str_val.split(",") if val.strip() != ""]
293
410
 
294
411
  @property
295
412
  def LLM_SUMMARIZE_HISTORY(self) -> bool:
296
- return to_boolean(os.getenv("ZRB_LLM_SUMMARIZE_HISTORY", "true"))
413
+ return to_boolean(self._getenv("LLM_SUMMARIZE_HISTORY", "true"))
297
414
 
298
415
  @property
299
416
  def LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD(self) -> int:
300
- return int(os.getenv("ZRB_LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD", "20000"))
417
+ threshold = int(
418
+ self._getenv(
419
+ "LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD",
420
+ str(self._get_max_threshold(0.6)),
421
+ )
422
+ )
423
+ return self._limit_token_threshold(threshold, 0.6)
301
424
 
302
425
  @property
303
426
  def LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD(self) -> int:
304
- return int(os.getenv("ZRB_LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_LIMIT", "35000"))
427
+ threshold = int(
428
+ self._getenv(
429
+ "LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD",
430
+ str(self._get_max_threshold(0.4)),
431
+ )
432
+ )
433
+ return self._limit_token_threshold(threshold, 0.4)
305
434
 
306
435
  @property
307
436
  def LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD(self) -> int:
308
- return int(
309
- os.getenv("ZRB_LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_LIMIT", "35000")
437
+ threshold = int(
438
+ self._getenv(
439
+ "LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD",
440
+ str(self._get_max_threshold(0.4)),
441
+ )
310
442
  )
443
+ return self._limit_token_threshold(threshold, 0.4)
311
444
 
312
445
  @property
313
- def LLM_FILE_ANALYSIS_TOKEN_LIMIT(self) -> int:
314
- return int(os.getenv("ZRB_LLM_FILE_ANALYSIS_TOKEN_LIMIT", "35000"))
446
+ def LLM_FILE_ANALYSIS_TOKEN_THRESHOLD(self) -> int:
447
+ threshold = int(
448
+ self._getenv(
449
+ "LLM_FILE_ANALYSIS_TOKEN_THRESHOLD", str(self._get_max_threshold(0.4))
450
+ )
451
+ )
452
+ return self._limit_token_threshold(threshold, 0.4)
453
+
454
+ def _limit_token_threshold(self, threshold: int, factor: float) -> int:
455
+ return min(threshold, self._get_max_threshold(factor))
456
+
457
+ def _get_max_threshold(self, factor: float) -> int:
458
+ return round(
459
+ factor
460
+ * min(self.LLM_MAX_TOKENS_PER_MINUTE, self.LLM_MAX_TOKENS_PER_REQUEST)
461
+ )
315
462
 
316
463
  @property
317
464
  def LLM_FILE_EXTRACTOR_SYSTEM_PROMPT(self) -> str:
318
- return os.getenv(
319
- "ZRB_LLM_FILE_EXTRACTOR_SYSTEM_PROMPT",
465
+ return self._getenv(
466
+ "LLM_FILE_EXTRACTOR_SYSTEM_PROMPT",
320
467
  self._get_internal_default_prompt("file_extractor_system_prompt"),
321
468
  )
322
469
 
323
470
  @property
324
471
  def LLM_REPO_EXTRACTOR_SYSTEM_PROMPT(self) -> str:
325
- return os.getenv(
326
- "ZRB_LLM_REPO_EXTRACTOR_SYSTEM_PROMPT",
472
+ return self._getenv(
473
+ "LLM_REPO_EXTRACTOR_SYSTEM_PROMPT",
327
474
  self._get_internal_default_prompt("repo_extractor_system_prompt"),
328
475
  )
329
476
 
330
477
  @property
331
478
  def LLM_REPO_SUMMARIZER_SYSTEM_PROMPT(self) -> str:
332
- return os.getenv(
333
- "ZRB_LLM_REPO_SUMMARIZER_SYSTEM_PROMPT",
479
+ return self._getenv(
480
+ "LLM_REPO_SUMMARIZER_SYSTEM_PROMPT",
334
481
  self._get_internal_default_prompt("repo_summarizer_system_prompt"),
335
482
  )
336
483
 
337
484
  @property
338
485
  def LLM_HISTORY_DIR(self) -> str:
339
- return os.getenv(
340
- "ZRB_LLM_HISTORY_DIR",
486
+ return self._getenv(
487
+ "LLM_HISTORY_DIR",
341
488
  os.path.expanduser(os.path.join("~", ".zrb-llm-history")),
342
489
  )
343
490
 
344
491
  @property
345
492
  def LLM_ALLOW_ACCESS_LOCAL_FILE(self) -> bool:
346
- return to_boolean(os.getenv("ZRB_LLM_ACCESS_LOCAL_FILE", "1"))
493
+ return to_boolean(self._getenv("LLM_ALLOW_ACCESS_LOCAL_FILE", "1"))
494
+
495
+ @property
496
+ def LLM_ALLOW_ANALYZE_FILE(self) -> bool:
497
+ return to_boolean(self._getenv("LLM_ALLOW_ANALYZE_LOCAL_FILE", "1"))
498
+
499
+ @property
500
+ def LLM_ALLOW_ANALYZE_REPO(self) -> bool:
501
+ return to_boolean(self._getenv("LLM_ALLOW_ANALYZE_REPO", "1"))
347
502
 
348
503
  @property
349
504
  def LLM_ALLOW_ACCESS_SHELL(self) -> bool:
350
- return to_boolean(os.getenv("ZRB_LLM_ACCESS_SHELL", "1"))
505
+ return to_boolean(self._getenv("LLM_ALLOW_ACCESS_SHELL", "1"))
351
506
 
352
507
  @property
353
- def LLM_ALLOW_ACCESS_INTERNET(self) -> bool:
354
- return to_boolean(os.getenv("ZRB_LLM_ACCESS_INTERNET", "1"))
508
+ def LLM_ALLOW_OPEN_WEB_PAGE(self) -> bool:
509
+ return to_boolean(self._getenv("LLM_ALLOW_OPEN_WEB_PAGE", "1"))
355
510
 
356
511
  @property
357
- def RAG_EMBEDDING_API_KEY(self) -> str:
358
- return os.getenv("ZRB_RAG_EMBEDDING_API_KEY", None)
512
+ def LLM_ALLOW_SEARCH_INTERNET(self) -> bool:
513
+ return to_boolean(self._getenv("LLM_ALLOW_SEARCH_INTERNET", "1"))
359
514
 
360
515
  @property
361
- def RAG_EMBEDDING_BASE_URL(self) -> str:
362
- return os.getenv("ZRB_RAG_EMBEDDING_BASE_URL", None)
516
+ def LLM_ALLOW_GET_CURRENT_LOCATION(self) -> bool:
517
+ return to_boolean(self._getenv("LLM_ALLOW_GET_CURRENT_LOCATION", "1"))
518
+
519
+ @property
520
+ def LLM_ALLOW_GET_CURRENT_WEATHER(self) -> bool:
521
+ return to_boolean(self._getenv("LLM_ALLOW_GET_CURRENT_WEATHER", "1"))
522
+
523
+ @property
524
+ def RAG_EMBEDDING_API_KEY(self) -> str | None:
525
+ value = self._getenv("RAG_EMBEDDING_API_KEY")
526
+ return None if value == "" else value
527
+
528
+ @property
529
+ def RAG_EMBEDDING_BASE_URL(self) -> str | None:
530
+ value = self._getenv("RAG_EMBEDDING_BASE_URL")
531
+ return None if value == "" else value
363
532
 
364
533
  @property
365
534
  def RAG_EMBEDDING_MODEL(self) -> str:
366
- return os.getenv("ZRB_RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
535
+ return self._getenv("RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
367
536
 
368
537
  @property
369
538
  def RAG_CHUNK_SIZE(self) -> int:
370
- return int(os.getenv("ZRB_RAG_CHUNK_SIZE", "1024"))
539
+ return int(self._getenv("RAG_CHUNK_SIZE", "1024"))
371
540
 
372
541
  @property
373
542
  def RAG_OVERLAP(self) -> int:
374
- return int(os.getenv("ZRB_RAG_OVERLAP", "128"))
543
+ return int(self._getenv("RAG_OVERLAP", "128"))
375
544
 
376
545
  @property
377
546
  def RAG_MAX_RESULT_COUNT(self) -> int:
378
- return int(os.getenv("ZRB_RAG_MAX_RESULT_COUNT", "5"))
547
+ return int(self._getenv("RAG_MAX_RESULT_COUNT", "5"))
548
+
549
+ @property
550
+ def SEARCH_INTERNET_METHOD(self) -> str:
551
+ """Either serpapi or searxng"""
552
+ return self._getenv("SEARCH_INTERNET_METHOD", "serpapi")
553
+
554
+ @property
555
+ def BRAVE_API_KEY(self) -> str:
556
+ return os.getenv("BRAVE_API_KEY", "")
557
+
558
+ @property
559
+ def BRAVE_API_SAFE(self) -> str:
560
+ return self._getenv("BRAVE_API_SAFE", "off")
561
+
562
+ @property
563
+ def BRAVE_API_LANG(self) -> str:
564
+ return self._getenv("BRAVE_API_LANG", "en")
379
565
 
380
566
  @property
381
567
  def SERPAPI_KEY(self) -> str:
382
568
  return os.getenv("SERPAPI_KEY", "")
383
569
 
570
+ @property
571
+ def SERPAPI_SAFE(self) -> str:
572
+ return self._getenv("SERPAPI_SAFE", "off")
573
+
574
+ @property
575
+ def SERPAPI_LANG(self) -> str:
576
+ return self._getenv("SERPAPI_LANG", "en")
577
+
578
+ @property
579
+ def SEARXNG_PORT(self) -> int:
580
+ return int(self._getenv("SEARXNG_PORT", "8080"))
581
+
582
+ @property
583
+ def SEARXNG_BASE_URL(self) -> str:
584
+ return self._getenv("SEARXNG_BASE_URL", f"http://localhost:{self.SEARXNG_PORT}")
585
+
586
+ @property
587
+ def SEARXNG_SAFE(self) -> int:
588
+ return int(self._getenv("SEARXNG_SAFE", "0"))
589
+
590
+ @property
591
+ def SEARXNG_LANG(self) -> str:
592
+ return self._getenv("SEARXNG_LANG", "en")
593
+
384
594
  @property
385
595
  def BANNER(self) -> str:
386
596
  return fstring_format(
387
- os.getenv("ZRB_BANNER", _DEFAULT_BANNER),
597
+ self._getenv("BANNER", _DEFAULT_BANNER),
388
598
  {"VERSION": self.VERSION},
389
599
  )
390
600
 
391
601
  @property
392
602
  def LLM_CONTEXT_FILE(self) -> str:
393
- return os.getenv("LLM_CONTEXT_FILE", "ZRB.md")
603
+ return self._getenv("LLM_CONTEXT_FILE", "ZRB.md")
604
+
605
+ @property
606
+ def USE_TIKTOKEN(self) -> bool:
607
+ return to_boolean(self._getenv("USE_TIKTOKEN", "true"))
608
+
609
+ @property
610
+ def TIKTOKEN_ENCODING_NAME(self) -> str:
611
+ return self._getenv("TIKTOKEN_ENCODING_NAME", "cl100k_base")
394
612
 
395
613
 
396
614
  CFG = Config()