zrb 1.0.0a21__py3-none-any.whl → 1.0.0b3__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 (195) hide show
  1. zrb/__init__.py +2 -1
  2. zrb/__main__.py +3 -3
  3. zrb/builtin/__init__.py +3 -0
  4. zrb/builtin/group.py +1 -0
  5. zrb/builtin/llm/llm_chat.py +5 -3
  6. zrb/builtin/llm/tool/cli.py +1 -1
  7. zrb/builtin/llm/tool/rag.py +108 -145
  8. zrb/builtin/llm/tool/web.py +1 -1
  9. zrb/builtin/project/add/fastapp/fastapp_task.py +2 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -2
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +80 -20
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +150 -42
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service.py +113 -0
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_service_factory.py +9 -0
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_db_repository.py +0 -10
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/my_entity_repository.py +37 -16
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/repository/{factory.py → my_entity_repository_factory.py} +2 -2
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/schema/my_entity.py +16 -6
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/client_method.py +57 -0
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +74 -0
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/format_task.py +1 -1
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/input.py +13 -0
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_task.py +23 -0
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +42 -0
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +7 -0
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_api_client.py +6 -0
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/{any_client.py → my_module_client.py} +1 -1
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_client_factory.py +11 -0
  29. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/my_module_direct_client.py +5 -0
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/route.py +11 -11
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +2 -2
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +8 -8
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/util.py +47 -20
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app_factory.py +29 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_db_repository.py +230 -102
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +236 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/{db_engine.py → db_engine_factory.py} +1 -1
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/error.py +12 -0
  39. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/logger_factory.py +10 -0
  40. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/parser_factory.py +7 -0
  41. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/app.py +47 -0
  42. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +105 -0
  43. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/user_agent.py +58 -0
  44. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/view.py +37 -0
  45. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +37 -1
  46. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/main.py +1 -1
  47. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_api_client.py +16 -0
  48. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client.py +169 -0
  49. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_client_factory.py +9 -0
  50. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/auth_direct_client.py +15 -0
  51. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +160 -0
  52. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration_metadata.py +18 -1
  53. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/route.py +7 -3
  54. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service.py +117 -0
  55. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/permission_service_factory.py +11 -0
  56. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_db_repository.py +26 -0
  57. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository.py +61 -0
  58. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/repository/permission_repository_factory.py +13 -0
  59. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_db_repository.py +89 -0
  60. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository.py +67 -0
  61. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/repository/role_repository_factory.py +13 -0
  62. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service.py +137 -0
  63. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/role_service_factory.py +7 -0
  64. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +179 -12
  65. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository.py +67 -17
  66. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_repository_factory.py +2 -2
  67. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +127 -0
  68. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service_factory.py +7 -0
  69. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/route.py +43 -14
  70. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +200 -30
  71. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/view.py +74 -0
  72. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/error.html +6 -0
  73. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/content/homepage.html +6 -0
  74. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-192x192.png +0 -0
  75. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/android-chrome-512x512.png +0 -0
  76. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/images/favicon-32x32.png +0 -0
  77. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.amber.min.css +4 -0
  78. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.blue.min.css +4 -0
  79. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.cyan.min.css +4 -0
  80. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.fuchsia.min.css +4 -0
  81. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.green.min.css +4 -0
  82. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.grey.min.css +4 -0
  83. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.indigo.min.css +4 -0
  84. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.jade.min.css +4 -0
  85. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.lime.min.css +4 -0
  86. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.min.css +4 -0
  87. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.orange.min.css +4 -0
  88. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pink.min.css +4 -0
  89. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.pumpkin.min.css +4 -0
  90. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.purple.min.css +4 -0
  91. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.red.min.css +4 -0
  92. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.sand.min.css +4 -0
  93. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.slate.min.css +4 -0
  94. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.violet.min.css +4 -0
  95. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.yellow.min.css +4 -0
  96. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/static/pico-css/pico.zinc.min.css +4 -0
  97. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/view/template/default.html +34 -0
  98. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +1 -0
  99. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +17 -5
  100. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/role.py +78 -4
  101. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/session.py +48 -0
  102. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +69 -5
  103. zrb/builtin/python.py +1 -1
  104. zrb/builtin/random.py +61 -0
  105. zrb/cmd/cmd_val.py +6 -5
  106. zrb/config.py +14 -1
  107. zrb/content_transformer/any_content_transformer.py +7 -0
  108. zrb/content_transformer/content_transformer.py +6 -0
  109. zrb/runner/cli.py +14 -7
  110. zrb/runner/web_app.py +28 -280
  111. zrb/runner/web_config/config.py +91 -0
  112. zrb/runner/web_config/config_factory.py +26 -0
  113. zrb/runner/web_route/docs_route.py +17 -0
  114. zrb/runner/web_route/error_page/serve_default_404.py +28 -0
  115. zrb/runner/{web_controller/error_page/controller.py → web_route/error_page/show_error_page.py} +4 -3
  116. zrb/runner/{web_controller → web_route}/error_page/view.html +5 -0
  117. zrb/runner/web_route/home_page/home_page_route.py +51 -0
  118. zrb/runner/web_route/login_api_route.py +31 -0
  119. zrb/runner/web_route/login_page/login_page_route.py +39 -0
  120. zrb/runner/web_route/logout_api_route.py +18 -0
  121. zrb/runner/web_route/logout_page/logout_page_route.py +40 -0
  122. zrb/runner/{web_controller/group_info_page/controller.py → web_route/node_page/group/show_group_page.py} +3 -3
  123. zrb/runner/web_route/node_page/node_page_route.py +50 -0
  124. zrb/runner/{web_controller/session_page/controller.py → web_route/node_page/task/show_task_page.py} +3 -3
  125. zrb/runner/web_route/refresh_token_api_route.py +38 -0
  126. zrb/runner/{web_controller/static → web_route/static/resources}/session/current-session.js +5 -2
  127. zrb/runner/{web_controller/static → web_route/static/resources}/session/event.js +5 -2
  128. zrb/runner/web_route/static/static_route.py +44 -0
  129. zrb/runner/web_route/task_input_api_route.py +47 -0
  130. zrb/runner/web_route/task_session_api_route.py +147 -0
  131. zrb/runner/web_schema/session.py +5 -0
  132. zrb/runner/web_schema/token.py +11 -0
  133. zrb/runner/web_schema/user.py +32 -0
  134. zrb/runner/web_util/cookie.py +29 -0
  135. zrb/runner/{web_util.py → web_util/html.py} +1 -23
  136. zrb/runner/web_util/token.py +72 -0
  137. zrb/runner/web_util/user.py +63 -0
  138. zrb/session/session.py +6 -4
  139. zrb/session_state_logger/{default_session_state_logger.py → session_state_logger_factory.py} +1 -1
  140. zrb/task/base_task.py +56 -8
  141. zrb/task/base_trigger.py +2 -0
  142. zrb/task/cmd_task.py +9 -5
  143. zrb/task/http_check.py +2 -0
  144. zrb/task/llm_task.py +184 -71
  145. zrb/task/make_task.py +2 -0
  146. zrb/task/rsync_task.py +2 -0
  147. zrb/task/scaffolder.py +8 -5
  148. zrb/task/scheduler.py +2 -0
  149. zrb/task/tcp_check.py +2 -0
  150. zrb/task_status/task_status.py +4 -3
  151. zrb/util/cmd/command.py +1 -0
  152. zrb/util/file.py +7 -1
  153. zrb/util/llm/tool.py +3 -7
  154. {zrb-1.0.0a21.dist-info → zrb-1.0.0b3.dist-info}/METADATA +2 -1
  155. zrb-1.0.0b3.dist-info/RECORD +307 -0
  156. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/any_client_method.py +0 -27
  157. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/module/my_module/service/my_entity/my_entity_usecase.py +0 -65
  158. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/api_client.py +0 -6
  159. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/direct_client.py +0 -6
  160. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/client/factory.py +0 -9
  161. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/app.py +0 -20
  162. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_usecase.py +0 -245
  163. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/any_client.py +0 -33
  164. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/api_client.py +0 -7
  165. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/direct_client.py +0 -6
  166. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/client/factory.py +0 -9
  167. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_user_table.py +0 -37
  168. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase.py +0 -53
  169. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_usecase_factory.py +0 -6
  170. zrb/runner/web_config.py +0 -274
  171. zrb/runner/web_controller/home_page/controller.py +0 -33
  172. zrb/runner/web_controller/login_page/controller.py +0 -25
  173. zrb/runner/web_controller/logout_page/controller.py +0 -26
  174. zrb-1.0.0a21.dist-info/RECORD +0 -244
  175. /zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/{create_column_task.py → add_column_task.py} +0 -0
  176. /zrb/{runner/web_controller → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission}/__init__.py +0 -0
  177. /zrb/{runner/web_controller/group_info_page → builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role}/__init__.py +0 -0
  178. /zrb/runner/{web_controller/home_page → web_route}/__init__.py +0 -0
  179. /zrb/runner/{web_controller/session_page → web_route/home_page}/__init__.py +0 -0
  180. /zrb/runner/{web_controller → web_route}/home_page/view.html +0 -0
  181. /zrb/runner/{web_controller → web_route}/login_page/view.html +0 -0
  182. /zrb/runner/{web_controller → web_route}/logout_page/view.html +0 -0
  183. /zrb/runner/{web_controller/group_info_page → web_route/node_page/group}/view.html +0 -0
  184. /zrb/runner/{web_controller/session_page → web_route/node_page/task}/partial/input.html +0 -0
  185. /zrb/runner/{web_controller/session_page → web_route/node_page/task}/view.html +0 -0
  186. /zrb/runner/{refresh-token.template.js → web_route/static/refresh-token.template.js} +0 -0
  187. /zrb/runner/{web_controller/static → web_route/static/resources}/common.css +0 -0
  188. /zrb/runner/{web_controller/static → web_route/static/resources}/favicon-32x32.png +0 -0
  189. /zrb/runner/{web_controller/static → web_route/static/resources}/login/event.js +0 -0
  190. /zrb/runner/{web_controller/static → web_route/static/resources}/logout/event.js +0 -0
  191. /zrb/runner/{web_controller/static → web_route/static/resources}/pico.min.css +0 -0
  192. /zrb/runner/{web_controller/static → web_route/static/resources}/session/common-util.js +0 -0
  193. /zrb/runner/{web_controller/static → web_route/static/resources}/session/past-session.js +0 -0
  194. {zrb-1.0.0a21.dist-info → zrb-1.0.0b3.dist-info}/WHEEL +0 -0
  195. {zrb-1.0.0a21.dist-info → zrb-1.0.0b3.dist-info}/entry_points.txt +0 -0
