zrb 1.21.29__py3-none-any.whl → 2.0.0a4__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.

Potentially problematic release.


This version of zrb might be problematic. Click here for more details.

Files changed (192) hide show
  1. zrb/__init__.py +118 -129
  2. zrb/builtin/__init__.py +54 -2
  3. zrb/builtin/llm/chat.py +147 -0
  4. zrb/callback/callback.py +8 -1
  5. zrb/cmd/cmd_result.py +2 -1
  6. zrb/config/config.py +491 -280
  7. zrb/config/helper.py +84 -0
  8. zrb/config/web_auth_config.py +50 -35
  9. zrb/context/any_shared_context.py +13 -2
  10. zrb/context/context.py +31 -3
  11. zrb/context/print_fn.py +13 -0
  12. zrb/context/shared_context.py +14 -1
  13. zrb/input/option_input.py +30 -2
  14. zrb/llm/agent/__init__.py +9 -0
  15. zrb/llm/agent/agent.py +215 -0
  16. zrb/llm/agent/summarizer.py +20 -0
  17. zrb/llm/app/__init__.py +10 -0
  18. zrb/llm/app/completion.py +281 -0
  19. zrb/llm/app/confirmation/allow_tool.py +66 -0
  20. zrb/llm/app/confirmation/handler.py +178 -0
  21. zrb/llm/app/confirmation/replace_confirmation.py +77 -0
  22. zrb/llm/app/keybinding.py +34 -0
  23. zrb/llm/app/layout.py +117 -0
  24. zrb/llm/app/lexer.py +155 -0
  25. zrb/llm/app/redirection.py +28 -0
  26. zrb/llm/app/style.py +16 -0
  27. zrb/llm/app/ui.py +733 -0
  28. zrb/llm/config/__init__.py +4 -0
  29. zrb/llm/config/config.py +122 -0
  30. zrb/llm/config/limiter.py +247 -0
  31. zrb/llm/history_manager/__init__.py +4 -0
  32. zrb/llm/history_manager/any_history_manager.py +23 -0
  33. zrb/llm/history_manager/file_history_manager.py +91 -0
  34. zrb/llm/history_processor/summarizer.py +108 -0
  35. zrb/llm/note/__init__.py +3 -0
  36. zrb/llm/note/manager.py +122 -0
  37. zrb/llm/prompt/__init__.py +29 -0
  38. zrb/llm/prompt/claude_compatibility.py +92 -0
  39. zrb/llm/prompt/compose.py +55 -0
  40. zrb/llm/prompt/default.py +51 -0
  41. zrb/llm/prompt/markdown/mandate.md +23 -0
  42. zrb/llm/prompt/markdown/persona.md +3 -0
  43. zrb/llm/prompt/markdown/summarizer.md +21 -0
  44. zrb/llm/prompt/note.py +41 -0
  45. zrb/llm/prompt/system_context.py +46 -0
  46. zrb/llm/prompt/zrb.py +41 -0
  47. zrb/llm/skill/__init__.py +3 -0
  48. zrb/llm/skill/manager.py +86 -0
  49. zrb/llm/task/__init__.py +4 -0
  50. zrb/llm/task/llm_chat_task.py +316 -0
  51. zrb/llm/task/llm_task.py +245 -0
  52. zrb/llm/tool/__init__.py +39 -0
  53. zrb/llm/tool/bash.py +75 -0
  54. zrb/llm/tool/code.py +266 -0
  55. zrb/llm/tool/file.py +419 -0
  56. zrb/llm/tool/note.py +70 -0
  57. zrb/{builtin/llm → llm}/tool/rag.py +8 -5
  58. zrb/llm/tool/search/brave.py +53 -0
  59. zrb/llm/tool/search/searxng.py +47 -0
  60. zrb/llm/tool/search/serpapi.py +47 -0
  61. zrb/llm/tool/skill.py +19 -0
  62. zrb/llm/tool/sub_agent.py +70 -0
  63. zrb/llm/tool/web.py +97 -0
  64. zrb/llm/tool/zrb_task.py +66 -0
  65. zrb/llm/util/attachment.py +101 -0
  66. zrb/llm/util/prompt.py +104 -0
  67. zrb/llm/util/stream_response.py +178 -0
  68. zrb/session/any_session.py +0 -3
  69. zrb/session/session.py +1 -1
  70. zrb/task/base/context.py +25 -13
  71. zrb/task/base/execution.py +52 -47
  72. zrb/task/base/lifecycle.py +7 -4
  73. zrb/task/base_task.py +48 -49
  74. zrb/task/base_trigger.py +4 -1
  75. zrb/task/cmd_task.py +6 -0
  76. zrb/task/http_check.py +11 -5
  77. zrb/task/make_task.py +3 -0
  78. zrb/task/rsync_task.py +5 -0
  79. zrb/task/scaffolder.py +7 -4
  80. zrb/task/scheduler.py +3 -0
  81. zrb/task/tcp_check.py +6 -4
  82. zrb/util/ascii_art/art/bee.txt +17 -0
  83. zrb/util/ascii_art/art/cat.txt +9 -0
  84. zrb/util/ascii_art/art/ghost.txt +16 -0
  85. zrb/util/ascii_art/art/panda.txt +17 -0
  86. zrb/util/ascii_art/art/rose.txt +14 -0
  87. zrb/util/ascii_art/art/unicorn.txt +15 -0
  88. zrb/util/ascii_art/banner.py +92 -0
  89. zrb/util/cli/markdown.py +22 -2
  90. zrb/util/cmd/command.py +33 -10
  91. zrb/util/file.py +51 -32
  92. zrb/util/match.py +78 -0
  93. zrb/util/run.py +3 -3
  94. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/METADATA +9 -15
  95. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/RECORD +100 -128
  96. zrb/attr/__init__.py +0 -0
  97. zrb/builtin/llm/attachment.py +0 -40
  98. zrb/builtin/llm/chat_completion.py +0 -274
  99. zrb/builtin/llm/chat_session.py +0 -270
  100. zrb/builtin/llm/chat_session_cmd.py +0 -288
  101. zrb/builtin/llm/chat_trigger.py +0 -79
  102. zrb/builtin/llm/history.py +0 -71
  103. zrb/builtin/llm/input.py +0 -27
  104. zrb/builtin/llm/llm_ask.py +0 -269
  105. zrb/builtin/llm/previous-session.js +0 -21
  106. zrb/builtin/llm/tool/__init__.py +0 -0
  107. zrb/builtin/llm/tool/api.py +0 -75
  108. zrb/builtin/llm/tool/cli.py +0 -52
  109. zrb/builtin/llm/tool/code.py +0 -236
  110. zrb/builtin/llm/tool/file.py +0 -560
  111. zrb/builtin/llm/tool/note.py +0 -84
  112. zrb/builtin/llm/tool/sub_agent.py +0 -150
  113. zrb/builtin/llm/tool/web.py +0 -171
  114. zrb/builtin/project/__init__.py +0 -0
  115. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
  116. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
  117. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
  118. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
  119. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
  120. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
  121. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
  122. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
  123. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
  124. zrb/builtin/project/create/__init__.py +0 -0
  125. zrb/builtin/shell/__init__.py +0 -0
  126. zrb/builtin/shell/autocomplete/__init__.py +0 -0
  127. zrb/callback/__init__.py +0 -0
  128. zrb/cmd/__init__.py +0 -0
  129. zrb/config/default_prompt/interactive_system_prompt.md +0 -29
  130. zrb/config/default_prompt/persona.md +0 -1
  131. zrb/config/default_prompt/summarization_prompt.md +0 -57
  132. zrb/config/default_prompt/system_prompt.md +0 -38
  133. zrb/config/llm_config.py +0 -339
  134. zrb/config/llm_context/config.py +0 -166
  135. zrb/config/llm_context/config_parser.py +0 -40
  136. zrb/config/llm_context/workflow.py +0 -81
  137. zrb/config/llm_rate_limitter.py +0 -190
  138. zrb/content_transformer/__init__.py +0 -0
  139. zrb/context/__init__.py +0 -0
  140. zrb/dot_dict/__init__.py +0 -0
  141. zrb/env/__init__.py +0 -0
  142. zrb/group/__init__.py +0 -0
  143. zrb/input/__init__.py +0 -0
  144. zrb/runner/__init__.py +0 -0
  145. zrb/runner/web_route/__init__.py +0 -0
  146. zrb/runner/web_route/home_page/__init__.py +0 -0
  147. zrb/session/__init__.py +0 -0
  148. zrb/session_state_log/__init__.py +0 -0
  149. zrb/session_state_logger/__init__.py +0 -0
  150. zrb/task/__init__.py +0 -0
  151. zrb/task/base/__init__.py +0 -0
  152. zrb/task/llm/__init__.py +0 -0
  153. zrb/task/llm/agent.py +0 -204
  154. zrb/task/llm/agent_runner.py +0 -152
  155. zrb/task/llm/config.py +0 -122
  156. zrb/task/llm/conversation_history.py +0 -209
  157. zrb/task/llm/conversation_history_model.py +0 -67
  158. zrb/task/llm/default_workflow/coding/workflow.md +0 -41
  159. zrb/task/llm/default_workflow/copywriting/workflow.md +0 -68
  160. zrb/task/llm/default_workflow/git/workflow.md +0 -118
  161. zrb/task/llm/default_workflow/golang/workflow.md +0 -128
  162. zrb/task/llm/default_workflow/html-css/workflow.md +0 -135
  163. zrb/task/llm/default_workflow/java/workflow.md +0 -146
  164. zrb/task/llm/default_workflow/javascript/workflow.md +0 -158
  165. zrb/task/llm/default_workflow/python/workflow.md +0 -160
  166. zrb/task/llm/default_workflow/researching/workflow.md +0 -153
  167. zrb/task/llm/default_workflow/rust/workflow.md +0 -162
  168. zrb/task/llm/default_workflow/shell/workflow.md +0 -299
  169. zrb/task/llm/error.py +0 -95
  170. zrb/task/llm/file_replacement.py +0 -206
  171. zrb/task/llm/file_tool_model.py +0 -57
  172. zrb/task/llm/history_processor.py +0 -206
  173. zrb/task/llm/history_summarization.py +0 -25
  174. zrb/task/llm/print_node.py +0 -221
  175. zrb/task/llm/prompt.py +0 -321
  176. zrb/task/llm/subagent_conversation_history.py +0 -41
  177. zrb/task/llm/tool_wrapper.py +0 -361
  178. zrb/task/llm/typing.py +0 -3
  179. zrb/task/llm/workflow.py +0 -76
  180. zrb/task/llm_task.py +0 -379
  181. zrb/task_status/__init__.py +0 -0
  182. zrb/util/__init__.py +0 -0
  183. zrb/util/cli/__init__.py +0 -0
  184. zrb/util/cmd/__init__.py +0 -0
  185. zrb/util/codemod/__init__.py +0 -0
  186. zrb/util/string/__init__.py +0 -0
  187. zrb/xcom/__init__.py +0 -0
  188. /zrb/{config/default_prompt/file_extractor_system_prompt.md → llm/prompt/markdown/file_extractor.md} +0 -0
  189. /zrb/{config/default_prompt/repo_extractor_system_prompt.md → llm/prompt/markdown/repo_extractor.md} +0 -0
  190. /zrb/{config/default_prompt/repo_summarizer_system_prompt.md → llm/prompt/markdown/repo_summarizer.md} +0 -0
  191. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +0 -0
  192. {zrb-1.21.29.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
zrb/config/config.py CHANGED
@@ -1,8 +1,15 @@
1
1
  import importlib.metadata as metadata
2
2
  import logging
3
3
  import os
4
- import platform
5
4
 
5
+ from zrb.config.helper import (
6
+ get_current_shell,
7
+ get_default_diff_edit_command,
8
+ get_env,
9
+ get_log_level,
10
+ get_max_token_threshold,
11
+ limit_token_threshold,
12
+ )
6
13
  from zrb.util.string.conversion import to_boolean
7
14
  from zrb.util.string.format import fstring_format
8
15
 
@@ -11,7 +18,7 @@ _DEFAULT_BANNER = """
11
18
  zzzzz rr rr bb
12
19
  zz rrr r bbbbbb
13
20
  zz rr bb bb
14
- zzzzz rr bbbbbb {VERSION} Janggala
21
+ zzzzz rr bbbbbb {VERSION} Pollux
15
22
  _ _ . . . _ . _ . . .
16
23
  Your Automation Powerhouse
17
24
  ☕ Donate at: https://stalchmst.com
@@ -22,28 +29,15 @@ Your Automation Powerhouse
22
29
 
23
30
  class Config:
24
31
  def __init__(self):
25
- self.__internal_default_prompt: dict[str, str] = {}
32
+ pass
26
33
 
27
34
  @property
28
35
  def ENV_PREFIX(self) -> str:
29
36
  return os.getenv("_ZRB_ENV_PREFIX", "ZRB")
30
37
 
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
-
39
- def _get_internal_default_prompt(self, name: str) -> str:
40
- if name not in self.__internal_default_prompt:
41
- file_path = os.path.join(
42
- os.path.dirname(__file__), "default_prompt", f"{name}.md"
43
- )
44
- with open(file_path, "r") as f:
45
- self.__internal_default_prompt[name] = f.read().strip()
46
- return self.__internal_default_prompt[name]
38
+ @ENV_PREFIX.setter
39
+ def ENV_PREFIX(self, value: str):
40
+ os.environ["_ZRB_ENV_PREFIX"] = value
47
41
 
48
42
  @property
49
43
  def LOGGER(self) -> logging.Logger:
@@ -51,55 +45,35 @@ class Config:
51
45
 
52
46
  @property
53
47
  def DEFAULT_SHELL(self) -> str:
54
- return self._getenv("SHELL", self._get_current_shell())
48
+ return get_env("SHELL", get_current_shell(), self.ENV_PREFIX)
55
49
 
56
- def _get_current_shell(self) -> str:
57
- if platform.system() == "Windows":
58
- return "PowerShell"
59
- current_shell = os.getenv("SHELL", "")
60
- if current_shell.endswith("zsh"):
61
- return "zsh"
62
- return "bash"
50
+ @DEFAULT_SHELL.setter
51
+ def DEFAULT_SHELL(self, value: str):
52
+ os.environ[f"{self.ENV_PREFIX}_SHELL"] = value
63
53
 
64
54
  @property
65
55
  def DEFAULT_EDITOR(self) -> str:
66
- return self._getenv("EDITOR", "nano")
56
+ return get_env("EDITOR", "nano", self.ENV_PREFIX)
57
+
58
+ @DEFAULT_EDITOR.setter
59
+ def DEFAULT_EDITOR(self, value: str):
60
+ os.environ[f"{self.ENV_PREFIX}_EDITOR"] = value
67
61
 
68
62
  @property
69
63
  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
64
+ return get_env(
65
+ "DIFF_EDIT_COMMAND",
66
+ get_default_diff_edit_command(self.DEFAULT_EDITOR),
67
+ self.ENV_PREFIX,
68
+ )
69
+
70
+ @DEFAULT_DIFF_EDIT_COMMAND_TPL.setter
71
+ def DEFAULT_DIFF_EDIT_COMMAND_TPL(self, value: str):
72
+ os.environ[f"{self.ENV_PREFIX}_DIFF_EDIT_COMMAND"] = value
99
73
 
100
74
  @property
101
75
  def INIT_MODULES(self) -> list[str]:
102
- init_modules_str = self._getenv("INIT_MODULES", "")
76
+ init_modules_str = get_env("INIT_MODULES", "", self.ENV_PREFIX)
103
77
  if init_modules_str != "":
104
78
  return [
105
79
  module.strip()
@@ -108,17 +82,31 @@ class Config:
108
82
  ]
109
83
  return []
110
84
 
85
+ @INIT_MODULES.setter
86
+ def INIT_MODULES(self, value: list[str]):
87
+ os.environ[f"{self.ENV_PREFIX}_INIT_MODULES"] = ":".join(value)
88
+
111
89
  @property
112
90
  def ROOT_GROUP_NAME(self) -> str:
113
- return self._getenv("ROOT_GROUP_NAME", "zrb")
91
+ return get_env("ROOT_GROUP_NAME", "zrb", self.ENV_PREFIX)
92
+
93
+ @ROOT_GROUP_NAME.setter
94
+ def ROOT_GROUP_NAME(self, value: str):
95
+ os.environ[f"{self.ENV_PREFIX}_ROOT_GROUP_NAME"] = value
114
96
 
115
97
  @property
116
98
  def ROOT_GROUP_DESCRIPTION(self) -> str:
117
- return self._getenv("ROOT_GROUP_DESCRIPTION", "Your Automation Powerhouse")
99
+ return get_env(
100
+ "ROOT_GROUP_DESCRIPTION", "Your Automation Powerhouse", self.ENV_PREFIX
101
+ )
102
+
103
+ @ROOT_GROUP_DESCRIPTION.setter
104
+ def ROOT_GROUP_DESCRIPTION(self, value: str):
105
+ os.environ[f"{self.ENV_PREFIX}_ROOT_GROUP_DESCRIPTION"] = value
118
106
 
119
107
  @property
120
108
  def INIT_SCRIPTS(self) -> list[str]:
121
- init_scripts_str = self._getenv("INIT_SCRIPTS", "")
109
+ init_scripts_str = get_env("INIT_SCRIPTS", "", self.ENV_PREFIX)
122
110
  if init_scripts_str != "":
123
111
  return [
124
112
  script.strip()
@@ -127,55 +115,84 @@ class Config:
127
115
  ]
128
116
  return []
129
117
 
118
+ @INIT_SCRIPTS.setter
119
+ def INIT_SCRIPTS(self, value: list[str]):
120
+ os.environ[f"{self.ENV_PREFIX}_INIT_SCRIPTS"] = ":".join(value)
121
+
130
122
  @property
131
123
  def INIT_FILE_NAME(self) -> str:
132
- return self._getenv("INIT_FILE_NAME", "zrb_init.py")
124
+ return get_env("INIT_FILE_NAME", "zrb_init.py", self.ENV_PREFIX)
125
+
126
+ @INIT_FILE_NAME.setter
127
+ def INIT_FILE_NAME(self, value: str):
128
+ os.environ[f"{self.ENV_PREFIX}_INIT_FILE_NAME"] = value
133
129
 
134
130
  @property
135
131
  def LOGGING_LEVEL(self) -> int:
136
- return self._get_log_level(self._getenv("LOGGING_LEVEL", "WARNING"))
137
-
138
- def _get_log_level(self, level: str) -> int:
139
- level = level.upper()
140
- log_levels = {
141
- "CRITICAL": logging.CRITICAL, # 50
142
- "FATAL": logging.CRITICAL, # 50
143
- "ERROR": logging.ERROR, # 40
144
- "WARN": logging.WARNING, # 30
145
- "WARNING": logging.WARNING, # 30
146
- "INFO": logging.INFO, # 20
147
- "DEBUG": logging.DEBUG, # 10
148
- "NOTSET": logging.NOTSET, # 0
149
- }
150
- if level in log_levels:
151
- return log_levels[level]
152
- return logging.WARNING
132
+ return get_log_level(get_env("LOGGING_LEVEL", "WARNING", self.ENV_PREFIX))
133
+
134
+ @LOGGING_LEVEL.setter
135
+ def LOGGING_LEVEL(self, value: int | str):
136
+ if isinstance(value, int):
137
+ value = logging.getLevelName(value)
138
+ os.environ[f"{self.ENV_PREFIX}_LOGGING_LEVEL"] = str(value)
153
139
 
154
140
  @property
155
141
  def LOAD_BUILTIN(self) -> bool:
156
- return to_boolean(self._getenv("LOAD_BUILTIN", "1"))
142
+ return to_boolean(get_env("LOAD_BUILTIN", "1", self.ENV_PREFIX))
143
+
144
+ @LOAD_BUILTIN.setter
145
+ def LOAD_BUILTIN(self, value: bool):
146
+ os.environ[f"{self.ENV_PREFIX}_LOAD_BUILTIN"] = "1" if value else "0"
157
147
 
158
148
  @property
159
149
  def WARN_UNRECOMMENDED_COMMAND(self) -> bool:
160
- return to_boolean(self._getenv("WARN_UNRECOMMENDED_COMMAND", "1"))
150
+ return to_boolean(get_env("WARN_UNRECOMMENDED_COMMAND", "1", self.ENV_PREFIX))
151
+
152
+ @WARN_UNRECOMMENDED_COMMAND.setter
153
+ def WARN_UNRECOMMENDED_COMMAND(self, value: bool):
154
+ os.environ[f"{self.ENV_PREFIX}_WARN_UNRECOMMENDED_COMMAND"] = (
155
+ "1" if value else "0"
156
+ )
161
157
 
162
158
  @property
163
159
  def SESSION_LOG_DIR(self) -> str:
164
- return os.getenv(
165
- "ZRB_SESSION_LOG_DIR", os.path.expanduser(os.path.join("~", ".zrb-session"))
160
+ return get_env(
161
+ "SESSION_LOG_DIR",
162
+ os.path.expanduser(os.path.join("~", f".{self.ROOT_GROUP_NAME}-session")),
166
163
  )
167
164
 
165
+ @SESSION_LOG_DIR.setter
166
+ def SESSION_LOG_DIR(self, value: str):
167
+ os.environ[f"{self.ENV_PREFIX}_SESSION_LOG_DIR"] = value
168
+
168
169
  @property
169
170
  def TODO_DIR(self) -> str:
170
- return self._getenv("TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
171
+ return get_env(
172
+ "TODO_DIR",
173
+ os.path.expanduser(os.path.join("~", "todo")),
174
+ self.ENV_PREFIX,
175
+ )
176
+
177
+ @TODO_DIR.setter
178
+ def TODO_DIR(self, value: str):
179
+ os.environ[f"{self.ENV_PREFIX}_TODO_DIR"] = value
171
180
 
172
181
  @property
173
182
  def TODO_VISUAL_FILTER(self) -> str:
174
- return self._getenv("TODO_FILTER", "")
183
+ return get_env("TODO_FILTER", "", self.ENV_PREFIX)
184
+
185
+ @TODO_VISUAL_FILTER.setter
186
+ def TODO_VISUAL_FILTER(self, value: str):
187
+ os.environ[f"{self.ENV_PREFIX}_TODO_FILTER"] = value
175
188
 
176
189
  @property
177
190
  def TODO_RETENTION(self) -> str:
178
- return self._getenv("TODO_RETENTION", "2w")
191
+ return get_env("TODO_RETENTION", "2w", self.ENV_PREFIX)
192
+
193
+ @TODO_RETENTION.setter
194
+ def TODO_RETENTION(self, value: str):
195
+ os.environ[f"{self.ENV_PREFIX}_TODO_RETENTION"] = value
179
196
 
180
197
  @property
181
198
  def VERSION(self) -> str:
@@ -184,9 +201,13 @@ class Config:
184
201
  return custom_version
185
202
  return metadata.version("zrb")
186
203
 
204
+ @VERSION.setter
205
+ def VERSION(self, value: str):
206
+ os.environ["_ZRB_CUSTOM_VERSION"] = value
207
+
187
208
  @property
188
209
  def WEB_CSS_PATH(self) -> list[str]:
189
- web_css_path_str = self._getenv("WEB_CSS_PATH", "")
210
+ web_css_path_str = get_env("WEB_CSS_PATH", "", self.ENV_PREFIX)
190
211
  if web_css_path_str != "":
191
212
  return [
192
213
  path.strip()
@@ -195,9 +216,13 @@ class Config:
195
216
  ]
196
217
  return []
197
218
 
219
+ @WEB_CSS_PATH.setter
220
+ def WEB_CSS_PATH(self, value: list[str]):
221
+ os.environ[f"{self.ENV_PREFIX}_WEB_CSS_PATH"] = ":".join(value)
222
+
198
223
  @property
199
224
  def WEB_JS_PATH(self) -> list[str]:
200
- web_js_path_str = self._getenv("WEB_JS_PATH", "")
225
+ web_js_path_str = get_env("WEB_JS_PATH", "", self.ENV_PREFIX)
201
226
  if web_js_path_str != "":
202
227
  return [
203
228
  path.strip()
@@ -206,148 +231,222 @@ class Config:
206
231
  ]
207
232
  return []
208
233
 
234
+ @WEB_JS_PATH.setter
235
+ def WEB_JS_PATH(self, value: list[str]):
236
+ os.environ[f"{self.ENV_PREFIX}_WEB_JS_PATH"] = ":".join(value)
237
+
209
238
  @property
210
239
  def WEB_FAVICON_PATH(self) -> str:
211
- return self._getenv("WEB_FAVICON_PATH", "/static/favicon-32x32.png")
240
+ return get_env("WEB_FAVICON_PATH", "/static/favicon-32x32.png", self.ENV_PREFIX)
241
+
242
+ @WEB_FAVICON_PATH.setter
243
+ def WEB_FAVICON_PATH(self, value: str):
244
+ os.environ[f"{self.ENV_PREFIX}_WEB_FAVICON_PATH"] = value
212
245
 
213
246
  @property
214
247
  def WEB_COLOR(self) -> str:
215
- return self._getenv("WEB_COLOR", "")
248
+ return get_env("WEB_COLOR", "", self.ENV_PREFIX)
249
+
250
+ @WEB_COLOR.setter
251
+ def WEB_COLOR(self, value: str):
252
+ os.environ[f"{self.ENV_PREFIX}_WEB_COLOR"] = value
216
253
 
217
254
  @property
218
255
  def WEB_HTTP_PORT(self) -> int:
219
- return int(self._getenv("WEB_HTTP_PORT", "21213"))
256
+ return int(get_env("WEB_HTTP_PORT", "21213", self.ENV_PREFIX))
257
+
258
+ @WEB_HTTP_PORT.setter
259
+ def WEB_HTTP_PORT(self, value: int):
260
+ os.environ[f"{self.ENV_PREFIX}_WEB_HTTP_PORT"] = str(value)
220
261
 
221
262
  @property
222
263
  def WEB_GUEST_USERNAME(self) -> str:
223
- return self._getenv("WEB_GUEST_USERNAME", "user")
264
+ return get_env("WEB_GUEST_USERNAME", "user", self.ENV_PREFIX)
265
+
266
+ @WEB_GUEST_USERNAME.setter
267
+ def WEB_GUEST_USERNAME(self, value: str):
268
+ os.environ[f"{self.ENV_PREFIX}_WEB_GUEST_USERNAME"] = value
224
269
 
225
270
  @property
226
271
  def WEB_SUPER_ADMIN_USERNAME(self) -> str:
227
- return self._getenv("WEB_SUPER_ADMIN_USERNAME", "admin")
272
+ return get_env("WEB_SUPER_ADMIN_USERNAME", "admin", self.ENV_PREFIX)
273
+
274
+ @WEB_SUPER_ADMIN_USERNAME.setter
275
+ def WEB_SUPER_ADMIN_USERNAME(self, value: str):
276
+ os.environ[f"{self.ENV_PREFIX}_WEB_SUPER_ADMIN_USERNAME"] = value
228
277
 
229
278
  @property
230
279
  def WEB_SUPER_ADMIN_PASSWORD(self) -> str:
231
- return self._getenv("WEB_SUPER_ADMIN_PASSWORD", "admin")
280
+ return get_env("WEB_SUPER_ADMIN_PASSWORD", "admin", self.ENV_PREFIX)
281
+
282
+ @WEB_SUPER_ADMIN_PASSWORD.setter
283
+ def WEB_SUPER_ADMIN_PASSWORD(self, value: str):
284
+ os.environ[f"{self.ENV_PREFIX}_WEB_SUPER_ADMIN_PASSWORD"] = value
232
285
 
233
286
  @property
234
287
  def WEB_ACCESS_TOKEN_COOKIE_NAME(self) -> str:
235
- return self._getenv("WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token")
288
+ return get_env("WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token", self.ENV_PREFIX)
289
+
290
+ @WEB_ACCESS_TOKEN_COOKIE_NAME.setter
291
+ def WEB_ACCESS_TOKEN_COOKIE_NAME(self, value: str):
292
+ os.environ[f"{self.ENV_PREFIX}_WEB_ACCESS_TOKEN_COOKIE_NAME"] = value
236
293
 
237
294
  @property
238
295
  def WEB_REFRESH_TOKEN_COOKIE_NAME(self) -> str:
239
- return self._getenv("WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token")
296
+ return get_env(
297
+ "WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token", self.ENV_PREFIX
298
+ )
299
+
300
+ @WEB_REFRESH_TOKEN_COOKIE_NAME.setter
301
+ def WEB_REFRESH_TOKEN_COOKIE_NAME(self, value: str):
302
+ os.environ[f"{self.ENV_PREFIX}_WEB_REFRESH_TOKEN_COOKIE_NAME"] = value
240
303
 
241
304
  @property
242
305
  def WEB_SECRET_KEY(self) -> str:
243
- return self._getenv("WEB_SECRET", "zrb")
306
+ return get_env("WEB_SECRET", "zrb", self.ENV_PREFIX)
307
+
308
+ @WEB_SECRET_KEY.setter
309
+ def WEB_SECRET_KEY(self, value: str):
310
+ os.environ[f"{self.ENV_PREFIX}_WEB_SECRET"] = value
244
311
 
245
312
  @property
246
313
  def WEB_ENABLE_AUTH(self) -> bool:
247
- return to_boolean(self._getenv("WEB_ENABLE_AUTH", "0"))
314
+ return to_boolean(get_env("WEB_ENABLE_AUTH", "0", self.ENV_PREFIX))
315
+
316
+ @WEB_ENABLE_AUTH.setter
317
+ def WEB_ENABLE_AUTH(self, value: bool):
318
+ os.environ[f"{self.ENV_PREFIX}_WEB_ENABLE_AUTH"] = "1" if value else "0"
248
319
 
249
320
  @property
250
321
  def WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES(self) -> int:
251
- return int(self._getenv("WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
322
+ return int(get_env("WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30", self.ENV_PREFIX))
323
+
324
+ @WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES.setter
325
+ def WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES(self, value: int):
326
+ os.environ[f"{self.ENV_PREFIX}_WEB_ACCESS_TOKEN_EXPIRE_MINUTES"] = str(value)
252
327
 
253
328
  @property
254
329
  def WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES(self) -> int:
255
- return int(self._getenv("WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60"))
330
+ return int(get_env("WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60", self.ENV_PREFIX))
331
+
332
+ @WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES.setter
333
+ def WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES(self, value: int):
334
+ os.environ[f"{self.ENV_PREFIX}_WEB_REFRESH_TOKEN_EXPIRE_MINUTES"] = str(value)
256
335
 
257
336
  @property
258
337
  def WEB_TITLE(self) -> str:
259
- return self._getenv("WEB_TITLE", "Zrb")
338
+ return get_env("WEB_TITLE", "Zrb", self.ENV_PREFIX)
339
+
340
+ @WEB_TITLE.setter
341
+ def WEB_TITLE(self, value: str):
342
+ os.environ[f"{self.ENV_PREFIX}_WEB_TITLE"] = value
260
343
 
261
344
  @property
262
345
  def WEB_JARGON(self) -> str:
263
- return self._getenv("WEB_JARGON", "Your Automation PowerHouse")
346
+ return get_env("WEB_JARGON", "Your Automation PowerHouse", self.ENV_PREFIX)
264
347
 
265
- @property
266
- def WEB_HOMEPAGE_INTRO(self) -> str:
267
- return self._getenv("WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface")
348
+ @WEB_JARGON.setter
349
+ def WEB_JARGON(self, value: str):
350
+ os.environ[f"{self.ENV_PREFIX}_WEB_JARGON"] = value
268
351
 
269
352
  @property
270
- def LLM_MODEL(self) -> str | None:
271
- value = self._getenv("LLM_MODEL")
272
- return None if value == "" else value
353
+ def WEB_HOMEPAGE_INTRO(self) -> str:
354
+ return get_env(
355
+ "WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface", self.ENV_PREFIX
356
+ )
273
357
 
274
- @property
275
- def LLM_BASE_URL(self) -> str | None:
276
- value = self._getenv("LLM_BASE_URL")
277
- return None if value == "" else value
358
+ @WEB_HOMEPAGE_INTRO.setter
359
+ def WEB_HOMEPAGE_INTRO(self, value: str):
360
+ os.environ[f"{self.ENV_PREFIX}_WEB_HOMEPAGE_INTRO"] = value
278
361
 
279
362
  @property
280
- def LLM_API_KEY(self) -> str | None:
281
- value = self._getenv("LLM_API_KEY")
282
- return None if value == "" else value
363
+ def LLM_ASSISTANT_NAME(self) -> str:
364
+ return get_env("LLM_ASSISTANT_NAME", self.ROOT_GROUP_NAME)
283
365
 
284
- @property
285
- def LLM_MODEL_SMALL(self) -> str | None:
286
- value = self._getenv("LLM_MODEL_SMALL")
287
- return None if value == "" else value
366
+ @LLM_ASSISTANT_NAME.setter
367
+ def LLM_ASSISTANT_NAME(self, value: str):
368
+ os.environ[f"{self.ENV_PREFIX}_LLM_ASSISTANT_NAME"] = value
288
369
 
289
370
  @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
371
+ def LLM_ASSISTANT_ASCII_ART(self) -> str:
372
+ return get_env("LLM_ASSISTANT_ASCII_ART", "cat")
293
373
 
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
374
+ @LLM_ASSISTANT_ASCII_ART.setter
375
+ def LLM_ASSISTANT_ASCII_ART(self, value: str):
376
+ os.environ[f"{self.ENV_PREFIX}_LLM_ASSISTANT_ASCII_ART"] = value
298
377
 
299
378
  @property
300
- def LLM_SYSTEM_PROMPT(self) -> str | None:
301
- value = self._getenv("LLM_SYSTEM_PROMPT")
302
- return None if value == "" else value
379
+ def LLM_ASSISTANT_JARGON(self) -> str:
380
+ return get_env("LLM_ASSISTANT_JARGON", self.ROOT_GROUP_DESCRIPTION)
303
381
 
304
- @property
305
- def LLM_INTERACTIVE_SYSTEM_PROMPT(self) -> str | None:
306
- value = self._getenv("LLM_INTERACTIVE_SYSTEM_PROMPT")
307
- return None if value == "" else value
382
+ @LLM_ASSISTANT_JARGON.setter
383
+ def LLM_ASSISTANT_JARGON(self, value: str):
384
+ os.environ[f"{self.ENV_PREFIX}_LLM_ASSISTANT_JARGON"] = value
308
385
 
309
386
  @property
310
- def LLM_PERSONA(self) -> str | None:
311
- value = self._getenv("LLM_PERSONA")
312
- return None if value == "" else value
387
+ def LLM_HISTORY_DIR(self) -> str:
388
+ return get_env(
389
+ "LLM_HISTORY_DIR",
390
+ os.path.expanduser(
391
+ os.path.join("~", f".{self.ROOT_GROUP_NAME}", "llm-history")
392
+ ),
393
+ )
313
394
 
314
- @property
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
395
+ @LLM_HISTORY_DIR.setter
396
+ def LLM_HISTORY_DIR(self, value: str):
397
+ os.environ[f"{self.ENV_PREFIX}_LLM_HISTORY_DIR"] = value
323
398
 
324
399
  @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"], ""
400
+ def LLM_NOTE_FILE(self) -> str:
401
+ return get_env(
402
+ "LLM_NOTE_FILE",
403
+ os.path.expanduser(
404
+ os.path.join("~", f".{self.ROOT_GROUP_NAME}", "notes.json")
405
+ ),
329
406
  )
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 []
407
+
408
+ @LLM_NOTE_FILE.setter
409
+ def LLM_NOTE_FILE(self, value: str):
410
+ os.environ[f"{self.ENV_PREFIX}_LLM_NOTE_FILE"] = value
337
411
 
338
412
  @property
339
- def LLM_SPECIAL_INSTRUCTION_PROMPT(self) -> str | None:
340
- value = self._getenv("LLM_SPECIAL_INSTRUCTION_PROMPT")
413
+ def LLM_MODEL(self) -> str | None:
414
+ value = get_env("LLM_MODEL", "", self.ENV_PREFIX)
341
415
  return None if value == "" else value
342
416
 
417
+ @LLM_MODEL.setter
418
+ def LLM_MODEL(self, value: str | None):
419
+ if value is None:
420
+ if f"{self.ENV_PREFIX}_LLM_MODEL" in os.environ:
421
+ del os.environ[f"{self.ENV_PREFIX}_LLM_MODEL"]
422
+ else:
423
+ os.environ[f"{self.ENV_PREFIX}_LLM_MODEL"] = value
424
+
343
425
  @property
344
- def LLM_SUMMARIZATION_PROMPT(self) -> str | None:
345
- value = self._getenv("LLM_SUMMARIZATION_PROMPT")
426
+ def LLM_BASE_URL(self) -> str | None:
427
+ value = get_env("LLM_BASE_URL", "", self.ENV_PREFIX)
346
428
  return None if value == "" else value
347
429
 
430
+ @LLM_BASE_URL.setter
431
+ def LLM_BASE_URL(self, value: str | None):
432
+ if value is None:
433
+ if f"{self.ENV_PREFIX}_LLM_BASE_URL" in os.environ:
434
+ del os.environ[f"{self.ENV_PREFIX}_LLM_BASE_URL"]
435
+ else:
436
+ os.environ[f"{self.ENV_PREFIX}_LLM_BASE_URL"] = value
437
+
348
438
  @property
349
- def LLM_SHOW_TOOL_CALL_RESULT(self) -> bool:
350
- return to_boolean(self._getenv("LLM_SHOW_TOOL_CALL_RESULT", "false"))
439
+ def LLM_API_KEY(self) -> str | None:
440
+ value = get_env("LLM_API_KEY", "", self.ENV_PREFIX)
441
+ return None if value == "" else value
442
+
443
+ @LLM_API_KEY.setter
444
+ def LLM_API_KEY(self, value: str | None):
445
+ if value is None:
446
+ if f"{self.ENV_PREFIX}_LLM_API_KEY" in os.environ:
447
+ del os.environ[f"{self.ENV_PREFIX}_LLM_API_KEY"]
448
+ else:
449
+ os.environ[f"{self.ENV_PREFIX}_LLM_API_KEY"] = value
351
450
 
352
451
  @property
353
452
  def LLM_MAX_REQUESTS_PER_MINUTE(self) -> int:
@@ -356,11 +455,17 @@ class Config:
356
455
  Default is conservative to accommodate free-tier LLM providers.
357
456
  """
358
457
  return int(
359
- self._getenv(
360
- ["LLM_MAX_REQUEST_PER_MINUTE", "LLM_MAX_REQUESTS_PER_MINUTE"], "60"
458
+ get_env(
459
+ ["LLM_MAX_REQUEST_PER_MINUTE", "LLM_MAX_REQUESTS_PER_MINUTE"],
460
+ "60",
461
+ self.ENV_PREFIX,
361
462
  )
362
463
  )
363
464
 
465
+ @LLM_MAX_REQUESTS_PER_MINUTE.setter
466
+ def LLM_MAX_REQUESTS_PER_MINUTE(self, value: int):
467
+ os.environ[f"{self.ENV_PREFIX}_LLM_MAX_REQUESTS_PER_MINUTE"] = str(value)
468
+
364
469
  @property
365
470
  def LLM_MAX_TOKENS_PER_MINUTE(self) -> int:
366
471
  """
@@ -368,247 +473,353 @@ class Config:
368
473
  Default is conservative to accommodate free-tier LLM providers.
369
474
  """
370
475
  return int(
371
- self._getenv(
372
- ["LLM_MAX_TOKEN_PER_MINUTE", "LLM_MAX_TOKENS_PER_MINUTE"], "100000"
476
+ get_env(
477
+ ["LLM_MAX_TOKEN_PER_MINUTE", "LLM_MAX_TOKENS_PER_MINUTE"],
478
+ "120000",
479
+ self.ENV_PREFIX,
373
480
  )
374
481
  )
375
482
 
483
+ @LLM_MAX_TOKENS_PER_MINUTE.setter
484
+ def LLM_MAX_TOKENS_PER_MINUTE(self, value: int):
485
+ os.environ[f"{self.ENV_PREFIX}_LLM_MAX_TOKENS_PER_MINUTE"] = str(value)
486
+
376
487
  @property
377
488
  def LLM_MAX_TOKENS_PER_REQUEST(self) -> int:
378
489
  """Maximum number of tokens allowed per individual LLM request."""
379
490
  return int(
380
- self._getenv(
381
- ["LLM_MAX_TOKEN_PER_REQUEST", "LLM_MAX_TOKENS_PER_REQUEST"], "120000"
491
+ get_env(
492
+ ["LLM_MAX_TOKEN_PER_REQUEST", "LLM_MAX_TOKENS_PER_REQUEST"],
493
+ "120000",
494
+ self.ENV_PREFIX,
382
495
  )
383
496
  )
384
497
 
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
- )
498
+ @LLM_MAX_TOKENS_PER_REQUEST.setter
499
+ def LLM_MAX_TOKENS_PER_REQUEST(self, value: int):
500
+ os.environ[f"{self.ENV_PREFIX}_LLM_MAX_TOKENS_PER_REQUEST"] = str(value)
397
501
 
398
502
  @property
399
503
  def LLM_THROTTLE_SLEEP(self) -> float:
400
504
  """Number of seconds to sleep when throttling is required."""
401
- return float(self._getenv("LLM_THROTTLE_SLEEP", "5.0"))
505
+ return float(get_env("LLM_THROTTLE_SLEEP", "1.0", self.ENV_PREFIX))
402
506
 
403
- @property
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() != ""]
507
+ @LLM_THROTTLE_SLEEP.setter
508
+ def LLM_THROTTLE_SLEEP(self, value: float):
509
+ os.environ[f"{self.ENV_PREFIX}_LLM_THROTTLE_SLEEP"] = str(value)
410
510
 
411
511
  @property
412
- def LLM_SUMMARIZE_HISTORY(self) -> bool:
413
- return to_boolean(self._getenv("LLM_SUMMARIZE_HISTORY", "true"))
512
+ def LLM_HISTORY_SUMMARIZATION_WINDOW(self) -> int:
513
+ return int(get_env("LLM_HISTORY_SUMMARIZATION_WINDOW", "5", self.ENV_PREFIX))
514
+
515
+ @LLM_HISTORY_SUMMARIZATION_WINDOW.setter
516
+ def LLM_HISTORY_SUMMARIZATION_WINDOW(self, value: int):
517
+ os.environ[f"{self.ENV_PREFIX}_LLM_HISTORY_SUMMARIZATION_WINDOW"] = str(value)
414
518
 
415
519
  @property
416
520
  def LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD(self) -> int:
417
521
  threshold = int(
418
- self._getenv(
522
+ get_env(
419
523
  "LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD",
420
524
  str(self._get_max_threshold(0.6)),
525
+ self.ENV_PREFIX,
421
526
  )
422
527
  )
423
- return self._limit_token_threshold(threshold, 0.6)
528
+ return limit_token_threshold(
529
+ threshold,
530
+ 0.6,
531
+ self.LLM_MAX_TOKENS_PER_MINUTE,
532
+ self.LLM_MAX_TOKENS_PER_REQUEST,
533
+ )
534
+
535
+ @LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD.setter
536
+ def LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD(self, value: int):
537
+ os.environ[f"{self.ENV_PREFIX}_LLM_HISTORY_SUMMARIZATION_TOKEN_THRESHOLD"] = (
538
+ str(value)
539
+ )
424
540
 
425
541
  @property
426
542
  def LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD(self) -> int:
427
543
  threshold = int(
428
- self._getenv(
544
+ get_env(
429
545
  "LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD",
430
546
  str(self._get_max_threshold(0.4)),
547
+ self.ENV_PREFIX,
431
548
  )
432
549
  )
433
- return self._limit_token_threshold(threshold, 0.4)
550
+ return limit_token_threshold(
551
+ threshold,
552
+ 0.4,
553
+ self.LLM_MAX_TOKENS_PER_MINUTE,
554
+ self.LLM_MAX_TOKENS_PER_REQUEST,
555
+ )
556
+
557
+ @LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD.setter
558
+ def LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD(self, value: int):
559
+ os.environ[
560
+ f"{self.ENV_PREFIX}_LLM_REPO_ANALYSIS_EXTRACTION_TOKEN_THRESHOLD"
561
+ ] = str(value)
434
562
 
435
563
  @property
436
564
  def LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD(self) -> int:
437
565
  threshold = int(
438
- self._getenv(
566
+ get_env(
439
567
  "LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD",
440
568
  str(self._get_max_threshold(0.4)),
569
+ self.ENV_PREFIX,
441
570
  )
442
571
  )
443
- return self._limit_token_threshold(threshold, 0.4)
572
+ return limit_token_threshold(
573
+ threshold,
574
+ 0.4,
575
+ self.LLM_MAX_TOKENS_PER_MINUTE,
576
+ self.LLM_MAX_TOKENS_PER_REQUEST,
577
+ )
578
+
579
+ @LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD.setter
580
+ def LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD(self, value: int):
581
+ os.environ[
582
+ f"{self.ENV_PREFIX}_LLM_REPO_ANALYSIS_SUMMARIZATION_TOKEN_THRESHOLD"
583
+ ] = str(value)
444
584
 
445
585
  @property
446
586
  def LLM_FILE_ANALYSIS_TOKEN_THRESHOLD(self) -> int:
447
587
  threshold = int(
448
- self._getenv(
449
- "LLM_FILE_ANALYSIS_TOKEN_THRESHOLD", str(self._get_max_threshold(0.4))
588
+ get_env(
589
+ "LLM_FILE_ANALYSIS_TOKEN_THRESHOLD",
590
+ str(self._get_max_threshold(0.4)),
591
+ self.ENV_PREFIX,
450
592
  )
451
593
  )
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)
594
+ return limit_token_threshold(
595
+ threshold,
596
+ 0.4,
597
+ self.LLM_MAX_TOKENS_PER_MINUTE,
598
+ self.LLM_MAX_TOKENS_PER_REQUEST,
461
599
  )
462
600
 
463
- @property
464
- def LLM_FILE_EXTRACTOR_SYSTEM_PROMPT(self) -> str:
465
- return self._getenv(
466
- "LLM_FILE_EXTRACTOR_SYSTEM_PROMPT",
467
- self._get_internal_default_prompt("file_extractor_system_prompt"),
468
- )
601
+ @LLM_FILE_ANALYSIS_TOKEN_THRESHOLD.setter
602
+ def LLM_FILE_ANALYSIS_TOKEN_THRESHOLD(self, value: int):
603
+ os.environ[f"{self.ENV_PREFIX}_LLM_FILE_ANALYSIS_TOKEN_THRESHOLD"] = str(value)
469
604
 
470
605
  @property
471
- def LLM_REPO_EXTRACTOR_SYSTEM_PROMPT(self) -> str:
472
- return self._getenv(
473
- "LLM_REPO_EXTRACTOR_SYSTEM_PROMPT",
474
- self._get_internal_default_prompt("repo_extractor_system_prompt"),
606
+ def LLM_PROMPT_DIR(self) -> str:
607
+ return get_env(
608
+ "LLM_PROMPT_DIR",
609
+ os.path.join(f".{self.ROOT_GROUP_NAME}", "llm", "prompt"),
610
+ self.ENV_PREFIX,
475
611
  )
476
612
 
477
- @property
478
- def LLM_REPO_SUMMARIZER_SYSTEM_PROMPT(self) -> str:
479
- return self._getenv(
480
- "LLM_REPO_SUMMARIZER_SYSTEM_PROMPT",
481
- self._get_internal_default_prompt("repo_summarizer_system_prompt"),
482
- )
613
+ @LLM_PROMPT_DIR.setter
614
+ def LLM_PROMPT_DIR(self, value: str):
615
+ os.environ[f"{self.ENV_PREFIX}_LLM_PROMPT_DIR"] = value
483
616
 
484
- @property
485
- def LLM_HISTORY_DIR(self) -> str:
486
- return self._getenv(
487
- "LLM_HISTORY_DIR",
488
- os.path.expanduser(os.path.join("~", ".zrb-llm-history")),
617
+ def _get_max_threshold(self, factor: float) -> int:
618
+ return get_max_token_threshold(
619
+ factor, self.LLM_MAX_TOKENS_PER_MINUTE, self.LLM_MAX_TOKENS_PER_REQUEST
489
620
  )
490
621
 
491
- @property
492
- def LLM_ALLOW_ACCESS_LOCAL_FILE(self) -> bool:
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"))
502
-
503
- @property
504
- def LLM_ALLOW_ACCESS_SHELL(self) -> bool:
505
- return to_boolean(self._getenv("LLM_ALLOW_ACCESS_SHELL", "1"))
506
-
507
- @property
508
- def LLM_ALLOW_OPEN_WEB_PAGE(self) -> bool:
509
- return to_boolean(self._getenv("LLM_ALLOW_OPEN_WEB_PAGE", "1"))
510
-
511
- @property
512
- def LLM_ALLOW_SEARCH_INTERNET(self) -> bool:
513
- return to_boolean(self._getenv("LLM_ALLOW_SEARCH_INTERNET", "1"))
514
-
515
- @property
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
622
  @property
524
623
  def RAG_EMBEDDING_API_KEY(self) -> str | None:
525
- value = self._getenv("RAG_EMBEDDING_API_KEY")
624
+ value = get_env("RAG_EMBEDDING_API_KEY", "", self.ENV_PREFIX)
526
625
  return None if value == "" else value
527
626
 
627
+ @RAG_EMBEDDING_API_KEY.setter
628
+ def RAG_EMBEDDING_API_KEY(self, value: str | None):
629
+ if value is None:
630
+ if f"{self.ENV_PREFIX}_RAG_EMBEDDING_API_KEY" in os.environ:
631
+ del os.environ[f"{self.ENV_PREFIX}_RAG_EMBEDDING_API_KEY"]
632
+ else:
633
+ os.environ[f"{self.ENV_PREFIX}_RAG_EMBEDDING_API_KEY"] = value
634
+
528
635
  @property
529
636
  def RAG_EMBEDDING_BASE_URL(self) -> str | None:
530
- value = self._getenv("RAG_EMBEDDING_BASE_URL")
637
+ value = get_env("RAG_EMBEDDING_BASE_URL", "", self.ENV_PREFIX)
531
638
  return None if value == "" else value
532
639
 
640
+ @RAG_EMBEDDING_BASE_URL.setter
641
+ def RAG_EMBEDDING_BASE_URL(self, value: str | None):
642
+ if value is None:
643
+ if f"{self.ENV_PREFIX}_RAG_EMBEDDING_BASE_URL" in os.environ:
644
+ del os.environ[f"{self.ENV_PREFIX}_RAG_EMBEDDING_BASE_URL"]
645
+ else:
646
+ os.environ[f"{self.ENV_PREFIX}_RAG_EMBEDDING_BASE_URL"] = value
647
+
533
648
  @property
534
649
  def RAG_EMBEDDING_MODEL(self) -> str:
535
- return self._getenv("RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
650
+ return get_env("RAG_EMBEDDING_MODEL", "text-embedding-ada-002", self.ENV_PREFIX)
651
+
652
+ @RAG_EMBEDDING_MODEL.setter
653
+ def RAG_EMBEDDING_MODEL(self, value: str):
654
+ os.environ[f"{self.ENV_PREFIX}_RAG_EMBEDDING_MODEL"] = value
536
655
 
537
656
  @property
538
657
  def RAG_CHUNK_SIZE(self) -> int:
539
- return int(self._getenv("RAG_CHUNK_SIZE", "1024"))
658
+ return int(get_env("RAG_CHUNK_SIZE", "1024", self.ENV_PREFIX))
659
+
660
+ @RAG_CHUNK_SIZE.setter
661
+ def RAG_CHUNK_SIZE(self, value: int):
662
+ os.environ[f"{self.ENV_PREFIX}_RAG_CHUNK_SIZE"] = str(value)
540
663
 
541
664
  @property
542
665
  def RAG_OVERLAP(self) -> int:
543
- return int(self._getenv("RAG_OVERLAP", "128"))
666
+ return int(get_env("RAG_OVERLAP", "128", self.ENV_PREFIX))
667
+
668
+ @RAG_OVERLAP.setter
669
+ def RAG_OVERLAP(self, value: int):
670
+ os.environ[f"{self.ENV_PREFIX}_RAG_OVERLAP"] = str(value)
544
671
 
545
672
  @property
546
673
  def RAG_MAX_RESULT_COUNT(self) -> int:
547
- return int(self._getenv("RAG_MAX_RESULT_COUNT", "5"))
674
+ return int(get_env("RAG_MAX_RESULT_COUNT", "5", self.ENV_PREFIX))
675
+
676
+ @RAG_MAX_RESULT_COUNT.setter
677
+ def RAG_MAX_RESULT_COUNT(self, value: int):
678
+ os.environ[f"{self.ENV_PREFIX}_RAG_MAX_RESULT_COUNT"] = str(value)
548
679
 
549
680
  @property
550
681
  def SEARCH_INTERNET_METHOD(self) -> str:
551
682
  """Either serpapi or searxng"""
552
- return self._getenv("SEARCH_INTERNET_METHOD", "serpapi")
683
+ return get_env("SEARCH_INTERNET_METHOD", "serpapi", self.ENV_PREFIX)
684
+
685
+ @SEARCH_INTERNET_METHOD.setter
686
+ def SEARCH_INTERNET_METHOD(self, value: str):
687
+ os.environ[f"{self.ENV_PREFIX}_SEARCH_INTERNET_METHOD"] = value
553
688
 
554
689
  @property
555
690
  def BRAVE_API_KEY(self) -> str:
556
691
  return os.getenv("BRAVE_API_KEY", "")
557
692
 
693
+ @BRAVE_API_KEY.setter
694
+ def BRAVE_API_KEY(self, value: str):
695
+ os.environ["BRAVE_API_KEY"] = value
696
+
558
697
  @property
559
698
  def BRAVE_API_SAFE(self) -> str:
560
- return self._getenv("BRAVE_API_SAFE", "off")
699
+ return get_env("BRAVE_API_SAFE", "off", self.ENV_PREFIX)
700
+
701
+ @BRAVE_API_SAFE.setter
702
+ def BRAVE_API_SAFE(self, value: str):
703
+ os.environ[f"{self.ENV_PREFIX}_BRAVE_API_SAFE"] = value
561
704
 
562
705
  @property
563
706
  def BRAVE_API_LANG(self) -> str:
564
- return self._getenv("BRAVE_API_LANG", "en")
707
+ return get_env("BRAVE_API_LANG", "en", self.ENV_PREFIX)
708
+
709
+ @BRAVE_API_LANG.setter
710
+ def BRAVE_API_LANG(self, value: str):
711
+ os.environ[f"{self.ENV_PREFIX}_BRAVE_API_LANG"] = value
565
712
 
566
713
  @property
567
714
  def SERPAPI_KEY(self) -> str:
568
715
  return os.getenv("SERPAPI_KEY", "")
569
716
 
717
+ @SERPAPI_KEY.setter
718
+ def SERPAPI_KEY(self, value: str):
719
+ os.environ["SERPAPI_KEY"] = value
720
+
570
721
  @property
571
722
  def SERPAPI_SAFE(self) -> str:
572
- return self._getenv("SERPAPI_SAFE", "off")
723
+ return get_env("SERPAPI_SAFE", "off", self.ENV_PREFIX)
724
+
725
+ @SERPAPI_SAFE.setter
726
+ def SERPAPI_SAFE(self, value: str):
727
+ os.environ[f"{self.ENV_PREFIX}_SERPAPI_SAFE"] = value
573
728
 
574
729
  @property
575
730
  def SERPAPI_LANG(self) -> str:
576
- return self._getenv("SERPAPI_LANG", "en")
731
+ return get_env("SERPAPI_LANG", "en", self.ENV_PREFIX)
732
+
733
+ @SERPAPI_LANG.setter
734
+ def SERPAPI_LANG(self, value: str):
735
+ os.environ[f"{self.ENV_PREFIX}_SERPAPI_LANG"] = value
577
736
 
578
737
  @property
579
738
  def SEARXNG_PORT(self) -> int:
580
- return int(self._getenv("SEARXNG_PORT", "8080"))
739
+ return int(get_env("SEARXNG_PORT", "8080", self.ENV_PREFIX))
740
+
741
+ @SEARXNG_PORT.setter
742
+ def SEARXNG_PORT(self, value: int):
743
+ os.environ[f"{self.ENV_PREFIX}_SEARXNG_PORT"] = str(value)
581
744
 
582
745
  @property
583
746
  def SEARXNG_BASE_URL(self) -> str:
584
- return self._getenv("SEARXNG_BASE_URL", f"http://localhost:{self.SEARXNG_PORT}")
747
+ return get_env(
748
+ "SEARXNG_BASE_URL", f"http://localhost:{self.SEARXNG_PORT}", self.ENV_PREFIX
749
+ )
750
+
751
+ @SEARXNG_BASE_URL.setter
752
+ def SEARXNG_BASE_URL(self, value: str):
753
+ os.environ[f"{self.ENV_PREFIX}_SEARXNG_BASE_URL"] = value
585
754
 
586
755
  @property
587
756
  def SEARXNG_SAFE(self) -> int:
588
- return int(self._getenv("SEARXNG_SAFE", "0"))
757
+ return int(get_env("SEARXNG_SAFE", "0", self.ENV_PREFIX))
758
+
759
+ @SEARXNG_SAFE.setter
760
+ def SEARXNG_SAFE(self, value: int):
761
+ os.environ[f"{self.ENV_PREFIX}_SEARXNG_SAFE"] = str(value)
589
762
 
590
763
  @property
591
764
  def SEARXNG_LANG(self) -> str:
592
- return self._getenv("SEARXNG_LANG", "en")
765
+ return get_env("SEARXNG_LANG", "en", self.ENV_PREFIX)
766
+
767
+ @SEARXNG_LANG.setter
768
+ def SEARXNG_LANG(self, value: str):
769
+ os.environ[f"{self.ENV_PREFIX}_SEARXNG_LANG"] = value
593
770
 
594
771
  @property
595
772
  def BANNER(self) -> str:
596
773
  return fstring_format(
597
- self._getenv("BANNER", _DEFAULT_BANNER),
774
+ get_env("BANNER", _DEFAULT_BANNER, self.ENV_PREFIX),
598
775
  {"VERSION": self.VERSION},
599
776
  )
600
777
 
778
+ @BANNER.setter
779
+ def BANNER(self, value: str):
780
+ os.environ[f"{self.ENV_PREFIX}_BANNER"] = value
781
+
782
+ @property
783
+ def LLM_SHOW_TOOL_CALL_PREPARATION(self) -> bool:
784
+ return to_boolean(
785
+ get_env("LLM_SHOW_TOOL_CALL_PREPARATION", "0", self.ENV_PREFIX)
786
+ )
787
+
788
+ @LLM_SHOW_TOOL_CALL_PREPARATION.setter
789
+ def LLM_SHOW_TOOL_CALL_PREPARATION(self, value: bool):
790
+ os.environ[f"{self.ENV_PREFIX}_LLM_SHOW_TOOL_CALL_PREPARATION"] = (
791
+ "1" if value else "0"
792
+ )
793
+
601
794
  @property
602
- def LLM_CONTEXT_FILE(self) -> str:
603
- return self._getenv("LLM_CONTEXT_FILE", "ZRB.md")
795
+ def LLM_SHOW_TOOL_CALL_RESULT(self) -> bool:
796
+ return to_boolean(get_env("LLM_SHOW_TOOL_CALL_RESULT", "0", self.ENV_PREFIX))
797
+
798
+ @LLM_SHOW_TOOL_CALL_RESULT.setter
799
+ def LLM_SHOW_TOOL_CALL_RESULT(self, value: bool):
800
+ os.environ[f"{self.ENV_PREFIX}_LLM_SHOW_TOOL_CALL_RESULT"] = (
801
+ "1" if value else "0"
802
+ )
604
803
 
605
804
  @property
606
805
  def USE_TIKTOKEN(self) -> bool:
607
- return to_boolean(self._getenv("USE_TIKTOKEN", "true"))
806
+ return to_boolean(get_env("USE_TIKTOKEN", "true", self.ENV_PREFIX))
807
+
808
+ @USE_TIKTOKEN.setter
809
+ def USE_TIKTOKEN(self, value: bool):
810
+ os.environ[f"{self.ENV_PREFIX}_USE_TIKTOKEN"] = "1" if value else "0"
608
811
 
609
812
  @property
610
813
  def TIKTOKEN_ENCODING_NAME(self) -> str:
611
- return self._getenv("TIKTOKEN_ENCODING_NAME", "cl100k_base")
814
+ return get_env(
815
+ ["TIKTOKEN_ENCODING", "TIKTOKEN_ENCODING_NAME"],
816
+ "cl100k_base",
817
+ self.ENV_PREFIX,
818
+ )
819
+
820
+ @TIKTOKEN_ENCODING_NAME.setter
821
+ def TIKTOKEN_ENCODING_NAME(self, value: str):
822
+ os.environ[f"{self.ENV_PREFIX}_TIKTOKEN_ENCODING_NAME"] = value
612
823
 
613
824
 
614
825
  CFG = Config()