aegis-stack 0.2.0rc2__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 (392) hide show
  1. aegis/__init__.py +5 -0
  2. aegis/__main__.py +51 -0
  3. aegis/cli/__init__.py +6 -0
  4. aegis/cli/callbacks.py +114 -0
  5. aegis/cli/interactive.py +611 -0
  6. aegis/cli/utils.py +70 -0
  7. aegis/cli/validators.py +34 -0
  8. aegis/commands/__init__.py +6 -0
  9. aegis/commands/add.py +353 -0
  10. aegis/commands/add_service.py +332 -0
  11. aegis/commands/components.py +35 -0
  12. aegis/commands/init.py +370 -0
  13. aegis/commands/remove.py +227 -0
  14. aegis/commands/services.py +52 -0
  15. aegis/commands/update.py +252 -0
  16. aegis/commands/version.py +12 -0
  17. aegis/config/__init__.py +1 -0
  18. aegis/config/shared_files.py +136 -0
  19. aegis/core/CLAUDE.md +377 -0
  20. aegis/core/__init__.py +6 -0
  21. aegis/core/component_files.py +228 -0
  22. aegis/core/component_utils.py +220 -0
  23. aegis/core/components.py +127 -0
  24. aegis/core/copier_manager.py +315 -0
  25. aegis/core/copier_updater.py +475 -0
  26. aegis/core/dependency_resolver.py +119 -0
  27. aegis/core/manual_updater.py +554 -0
  28. aegis/core/post_gen_tasks.py +547 -0
  29. aegis/core/service_resolver.py +261 -0
  30. aegis/core/services.py +157 -0
  31. aegis/core/template_generator.py +266 -0
  32. aegis/core/version_compatibility.py +259 -0
  33. aegis/templates/CLAUDE.md +591 -0
  34. aegis/templates/cookiecutter-aegis-project/cookiecutter.json +39 -0
  35. aegis/templates/cookiecutter-aegis-project/hooks/post_gen_project.py +214 -0
  36. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.dockerignore +71 -0
  37. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.env.example.j2 +130 -0
  38. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/.gitignore +131 -0
  39. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Dockerfile +53 -0
  40. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/Makefile +236 -0
  41. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/README.md.j2 +196 -0
  42. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/alembic.ini.j2 +111 -0
  43. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/env.py.j2 +91 -0
  44. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/script.py.mako +25 -0
  45. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/alembic/versions/001_initial_auth.py.j2 +51 -0
  46. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/__init__.py +5 -0
  47. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/__init__.py +6 -0
  48. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai.py.j2 +700 -0
  49. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/ai_rendering.py +361 -0
  50. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/auth.py.j2 +253 -0
  51. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/health.py.j2 +419 -0
  52. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/load_test.py.j2 +656 -0
  53. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/main.py.j2 +65 -0
  54. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/marko_terminal_renderer.py +489 -0
  55. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/tasks.py.j2 +328 -0
  56. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/cli/{% if cookiecutter.include_scheduler == /"yes/" %}tasks.py{% endif %}" +340 -0
  57. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/__init__.py +0 -0
  58. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/__init__.py +0 -0
  59. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/__init__.py +8 -0
  60. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/ai/router.py +329 -0
  61. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/__init__.py +1 -0
  62. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/auth/router.py +64 -0
  63. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/deps.py +58 -0
  64. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/health.py +163 -0
  65. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/models.py.j2 +280 -0
  66. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/routing.py.j2 +32 -0
  67. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/scheduler.py.j2 +121 -0
  68. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/api/worker.py.j2 +478 -0
  69. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/hooks.py +144 -0
  70. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/main.py +31 -0
  71. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/__init__.py +1 -0
  72. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/middleware/cors.py +20 -0
  73. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/__init__.py +1 -0
  74. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/shutdown/cleanup.py +14 -0
  75. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/__init__.py +1 -0
  76. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/component_health.py.j2 +418 -0
  77. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/backend/startup/database_init.py.j2 +83 -0
  78. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/__init__.py +5 -0
  79. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/__init__.py +27 -0
  80. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/table.py +78 -0
  81. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/controls/text.py +142 -0
  82. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/__init__.py.j2 +47 -0
  83. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  84. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  85. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  86. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  87. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  88. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  89. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  90. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  91. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  92. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  93. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  94. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  95. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/main.py.j2 +653 -0
  96. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/frontend/theme.py +48 -0
  97. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/__init__.py +1 -0
  98. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/scheduler/main.py.j2 +156 -0
  99. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/CLAUDE.md.j2 +213 -0
  100. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/__init__.py +6 -0
  101. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/constants.py.j2 +30 -0
  102. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/pools.py +97 -0
  103. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/__init__.py +1 -0
  104. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/load_test.py +55 -0
  105. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/media.py +49 -0
  106. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/queues/system.py +44 -0
  107. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/registry.py +139 -0
  108. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/__init__.py +120 -0
  109. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/load_tasks.py +507 -0
  110. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  111. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/components/worker/tasks/system_tasks.py +281 -0
  112. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/config.py.j2 +178 -0
  113. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/constants.py +58 -0
  114. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/db.py.j2 +176 -0
  115. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/log.py +92 -0
  116. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/core/security.py +62 -0
  117. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/__init__.py +1 -0
  118. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/webserver.py +40 -0
  119. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/entrypoints/{% if cookiecutter.include_scheduler == /"yes/" %}scheduler.py{% endif %}" +21 -0
  120. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/__init__.py +0 -0
  121. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/integrations/main.py +62 -0
  122. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/__init__.py +1 -0
  123. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/models/user.py +44 -0
  124. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/py.typed +0 -0
  125. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/__init__.py +1 -0
  126. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/__init__.py +8 -0
  127. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/config.py +130 -0
  128. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/conversation.py +213 -0
  129. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/health.py +96 -0
  130. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/models.py +229 -0
  131. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/providers.py +370 -0
  132. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/ai/service.py +388 -0
  133. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/__init__.py +1 -0
  134. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/auth_service.py +41 -0
  135. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/health.py +164 -0
  136. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/auth/user_service.py +83 -0
  137. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/middleware_inspector.py.j2 +223 -0
  138. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/models.py.j2 +70 -0
  139. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/backend/route_inspector.py.j2 +155 -0
  140. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test.py +679 -0
  141. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/load_test_models.py +266 -0
  142. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/__init__.py.j2 +21 -0
  143. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/models.py.j2 +119 -0
  144. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/scheduled_task_manager.py.j2 +273 -0
  145. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/scheduler/task_monitor.py.j2 +189 -0
  146. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/__init__.py +15 -0
  147. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/shared/models.py +26 -0
  148. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/__init__.py +52 -0
  149. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/alerts.py +94 -0
  150. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/backup.py.j2 +119 -0
  151. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/health.py.j2 +1333 -0
  152. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/models.py +243 -0
  153. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/app/services/system/ui.py +52 -0
  154. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-dark.png +0 -0
  155. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto-square-backup.png +0 -0
  156. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/assets/aegis-manifesto.png +0 -0
  157. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.dockerignore +71 -0
  158. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.env.example.j2 +64 -0
  159. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/.gitignore +131 -0
  160. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Dockerfile +53 -0
  161. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/Makefile +211 -0
  162. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/README.md.j2 +172 -0
  163. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/docker-compose.yml.j2 +78 -0
  164. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/mkdocs.yml.j2 +62 -0
  165. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/pyproject.toml.j2 +120 -0
  166. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/clean-validation/uv.lock +1673 -0
  167. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docker-compose.yml.j2 +200 -0
  168. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/api.md +191 -0
  169. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md +0 -0
  170. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/components/scheduler.md.j2 +621 -0
  171. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/development.md +215 -0
  172. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/health.md +240 -0
  173. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/javascripts/mermaid-config.js +62 -0
  174. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/docs/stylesheets/mermaid.css +95 -0
  175. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/mkdocs.yml.j2 +62 -0
  176. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/pyproject.toml.j2 +131 -0
  177. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh +87 -0
  178. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/entrypoint.sh.j2 +93 -0
  179. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/scripts/gen_docs.py +16 -0
  180. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/__init__.py +1 -0
  181. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_auth_endpoints.py.j2 +307 -0
  182. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_health_endpoints.py.j2 +262 -0
  183. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_scheduler_endpoints.py.j2 +214 -0
  184. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/api/test_worker_endpoints.py.j2 +165 -0
  185. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_ai_rendering.py +427 -0
  186. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/cli/test_conversation_memory.py +465 -0
  187. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/components/test_scheduler.py +43 -0
  188. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/conftest.py.j2 +195 -0
  189. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/__init__.py +1 -0
  190. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/__init__.py +1 -0
  191. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/conftest.py +78 -0
  192. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_health.py +157 -0
  193. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_models.py +164 -0
  194. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/ai/test_service.py +198 -0
  195. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_auth_integration.py.j2 +528 -0
  196. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_component_integration.py.j2 +387 -0
  197. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_conversation_persistence.py +342 -0
  198. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_health_logic.py.j2 +663 -0
  199. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_models.py +619 -0
  200. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_load_test_service.py +603 -0
  201. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_middleware_inspector.py.j2 +248 -0
  202. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_scheduled_task_manager.py.j2 +292 -0
  203. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_system_service.py +98 -0
  204. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/services/test_worker_health_registration.py.j2 +257 -0
  205. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/tests/test_core.py +49 -0
  206. aegis/templates/cookiecutter-aegis-project/{{cookiecutter.project_slug}}/uv.lock +1673 -0
  207. aegis/templates/copier-aegis-project/{{ project_slug }}/.copier-answers.yml.jinja +21 -0
  208. aegis/templates/copier-aegis-project/{{ project_slug }}/.dockerignore +71 -0
  209. aegis/templates/copier-aegis-project/{{ project_slug }}/.env.example.jinja +130 -0
  210. aegis/templates/copier-aegis-project/{{ project_slug }}/.gitignore +131 -0
  211. aegis/templates/copier-aegis-project/{{ project_slug }}/Dockerfile +53 -0
  212. aegis/templates/copier-aegis-project/{{ project_slug }}/Makefile.jinja +236 -0
  213. aegis/templates/copier-aegis-project/{{ project_slug }}/README.md.jinja +196 -0
  214. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/alembic.ini.jinja +111 -0
  215. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/env.py.jinja +91 -0
  216. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/script.py.mako +25 -0
  217. aegis/templates/copier-aegis-project/{{ project_slug }}/alembic/versions/001_initial_auth.py.jinja +51 -0
  218. aegis/templates/copier-aegis-project/{{ project_slug }}/app/__init__.py.jinja +5 -0
  219. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/__init__.py.jinja +6 -0
  220. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai.py.jinja +700 -0
  221. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/ai_rendering.py +360 -0
  222. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/auth.py.jinja +253 -0
  223. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/health.py.jinja +419 -0
  224. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/load_test.py.jinja +656 -0
  225. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/main.py.jinja +65 -0
  226. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/marko_terminal_renderer.py +489 -0
  227. aegis/templates/copier-aegis-project/{{ project_slug }}/app/cli/tasks.py.jinja +328 -0
  228. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/__init__.py +0 -0
  229. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/__init__.py +0 -0
  230. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/__init__.py +8 -0
  231. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/ai/router.py +329 -0
  232. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/__init__.py +1 -0
  233. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/auth/router.py +64 -0
  234. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/deps.py +58 -0
  235. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/health.py.jinja +163 -0
  236. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/models.py.jinja +280 -0
  237. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/routing.py.jinja +32 -0
  238. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/scheduler.py.jinja +121 -0
  239. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/api/worker.py.jinja +478 -0
  240. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/hooks.py +144 -0
  241. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/main.py +31 -0
  242. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/__init__.py +1 -0
  243. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/middleware/cors.py +20 -0
  244. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/__init__.py +1 -0
  245. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/shutdown/cleanup.py +14 -0
  246. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/__init__.py +1 -0
  247. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/component_health.py.jinja +418 -0
  248. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/backend/startup/database_init.py.jinja +83 -0
  249. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/__init__.py +5 -0
  250. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/__init__.py +27 -0
  251. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/table.py +78 -0
  252. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/controls/text.py +142 -0
  253. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/__init__.py.jinja +47 -0
  254. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/ai_card.py +287 -0
  255. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/auth_card.py +198 -0
  256. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/base_card.py +256 -0
  257. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_factory.py +227 -0
  258. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/card_utils.py +333 -0
  259. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/database_card.py +420 -0
  260. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/fastapi_card.py +328 -0
  261. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/flet_card.py +267 -0
  262. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/redis_card.py +322 -0
  263. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/scheduler_card.py +352 -0
  264. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/services_card.py +233 -0
  265. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/dashboard/cards/worker_card.py +684 -0
  266. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/main.py.jinja +653 -0
  267. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/frontend/theme.py +48 -0
  268. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/__init__.py +1 -0
  269. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/scheduler/main.py.jinja +156 -0
  270. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/CLAUDE.md.jinja +213 -0
  271. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/__init__.py +6 -0
  272. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/constants.py.jinja +30 -0
  273. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/pools.py +97 -0
  274. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/__init__.py +1 -0
  275. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/load_test.py +55 -0
  276. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/media.py +49 -0
  277. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/queues/system.py +44 -0
  278. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/registry.py +139 -0
  279. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/__init__.py +120 -0
  280. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/load_tasks.py +507 -0
  281. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/simple_system_tasks.py +33 -0
  282. aegis/templates/copier-aegis-project/{{ project_slug }}/app/components/worker/tasks/system_tasks.py +281 -0
  283. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/config.py.jinja +178 -0
  284. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/constants.py +58 -0
  285. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/db.py.jinja +176 -0
  286. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/log.py +92 -0
  287. aegis/templates/copier-aegis-project/{{ project_slug }}/app/core/security.py +62 -0
  288. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/__init__.py +1 -0
  289. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/scheduler.py.jinja +21 -0
  290. aegis/templates/copier-aegis-project/{{ project_slug }}/app/entrypoints/webserver.py +39 -0
  291. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/__init__.py +0 -0
  292. aegis/templates/copier-aegis-project/{{ project_slug }}/app/integrations/main.py +61 -0
  293. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/__init__.py +1 -0
  294. aegis/templates/copier-aegis-project/{{ project_slug }}/app/models/user.py +44 -0
  295. aegis/templates/copier-aegis-project/{{ project_slug }}/app/py.typed +0 -0
  296. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/__init__.py +1 -0
  297. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/__init__.py +8 -0
  298. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/config.py +130 -0
  299. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/conversation.py +213 -0
  300. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/health.py +96 -0
  301. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/models.py +229 -0
  302. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/providers.py.jinja +370 -0
  303. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/ai/service.py +387 -0
  304. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/__init__.py +1 -0
  305. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/auth_service.py +40 -0
  306. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/health.py +162 -0
  307. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/auth/user_service.py +82 -0
  308. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/middleware_inspector.py.jinja +223 -0
  309. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/models.py.jinja +70 -0
  310. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/backend/route_inspector.py.jinja +155 -0
  311. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test.py +678 -0
  312. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/load_test_models.py +265 -0
  313. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/__init__.py.jinja +21 -0
  314. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/models.py.jinja +119 -0
  315. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/scheduled_task_manager.py.jinja +273 -0
  316. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/scheduler/task_monitor.py.jinja +189 -0
  317. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/__init__.py +15 -0
  318. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/shared/models.py +26 -0
  319. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/__init__.py +52 -0
  320. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/alerts.py +94 -0
  321. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/backup.py.jinja +119 -0
  322. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/health.py.jinja +1333 -0
  323. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/models.py +243 -0
  324. aegis/templates/copier-aegis-project/{{ project_slug }}/app/services/system/ui.py +52 -0
  325. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57223!aegis-manifesto.png +0 -0
  326. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57224!aegis-manifesto-dark.png +0 -0
  327. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57225!aegis-manifesto-square-backup.png +0 -0
  328. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57533!aegis-manifesto.png +0 -0
  329. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57534!aegis-manifesto-dark.png +0 -0
  330. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57538!aegis-manifesto-square-backup.png +0 -0
  331. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57897!aegis-manifesto.png +0 -0
  332. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57898!aegis-manifesto-dark.png +0 -0
  333. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!57904!aegis-manifesto-square-backup.png +0 -0
  334. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58315!aegis-manifesto.png +0 -0
  335. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58316!aegis-manifesto-dark.png +0 -0
  336. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58324!aegis-manifesto-square-backup.png +0 -0
  337. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58837!aegis-manifesto.png +0 -0
  338. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58838!aegis-manifesto-dark.png +0 -0
  339. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/.!58849!aegis-manifesto-square-backup.png +0 -0
  340. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-dark.png +0 -0
  341. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto-square-backup.png +0 -0
  342. aegis/templates/copier-aegis-project/{{ project_slug }}/assets/aegis-manifesto.png +0 -0
  343. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/.env.example.jinja +64 -0
  344. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/README.md.jinja +172 -0
  345. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/docker-compose.yml.jinja +78 -0
  346. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/mkdocs.yml.jinja +62 -0
  347. aegis/templates/copier-aegis-project/{{ project_slug }}/clean-validation/pyproject.toml.jinja +120 -0
  348. aegis/templates/copier-aegis-project/{{ project_slug }}/docker-compose.yml.jinja +200 -0
  349. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/api.md.jinja +191 -0
  350. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md +0 -0
  351. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/components/scheduler.md.jinja +621 -0
  352. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/development.md.jinja +215 -0
  353. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/health.md.jinja +240 -0
  354. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/javascripts/mermaid-config.js +62 -0
  355. aegis/templates/copier-aegis-project/{{ project_slug }}/docs/stylesheets/mermaid.css +95 -0
  356. aegis/templates/copier-aegis-project/{{ project_slug }}/mkdocs.yml.jinja +62 -0
  357. aegis/templates/copier-aegis-project/{{ project_slug }}/pyproject.toml.jinja +131 -0
  358. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh +87 -0
  359. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/entrypoint.sh.jinja +93 -0
  360. aegis/templates/copier-aegis-project/{{ project_slug }}/scripts/gen_docs.py +16 -0
  361. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/__init__.py +1 -0
  362. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_auth_endpoints.py.jinja +307 -0
  363. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_health_endpoints.py.jinja +262 -0
  364. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_scheduler_endpoints.py.jinja +214 -0
  365. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/api/test_worker_endpoints.py.jinja +165 -0
  366. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_ai_rendering.py +427 -0
  367. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/cli/test_conversation_memory.py +465 -0
  368. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/components/test_scheduler.py +43 -0
  369. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/conftest.py.jinja +195 -0
  370. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/__init__.py +1 -0
  371. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/__init__.py +1 -0
  372. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/conftest.py +78 -0
  373. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_health.py +157 -0
  374. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_models.py +164 -0
  375. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/ai/test_service.py +198 -0
  376. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_auth_integration.py.jinja +528 -0
  377. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_component_integration.py.jinja +387 -0
  378. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_conversation_persistence.py +342 -0
  379. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_health_logic.py.jinja +663 -0
  380. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_models.py +619 -0
  381. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_load_test_service.py +603 -0
  382. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_middleware_inspector.py.jinja +248 -0
  383. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_scheduled_task_manager.py.jinja +292 -0
  384. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_system_service.py +98 -0
  385. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/services/test_worker_health_registration.py.jinja +257 -0
  386. aegis/templates/copier-aegis-project/{{ project_slug }}/tests/test_core.py +49 -0
  387. aegis/templates/copier-aegis-project/{{ project_slug }}/uv.lock +1673 -0
  388. aegis_stack-0.2.0rc2.dist-info/METADATA +165 -0
  389. aegis_stack-0.2.0rc2.dist-info/RECORD +392 -0
  390. aegis_stack-0.2.0rc2.dist-info/WHEEL +4 -0
  391. aegis_stack-0.2.0rc2.dist-info/entry_points.txt +3 -0
  392. aegis_stack-0.2.0rc2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,684 @@
