zrb 1.5.17__py3-none-any.whl → 1.6.1__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 (50) 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 +73 -79
  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 +11 -3
  39. zrb/task/llm/context.py +1 -3
  40. zrb/task/llm/context_enrichment.py +21 -15
  41. zrb/task/llm/history.py +32 -63
  42. zrb/task/llm/history_summarization.py +19 -19
  43. zrb/task/llm/print_node.py +5 -6
  44. zrb/task/llm/prompt.py +0 -27
  45. zrb/task/llm_task.py +22 -18
  46. {zrb-1.5.17.dist-info → zrb-1.6.1.dist-info}/METADATA +1 -1
  47. {zrb-1.5.17.dist-info → zrb-1.6.1.dist-info}/RECORD +49 -46
  48. zrb/builtin/llm/llm_chat.py +0 -124
  49. {zrb-1.5.17.dist-info → zrb-1.6.1.dist-info}/WHEEL +0 -0
  50. {zrb-1.5.17.dist-info → zrb-1.6.1.dist-info}/entry_points.txt +0 -0
zrb/llm_config.py CHANGED
@@ -1,4 +1,3 @@
1
- import os
2
1
  from typing import TYPE_CHECKING, Any
3
2
 
4
3
  if TYPE_CHECKING:
@@ -8,7 +7,7 @@ else:
8
7
  Model = Any
9
8
  Provider = Any
10
9
 
11
- from zrb.util.string.conversion import to_boolean
10
+ from zrb.config import CFG
12
11
 
13
12
  DEFAULT_SYSTEM_PROMPT = """
14
13
  You have access to tools.
@@ -27,9 +26,10 @@ You are an expert in various fields including technology, science, history, and
27
26
 
28
27
  # Concise summarization focused on preserving critical context for continuity.
29
28
  DEFAULT_SUMMARIZATION_PROMPT = """
30
- You are a summarization assistant. Create an updated, concise summary integrating
31
- the previous summary (if any) with the new conversation history.
32
- Your primary goal is to preserve ALL critical context needed for the main assistant
29
+ You are a summarization assistant.
30
+ Your goal is to help main assistant to continue the conversation by creating an updated,
31
+ concise summary integrating the previous summary (if any) with the new conversation history.
32
+ Preserve ALL critical context needed for the main assistant
33
33
  to continue the task effectively. This includes key facts, decisions, tool usage
34
34
  results, and essential background. Do not omit details that would force the main
35
35
  assistant to re-gather information.
@@ -38,11 +38,13 @@ Output *only* the updated summary text.
38
38
 
39
39
  DEFAULT_CONTEXT_ENRICHMENT_PROMPT = """
40
40
  You are an information extraction assistant.
41
+ Your goal is to help main assistant to continue the conversation by extracting
42
+ important informations.
41
43
  Analyze the conversation history and current context to extract key facts like
42
44
  user_name, user_roles, preferences, goals, etc.
43
45
  Return only a JSON object containing a single key "response", whose value is
44
- another JSON object with these details.
45
- If nothing is found, output {"response": {}}.
46
+ another JSON object with these details (i.e., {"response": {"context_name": "value"}}).
47
+ If no context can be extracted, return {"response": {}}.
46
48
  """.strip()
47
49
 
48
50
  DEFAULT_SPECIAL_INSTRUCTION_PROMPT = "" # Default to empty
@@ -65,72 +67,30 @@ class LLMConfig:
65
67
  default_enrich_context: bool | None = None,
66
68
  default_context_enrichment_threshold: int | None = None,
67
69
  ):
