zrb 1.5.16__py3-none-any.whl → 1.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. zrb/__init__.py +2 -2
  2. zrb/__main__.py +12 -12
  3. zrb/builtin/__init__.py +2 -2
  4. zrb/builtin/llm/chat_session.py +202 -0
  5. zrb/builtin/llm/history.py +6 -6
  6. zrb/builtin/llm/llm_ask.py +142 -0
  7. zrb/builtin/llm/tool/rag.py +39 -23
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/default/script.js +1 -1
  9. zrb/builtin/todo.py +21 -19
  10. zrb/callback/any_callback.py +1 -1
  11. zrb/callback/callback.py +69 -7
  12. zrb/config.py +261 -91
  13. zrb/context/shared_context.py +4 -2
  14. zrb/input/text_input.py +9 -6
  15. zrb/llm_config.py +65 -74
  16. zrb/runner/cli.py +13 -4
  17. zrb/runner/web_app.py +3 -3
  18. zrb/runner/web_config/config_factory.py +11 -22
  19. zrb/runner/web_route/error_page/show_error_page.py +16 -6
  20. zrb/runner/web_route/home_page/home_page_route.py +23 -7
  21. zrb/runner/web_route/home_page/view.html +19 -33
  22. zrb/runner/web_route/login_page/login_page_route.py +14 -4
  23. zrb/runner/web_route/login_page/view.html +33 -51
  24. zrb/runner/web_route/logout_page/logout_page_route.py +15 -5
  25. zrb/runner/web_route/logout_page/view.html +23 -41
  26. zrb/runner/web_route/node_page/group/show_group_page.py +26 -10
  27. zrb/runner/web_route/node_page/group/view.html +22 -37
  28. zrb/runner/web_route/node_page/task/show_task_page.py +34 -19
  29. zrb/runner/web_route/node_page/task/view.html +74 -88
  30. zrb/runner/web_route/static/global_template.html +27 -0
  31. zrb/runner/web_route/static/resources/common.css +21 -0
  32. zrb/runner/web_route/static/resources/common.js +28 -0
  33. zrb/runner/web_route/task_session_api_route.py +3 -1
  34. zrb/session_state_logger/session_state_logger_factory.py +2 -2
  35. zrb/task/base_task.py +4 -1
  36. zrb/task/base_trigger.py +47 -2
  37. zrb/task/cmd_task.py +3 -3
  38. zrb/task/llm/agent.py +10 -1
  39. zrb/task/llm/print_node.py +5 -6
  40. zrb/task/llm_task.py +1 -1
  41. zrb/util/git_subtree.py +1 -1
  42. {zrb-1.5.16.dist-info → zrb-1.6.0.dist-info}/METADATA +1 -1
  43. {zrb-1.5.16.dist-info → zrb-1.6.0.dist-info}/RECORD +45 -42
  44. zrb/builtin/llm/llm_chat.py +0 -124
  45. {zrb-1.5.16.dist-info → zrb-1.6.0.dist-info}/WHEEL +0 -0
  46. {zrb-1.5.16.dist-info → zrb-1.6.0.dist-info}/entry_points.txt +0 -0
zrb/builtin/todo.py CHANGED
@@ -4,7 +4,7 @@ import os
4
4
  from typing import Any
5
5
 
6
6
  from zrb.builtin.group import todo_group
7
- from zrb.config import TODO_DIR, TODO_RETENTION, TODO_VISUAL_FILTER
7
+ from zrb.config import CFG
8
8
  from zrb.context.any_context import AnyContext
9
9
  from zrb.input.str_input import StrInput
10
10
  from zrb.input.text_input import TextInput
@@ -25,7 +25,9 @@ from zrb.util.todo import (
25
25
  )
26
26
 
27
27
 
28
- def _get_filter_input(allow_positional_parsing: bool = False) -> StrInput:
28
+ def _get_filter_input(
29
+ allow_positional_parsing: bool = False, default: str | None = None
30
+ ) -> StrInput:
29
31
  return StrInput(
30
32
  name="filter",
31
33
  description="Visual filter",
@@ -33,7 +35,7 @@ def _get_filter_input(allow_positional_parsing: bool = False) -> StrInput:
33
35
  allow_empty=True,
34
36
  allow_positional_parsing=allow_positional_parsing,
35
37
  always_prompt=False,
36
- default=TODO_VISUAL_FILTER,
38
+ default=default if default is not None else CFG.TODO_VISUAL_FILTER,
37
39
  )
38
40
 
39
41
 