zrb/cmd/cmd_val.py CHANGED
@@ -1,8 +1,9 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from collections.abc import Callable
3
3
 
4
- from zrb.attr.type import fstring
4
+ from zrb.attr.type import StrAttr, fstring
5
5
  from zrb.context.context import Context
6
+ from zrb.util.attr import get_str_attr
6
7
  from zrb.util.file import read_file
7
8
 
8
9
 
@@ -13,22 +14,22 @@ class AnyCmdVal(ABC):
13
14
 
14
15
 
15
16
  class CmdPath(AnyCmdVal):
16
- def __init__(self, path: str, auto_render: bool = True):
17
+ def __init__(self, path: StrAttr, auto_render: bool = True):
17
18
  self._path = path
18
19
  self._auto_render = auto_render
19
20
 
20
21
  def to_str(self, ctx: Context) -> str:
21
- file_path = ctx.render(self._path) if self._auto_render else self._path
22
+ file_path = get_str_attr(ctx, self._path, "", self._auto_render)
22
23
  return read_file(file_path)
23
24
 
24
25
 
25
26
  class Cmd(AnyCmdVal):
26
- def __init__(self, cmd: str, auto_render: str):
27
+ def __init__(self, cmd: StrAttr, auto_render: bool = True):
27
28
  self._cmd = cmd