68
- self._default_model_name = (
69
- default_model_name
70
- if default_model_name is not None
71
- else os.getenv("ZRB_LLM_MODEL", None)
72
- )
73
- self._default_model_base_url = (
74
- default_base_url
75
- if default_base_url is not None
76
- else os.getenv("ZRB_LLM_BASE_URL", None)
77
- )
78
- self._default_model_api_key = (
79
- default_api_key
80
- if default_api_key is not None
81
- else os.getenv("ZRB_LLM_API_KEY", None)
82
- )
83
- self._default_system_prompt = (
84
- default_system_prompt
85
- if default_system_prompt is not None
86
- else os.getenv("ZRB_LLM_SYSTEM_PROMPT", None)
87
- )
88
- self._default_persona = (
89
- default_persona
90
- if default_persona is not None
91
- else os.getenv("ZRB_LLM_PERSONA", None)
92
- )
93
- self._default_special_instruction_prompt = (
94
- default_special_instruction_prompt
95
- if default_special_instruction_prompt is not None
96
- else os.getenv("ZRB_LLM_SPECIAL_INSTRUCTION_PROMPT", None)
97
- )
98
- self._default_summarization_prompt = (
99
- default_summarization_prompt
100
- if default_summarization_prompt is not None
101
- else os.getenv("ZRB_LLM_SUMMARIZATION_PROMPT", None)
102
- )
103
- self._default_context_enrichment_prompt = (
104
- default_context_enrichment_prompt
105
- if default_context_enrichment_prompt is not None
106
- else os.getenv("ZRB_LLM_CONTEXT_ENRICHMENT_PROMPT", None)
107
- )
108
- self._default_summarize_history = (
109
- default_summarize_history
110
- if default_summarize_history is not None
111
- else to_boolean(os.getenv("ZRB_LLM_SUMMARIZE_HISTORY", "true"))
112
- )
70
+ self._default_model_name = default_model_name
71
+ self._default_model_base_url = default_base_url
72
+ self._default_model_api_key = default_api_key
73
+ self._default_persona = default_persona
74
+ self._default_system_prompt = default_system_prompt
75
+ self._default_special_instruction_prompt = default_special_instruction_prompt
76
+ self._default_summarization_prompt = default_summarization_prompt
77
+ self._default_context_enrichment_prompt = default_context_enrichment_prompt
78
+ self._default_summarize_history = default_summarize_history
113
79
  self._default_history_summarization_threshold = (
114
80
  default_history_summarization_threshold
115
- if default_history_summarization_threshold is not None
116
- else int(os.getenv("ZRB_LLM_HISTORY_SUMMARIZATION_THRESHOLD", "5"))
117
- )
118
- self._default_enrich_context = (
119
- default_enrich_context
120
- if default_enrich_context is not None
121
- else to_boolean(os.getenv("ZRB_LLM_ENRICH_CONTEXT", "true"))
122
81
  )
82
+ self._default_enrich_context = default_enrich_context
123
83
  self._default_context_enrichment_threshold = (
124
84
  default_context_enrichment_threshold
125
- if default_context_enrichment_threshold is not None
126
- else int(os.getenv("ZRB_LLM_CONTEXT_ENRICHMENT_THRESHOLD", "5"))
127
85
  )
128
86
  self._default_provider = None
129
87
  self._default_model = None
130
88
 
131
89
  def _get_model_name(self) -> str | None:
132
90
  return (
133
- self._default_model_name if self._default_model_name is not None else None
91
+ self._default_model_name
92
+ if self._default_model_name is not None
93
+ else CFG.LLM_MODEL
134
94
  )
135
95
 
136
96
  def get_default_model_provider(self) -> Provider | str:
@@ -146,35 +106,53 @@ class LLMConfig:
146
106
 
147
107
  def get_default_system_prompt(self) -> str:
148
108
  return (
149
- DEFAULT_SYSTEM_PROMPT
150
- if self._default_system_prompt is None
151
- else self._default_system_prompt
109
+ self._default_system_prompt
110
+ if self._default_system_prompt is not None
111
+ else (
112
+ CFG.LLM_SYSTEM_PROMPT
113
+ if CFG.LLM_SYSTEM_PROMPT is not None
114
+ else DEFAULT_SYSTEM_PROMPT
115
+ )
152
116
  )
153
117
 
154
118
  def get_default_persona(self) -> str:
155
119
  return (
156
- DEFAULT_PERSONA if self._default_persona is None else self._default_persona
120
+ self._default_persona
121
+ if self._default_persona is not None
122
+ else (CFG.LLM_PERSONA if CFG.LLM_PERSONA is not None else DEFAULT_PERSONA)
157
123
  )