@@ -70,12 +72,12 @@ def _get_filter_input(allow_positional_parsing: bool = False) -> StrInput:
70
72
  alias="add",
71
73
  )
72
74
  def add_todo(ctx: AnyContext):
73
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
75
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
74
76
  todo_list: list[TodoTaskModel] = []
75
77
  if os.path.isfile(todo_file_path):
76
78
  todo_list = load_todo_list(todo_file_path)
77
79
  else:
78
- os.makedirs(TODO_DIR, exist_ok=True)
80
+ os.makedirs(CFG.TODO_DIR, exist_ok=True)
79
81
  todo_list.append(
80
82
  cascade_todo_task(
81
83
  TodoTaskModel(
@@ -106,7 +108,7 @@ def add_todo(ctx: AnyContext):
106
108
  alias="list",
107
109
  )
108
110
  def list_todo(ctx: AnyContext):
109
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
111
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
110
112
  todo_list: list[TodoTaskModel] = []
111
113
  if os.path.isfile(todo_file_path):
112
114
  todo_list = load_todo_list(todo_file_path)
@@ -121,7 +123,7 @@ def list_todo(ctx: AnyContext):
121
123
  alias="show",
122
124
  )
123
125
  def show_todo(ctx: AnyContext):
124
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
126
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
125
127
  todo_list: list[TodoTaskModel] = []
126
128
  todo_list: list[TodoTaskModel] = []
127
129
  if os.path.isfile(todo_file_path):
@@ -137,7 +139,7 @@ def show_todo(ctx: AnyContext):
137
139
  # Update todo task
138
140
  todo_task = cascade_todo_task(todo_task)
139
141
  task_id = todo_task.keyval.get("id", "")
140
- log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
142
+ log_work_path = os.path.join(CFG.TODO_DIR, "log-work", f"{task_id}.json")
141
143
  log_work_list = []
142
144
  if os.path.isfile(log_work_path):
143
145
  log_work_list = json.loads(read_file(log_work_path))
@@ -155,7 +157,7 @@ def show_todo(ctx: AnyContext):
155
157
  alias="complete",
156
158
  )
157
159
  def complete_todo(ctx: AnyContext):
158
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
160
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
159
161
  todo_list: list[TodoTaskModel] = []
160
162
  if os.path.isfile(todo_file_path):
161
163
  todo_list = load_todo_list(todo_file_path)
@@ -185,11 +187,11 @@ def complete_todo(ctx: AnyContext):
185
187
  alias="archive",
186
188
  )
187
189
  def archive_todo(ctx: AnyContext):
188
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
190
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
189
191
  todo_list: list[TodoTaskModel] = []
190
192
  if os.path.isfile(todo_file_path):
191
193
  todo_list = load_todo_list(todo_file_path)
192
- retention_duration = datetime.timedelta(seconds=parse_duration(TODO_RETENTION))
194
+ retention_duration = datetime.timedelta(seconds=parse_duration(CFG.TODO_RETENTION))
193
195
  threshold_date = datetime.date.today() - retention_duration
194
196
  new_archived_todo_list = [
195
197
  todo_task
@@ -204,9 +206,9 @@ def archive_todo(ctx: AnyContext):
204
206
  if len(new_archived_todo_list) == 0:
205
207
  ctx.print("No completed task to archive")
206
208
  return get_visual_todo_list(todo_list, filter=ctx.input.filter)
207
- archive_file_path = os.path.join(TODO_DIR, "archive.txt")
208
- if not os.path.isdir(TODO_DIR):
209
- os.make_dirs(TODO_DIR, exist_ok=True)
209
+ archive_file_path = os.path.join(CFG.TODO_DIR, "archive.txt")
210
+ if not os.path.isdir(CFG.TODO_DIR):
211
+ os.make_dirs(CFG.TODO_DIR, exist_ok=True)
210
212
  # Get archived todo list
211
213
  archived_todo_list = []
212
214
  if os.path.isfile(archive_file_path):
@@ -246,7 +248,7 @@ def archive_todo(ctx: AnyContext):
246
248
  alias="log",
247
249
  )
248
250
  def log_todo(ctx: AnyContext):
249
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
251
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
250
252
  todo_list: list[TodoTaskModel] = []
251
253
  if os.path.isfile(todo_file_path):
252
254
  todo_list = load_todo_list(todo_file_path)
@@ -264,7 +266,7 @@ def log_todo(ctx: AnyContext):
264
266
  # Save todo list
265
267
  save_todo_list(todo_file_path, todo_list)