1
+ """
2
+ Stunning Worker Component Card
3
+
4
+ Modern, visually striking card component that displays rich Worker/arq metrics,
5
+ queue statistics, and job processing data using ee-toolset design standards.
6
+ """
7
+
8
+ import flet as ft
9
+ from app.components.frontend.controls import (
10
+ LabelText,
11
+ PrimaryText,
12
+ SecondaryText,
13
+ TableCellText,
14
+ TableHeaderText,
15
+ TableNameText,
16
+ TitleText,
17
+ )
18
+ from app.services.system.models import ComponentStatus, ComponentStatusType
19
+
20
+
21
+ class WorkerCard:
22
+ """
23
+ A visually stunning, wide component card for displaying Worker/arq metrics.
24
+
25
+ Features:
26
+ - Modern Material Design 3 styling
27
+ - Three-section layout (badge, queues, stats)
28
+ - Real-time queue metrics from health check data
29
+ - Comprehensive job statistics (queued, processing, completed, failed)
30
+ - Status-aware coloring and visual indicators (green/yellow/red)
31
+ - Worker failure rate monitoring with thresholds
32
+ - Responsive design for different screen sizes
33
+ """
34
+
35
+ def __init__(self, component_data: ComponentStatus) -> None:
36
+ """
37
+ Initialize the Worker card with component data.
38
+
39
+ Args:
40
+ component_data: ComponentStatus with worker health and queue metrics.
41
+ Expected metadata structure:
42
+ - active_workers: number of active worker processes
43
+ - total_queued: total jobs queued across all queues
44
+ - total_completed: total jobs completed
45
+ - total_failed: total jobs failed
46
+ - total_ongoing: total jobs currently processing
47
+ - overall_failure_rate_percent: overall failure percentage
48
+ Expected sub_components['queues'] with per-queue metadata:
49
+ - queued_jobs: jobs waiting in this queue
50
+ - jobs_ongoing: jobs currently processing
51
+ - jobs_completed: total completed for this queue
52
+ - jobs_failed: total failed for this queue
53
+ - failure_rate_percent: failure rate for this queue
54
+ - worker_alive: boolean indicating if worker is responsive
55
+ """
56
+ self.component_data = component_data
57
+ self._card_container: ft.Container | None = None
58
+
59
+ def _get_status_colors(self) -> tuple[str, str, str]:
60
+ """
61
+ Get status-aware colors for the card with UI-specific logic.
62
+
63
+ Returns:
64
+ Tuple of (primary_color, background_color, border_color)
65
+ """
66
+ status = self.component_data.status
67
+
68
+ # Override harsh "unhealthy" status for partial worker functionality
69
+ if status == ComponentStatusType.UNHEALTHY:
70
+ # Check if any workers are actually running
71
+ metadata = self.component_data.metadata or {}
72
+ active_workers = metadata.get("active_workers", 0)
73
+ total_queued = metadata.get("total_queued", 0)
74
+
75
+ if active_workers > 0:
76
+ # Some workers are running - this is WARNING, not UNHEALTHY
77
+ if total_queued > 0:
78
+ # WARNING: queued jobs with some workers down
79
+ return (ft.Colors.ORANGE, ft.Colors.SURFACE, ft.Colors.ORANGE)
80
+ else:
81
+ # INFO: some workers offline but no backlog
82
+ return (ft.Colors.BLUE, ft.Colors.SURFACE, ft.Colors.BLUE)
83
+ else:
84
+ # No workers running - truly unhealthy
85
+ return (ft.Colors.RED, ft.Colors.SURFACE, ft.Colors.RED)
86
+ elif status == ComponentStatusType.HEALTHY:
87
+ return (ft.Colors.GREEN, ft.Colors.SURFACE, ft.Colors.GREEN)
88
+ elif status == ComponentStatusType.INFO:
89
+ return (ft.Colors.BLUE, ft.Colors.SURFACE, ft.Colors.BLUE)
90
+ elif status == ComponentStatusType.WARNING:
91
+ return (ft.Colors.ORANGE, ft.Colors.SURFACE, ft.Colors.ORANGE)
92
+ else:
93
+ return (ft.Colors.RED, ft.Colors.SURFACE, ft.Colors.RED)
94
+
95
+ def _create_queue_indicator(
96
+ self, queue_name: str, queue_data: ComponentStatus
97
+ ) -> ft.Container:
98
+ """Create a comprehensive queue status indicator with detailed metrics."""
99
+ # Extract metrics from queue metadata
100
+ metadata = queue_data.metadata if queue_data else {}
101
+
102
+ queued_jobs = metadata.get("queued_jobs", 0)
103
+ jobs_ongoing = metadata.get("jobs_ongoing", 0)
104
+ jobs_completed = metadata.get("jobs_completed", 0)
105
+ jobs_failed = metadata.get("jobs_failed", 0)
106
+ failure_rate = metadata.get("failure_rate_percent", 0)
107
+ worker_alive = metadata.get("worker_alive", False)
108
+
109
+ # Determine color based on failure rate and worker status
110
+ if not worker_alive:
111
+ queue_color = ft.Colors.GREY
112
+ status_icon = "⚫"
113
+ elif failure_rate > 10:
114
+ queue_color = ft.Colors.RED
115
+ status_icon = "🔴"
116
+ elif failure_rate > 5:
117
+ queue_color = ft.Colors.ORANGE
118
+ status_icon = "🟡"
119
+ else:
120
+ queue_color = ft.Colors.GREEN
121
+ status_icon = "🟢"
122
+
123
+ return ft.Container(
124
+ content=ft.Column(
125
+ [
126
+ # Queue name and status
127
+ ft.Row(
128
+ [
129
+ ft.Text(status_icon, size=10),
130
+ LabelText(queue_name.upper(), size=9),
131
+ ],
132
+ spacing=3,
133
+ ),
134
+ # Health bar
135
+ ft.Container(
136
+ height=3,
137
+ bgcolor=queue_color,
138
+ border_radius=1,
139
+ margin=ft.margin.symmetric(vertical=2),
140
+ ),
141
+ # Metrics in compact format
142
+ ft.Column(
143
+ [
144
+ LabelText(f"Q:{queued_jobs} A:{jobs_ongoing}", size=8),
145
+ LabelText(f"✓{jobs_completed} ✗{jobs_failed}", size=8),
146
+ LabelText(
147
+ f"{failure_rate:.1f}%" if worker_alive else "OFFLINE",
148
+ size=8,
149
+ color=queue_color,
150
+ ),
151
+ ],
152
+ spacing=1,
153
+ alignment=ft.MainAxisAlignment.CENTER,
154
+ ),
155
+ ],
156
+ spacing=2,
157
+ alignment=ft.MainAxisAlignment.START,
158
+ ),
159
+ padding=ft.padding.all(6),
160
+ bgcolor=ft.Colors.SURFACE,
161
+ border=ft.border.all(1, queue_color),
162
+ border_radius=8,
163
+ width=110, # Slightly wider for more metrics
164
+ height=80, # Slightly taller for more content
165
+ )
166
+
167
+ def _create_technology_badge(self) -> ft.Container:
168
+ """Create the Worker/arq technology badge section."""
169
+ primary_color, _, _ = self._get_status_colors()
170
+
171
+ return ft.Container(
172
+ content=ft.Column(
173
+ [
174
+ ft.Container(
175
+ content=ft.Text("⚡", size=32),
176
+ padding=ft.padding.all(8),
177
+ bgcolor=primary_color,
178
+ border_radius=12,
179
+ margin=ft.margin.only(bottom=8),
180
+ ),
181
+ TitleText("Worker"),
182
+ SecondaryText("arq + Redis"),
183
+ ft.Container(
184
+ content=LabelText(
185
+ "QUEUES",
186
+ color=ft.Colors.WHITE,
187
+ ),
188
+ padding=ft.padding.symmetric(horizontal=8, vertical=2),
189
+ bgcolor=ft.Colors.PURPLE,
190
+ border_radius=8,
191
+ margin=ft.margin.only(top=4),
192
+ ),
193
+ ],
194
+ alignment=ft.MainAxisAlignment.CENTER,
195
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
196
+ spacing=4,
197
+ ),
198
+ padding=ft.padding.all(16),
199
+ width=160,
200
+ alignment=ft.alignment.center,
201
+ )
202
+
203
+ def _create_queues_section(self) -> ft.Container:
204
+ """Create the queues section with individual queue indicators."""
205
+ queues_data = {}
206
+ if (
207
+ self.component_data.sub_components
208
+ and "queues" in self.component_data.sub_components
209
+ ):
210
+ queues_comp = self.component_data.sub_components["queues"]
211
+ if queues_comp.sub_components:
212
+ queues_data = queues_comp.sub_components
213
+
214
+ queue_controls = []
215
+
216
+ if queues_data:
217
+ # Show max 3 queues
218
+ for queue_name, queue_data in list(queues_data.items())[:3]:
219
+ queue_controls.append(
220
+ self._create_queue_indicator(queue_name, queue_data)
221
+ )
222
+ else:
223
+ # Show placeholder when no queue data
224
+ queue_controls.append(
225
+ ft.Container(
226
+ content=ft.Column(
227
+ [
228
+ ft.Text("📭", size=24),
229
+ LabelText("No Active Queues"),
230
+ ],
231
+ alignment=ft.MainAxisAlignment.CENTER,
232
+ horizontal_alignment=ft.CrossAxisAlignment.CENTER,
233
+ ),
234
+ padding=ft.padding.all(16),
235
+ bgcolor=ft.Colors.GREY_200,
236
+ border=ft.border.all(1, ft.Colors.GREY_300),
237
+ border_radius=8,
238
+ height=80,
239
+ )
240
+ )
241
+
242
+ return ft.Container(
243
+ content=ft.Column(
244
+ [
245
+ PrimaryText("Queue Status"),
246
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
247
+ ft.Container(
248
+ content=ft.Row(
249
+ queue_controls,
250
+ spacing=12,
251
+ wrap=True,
252
+ alignment=ft.MainAxisAlignment.CENTER,
253
+ ),
254
+ width=400, # Expanded width for queue metrics
255
+ alignment=ft.alignment.center,
256
+ ),
257
+ ],
258
+ spacing=12,
259
+ alignment=ft.MainAxisAlignment.START,
260
+ ),
261
+ alignment=ft.alignment.top_center,
262
+ )
263
+
264
+ def _create_stats_section(self) -> ft.Container:
265
+ """Create the worker statistics section using real health check data."""
266
+ # Extract real metrics from health check data
267
+ metadata = self.component_data.metadata or {}
268
+
269
+ active_workers = metadata.get("active_workers", 0)
270
+ total_completed = metadata.get("total_completed", 0)
271
+ total_failed = metadata.get("total_failed", 0)
272
+ total_ongoing = metadata.get("total_ongoing", 0)
273
+ total_queued = metadata.get("total_queued", 0)
274
+ failure_rate = metadata.get("overall_failure_rate_percent", 0)
275
+
276
+ # Format numbers for display
277
+ worker_stats = {
278
+ "Active Workers": str(active_workers),
279
+ "Jobs Processing": str(total_ongoing),
280
+ "Jobs Queued": f"{total_queued:,}",
281
+ "Completed": f"{total_completed:,}",
282
+ "Failed": f"{total_failed:,}",
283
+ "Failure Rate": f"{failure_rate:.1f}%",
284
+ }
285
+
286
+ stats_content = [
287
+ PrimaryText("Worker Stats"),
288
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
289
+ ]
290
+
291
+ for stat_name, stat_value in worker_stats.items():
292
+ # Color code certain metrics
293
+ value_color = None
294
+ if stat_name == "Failure Rate":
295
+ if failure_rate > 10:
296
+ value_color = ft.Colors.RED
297
+ elif failure_rate > 5:
298
+ value_color = ft.Colors.ORANGE
299
+ else:
300
+ value_color = ft.Colors.GREEN
301
+ elif stat_name == "Failed" and total_failed > 0:
302
+ value_color = ft.Colors.RED
303
+ elif stat_name == "Jobs Processing" and total_ongoing > 0:
304
+ value_color = ft.Colors.BLUE
305
+
306
+ value_label = LabelText(stat_value)
307
+ if value_color:
308
+ value_label.color = value_color
309
+
310
+ stats_content.append(
311
+ ft.Row(
312
+ [
313
+ SecondaryText(f"{stat_name}:"),
314
+ value_label,
315
+ ],
316
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
317
+ )
318
+ )
319
+
320
+ # Add status info
321
+ stats_content.extend(
322
+ [
323
+ ft.Divider(height=1, color=ft.Colors.GREY_300),
324
+ ft.Row(
325
+ [
326
+ SecondaryText("Status:"),
327
+ LabelText(
328
+ self.component_data.status.value.title(),
329
+ color=self._get_status_colors()[0],
330
+ ),
331
+ ],
332
+ alignment=ft.MainAxisAlignment.SPACE_BETWEEN,
333
+ ),
334
+ ]
335
+ )
336
+
337
+ return ft.Container(
338
+ content=ft.Column(
339
+ stats_content,
340
+ spacing=8, # Increased spacing for better vertical distribution
341
+ alignment=ft.MainAxisAlignment.START,
342
+ ),
343
+ alignment=ft.alignment.top_left, # Ensure proper alignment
344
+ )
345
+
346
+ def _create_queue_row(
347
+ self, queue_name: str, queue_data: ComponentStatus
348
+ ) -> ft.Container:
349
+ """Create a single row for a queue with all metrics."""
350
+ metadata = queue_data.metadata if queue_data else {}
351
+
352
+ queued = metadata.get("queued_jobs", 0)
353
+ active = metadata.get("jobs_ongoing", 0)
354
+ completed = metadata.get("jobs_completed", 0)
355
+ failed = metadata.get("jobs_failed", 0)
356
+ failure_rate = metadata.get("failure_rate_percent", 0)
357
+ worker_alive = metadata.get("worker_alive", False)
358
+
359
+ # Get status icon
360
+ if not worker_alive:
361
+ status_icon = "⚫"
362
+ status_color = ft.Colors.GREY
363
+ elif failure_rate > 10:
364
+ status_icon = "🔴"
365
+ status_color = ft.Colors.RED
366
+ elif failure_rate > 5:
367
+ status_icon = "🟠"
368
+ status_color = ft.Colors.ORANGE
369
+ else:
370
+ status_icon = "🟢"
371
+ status_color = ft.Colors.GREEN
372
+
373
+ # Calculate success rate
374
+ total_jobs = completed + failed
375
+ success_rate = (
376
+ f"{((completed / total_jobs) * 100):.1f}%" if total_jobs > 0 else "N/A"
377
+ )
378
+
379
+ return ft.Container(
380
+ content=ft.Row(
381
+ [
382
+ # Queue name with status icon
383
+ ft.Container(
384
+ content=ft.Text(
385
+ f"{status_icon} {queue_name}",
386
+ weight=ft.FontWeight.W_500,
387
+ color=ft.Colors.ON_SURFACE,
388
+ ),
389
+ width=120,
390
+ ),
391
+ # Queued
392
+ ft.Container(
393
+ content=ft.Text(f"{queued:,}", color=ft.Colors.ON_SURFACE),
394
+ width=60,
395
+ alignment=ft.alignment.center_right,
396
+ ),
397
+ # Active
398
+ ft.Container(
399
+ content=ft.Text(f"{active:,}", color=ft.Colors.ON_SURFACE),
400
+ width=50,
401
+ alignment=ft.alignment.center_right,
402
+ ),
403
+ # Completed
404
+ ft.Container(
405
+ content=ft.Text(f"{completed:,}", color=ft.Colors.ON_SURFACE),
406
+ width=70,
407
+ alignment=ft.alignment.center_right,
408
+ ),
409
+ # Failed
410
+ ft.Container(
411
+ content=ft.Text(
412
+ f"{failed:,}",
413
+ color=ft.Colors.ERROR
414
+ if failed > 0
415
+ else ft.Colors.ON_SURFACE,
416
+ ),
417
+ width=50,
418
+ alignment=ft.alignment.center_right,
419
+ ),
420
+ # Success rate
421
+ ft.Container(
422
+ content=ft.Text(
423
+ success_rate, color=status_color, weight=ft.FontWeight.W_500
424
+ ),
425
+ width=60,
426
+ alignment=ft.alignment.center_right,
427
+ ),
428
+ ],
429
+ alignment=ft.MainAxisAlignment.START,
430
+ ),
431
+ padding=ft.padding.symmetric(horizontal=15, vertical=8),
432
+ bgcolor=(
433
+ ft.Colors.with_opacity(0.05, ft.Colors.GREY)
434
+ if not worker_alive
435
+ else None
436
+ ),
437
+ border_radius=8,
438
+ )
439
+
440
+ def _create_compact_queue_row(
441
+ self, queue_name: str, queue_data: ComponentStatus
442
+ ) -> ft.Container:
443
+ """Create a compact queue row for the 2-section layout."""
444
+ metadata = queue_data.metadata if queue_data else {}
445
+
446
+ queued = metadata.get("queued_jobs", 0)
447
+ active = metadata.get("jobs_ongoing", 0)
448
+ completed = metadata.get("jobs_completed", 0)
449
+ failed = metadata.get("jobs_failed", 0)
450
+ failure_rate = metadata.get("failure_rate_percent", 0)
451
+ worker_alive = metadata.get("worker_alive", False)
452
+
453
+ # Get status icon and smart status message (Option B)
454
+ if not worker_alive:
455
+ # Check if this is media queue with no tasks defined
456
+ if queue_data and "no functions" in queue_data.message.lower():
457
+ status_icon = "⚪"
458
+ status_message = "no tasks defined"
459
+ status_color = ft.Colors.GREY_600
460
+ else:
461
+ # Any other worker offline should be red (it's a problem)
462
+ status_icon = "🔴"
463
+ status_message = "worker offline"
464
+ status_color = ft.Colors.RED
465
+ elif failure_rate > 10:
466
+ status_icon = "🔴"
467
+ status_color = ft.Colors.RED
468
+ total_jobs = completed + failed
469
+ status_message = (
470
+ f"{((completed / total_jobs) * 100):.1f}%" if total_jobs > 0 else "N/A"
471
+ )
472
+ elif failure_rate > 5:
473
+ status_icon = "🟠"
474
+ status_color = ft.Colors.ORANGE
475
+ total_jobs = completed + failed
476
+ status_message = (
477
+ f"{((completed / total_jobs) * 100):.1f}%" if total_jobs > 0 else "N/A"
478
+ )
479
+ else:
480
+ status_icon = "🟢"
481
+ status_color = ft.Colors.GREEN
482
+ total_jobs = completed + failed
483
+ if total_jobs > 0:
484
+ status_message = f"{((completed / total_jobs) * 100):.1f}%"
485
+ else:
486
+ status_message = "ready"
487
+
488
+ # Calculate speed/performance metric
489
+ if not worker_alive:
490
+ speed_display = "-"
491
+ speed_color = ft.Colors.GREY_600
492
+ elif active > 0:
493
+ speed_display = "Active"
494
+ speed_color = ft.Colors.BLUE
495
+ else:
496
+ # For now, just show "-" since we don't have reliable timing data
497
+ # to calculate actual throughput. The completed count is cumulative
498
+ # since worker startup, not a time-windowed rate.
499
+ speed_display = "-"
500
+ speed_color = ft.Colors.GREY_600
501
+
502
+ return ft.Container(
503
+ content=ft.Row(
504
+ [
505
+ # Queue name with status icon
506
+ ft.Container(
507
+ content=TableNameText(f"{status_icon} {queue_name}"),
508
+ width=90,
509
+ ),
510
+ # Queued
511
+ ft.Container(
512
+ content=TableCellText(f"{queued}"),
513
+ width=50,
514
+ alignment=ft.alignment.center_right,
515
+ ),
516
+ # Active
517
+ ft.Container(
518
+ content=TableCellText(f"{active}"),
519
+ width=40,
520
+ alignment=ft.alignment.center_right,
521
+ ),
522
+ # Completed
523
+ ft.Container(
524
+ content=TableCellText(f"{completed}"),
525
+ width=50,
526
+ alignment=ft.alignment.center_right,
527
+ ),
528
+ # Failed
529
+ ft.Container(
530
+ content=TableCellText(
531
+ f"{failed}",
532
+ color=(
533
+ ft.Colors.ERROR if failed > 0 else ft.Colors.ON_SURFACE
534
+ ),
535
+ ),
536
+ width=40,
537
+ alignment=ft.alignment.center_right,
538
+ ),
539
+ # Speed/Performance metric
540
+ ft.Container(
541
+ content=TableCellText(
542
+ speed_display,
543
+ color=speed_color,
544
+ ),
545
+ width=60,
546
+ alignment=ft.alignment.center_right,
547
+ ),
548
+ # Smart status (rate or message) - last column
549
+ ft.Container(
550
+ content=TableCellText(
551
+ status_message,
552
+ color=status_color,
553
+ ),
554
+ width=90,
555
+ alignment=ft.alignment.center_right,
556
+ ),
557
+ ],
558
+ alignment=ft.MainAxisAlignment.START,
559
+ ),
560
+ padding=ft.padding.symmetric(horizontal=10, vertical=6),
561
+ bgcolor=(
562
+ ft.Colors.with_opacity(0.05, ft.Colors.GREY)
563
+ if not worker_alive
564
+ else None
565
+ ),
566
+ border_radius=4,
567
+ )
568
+
569
+ def build(self) -> ft.Container:
570
+ """Build and return the Worker card with 2-section layout."""
571
+ primary_color, background_color, border_color = self._get_status_colors()
572
+
573
+ # Get queue data
574
+ queues_data = {}
575
+ if (
576
+ self.component_data.sub_components
577
+ and "queues" in self.component_data.sub_components
578
+ ):
579
+ queues_data = (
580
+ self.component_data.sub_components["queues"].sub_components or {}
581
+ )
582
+
583
+ # Header for queue table
584
+ header_row = ft.Container(
585
+ content=ft.Row(
586
+ [
587
+ ft.Container(
588
+ TableHeaderText("Queue"),
589
+ width=90,
590
+ ),
591
+ ft.Container(
592
+ TableHeaderText("Queued"),
593
+ width=50,
594
+ alignment=ft.alignment.center_right,
595
+ ),
596
+ ft.Container(
597
+ TableHeaderText("Active"),
598
+ width=40,
599
+ alignment=ft.alignment.center_right,
600
+ ),
601
+ ft.Container(
602
+ TableHeaderText("Done"),
603
+ width=50,
604
+ alignment=ft.alignment.center_right,
605
+ ),
606
+ ft.Container(
607
+ TableHeaderText("Failed"),
608
+ width=40,
609
+ alignment=ft.alignment.center_right,
610
+ ),
611
+ ft.Container(
612
+ TableHeaderText("Speed"),
613
+ width=60,
614
+ alignment=ft.alignment.center_right,
615
+ ),
616
+ ft.Container(
617
+ TableHeaderText("Status"),
618
+ width=90,
619
+ alignment=ft.alignment.center_right,
620
+ ),
621
+ ],
622
+ alignment=ft.MainAxisAlignment.START,
623
+ ),
624
+ padding=ft.padding.symmetric(horizontal=10, vertical=8),
625
+ border=ft.border.only(bottom=ft.BorderSide(1, ft.Colors.GREY_300)),
626
+ )
627
+
628
+ # Queue rows (adjusted widths for smaller space)
629
+ queue_rows = []
630
+ for queue_name, queue_data in queues_data.items():
631
+ queue_rows.append(self._create_compact_queue_row(queue_name, queue_data))
632
+
633
+ # Queue Status header
634
+ queue_status_header = ft.Container(
635
+ content=ft.Text(
636
+ "Queue Status",
637
+ size=16,
638
+ weight=ft.FontWeight.W_600,
639
+ color=ft.Colors.ON_SURFACE,
640
+ ),
641
+ padding=ft.padding.only(top=10, bottom=10),
642
+ )
643
+
644
+ # Queue table content with header
645
+ table_content = ft.Column(
646
+ [
647
+ queue_status_header,
648
+ header_row,
649
+ *queue_rows,
650
+ ],
651
+ spacing=0,
652
+ )
653
+
654
+ # 2-section layout: Left badge + Right table
655
+ content = ft.Row(
656
+ [
657
+ # Left: Technology badge (same width as other cards)
658
+ ft.Container(
659
+ content=self._create_technology_badge(),
660
+ expand=2, # Same as other cards
661
+ width=100, # Minimum width like other cards
662
+ ),
663
+ ft.VerticalDivider(width=1, color=ft.Colors.GREY_300),
664
+ # Right: Queue table
665
+ ft.Container(
666
+ content=table_content,
667
+ expand=8, # Takes remaining space (2:8 ratio)
668
+ padding=ft.padding.all(10),
669
+ width=200, # Minimum width
670
+ ),
671
+ ]
672
+ )
673
+
674
+ self._card_container = ft.Container(
675
+ content=content,
676
+ bgcolor=ft.Colors.SURFACE,
677
+ border=ft.border.all(1, border_color),
678
+ border_radius=16,
679
+ padding=0,
680
+ width=None, # Let ResponsiveRow handle the width
681
+ height=280,
682
+ )
683
+
684
+ return self._card_container