158
124
 
159
125
  def get_default_special_instruction_prompt(self) -> str:
160
126
  return (
161
- DEFAULT_SPECIAL_INSTRUCTION_PROMPT
162
- if self._default_special_instruction_prompt is None
163
- else self._default_special_instruction_prompt
127
+ self._default_special_instruction_prompt
128
+ if self._default_special_instruction_prompt is not None
129
+ else (
130
+ CFG.LLM_SPECIAL_INSTRUCTION_PROMPT
131
+ if CFG.LLM_SPECIAL_INSTRUCTION_PROMPT is not None
132
+ else DEFAULT_SPECIAL_INSTRUCTION_PROMPT
133
+ )
164
134
  )
165
135
 
166
136
  def get_default_summarization_prompt(self) -> str:
167
137
  return (
168
- DEFAULT_SUMMARIZATION_PROMPT
169
- if self._default_summarization_prompt is None
170
- else self._default_summarization_prompt
138
+ self._default_summarization_prompt
139
+ if self._default_summarization_prompt is not None
140
+ else (
141
+ CFG.LLM_SUMMARIZATION_PROMPT
142
+ if CFG.LLM_SUMMARIZATION_PROMPT is not None
143
+ else DEFAULT_SUMMARIZATION_PROMPT
144
+ )
171
145
  )
172
146
 
173
147
  def get_default_context_enrichment_prompt(self) -> str:
174
148
  return (
175
- DEFAULT_CONTEXT_ENRICHMENT_PROMPT
176
- if self._default_context_enrichment_prompt is None
177
- else self._default_context_enrichment_prompt
149
+ self._default_context_enrichment_prompt
150
+ if self._default_context_enrichment_prompt is not None
151
+ else (
152
+ CFG.LLM_CONTEXT_ENRICHMENT_PROMPT
153
+ if CFG.LLM_CONTEXT_ENRICHMENT_PROMPT is not None
154
+ else DEFAULT_CONTEXT_ENRICHMENT_PROMPT
155
+ )
178
156
  )
179
157
 
180
158
  def get_default_model(self) -> Model | str | None:
@@ -191,16 +169,32 @@ class LLMConfig:
191
169
  )
192
170
 
193
171
  def get_default_summarize_history(self) -> bool:
194
- return self._default_summarize_history
172
+ return (
173
+ self._default_summarize_history
174
+ if self._default_summarize_history is not None
175
+ else CFG.LLM_SUMMARIZE_HISTORY
176
+ )
195
177
 
196
178
  def get_default_history_summarization_threshold(self) -> int:
197
- return self._default_history_summarization_threshold
179
+ return (
180
+ self._default_history_summarization_threshold
181
+ if self._default_history_summarization_threshold is not None
182
+ else CFG.LLM_HISTORY_SUMMARIZATION_THRESHOLD
183
+ )
198
184
 
199
185
  def get_default_enrich_context(self) -> bool:
200
- return self._default_enrich_context
186
+ return (
187
+ self._default_enrich_context
188
+ if self._default_enrich_context is not None
189
+ else CFG.LLM_ENRICH_CONTEXT
190
+ )
201
191
 
202
192
  def get_default_context_enrichment_threshold(self) -> int:
203
- return self._default_context_enrichment_threshold
193
+ return (
194
+ self._default_context_enrichment_threshold
195
+ if self._default_context_enrichment_threshold is not None
196
+ else CFG.LLM_CONTEXT_ENRICHMENT_THRESHOLD
197
+ )
204
198
 
205
199
  def set_default_persona(self, persona: str):
206
200
  self._default_persona = persona
zrb/runner/cli.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from typing import Any
3
3
 
4
- from zrb.config import BANNER, VERSION, WEB_HTTP_PORT
4
+ from zrb.config import CFG
5
5
  from zrb.context.any_context import AnyContext
6
6
  from zrb.context.shared_context import SharedContext
7
7
  from zrb.group.any_group import AnyGroup
@@ -23,6 +23,13 @@ from zrb.util.string.conversion import double_quote
23
23
 
24
24
 