266
268
  # Add log work
267
- log_work_dir = os.path.join(TODO_DIR, "log-work")
269
+ log_work_dir = os.path.join(CFG.TODO_DIR, "log-work")
268
270
  os.makedirs(log_work_dir, exist_ok=True)
269
271
  log_work_file_path = os.path.join(
270
272
  log_work_dir, f"{todo_task.keyval.get('id')}.json"
@@ -286,7 +288,7 @@ def log_todo(ctx: AnyContext):
286
288
  write_file(log_work_file_path, json.dumps(log_work, indent=2))
287
289
  # get log work list
288
290
  task_id = todo_task.keyval.get("id", "")
289
- log_work_path = os.path.join(TODO_DIR, "log-work", f"{task_id}.json")
291
+ log_work_path = os.path.join(CFG.TODO_DIR, "log-work", f"{task_id}.json")
290
292
  log_work_list = []
291
293
  if os.path.isfile(log_work_path):
292
294
  log_work_list = json.loads(read_file(log_work_path))
@@ -333,14 +335,14 @@ def edit_todo(ctx: AnyContext):
333
335
  if line.strip() != ""
334
336
  ]
335
337
  new_content = "\n".join(todo_task_to_line(todo_task) for todo_task in todo_list)
336
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
338
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
337
339
  write_file(todo_file_path, new_content)
338
340
  todo_list = load_todo_list(todo_file_path)
339
341
  return get_visual_todo_list(todo_list, filter=ctx.input.filter)
340
342
 
341
343
 
342
344
  def _get_todo_txt_content() -> str:
343
- todo_file_path = os.path.join(TODO_DIR, "todo.txt")
345
+ todo_file_path = os.path.join(CFG.TODO_DIR, "todo.txt")
344
346
  if not os.path.isfile(todo_file_path):
345
347
  return ""
346
348
  return read_file(todo_file_path)
@@ -6,5 +6,5 @@ from zrb.session.any_session import AnySession
6
6
 
7
7
  class AnyCallback(ABC):
8
8
  @abstractmethod
9
- async def async_run(self, session: AnySession) -> Any:
9
+ async def async_run(self, parent_session: AnySession, session: AnySession) -> Any:
10
10
  pass
zrb/callback/callback.py CHANGED
@@ -5,23 +5,85 @@ from zrb.callback.any_callback import AnyCallback
5
5
  from zrb.session.any_session import AnySession
6
6
  from zrb.task.any_task import AnyTask
7
7
  from zrb.util.attr import get_str_dict_attr
8
+ from zrb.util.string.conversion import to_snake_case
9
+ from zrb.xcom.xcom import Xcom
8
10
 
9
11
 
10
12
  class Callback(AnyCallback):
13
+ """
14
+ Represents a callback that runs a task after a trigger or scheduler event.
15
+
16
+ It handles input mapping and can publish results and session names
17
+ back to the parent session via XCom.
18
+ """
19
+
11
20
  def __init__(
12
21
  self,
13
- input_mapping: StrDictAttr,
14
22
  task: AnyTask,
15
- auto_render: bool = True,
23
+ input_mapping: StrDictAttr,
24
+ render_input_mapping: bool = True,
25
+ result_queue: str | None = None,
26
+ session_name_queue: str | None = None,
16
27
  ):
17
- self._input_mapping = input_mapping
28
+ """
29
+ Initializes a new instance of the Callback class.
30
+
31
+ Args:
32
+ task: The task to be executed by the callback.
33
+ input_mapping: A dictionary or attribute mapping to prepare inputs for the task.
34
+ render_input_mapping: Whether to render the input mapping using
35
+ f-string like syntax.
36
+ result_queue: The name of the XCom queue in the parent session
37
+ to publish the task result.
38
+ session_name_queue: The name of the XCom queue in the parent
39
+ session to publish the session name.
40
+ """
18
41
  self._task = task
19
- self._auto_render = auto_render
42
+ self._input_mapping = input_mapping
43
+ self._render_input_mapping = render_input_mapping
44
+ self._result_queue = result_queue
45
+ self._session_name_queue = session_name_queue
20
46
 
21
- async def async_run(self, session: AnySession) -> Any:
47
+ async def async_run(self, parent_session: AnySession, session: AnySession) -> Any:
48
+ self._maybe_publish_session_name_to_parent_session(
49
+ parent_session=parent_session, session=session
50
+ )
51
+ # prepare input
22
52
  inputs = get_str_dict_attr(
23
- session.shared_ctx, self._input_mapping, auto_render=self._auto_render
53
+ session.shared_ctx,
54
+ self._input_mapping,
55
+ auto_render=self._render_input_mapping,
24
56
  )