28
29
  self._auto_render = auto_render
29
30
 
30
31
  def to_str(self, ctx: Context) -> str:
31
- return ctx.render(self._cmd) if self._auto_render else self._cmd
32
+ return get_str_attr(ctx, self._cmd, "", self._auto_render)
32
33
 
33
34
 
34
35
  SingleCmdVal = AnyCmdVal | fstring | Callable[[Context], str]
zrb/config.py CHANGED
@@ -77,7 +77,20 @@ WEB_AUTH_REFRESH_TOKEN_EXPIRE_MINUTES = int(
77
77
  os.getenv("ZRB_WEB_REFRESH_TOKEN_EXPIRE_MINUTES", "60")
78
78
  )
79
79
  LLM_MODEL = os.getenv("ZRB_LLM_MODEL", "ollama_chat/llama3.1")
80
- LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", "You are a helpful assistant")
80
+
81
+ _DEFAULT_PROMPT = """
82
+ You are a reliable assistant focused on providing accurate, helpful, and factual information.
83
+
84
+ Key guidelines:
85
+ 1. Prioritize correctness and clarity.
86
+ 2. Avoid guessing—clearly state if more information is needed.
87
+ 3. Distinguish facts from opinions.
88
+ 4. Use phrases like "to the best of my knowledge" when appropriate.
89
+
90
+ If unsure or lacking current data, inform the user and suggest verification.
91
+ Accuracy always takes precedence over completeness.
92
+ """.strip()
93
+ LLM_SYSTEM_PROMPT = os.getenv("ZRB_LLM_SYSTEM_PROMPT", _DEFAULT_PROMPT)
81
94
  LLM_HISTORY_FILE = os.getenv(
82
95
  "ZRB_LLM_HISTORY_FILE",
83
96
  os.path.expanduser(os.path.join("~", ".zrb-llm-history.json")),
@@ -4,6 +4,13 @@ from zrb.context.any_context import AnyContext
4
4
 
5
5
 
6
6
  class AnyContentTransformer(ABC):
7
+
8
+ @property
9
+ @abstractmethod
10
+ def name(self) -> str:
11
+ """Transformer's name"""
12
+ pass
13
+
7
14
  @abstractmethod
8
15
  def match(self, ctx: AnyContext, file_path: str) -> bool:
9
16
  """
@@ -10,6 +10,7 @@ from zrb.util.file import read_file, write_file
10
10
  class ContentTransformer(AnyContentTransformer):
11
11
  def __init__(
12
12
  self,
13
+ name: str,
13
14
  match: list[str] | str | Callable[[AnyContext, str], bool],
14
15
  transform: (
15
16
  dict[str, str | Callable[[AnyContext], str]]
@@ -17,10 +18,15 @@ class ContentTransformer(AnyContentTransformer):
17
18
  ),
18
19
  auto_render: bool = True,
19
20
  ):
21
+ self._name = name
20
22
  self._match = match
21
23
  self._transform_file = transform
22
24
  self._auto_render = auto_render
23
25
 
26
+ @property
27
+ def name(self) -> str:
28
+ return self._name
29
+
24
30
  def match(self, ctx: AnyContext, file_path: str) -> bool:
25
31
  if callable(self._match):
26
32
  return self._match(ctx, file_path)
zrb/runner/cli.py CHANGED
@@ -1,17 +1,15 @@
1
1
  import sys
2
2
  from typing import Any
3
3
 
4
- from zrb.config import BANNER, WEB_HTTP_PORT
4
+ from zrb.config import BANNER, VERSION, WEB_HTTP_PORT
5
5
  from zrb.context.any_context import AnyContext
6
6
  from zrb.context.shared_context import SharedContext
7
7
  from zrb.group.group import Group
8
8
  from zrb.runner.common_util import get_run_kwargs
9
- from zrb.runner.web_app import create_app
10
- from zrb.runner.web_config import web_config
9
+ from zrb.runner.web_app import create_web_app
10
+ from zrb.runner.web_config.config_factory import web_config
11
11
  from zrb.session.session import Session
12
- from zrb.session_state_logger.default_session_state_logger import (
13
- default_session_state_logger,
14
- )
12
+ from zrb.session_state_logger.session_state_logger_factory import session_state_logger
15
13
  from zrb.task.any_task import AnyTask
16
14
  from zrb.task.make_task import make_task
17
15
  from zrb.util.cli.style import (
@@ -151,6 +149,15 @@ class Cli(Group):
151
149
 
152
150
 
153
151
  cli = Cli(name="zrb", description="Your Automation Powerhouse", banner=BANNER)
152
+
153
+
154
+ @make_task(
155
+ name="version", description="🌟 Get current Zrb version", retries=0, group=cli
156
+ )
157
+ def get_version(_: AnyContext):
158
+ return VERSION
159
+
160
+
154
161
  server_group = cli.add_group(
155
162
  Group(name="server", description="🌐 Server related command")
156
163
  )
@@ -167,6 +174,6 @@ server_group = cli.add_group(
167
174
  async def start_server(_: AnyContext):
168
175
  from uvicorn import Config, Server
169
176
 
170
- app = create_app(cli, web_config, default_session_state_logger)
177
+ app = create_web_app(cli, web_config, session_state_logger)
171
178
  server = Server(Config(app=app, host="0.0.0.0", port=WEB_HTTP_PORT, loop="asyncio"))
172
179
  await server.serve()
zrb/runner/web_app.py CHANGED
@@ -1,60 +1,38 @@
1
1
  import asyncio
2
- import json
3
- import os
4
2
  import sys
5
- from datetime import datetime, timedelta, timezone
6
- from typing import TYPE_CHECKING, Annotated
3
+ from typing import TYPE_CHECKING
7
4
 
8
5
  from zrb.config import BANNER, VERSION
9
- from zrb.context.shared_context import SharedContext
10
6
  from zrb.group.any_group import AnyGroup
11
- from zrb.runner.common_util import get_run_kwargs
12
- from zrb.runner.web_config import (
13
- NewSessionResponse,
14
- RefreshTokenRequest,
15
- Token,
16
- WebConfig,
17
- )
18
- from zrb.runner.web_controller.error_page.controller import show_error_page
19
- from zrb.runner.web_controller.group_info_page.controller import show_group_info_page
20
- from zrb.runner.web_controller.home_page.controller import show_home_page
21
- from zrb.runner.web_controller.login_page.controller import show_login_page
22
- from zrb.runner.web_controller.logout_page.controller import show_logout_page
23
- from zrb.runner.web_controller.session_page.controller import show_session_page
24
- from zrb.runner.web_util import get_refresh_token_js
25
- from zrb.session.session import Session
26
- from zrb.session_state_log.session_state_log import SessionStateLog, SessionStateLogList
7
+ from zrb.runner.web_config.config import WebConfig
8
+ from zrb.runner.web_route.docs_route import serve_docs
9
+ from zrb.runner.web_route.error_page.serve_default_404 import serve_default_404
10
+ from zrb.runner.web_route.home_page.home_page_route import serve_home_page
11
+ from zrb.runner.web_route.login_api_route import serve_login_api
12
+ from zrb.runner.web_route.login_page.login_page_route import serve_login_page
13
+ from zrb.runner.web_route.logout_api_route import serve_logout_api
14
+ from zrb.runner.web_route.logout_page.logout_page_route import serve_logout_page
15
+ from zrb.runner.web_route.node_page.node_page_route import serve_node_page
16
+ from zrb.runner.web_route.refresh_token_api_route import serve_refresh_token_api
17
+ from zrb.runner.web_route.static.static_route import serve_static_resources
18
+ from zrb.runner.web_route.task_input_api_route import serve_task_input_api
19
+ from zrb.runner.web_route.task_session_api_route import serve_task_session_api
27
20
  from zrb.session_state_logger.any_session_state_logger import AnySessionStateLogger
28
- from zrb.task.any_task import AnyTask
29
- from zrb.util.group import NodeNotFoundError, extract_node_from_args, get_node_path
30
21
 
31
22
  if TYPE_CHECKING:
32
23
  # We want fastapi to only be loaded when necessary to decrease footprint
33
24
  from fastapi import FastAPI
34
25
 
35
26
 
36
- def create_app(
27
+ def create_web_app(
37
28
  root_group: AnyGroup,
38
29
  web_config: WebConfig,
39
30
  session_state_logger: AnySessionStateLogger,
40
31
  ) -> "FastAPI":
41
32
  from contextlib import asynccontextmanager
42
33
 
43
- from fastapi import (
44
- Cookie,
45
- Depends,
46
- FastAPI,
47
- HTTPException,
48
- Query,
49
- Request,
50
- Response,
51
- )
52
- from fastapi.openapi.docs import get_swagger_ui_html
53
- from fastapi.responses import FileResponse, HTMLResponse, PlainTextResponse
54
- from fastapi.security import OAuth2PasswordRequestForm
55
- from fastapi.staticfiles import StaticFiles
34
+ from fastapi import FastAPI
56
35
 
57
- _STATIC_DIR = os.path.join(os.path.dirname(__file__), "web_controller", "static")
58
36
  _COROS = []
59
37
 
60
38
  @asynccontextmanager
@@ -75,247 +53,17 @@ def create_app(
75
53
  lifespan=lifespan,
76
54
  docs_url=None,
77
55
  )
78
- app.mount("/static", StaticFiles(directory=_STATIC_DIR), name="static")
79
-
80
- # Serve static files
81
- @app.get("/static/{file_path:path}", include_in_schema=False)
82
- async def static_files(file_path: str):
83
- full_path = os.path.join(_STATIC_DIR, file_path)
84
- if os.path.isfile(full_path):
85
- return FileResponse(full_path)
86
- raise HTTPException(status_code=404, detail="File not found")
87
-
88
- @app.get("/refresh-token.js", include_in_schema=False)
89
- async def refresh_token_js():
90
- return PlainTextResponse(
91
- content=get_refresh_token_js(
92
- 60 * web_config.refresh_token_expire_minutes / 3
93
- ),
94
- media_type="application/javascript",
95
- )
96
-
97
- @app.get("/docs", include_in_schema=False)
98
- async def swagger_ui_html():
99
- return get_swagger_ui_html(
100
- openapi_url="/openapi.json",
101
- title="Zrb",
102
- swagger_favicon_url="/static/favicon-32x32.png",
103
- )
104
-
105
- # Serve homepage
106
- @app.get("/", response_class=HTMLResponse, include_in_schema=False)
107
- @app.get("/ui", response_class=HTMLResponse, include_in_schema=False)
108
- @app.get("/ui/", response_class=HTMLResponse, include_in_schema=False)
109
- async def home_page_ui(request: Request) -> HTMLResponse:
110
- user = await web_config.get_user_from_request(request)
111
- return show_home_page(user, root_group)
112
-
113
- @app.get("/ui/{path:path}", response_class=HTMLResponse, include_in_schema=False)
114
- async def ui_page(path: str, request: Request) -> HTMLResponse:
115
- user = await web_config.get_user_from_request(request)
116
- # Avoid capturing '/ui' itself
117
- if not path:
118
- return show_error_page(user, root_group, 422, "Undefined path")
119
- args = path.strip("/").split("/")
120
- try:
121
- node, node_path, residual_args = extract_node_from_args(root_group, args)
122
- except NodeNotFoundError as e:
123
- return show_error_page(user, root_group, 404, str(e))
124
- url = f"/ui/{'/'.join(node_path)}/"
125
- if isinstance(node, AnyTask):
126
- if not user.can_access_task(node):
127
- return show_error_page(user, root_group, 403, "Forbidden")
128
- shared_ctx = SharedContext(env=dict(os.environ))
129
- session = Session(shared_ctx=shared_ctx, root_group=root_group)
130
- return show_session_page(
131
- user, root_group, node, session, url, residual_args
132
- )
133
- elif isinstance(node, AnyGroup):
134
- if not user.can_access_group(node):
135
- return show_error_page(user, root_group, 403, "Forbidden")
136
- return show_group_info_page(user, root_group, node, url)
137
- return show_error_page(user, root_group, 404, "Not found")
138
-
139
- @app.get("/login", response_class=HTMLResponse, include_in_schema=False)
140
- async def login(request: Request) -> HTMLResponse:
141
- user = await web_config.get_user_from_request(request)
142
- return show_login_page(user, root_group)
143
-
144
- @app.get("/logout", response_class=HTMLResponse, include_in_schema=False)
145
- async def logout(request: Request) -> HTMLResponse:
146
- user = await web_config.get_user_from_request(request)
147
- return show_logout_page(user, root_group)
148
-
149
- @app.post("/api/v1/login")
150
- async def login_api(
151
- response: Response, form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
152
- ):
153
- token = web_config.generate_tokens_by_credentials(
154
- username=form_data.username, password=form_data.password
155
- )
156
- if token is None:
157
- raise HTTPException(
158
- status_code=400, detail="Incorrect username or password"
159
- )
160
- _set_auth_cookie(response, token)
161
- return token
162
-
163
- @app.post("/api/v1/refresh-token")
164
- async def refresh_token_api(
165
- response: Response,
166
- body: RefreshTokenRequest = None,
167
- refresh_token_cookie: str = Cookie(
168
- None, alias=web_config.refresh_token_cookie_name
169
- ),
170
- ):
171
- # Try to get the refresh token from the request body first
172
- refresh_token = body.refresh_token if body else None
173
- # If not in the body, try to get it from the cookie
174
- if not refresh_token:
175
- refresh_token = refresh_token_cookie
176
- # If we still don't have a refresh token, raise an exception
177
- if not refresh_token:
178
- raise HTTPException(status_code=400, detail="Refresh token not provided")
179
- # Get token
180
- new_token = web_config.regenerate_tokens(refresh_token)
181
- _set_auth_cookie(response, new_token)
182
- return new_token
183
-
184
- def _set_auth_cookie(response: Response, token: Token):
185
- access_token_max_age = web_config.access_token_expire_minutes * 60
186
- refresh_token_max_age = web_config.refresh_token_expire_minutes * 60
187
- now = datetime.now(timezone.utc)
188
- response.set_cookie(
189
- key=web_config.access_token_cookie_name,
190
- value=token.access_token,
191
- httponly=True,
192
- max_age=access_token_max_age,
193
- expires=now + timedelta(seconds=access_token_max_age),
194
- )
195
- response.set_cookie(
196
- key=web_config.refresh_token_cookie_name,
197
- value=token.refresh_token,
198
- httponly=True,
199
- max_age=refresh_token_max_age,
200
- expires=now + timedelta(seconds=refresh_token_max_age),
201
- )
202
-
203
- @app.get("/api/v1/logout")
204
- @app.post("/api/v1/logout")
205
- async def logout_api(response: Response):
206
- response.delete_cookie(web_config.access_token_cookie_name)
207
- response.delete_cookie(web_config.refresh_token_cookie_name)
208
- return {"message": "Logout successful"}
209
-
210
- @app.post("/api/v1/task-sessions/{path:path}")
211
- async def create_new_task_session_api(
212
- path: str,
213
- request: Request,
214
- ) -> NewSessionResponse:
215
- """
216
- Creating new session
217
- """
218
- user = await web_config.get_user_from_request(request)
219
- args = path.strip("/").split("/")
220
- task, _, residual_args = extract_node_from_args(root_group, args)
221
- if isinstance(task, AnyTask):
222
- if not user.can_access_task(task):
223
- raise HTTPException(status_code=403)
224
- session_name = residual_args[0] if residual_args else None
225
- if not session_name:
226
- body = await request.json()
227
- shared_ctx = SharedContext(env=dict(os.environ))
228
- session = Session(shared_ctx=shared_ctx, root_group=root_group)
229
- coro = asyncio.create_task(task.async_run(session, str_kwargs=body))
230
- _COROS.append(coro)
231
- coro.add_done_callback(lambda coro: _COROS.remove(coro))
232
- return NewSessionResponse(session_name=session.name)
233
- raise HTTPException(status_code=404)
234
-
235
- @app.get("/api/v1/task-inputs/{path:path}", response_model=dict[str, str])
236
- async def get_default_inputs_api(
237
- path: str,
238
- request: Request,
239
- query: str = Query("{}", description="JSON encoded inputs"),
240
- ) -> dict[str, str]:
241
- """
242
- Getting input completion for path
243
- """
244
- user = await web_config.get_user_from_request(request)
245
- args = path.strip("/").split("/")
246
- task, _, _ = extract_node_from_args(root_group, args)
247
- if isinstance(task, AnyTask):
248
- if not user.can_access_task(task):
249
- raise HTTPException(status_code=403)
250
- query_dict = json.loads(query)
251
- run_kwargs = get_run_kwargs(
252
- task=task, args=[], kwargs=query_dict, prompt=False
253
- )
254
- return run_kwargs
255
- raise HTTPException(status_code=404, detail="Not Found")
256
-
257
- @app.get(
258
- "/api/v1/task-sessions/{path:path}",
259
- response_model=SessionStateLog | SessionStateLogList,
260
- )
261
- async def get_session_api(
262
- path: str,
263
- request: Request,
264
- min_start_query: str = Query(default=None, alias="from"),
265
- max_start_query: str = Query(default=None, alias="to"),
266
- page: int = Query(default=0, alias="page"),
267
- limit: int = Query(default=10, alias="limit"),
268
- ) -> SessionStateLog | SessionStateLogList:
269
- """
270
- Getting existing session or sessions
271
- """
272
- user = await web_config.get_user_from_request(request)
273
- args = path.strip("/").split("/")
274
- task, _, residual_args = extract_node_from_args(root_group, args)
275
- if isinstance(task, AnyTask) and residual_args:
276
- if not user.can_access_task(task):
277
- raise HTTPException(status_code=403)
278
- if residual_args[0] == "list":
279
- task_path = get_node_path(root_group, task)
280
- max_start_time = (
281
- datetime.now()
282
- if max_start_query is None
283
- else datetime.strptime(max_start_query, "%Y-%m-%d %H:%M:%S")
284
- )
285
- min_start_time = (
286
- max_start_time - timedelta(hours=1)
287
- if min_start_query is None
288
- else datetime.strptime(min_start_query, "%Y-%m-%d %H:%M:%S")
289
- )
290
- return _get_existing_sessions(
291
- task_path, min_start_time, max_start_time, page, limit
292
- )
293
- else:
294
- return _read_session(residual_args[0])
295
- raise HTTPException(status_code=404, detail="Not Found")
296
-
297
- def _get_existing_sessions(
298
- task_path: list[str],
299
- min_start_time: datetime,
300
- max_start_time: datetime,
301
- page: int,
302
- limit: int,
303
- ) -> SessionStateLogList:
304
- try:
305
- return session_state_logger.list(
306
- task_path,
307
- min_start_time=min_start_time,
308
- max_start_time=max_start_time,
309
- page=page,
310
- limit=limit,
311
- )
312
- except Exception as e:
313
- raise HTTPException(status_code=500, detail=str(e))
314
-
315
- def _read_session(session_name: str) -> SessionStateLog:
316
- try:
317
- return session_state_logger.read(session_name)
318
- except Exception as e:
319
- raise HTTPException(status_code=500, detail=str(e))
320
56
 
57
+ serve_default_404(app, root_group, web_config)
58
+ serve_static_resources(app, web_config)
59
+ serve_docs(app)
60
+ serve_home_page(app, root_group, web_config)
61
+ serve_login_page(app, root_group, web_config)
62
+ serve_logout_page(app, root_group, web_config)
63
+ serve_node_page(app, root_group, web_config)
64
+ serve_login_api(app, web_config)
65
+ serve_logout_api(app, web_config)
66
+ serve_refresh_token_api(app, web_config)
67
+ serve_task_input_api(app, root_group, web_config)
68
+ serve_task_session_api(app, root_group, web_config, session_state_logger, _COROS)
321
69
  return app
@@ -0,0 +1,91 @@
1
+ from typing import Callable
2
+
3
+ from zrb.runner.web_schema.user import User
4
+ from zrb.task.any_task import AnyTask
5
+
6
+
7
+ class WebConfig:
8
+ def __init__(
9
+ self,
10
+ port: int,
11
+ secret_key: str,
12
+ access_token_expire_minutes: int,
13
+ refresh_token_expire_minutes: int,
14
+ access_token_cookie_name: str,
15
+ refresh_token_cookie_name: str,
16
+ enable_auth: bool,
17
+ super_admin_username: str,
18
+ super_admin_password: str,
19
+ guest_username: str,
20
+ guest_accessible_tasks: list[AnyTask | str] = [],
21
+ find_user_by_username: Callable[[str], User | None] | None = None,
22
+ ):
23
+ self.secret_key = secret_key
24
+ self.access_token_expire_minutes = access_token_expire_minutes
25
+ self.refresh_token_expire_minutes = refresh_token_expire_minutes
26
+ self.access_token_cookie_name = access_token_cookie_name
27
+ self.refresh_token_cookie_name = refresh_token_cookie_name
28
+ self.enable_auth = enable_auth
29
+ self.port = port
30
+ self._user_list = []
31
+ self.super_admin_username = super_admin_username
32
+ self.super_admin_password = super_admin_password
33
+ self.guest_username = guest_username
34
+ self.guest_accessible_tasks = guest_accessible_tasks
35
+ self._find_user_by_username = find_user_by_username
36
+
37
+ @property
38
+ def default_user(self) -> User:
39
+ if self.enable_auth:
40
+ return User(
41
+ username=self.guest_username,
42
+ password="",
43
+ is_guest=True,
44
+ accessible_tasks=self.guest_accessible_tasks,
45
+ )
46
+ return User(
47
+ username=self.guest_username,
48
+ password="",
49
+ is_guest=True,
50
+ is_super_admin=True,
51
+ )
52
+
53
+ @property
54
+ def super_admin(self) -> User:
55
+ return User(
56
+ username=self.super_admin_username,
57
+ password=self.super_admin_password,
58
+ is_super_admin=True,
59
+ )
60
+
61
+ @property
62
+ def user_list(self) -> list[User]:
63
+ if not self.enable_auth:
64
+ return [self.default_user]
65
+ return self._user_list + [self.super_admin, self.default_user]
66
+
67
+ def set_guest_accessible_tasks(self, tasks: list[AnyTask | str]):
68
+ self.guest_accessible_tasks = tasks
69
+
70
+ def set_find_user_by_username(
71
+ self, find_user_by_username: Callable[[str], User | None]
72
+ ):
73
+ self._find_user_by_username = find_user_by_username
74
+
75
+ def append_user(self, user: User):
76
+ duplicates = [
77
+ existing_user
78
+ for existing_user in self.user_list
79
+ if existing_user.username == user.username
80
+ ]
81
+ if len(duplicates) > 0:
82
+ raise ValueError(f"User already exists {user.username}")
83
+ self._user_list.append(user)
84
+
85
+ def find_user_by_username(self, username: str) -> User | None:
86
+ user = None
87
+ if self._find_user_by_username is not None:
88
+ user = self._find_user_by_username(username)
89
+ if user is None:
90
+ user = next((u for u in self.user_list if u.username == username), None)
91
+ return user
@@ -0,0 +1,26 @@
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
+ )
13
+ from zrb.runner.web_config.config import WebConfig
14
+
15
+ 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,
26
+ )
@@ -0,0 +1,17 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ # We want fastapi to only be loaded when necessary to decrease footprint
5
+ from fastapi import FastAPI
6
+
7
+
8
+ def serve_docs(app: "FastAPI") -> None:
9
+ from fastapi.openapi.docs import get_swagger_ui_html
10
+
11
+ @app.get("/docs", include_in_schema=False)
12
+ async def swagger_ui_html():
13
+ return get_swagger_ui_html(
14
+ openapi_url="/openapi.json",
15
+ title="Zrb",
16
+ swagger_favicon_url="/static/favicon-32x32.png",
17
+ )
@@ -0,0 +1,28 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from zrb.group.any_group import AnyGroup
4
+ from zrb.runner.web_config.config import WebConfig
5
+ from zrb.runner.web_route.error_page.show_error_page import show_error_page
6
+ from zrb.runner.web_util.user import get_user_from_request
7
+
8
+ if TYPE_CHECKING:
9
+ # We want fastapi to only be loaded when necessary to decrease footprint
10
+ from fastapi import FastAPI
11
+
12
+
13
+ def serve_default_404(
14
+ app: "FastAPI",
15
+ root_group: AnyGroup,
16
+ web_config: WebConfig,
17
+ ) -> None:
18
+ from fastapi import Request
19
+ from fastapi.exception_handlers import http_exception_handler
20
+ from fastapi.responses import HTMLResponse
21
+
22
+ @app.exception_handler(404)
23
+ async def default_404(request: Request, exc: Exception) -> HTMLResponse:
24
+ if request.url.path.startswith("/api"):
25
+ # Re-raise the exception to let FastAPI handle it
26
+ return await http_exception_handler(request, exc)
27
+ user = await get_user_from_request(web_config, request)
28
+ return show_error_page(user, root_group, 404, "Not found")
@@ -1,8 +1,8 @@
1
1
  import os
2
2
 
3
3
  from zrb.group.any_group import AnyGroup
4
- from zrb.runner.web_config import User
5
- from zrb.runner.web_util import get_html_auth_link
4
+ from zrb.runner.web_schema.user import User
5
+ from zrb.runner.web_util.html import get_html_auth_link
6
6
  from zrb.util.file import read_file
7
7
  from zrb.util.string.format import fstring_format
8
8
 
@@ -23,5 +23,6 @@ def show_error_page(user: User, root_group: AnyGroup, status_code: int, message:
23
23
  "error_status_code": status_code,
24
24
  "error_message": message,
25
25
  },
26
- )
26
+ ),
27
+ status_code=status_code,
27
28
  )