25
25
  class Cli(Group):
26
+
27
+ @property
28
+ def banner(self) -> str:
29
+ if self._banner is None:
30
+ return CFG.BANNER
31
+ return self._banner
32
+
26
33
  def run(self, args: list[str] = []):
27
34
  kwargs, args = self._extract_kwargs_from_args(args)
28
35
  node, node_path, args = extract_node_from_args(self, args)
@@ -147,14 +154,14 @@ class Cli(Group):
147
154
  return kwargs, residual_args
148
155
 
149
156
 
150
- cli = Cli(name="zrb", description="Your Automation Powerhouse", banner=BANNER)
157
+ cli = Cli(name="zrb", description="Your Automation Powerhouse")
151
158
 
152
159
 
153
160
  @make_task(
154
161
  name="version", description="🌟 Get current Zrb version", retries=0, group=cli
155
162
  )
156
163
  def get_version(_: AnyContext):
157
- return VERSION
164
+ return CFG.VERSION
158
165
 
159
166
 
160
167
  server_group = cli.add_group(
@@ -174,5 +181,7 @@ async def start_server(_: AnyContext):
174
181
  from uvicorn import Config, Server
175
182
 
176
183
  app = create_web_app(cli, web_config, session_state_logger)
177
- server = Server(Config(app=app, host="0.0.0.0", port=WEB_HTTP_PORT, loop="asyncio"))
184
+ server = Server(
185
+ Config(app=app, host="0.0.0.0", port=CFG.WEB_HTTP_PORT, loop="asyncio")
186
+ )
178
187
  await server.serve()
zrb/runner/web_app.py CHANGED
@@ -2,7 +2,7 @@ import asyncio
2
2
  import sys
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from zrb.config import BANNER, VERSION
5
+ from zrb.config import CFG
6
6
  from zrb.group.any_group import AnyGroup
7
7
  from zrb.runner.web_config.config import WebConfig
8
8
  from zrb.runner.web_route.docs_route import serve_docs
@@ -37,7 +37,7 @@ def create_web_app(
37
37
 
38
38
  @asynccontextmanager
39
39
  async def lifespan(app: FastAPI):
40
- for line in BANNER.split("\n") + [
40
+ for line in CFG.BANNER.split("\n") + [
41
41
  f"Zrb Server running on http://localhost:{web_config.port}"
42
42
  ]:
43
43
  print(line, file=sys.stderr)
@@ -48,7 +48,7 @@ def create_web_app(
48
48
 
49
49
  app = FastAPI(
50
50
  title="Zrb",
51
- version=VERSION,
51
+ version=CFG.VERSION,
52
52
  summary="Your Automation Powerhouse",
53
53
  lifespan=lifespan,
54
54
  docs_url=None,
@@ -1,26 +1,15 @@
1
- from zrb.config import (
2
- WEB_ACCESS_TOKEN_COOKIE_NAME,
3
- WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
4
- WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
5
- WEB_ENABLE_AUTH,
6
- WEB_GUEST_USERNAME,
7
- WEB_HTTP_PORT,
8
- WEB_REFRESH_TOKEN_COOKIE_NAME,
9
- WEB_SECRET_KEY,
10
- WEB_SUPER_ADMIN_PASSWORD,
11
- WEB_SUPER_ADMIN_USERNAME,
12
- )
1
+ from zrb.config import CFG
13
2
  from zrb.runner.web_config.config import WebConfig
14
3
 
15
4
  web_config = WebConfig(
16
- port=WEB_HTTP_PORT,
17
- secret_key=WEB_SECRET_KEY,
18
- access_token_expire_minutes=WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
19
- refresh_token_expire_minutes=WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
20
- access_token_cookie_name=WEB_ACCESS_TOKEN_COOKIE_NAME,
21
- refresh_token_cookie_name=WEB_REFRESH_TOKEN_COOKIE_NAME,
22
- enable_auth=WEB_ENABLE_AUTH,
23
- super_admin_username=WEB_SUPER_ADMIN_USERNAME,
24
- super_admin_password=WEB_SUPER_ADMIN_PASSWORD,
25
- guest_username=WEB_GUEST_USERNAME,
5
+ port=CFG.WEB_HTTP_PORT,
6
+ secret_key=CFG.WEB_SECRET_KEY,
7
+ access_token_expire_minutes=CFG.WEB_AUTH_ACCESS_TOKEN_EXPIRE_MINUTES,
8
+ refresh_token_expire_minutes=CFG.WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES,
9
+ access_token_cookie_name=CFG.WEB_ACCESS_TOKEN_COOKIE_NAME,
10
+ refresh_token_cookie_name=CFG.WEB_REFRESH_TOKEN_COOKIE_NAME,
11
+ enable_auth=CFG.WEB_ENABLE_AUTH,
12
+ super_admin_username=CFG.WEB_SUPER_ADMIN_USERNAME,
13
+ super_admin_password=CFG.WEB_SUPER_ADMIN_PASSWORD,
14
+ guest_username=CFG.WEB_GUEST_USERNAME,
26
15
  )
@@ -1,5 +1,6 @@
1
1
  import os
2
2
 
3
+ from zrb.config import CFG
3
4
  from zrb.group.any_group import AnyGroup
4
5
  from zrb.runner.web_schema.user import User
5
6
  from zrb.runner.web_util.html import get_html_auth_link
@@ -11,17 +12,26 @@ def show_error_page(user: User, root_group: AnyGroup, status_code: int, message:
11
12
  from fastapi.responses import HTMLResponse
12
13
 
13
14
  _DIR = os.path.dirname(__file__)
15
+ _GLOBAL_TEMPLATE = read_file(
16
+ os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
17
+ )
14
18
  _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
15
19
  auth_link = get_html_auth_link(user)
16
20
  return HTMLResponse(
17
21
  fstring_format(
18
- _VIEW_TEMPLATE,
22
+ _GLOBAL_TEMPLATE,
19
23
  {
20
- "name": root_group.name,
21
- "description": root_group.description,
22
- "auth_link": auth_link,
23
- "error_status_code": status_code,
24
- "error_message": message,
24
+ "web_title": CFG.WEB_TITLE,
25
+ "content": fstring_format(
26
+ _VIEW_TEMPLATE,
27
+ {
28
+ "name": root_group.name,
29
+ "description": root_group.description,
30
+ "auth_link": auth_link,
31
+ "error_status_code": status_code,
32
+ "error_message": message,
33
+ },
34
+ ),
25
35
  },
26
36
  ),
27
37
  status_code=status_code,
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from typing import TYPE_CHECKING
3
3
 
4
+ from zrb.config import CFG
4
5
  from zrb.group.any_group import AnyGroup
5
6
  from zrb.runner.web_config.config import WebConfig
6
7
  from zrb.runner.web_util.html import (
@@ -30,22 +31,37 @@ def serve_home_page(
30
31
  @app.get("/ui", response_class=HTMLResponse, include_in_schema=False)
31
32
  @app.get("/ui/", response_class=HTMLResponse, include_in_schema=False)
32
33
  async def home_page_ui(request: Request) -> HTMLResponse:
33
-
34
34
  _DIR = os.path.dirname(__file__)
35
+ _GLOBAL_TEMPLATE = read_file(
36
+ os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
37
+ )
35
38
  _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
39
+ web_title = CFG.WEB_TITLE if CFG.WEB_TITLE.strip() != "" else root_group.name
40
+ web_jargon = (
41
+ CFG.WEB_JARGON if CFG.WEB_JARGON.strip() != "" else root_group.description
42
+ )
36
43
  user = await get_user_from_request(web_config, request)
37
44
  group_info = get_html_subgroup_info(user, "/ui/", root_group)
38
45
  task_info = get_html_subtask_info(user, "/ui/", root_group)
39
46
  auth_link = get_html_auth_link(user)
40
47
  return HTMLResponse(
41
48
  fstring_format(
42
- _VIEW_TEMPLATE,
49
+ _GLOBAL_TEMPLATE,
43
50
  {
44
- "group_info": group_info,
45
- "task_info": task_info,
46
- "name": root_group.name,
47
- "description": root_group.description,
48
- "auth_link": auth_link,
51
+ "web_title": web_title,
52
+ "content": fstring_format(
53
+ _VIEW_TEMPLATE,
54
+ {
55
+ "web_title": web_title,
56
+ "web_jargon": web_jargon,
57
+ "web_homepage_intro": CFG.WEB_HOMEPAGE_INTRO,
58
+ "group_info": group_info,
59
+ "task_info": task_info,
60
+ "name": root_group.name,
61
+ "description": root_group.description,
62
+ "auth_link": auth_link,
63
+ },
64
+ ),
49
65
  },
50
66
  )
51
67
  )
@@ -1,33 +1,19 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <meta name="color-scheme" content="light dark">
7
- <link rel="stylesheet" href="/static/pico.min.css">
8
- <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
9
- <title>Zrb</title>
10
- <link rel="stylesheet" href="/static/common.css">
11
- </head>
12
- <body>
13
- <header class="container">
14
- <hgroup>
15
- <h1>{name}</h1>
16
- <p>{description}</p>
17
- <nav>
18
- <ul>
19
- <li><a href="/docs">💻 API Documentation</a></li>
20
- </ul>
21
- <ul>
22
- <li>{auth_link}</li>
23
- </ul>
24
- </nav>
25
- </hgroup>
26
- </header>
27
- <main class="container">
28
- {group_info}
29
- {task_info}
30
- </main>
31
- </body>
32
- <script src="/refresh-token.js"></script>
33
- </html>
1
+ <header class="container">
2
+ <hgroup>
3
+ <h1>{web_title}</h1>
4
+ <p>{web_jargon}</p>
5
+ <nav>
6
+ <ul>
7
+ <li><a href="/docs">💻 API Documentation</a></li>
8
+ </ul>
9
+ <ul>
10
+ <li>{auth_link}</li>
11
+ </ul>
12
+ </nav>
13
+ </hgroup>
14
+ </header>
15
+ <main class="container">
16
+ <p>{web_homepage_intro}</p>
17
+ {group_info}
18
+ {task_info}
19
+ </main>
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from typing import TYPE_CHECKING
3
3
 
4
+ from zrb.config import CFG
4
5
  from zrb.group.any_group import AnyGroup
5
6
  from zrb.runner.web_config.config import WebConfig
6
7
  from zrb.runner.web_util.html import get_html_auth_link
@@ -24,16 +25,25 @@ def serve_login_page(
24
25
  @app.get("/login", response_class=HTMLResponse, include_in_schema=False)
25
26
  async def login(request: Request) -> HTMLResponse:
26
27
  _DIR = os.path.dirname(__file__)
28
+ _GLOBAL_TEMPLATE = read_file(
29
+ os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
30
+ )
27
31
  _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
28
32
  user = await get_user_from_request(web_config, request)
29
33
  auth_link = get_html_auth_link(user)
30
34
  return HTMLResponse(
31
35
  fstring_format(
32
- _VIEW_TEMPLATE,
36
+ _GLOBAL_TEMPLATE,
33
37
  {
34
- "name": root_group.name,
35
- "description": root_group.description,
36
- "auth_link": auth_link,
38
+ "web_title": CFG.WEB_TITLE,
39
+ "content": fstring_format(
40
+ _VIEW_TEMPLATE,
41
+ {
42
+ "name": root_group.name,
43
+ "description": root_group.description,
44
+ "auth_link": auth_link,
45
+ },
46
+ ),
37
47
  },
38
48
  )
39
49
  )
@@ -1,51 +1,33 @@
1
- <!doctype html>
2
- <html lang="en">
3
-
4
- <head>
5
- <meta charset="utf-8">
6
- <meta name="viewport" content="width=device-width, initial-scale=1">
7
- <meta name="color-scheme" content="light dark">
8
- <link rel="stylesheet" href="/static/pico.min.css">
9
- <link rel="icon" href="/static/favicon-32x32.png" sizes="32x32" type="image/png">
10
- <title>Zrb</title>
11
- <link rel="stylesheet" href="/static/common.css">
12
- </head>
13
-
14
- <body>
15
- <header class="container">
16
- <hgroup>
17
- <h1>{name}</h1>
18
- <p>{description}</p>
19
- <nav>
20
- <ul>
21
- <li><a href="/">🏠 Home</a></li>
22
- <li><a href="/docs">💻 API Documentation</a></li>
23
- </ul>
24
- </nav>
25
- </hgroup>
26
- </header>
27
- <main class="container">
28
- <article>
29
- <h1>Login</h1>
30
- <form id="login-form" onsubmit="login(event)">
31
- <label for="username">
32
- Username
33
- <input type="text" id="username" name="username" placeholder="Enter your username" required />
34
- </label>
35
- <label for="password">
36
- Password
37
- <input type="password" id="password" name="password" placeholder="Enter your password" required />
38
- </label>
39
- <div class="button-group">
40
- <button class="primary">🔑 Login</button>
41
- <button class="secondary" onclick="window.history.back()">🔙 Cancel</button>
42
- </div>
43
- </form>
44
- <p id="error-message"></p>
45
- </article>
46
- </main>
47
- <script src="/static/login/event.js"></script>
48
- <script src="/refresh-token.js"></script>
49
- </body>
50
-
51
- </html>
1
+ <header class="container">
2
+ <hgroup>
3
+ <h1>{name}</h1>
4
+ <p>{description}</p>
5
+ <nav>
6
+ <ul>
7
+ <li><a href="/">🏠 Home</a></li>
8
+ <li><a href="/docs">💻 API Documentation</a></li>
9
+ </ul>
10
+ </nav>
11
+ </hgroup>
12
+ </header>
13
+ <main class="container">
14
+ <article>
15
+ <h1>Login</h1>
16
+ <form id="login-form" onsubmit="login(event)">
17
+ <label for="username">
18
+ Username
19
+ <input type="text" id="username" name="username" placeholder="Enter your username" required />
20
+ </label>
21
+ <label for="password">
22
+ Password
23
+ <input type="password" id="password" name="password" placeholder="Enter your password" required />
24
+ </label>
25
+ <div class="button-group">
26
+ <button class="primary">🔑 Login</button>
27
+ <button class="secondary" onclick="window.history.back()">🔙 Cancel</button>
28
+ </div>
29
+ </form>
30
+ <p id="error-message"></p>
31
+ </article>
32
+ </main>
33
+ <script src="/static/login/event.js"></script>
@@ -1,6 +1,7 @@
1
1
  import os
2
2
  from typing import TYPE_CHECKING
3
3
 
4
+ from zrb.config import CFG
4
5
  from zrb.group.any_group import AnyGroup
5
6
  from zrb.runner.web_config.config import WebConfig
6
7
  from zrb.runner.web_util.html import get_html_auth_link
@@ -24,17 +25,26 @@ def serve_logout_page(
24
25
  @app.get("/logout", response_class=HTMLResponse, include_in_schema=False)
25
26
  async def logout(request: Request) -> HTMLResponse:
26
27
  _DIR = os.path.dirname(__file__)
28
+ _GLOBAL_TEMPLATE = read_file(
29
+ os.path.join(os.path.dirname(_DIR), "static", "global_template.html")
30
+ )
27
31
  _VIEW_TEMPLATE = read_file(os.path.join(_DIR, "view.html"))
28
32
  user = await get_user_from_request(web_config, request)
29
33
  auth_link = get_html_auth_link(user)
30
34
  return HTMLResponse(
31
35
  fstring_format(
32
- _VIEW_TEMPLATE,
36
+ _GLOBAL_TEMPLATE,
33
37
  {
34
- "name": root_group.name,
35
- "description": root_group.description,
36
- "auth_link": auth_link,
37
- "user": user,
38
+ "web_title": CFG.WEB_TITLE,
39
+ "content": fstring_format(
40
+ _VIEW_TEMPLATE,
41
+ {
42
+ "name": root_group.name,
43
+ "description": root_group.description,
44
+ "auth_link": auth_link,
45
+ "user": user,
46
+ },
47
+ ),
38
48
  },
39
49
  )
40
50
  )