25
57
  for name, value in inputs.items():
26
58
  session.shared_ctx.input[name] = value
27
- return await self._task.async_run(session)
59
+ session.shared_ctx.input[to_snake_case(name)] = value
60
+ # run task and get result
61
+ result = await self._task.async_run(session)
62
+ self._maybe_publish_result_to_parent_session(parent_session, result)
63
+ return result
64
+
65
+ def _maybe_publish_session_name_to_parent_session(
66
+ self, parent_session: AnySession, session: AnySession
67
+ ):
68
+ self._maybe_publish_to_parent_session(
69
+ parent_session=parent_session,
70
+ xcom_name=self._session_name_queue,
71
+ value=session.name,
72
+ )
73
+
74
+ def _maybe_publish_result_to_parent_session(
75
+ self, parent_session: AnySession, result: Any
76
+ ):
77
+ self._maybe_publish_to_parent_session(
78
+ parent_session=parent_session, xcom_name=self._result_queue, value=result
79
+ )
80
+
81
+ def _maybe_publish_to_parent_session(
82
+ self, parent_session: AnySession, xcom_name: str | None, value: Any
83
+ ):
84
+ if xcom_name is None:
85
+ return
86
+ parent_xcom = parent_session.shared_ctx.xcom
87
+ if xcom_name not in parent_xcom:
88
+ parent_xcom[xcom_name] = Xcom([])
89
+ parent_xcom[xcom_name].push(value)
zrb/config.py CHANGED
@@ -2,108 +2,278 @@ import importlib.metadata as metadata
2
2
  import logging
3
3
  import os
4
4
  import platform
5
+ from textwrap import dedent
5
6
 
6
7
  from zrb.util.string.conversion import to_boolean
8
+ from zrb.util.string.format import fstring_format
7
9
 
8
-
9
- def _get_current_shell() -> str:
10
- if platform.system() == "Windows":
11
- return "PowerShell"
12
- current_shell = os.getenv("SHELL", "")
13
- if current_shell.endswith("zsh"):
14
- return "zsh"
15
- return "bash"
16
-
17
-
18
- def _get_log_level(level: str) -> int:
19
- level = level.upper()
20
- log_levels = {
21
- "CRITICAL": logging.CRITICAL, # 50
22
- "ERROR": logging.ERROR, # 40
23
- "WARN": logging.WARNING, # 30
24
- "WARNING": logging.WARNING, # 30
25
- "INFO": logging.INFO, # 20
26
- "DEBUG": logging.DEBUG, # 10
27
- "NOTSET": logging.NOTSET, # 0
28
- }
29
- if level in log_levels:
30
- return log_levels[level]
31
- return logging.WARNING
32
-
33
-
34
- LOGGER = logging.getLogger()
35
- DEFAULT_SHELL = os.getenv("ZRB_SHELL", _get_current_shell())
36
- DEFAULT_EDITOR = os.getenv("ZRB_EDITOR", "nano")
37
- INIT_MODULES_STR = os.getenv("ZRB_INIT_MODULES", "")
38
- INIT_MODULES = (
39
- [module.strip() for module in INIT_MODULES_STR.split(":") if module.strip() != ""]
40
- if INIT_MODULES_STR != ""
41
- else []
42
- )
43
- INIT_SCRIPTS_STR = os.getenv("ZRB_INIT_SCRIPTS", "")
44
- INIT_SCRIPTS = (
45
- [script.strip() for script in INIT_SCRIPTS_STR.split(":") if script.strip() != ""]
46
- if INIT_SCRIPTS_STR != ""
47
- else []
48
- )
49
- LOGGING_LEVEL = _get_log_level(os.getenv("ZRB_LOGGING_LEVEL", "WARNING"))
50
- LOAD_BUILTIN = to_boolean(os.getenv("ZRB_LOAD_BUILTIN", "1"))
51
- WARN_UNRECOMMENDED_COMMAND = to_boolean(
52
- os.getenv("ZRB_WARN_UNRECOMMENDED_COMMAND", "1")
53
- )
54
- SESSION_LOG_DIR = os.getenv(
55
- "ZRB_SESSION_LOG_DIR", os.path.expanduser(os.path.join("~", ".zrb-session"))
56
- )
57
- TODO_DIR = os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
58
- TODO_VISUAL_FILTER = os.getenv("ZRB_TODO_FILTER", "")
59
- TODO_RETENTION = os.getenv("ZRB_TODO_RETENTION", "2w")
60
- VERSION = metadata.version("zrb")
61
- WEB_HTTP_PORT = int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
62
- WEB_GUEST_USERNAME = os.getenv("ZRB_WEB_GUEST_USERNAME", "user")
63
- WEB_SUPER_ADMIN_USERNAME = os.getenv("ZRB_WEB_SUPERADMIN_USERNAME", "admin")
64
- WEB_SUPER_ADMIN_PASSWORD = os.getenv("ZRB_WEB_SUPERADMIN_PASSWORD", "admin")
65
- WEB_ACCESS_TOKEN_COOKIE_NAME = os.getenv(
66
- "ZRB_WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token"
67
- )
68
- WEB_REFRESH_TOKEN_COOKIE_NAME = os.getenv(
69
- "ZRB_WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token"
70
- )
71
- WEB_SECRET_KEY = os.getenv("ZRB_WEB_SECRET", "zrb")
72
- WEB_ENABLE_AUTH = to_boolean(os.getenv("ZRB_WEB_ENABLE_AUTH", "0"))
73
- WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES = int(
74
- os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30")
75
- )
76
- WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES = int(
77
- os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60")
78
- )
79
-
80
- LLM_HISTORY_DIR = os.getenv(
81
- "ZRB_LLM_HISTORY_DIR", os.path.expanduser(os.path.join("~", ".zrb-llm-history"))
82
- )
83
- LLM_ALLOW_ACCESS_LOCAL_FILE = to_boolean(os.getenv("ZRB_LLM_ACCESS_LOCAL_FILE", "1"))
84
- LLM_ALLOW_ACCESS_SHELL = to_boolean(os.getenv("ZRB_LLM_ACCESS_SHELL", "1"))
85
- LLM_ALLOW_ACCESS_INTERNET = to_boolean(os.getenv("ZRB_LLM_ACCESS_INTERNET", "1"))
86
- # RAG Configuration
87
- RAG_EMBEDDING_API_KEY = os.getenv("ZRB_RAG_EMBEDDING_API_KEY", None)
88
- RAG_EMBEDDING_BASE_URL = os.getenv("ZRB_RAG_EMBEDDING_BASE_URL", None)
89
- RAG_EMBEDDING_MODEL = os.getenv("ZRB_RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
90
- RAG_CHUNK_SIZE = int(os.getenv("ZRB_RAG_CHUNK_SIZE", "1024"))
91
- RAG_OVERLAP = int(os.getenv("ZRB_RAG_OVERLAP", "128"))
92
- RAG_MAX_RESULT_COUNT = int(os.getenv("ZRB_RAG_MAX_RESULT_COUNT", "5"))
93
- SERP_API_KEY = os.getenv("SERP_API_KEY", "")
94
-
95
-
96
- BANNER = f"""
10
+ _DEFAULT_BANNER = """
97
11
  bb
98
12
  zzzzz rr rr bb
99
13
  zz rrr r bbbbbb
100
14
  zz rr bb bb
101
15
  zzzzz rr bbbbbb {VERSION} Janggala
102
16
  _ _ . . . _ . _ . . .
103
-
104
17
  Your Automation Powerhouse
105
-
106
18
  ☕ Donate at: https://stalchmst.com/donation
107
19
  🐙 Submit issues/PR at: https://github.com/state-alchemists/zrb
108
20
  🐤 Follow us at: https://twitter.com/zarubastalchmst
109
21
  """
22
+
23
+
24
+ class Config:
25
+ @property
26
+ def LOGGER(self) -> logging.Logger:
27
+ return logging.getLogger()
28
+
29
+ @property
30
+ def DEFAULT_SHELL(self) -> str:
31
+ return os.getenv("ZRB_SHELL", self._get_current_shell())
32
+
33
+ def _get_current_shell(self) -> str:
34
+ if platform.system() == "Windows":
35
+ return "PowerShell"
36
+ current_shell = os.getenv("SHELL", "")
37
+ if current_shell.endswith("zsh"):
38
+ return "zsh"
39
+ return "bash"
40
+
41
+ @property
42
+ def DEFAULT_EDITOR(self) -> str:
43
+ return os.getenv("ZRB_EDITOR", "nano")
44
+
45
+ @property
46
+ def INIT_MODULES(self) -> list[str]:
47
+ init_modules_str = os.getenv("ZRB_INIT_MODULES", "")
48
+ return (
49
+ [
50
+ module.strip()
51
+ for module in init_modules_str.split(":")
52
+ if module.strip() != ""
53
+ ]
54
+ if init_modules_str != ""
55
+ else []
56
+ )
57
+
58
+ @property
59
+ def INIT_SCRIPTS(self) -> list[str]:
60
+ init_scripts_str = os.getenv("ZRB_INIT_SCRIPTS", "")
61
+ return (
62
+ [
63
+ script.strip()
64
+ for script in init_scripts_str.split(":")
65
+ if script.strip() != ""
66
+ ]
67
+ if init_scripts_str != ""
68
+ else []
69
+ )
70
+
71
+ @property
72
+ def INIT_FILE_NAME(self) -> str:
73
+ return os.getenv("ZRB_INIT_FILE_NAME", "zrb_init.py")
74
+
75
+ @property
76
+ def LOGGING_LEVEL(self) -> int:
77
+ return self._get_log_level(os.getenv("ZRB_LOGGING_LEVEL", "WARNING"))
78
+
79
+ def _get_log_level(self, level: str) -> int:
80
+ level = level.upper()
81
+ log_levels = {
82
+ "CRITICAL": logging.CRITICAL, # 50
83
+ "ERROR": logging.ERROR, # 40
84
+ "WARN": logging.WARNING, # 30
85
+ "WARNING": logging.WARNING, # 30
86
+ "INFO": logging.INFO, # 20
87
+ "DEBUG": logging.DEBUG, # 10
88
+ "NOTSET": logging.NOTSET, # 0
89
+ }
90
+ if level in log_levels:
91
+ return log_levels[level]
92
+ return logging.WARNING
93
+
94
+ @property
95
+ def LOAD_BUILTIN(self) -> bool:
96
+ return to_boolean(os.getenv("ZRB_LOAD_BUILTIN", "1"))
97
+
98
+ @property
99
+ def WARN_UNRECOMMENDED_COMMAND(self) -> bool:
100
+ return to_boolean(os.getenv("ZRB_WARN_UNRECOMMENDED_COMMAND", "1"))
101
+
102
+ @property
103
+ def SESSION_LOG_DIR(self) -> str:
104
+ return os.getenv(
105
+ "ZRB_SESSION_LOG_DIR", os.path.expanduser(os.path.join("~", ".zrb-session"))
106
+ )
107
+
108
+ @property
109
+ def TODO_DIR(self) -> str:
110
+ return os.getenv("ZRB_TODO_DIR", os.path.expanduser(os.path.join("~", "todo")))
111
+
112
+ @property
113
+ def TODO_VISUAL_FILTER(self) -> str:
114
+ return os.getenv("ZRB_TODO_FILTER", "")
115
+
116
+ @property
117
+ def TODO_RETENTION(self) -> str:
118
+ return os.getenv("ZRB_TODO_RETENTION", "2w")
119
+
120
+ @property
121
+ def VERSION(self) -> str:
122
+ return metadata.version("zrb")
123
+
124
+ @property
125
+ def WEB_HTTP_PORT(self) -> int:
126
+ return int(os.getenv("ZRB_WEB_HTTP_PORT", "21213"))
127
+
128
+ @property
129
+ def WEB_GUEST_USERNAME(self) -> str:
130
+ return os.getenv("ZRB_WEB_GUEST_USERNAME", "user")
131
+
132
+ @property
133
+ def WEB_SUPER_ADMIN_USERNAME(self) -> str:
134
+ return os.getenv("ZRB_WEB_SUPERADMIN_USERNAME", "admin")
135
+
136
+ @property
137
+ def WEB_SUPER_ADMIN_PASSWORD(self) -> str:
138
+ return os.getenv("ZRB_WEB_SUPERADMIN_PASSWORD", "admin")
139
+
140
+ @property
141
+ def WEB_ACCESS_TOKEN_COOKIE_NAME(self) -> str:
142
+ return os.getenv("ZRB_WEB_ACCESS_TOKEN_COOKIE_NAME", "access_token")
143
+
144
+ @property
145
+ def WEB_REFRESH_TOKEN_COOKIE_NAME(self) -> str:
146
+ return os.getenv("ZRB_WEB_REFRESH_TOKEN_COOKIE_NAME", "refresh_token")
147
+
148
+ @property
149
+ def WEB_SECRET_KEY(self) -> str:
150
+ return os.getenv("ZRB_WEB_SECRET", "zrb")
151
+
152
+ @property
153
+ def WEB_ENABLE_AUTH(self) -> bool:
154
+ return to_boolean(os.getenv("ZRB_WEB_ENABLE_AUTH", "0"))
155
+
156
+ @property
157
+ def WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES(self) -> int:
158
+ return int(os.getenv("ZRB_WEB_ACCESS_TOKEN_EXPIRE_MINUTES", "30"))
159
+
160
+ @property
161
+ def WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES(self) -> int:
162
+ return int(os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60"))
163
+
164
+ @property
165
+ def WEB_TITLE(self) -> str:
166
+ return os.getenv("ZRB_WEB_TITLE", "Zrb")
167
+
168
+ @property
169
+ def WEB_JARGON(self) -> str:
170
+ return os.getenv("ZRB_WEB_JARGON", "Your Automation PowerHouse")
171
+
172
+ @property
173
+ def WEB_HOMEPAGE_INTRO(self) -> str:
174
+ return os.getenv("ZRB_WEB_HOMEPAGE_INTRO", "Welcome to Zrb Web Interface")
175
+
176
+ @property
177
+ def LLM_MODEL(self) -> str | None:
178
+ return os.getenv("ZRB_LLM_MODEL", None)
179
+
180
+ @property
181
+ def LLM_BASE_URL(self) -> str | None:
182
+ return os.getenv("ZRB_LLM_BASE_URL", None)
183
+
184
+ @property
185
+ def LLM_API_KEY(self) -> str | None:
186
+ return os.getenv("ZRB_LLM_API_KEY", None)
187
+
188
+ @property
189
+ def LLM_SYSTEM_PROMPT(self) -> str | None:
190
+ return os.getenv("ZRB_LLM_SYSTEM_PROMPT", None)
191
+
192
+ @property
193
+ def LLM_PERSONA(self) -> str | None:
194
+ return os.getenv("ZRB_LLM_PERSONA", None)
195
+
196
+ @property
197
+ def LLM_SPECIAL_INSTRUCTION_PROMPT(self) -> str | None:
198
+ return os.getenv("ZRB_LLM_SPECIAL_INSTRUCTION_PROMPT", None)
199
+
200
+ @property
201
+ def LLM_SUMMARIZATION_PROMPT(self) -> str | None:
202
+ return os.getenv("ZRB_LLM_SUMMARIZATION_PROMPT", None)
203
+
204
+ @property
205
+ def LLM_CONTEXT_ENRICHMENT_PROMPT(self) -> str | None:
206
+ return os.getenv("ZRB_LLM_CONTEXT_ENRICHMENT_PROMPT", None)
207
+
208
+ @property
209
+ def LLM_SUMMARIZE_HISTORY(self) -> bool:
210
+ return to_boolean(os.getenv("ZRB_LLM_SUMMARIZE_HISTORY", "true"))
211
+
212
+ @property
213
+ def LLM_HISTORY_SUMMARIZATION_THRESHOLD(self) -> int:
214
+ return int(os.getenv("ZRB_LLM_HISTORY_SUMMARIZATION_THRESHOLD", "5"))
215
+
216
+ @property
217
+ def LLM_ENRICH_CONTEXT(self) -> bool:
218
+ return to_boolean(os.getenv("ZRB_LLM_ENRICH_CONTEXT", "true"))
219
+
220
+ @property
221
+ def LLM_CONTEXT_ENRICHMENT_THRESHOLD(self) -> int:
222
+ return int(os.getenv("ZRB_LLM_CONTEXT_ENRICHMENT_THRESHOLD", "5"))
223
+
224
+ @property
225
+ def LLM_HISTORY_DIR(self) -> str:
226
+ return os.getenv(
227
+ "ZRB_LLM_HISTORY_DIR",
228
+ os.path.expanduser(os.path.join("~", ".zrb-llm-history")),
229
+ )
230
+
231
+ @property
232
+ def LLM_ALLOW_ACCESS_LOCAL_FILE(self) -> bool:
233
+ return to_boolean(os.getenv("ZRB_LLM_ACCESS_LOCAL_FILE", "1"))
234
+
235
+ @property
236
+ def LLM_ALLOW_ACCESS_SHELL(self) -> bool:
237
+ return to_boolean(os.getenv("ZRB_LLM_ACCESS_SHELL", "1"))
238
+
239
+ @property
240
+ def LLM_ALLOW_ACCESS_INTERNET(self) -> bool:
241
+ return to_boolean(os.getenv("ZRB_LLM_ACCESS_INTERNET", "1"))
242
+
243
+ @property
244
+ def RAG_EMBEDDING_API_KEY(self) -> str:
245
+ return os.getenv("ZRB_RAG_EMBEDDING_API_KEY", None)
246
+
247
+ @property
248
+ def RAG_EMBEDDING_BASE_URL(self) -> str:
249
+ return os.getenv("ZRB_RAG_EMBEDDING_BASE_URL", None)
250
+
251
+ @property
252
+ def RAG_EMBEDDING_MODEL(self) -> str:
253
+ return os.getenv("ZRB_RAG_EMBEDDING_MODEL", "text-embedding-ada-002")
254
+
255
+ @property
256
+ def RAG_CHUNK_SIZE(self) -> int:
257
+ return int(os.getenv("ZRB_RAG_CHUNK_SIZE", "1024"))
258
+
259
+ @property
260
+ def RAG_OVERLAP(self) -> int:
261
+ return int(os.getenv("ZRB_RAG_OVERLAP", "128"))
262
+
263
+ @property
264
+ def RAG_MAX_RESULT_COUNT(self) -> int:
265
+ return int(os.getenv("ZRB_RAG_MAX_RESULT_COUNT", "5"))
266
+
267
+ @property
268
+ def SERP_API_KEY(self) -> str:
269
+ return os.getenv("SERP_API_KEY", "")
270
+
271
+ @property
272
+ def BANNER(self) -> str:
273
+ return fstring_format(
274
+ os.getenv("ZRB_BANNER", _DEFAULT_BANNER),
275
+ {"VERSION": self.VERSION},
276
+ )
277
+
278
+
279
+ CFG = Config()
@@ -1,7 +1,7 @@
1
1
  import datetime
2
2
  from typing import Any
3
3
 
4
- from zrb.config import LOGGING_LEVEL
4
+ from zrb.config import CFG
5
5
  from zrb.context.any_shared_context import AnySharedContext
6
6
  from zrb.dot_dict.dot_dict import DotDict
7
7
  from zrb.session.any_session import AnySession
@@ -25,7 +25,7 @@ class SharedContext(AnySharedContext):
25
25
  args: list[Any] = [],
26
26
  env: dict[str, str] = {},
27
27
  xcom: dict[str, Xcom] = {},
28
- logging_level: int = LOGGING_LEVEL,
28
+ logging_level: int | None = None,
29
29
  ):
30
30
  self.__logging_level = logging_level
31
31
  self._input = DotDict(input)
@@ -79,6 +79,8 @@ class SharedContext(AnySharedContext):
79
79
  self._session = session
80
80
 
81
81
  def get_logging_level(self) -> int:
82
+ if self.__logging_level is None:
83
+ return CFG.LOGGING_LEVEL
82
84
  return self.__logging_level
83
85
 
84
86
  def render(self, template: str) -> str:
zrb/input/text_input.py CHANGED
@@ -3,7 +3,7 @@ import subprocess
3
3
  import tempfile
4
4
  from collections.abc import Callable
5
5
 
6
- from zrb.config import DEFAULT_EDITOR
6
+ from zrb.config import CFG
7
7
  from zrb.context.any_shared_context import AnySharedContext
8
8
  from zrb.input.base_input import BaseInput
9
9
  from zrb.util.file import read_file
@@ -20,7 +20,7 @@ class TextInput(BaseInput):
20
20
  allow_empty: bool = False,
21
21
  allow_positional_parsing: bool = True,
22
22
  always_prompt: bool = True,
23
- editor: str = DEFAULT_EDITOR,
23
+ editor: str | None = None,
24
24
  extension: str = ".txt",
25
25
  comment_start: str | None = None,
26
26
  comment_end: str | None = None,
@@ -86,10 +86,13 @@ class TextInput(BaseInput):
86
86
  if default_value:
87
87
  temp_file.write(default_value.encode())
88
88
  temp_file.flush()
89
- # Open the editor
90
- subprocess.call([self._editor, temp_file_name])
91
- # Read the edited content
92
- edited_content = read_file(temp_file_name)
89
+ # Open the editor
90
+ editor_cmd = (
91
+ self._editor if self._editor is not None else CFG.DEFAULT_EDITOR
92
+ )
93
+ subprocess.call([editor_cmd, temp_file_name])
94
+ # Read the edited content
95
+ edited_content = read_file(temp_file_name)
93
96
  parts = [
94
97
  text.strip() for text in edited_content.split(comment_prompt_message, 1)
95
98
  ]