moai-adk 0.25.4__py3-none-any.whl → 0.32.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of moai-adk might be problematic. Click here for more details.

Files changed (378) hide show
  1. moai_adk/__init__.py +2 -5
  2. moai_adk/__main__.py +114 -82
  3. moai_adk/cli/__init__.py +6 -1
  4. moai_adk/cli/commands/__init__.py +1 -3
  5. moai_adk/cli/commands/analyze.py +5 -16
  6. moai_adk/cli/commands/doctor.py +6 -18
  7. moai_adk/cli/commands/init.py +56 -125
  8. moai_adk/cli/commands/language.py +14 -35
  9. moai_adk/cli/commands/status.py +9 -15
  10. moai_adk/cli/commands/update.py +1555 -190
  11. moai_adk/cli/prompts/init_prompts.py +112 -56
  12. moai_adk/cli/spec_status.py +263 -0
  13. moai_adk/cli/ui/__init__.py +44 -0
  14. moai_adk/cli/ui/progress.py +422 -0
  15. moai_adk/cli/ui/prompts.py +389 -0
  16. moai_adk/cli/ui/theme.py +129 -0
  17. moai_adk/cli/worktree/__init__.py +27 -0
  18. moai_adk/cli/worktree/__main__.py +31 -0
  19. moai_adk/cli/worktree/cli.py +672 -0
  20. moai_adk/cli/worktree/exceptions.py +89 -0
  21. moai_adk/cli/worktree/manager.py +490 -0
  22. moai_adk/cli/worktree/models.py +65 -0
  23. moai_adk/cli/worktree/registry.py +128 -0
  24. moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
  25. moai_adk/core/analysis/session_analyzer.py +17 -56
  26. moai_adk/core/claude_integration.py +26 -54
  27. moai_adk/core/command_helpers.py +10 -10
  28. moai_adk/core/comprehensive_monitoring_system.py +1183 -0
  29. moai_adk/core/config/auto_spec_config.py +5 -11
  30. moai_adk/core/config/migration.py +19 -9
  31. moai_adk/core/config/unified.py +436 -0
  32. moai_adk/core/context_manager.py +6 -12
  33. moai_adk/core/enterprise_features.py +1404 -0
  34. moai_adk/core/error_recovery_system.py +725 -112
  35. moai_adk/core/event_driven_hook_system.py +1371 -0
  36. moai_adk/core/git/__init__.py +8 -0
  37. moai_adk/core/git/branch_manager.py +3 -11
  38. moai_adk/core/git/checkpoint.py +1 -3
  39. moai_adk/core/git/conflict_detector.py +413 -0
  40. moai_adk/core/git/manager.py +91 -1
  41. moai_adk/core/hooks/post_tool_auto_spec_completion.py +56 -80
  42. moai_adk/core/input_validation_middleware.py +1006 -0
  43. moai_adk/core/integration/engine.py +6 -18
  44. moai_adk/core/integration/integration_tester.py +10 -9
  45. moai_adk/core/integration/utils.py +1 -1
  46. moai_adk/core/issue_creator.py +10 -28
  47. moai_adk/core/jit_context_loader.py +956 -0
  48. moai_adk/core/jit_enhanced_hook_manager.py +1987 -0
  49. moai_adk/core/language_config_resolver.py +485 -0
  50. moai_adk/core/language_validator.py +28 -41
  51. moai_adk/core/mcp/setup.py +15 -12
  52. moai_adk/core/merge/__init__.py +9 -0
  53. moai_adk/core/merge/analyzer.py +481 -0
  54. moai_adk/core/migration/alfred_to_moai_migrator.py +383 -0
  55. moai_adk/core/migration/backup_manager.py +78 -9
  56. moai_adk/core/migration/custom_element_scanner.py +358 -0
  57. moai_adk/core/migration/file_migrator.py +8 -17
  58. moai_adk/core/migration/interactive_checkbox_ui.py +488 -0
  59. moai_adk/core/migration/selective_restorer.py +470 -0
  60. moai_adk/core/migration/template_utils.py +74 -0
  61. moai_adk/core/migration/user_selection_ui.py +338 -0
  62. moai_adk/core/migration/version_detector.py +6 -10
  63. moai_adk/core/migration/version_migrator.py +3 -3
  64. moai_adk/core/performance/cache_system.py +8 -10
  65. moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
  66. moai_adk/core/project/checker.py +2 -4
  67. moai_adk/core/project/detector.py +1 -3
  68. moai_adk/core/project/initializer.py +135 -23
  69. moai_adk/core/project/phase_executor.py +54 -81
  70. moai_adk/core/project/validator.py +6 -12
  71. moai_adk/core/quality/trust_checker.py +9 -27
  72. moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
  73. moai_adk/core/robust_json_parser.py +611 -0
  74. moai_adk/core/rollback_manager.py +73 -148
  75. moai_adk/core/session_manager.py +10 -26
  76. moai_adk/core/skill_loading_system.py +579 -0
  77. moai_adk/core/spec/confidence_scoring.py +31 -100
  78. moai_adk/core/spec/ears_template_engine.py +351 -286
  79. moai_adk/core/spec/quality_validator.py +35 -69
  80. moai_adk/core/spec_status_manager.py +64 -74
  81. moai_adk/core/template/backup.py +45 -20
  82. moai_adk/core/template/config.py +112 -39
  83. moai_adk/core/template/merger.py +11 -19
  84. moai_adk/core/template/processor.py +253 -149
  85. moai_adk/core/template_engine.py +73 -40
  86. moai_adk/core/template_variable_synchronizer.py +417 -0
  87. moai_adk/core/unified_permission_manager.py +745 -0
  88. moai_adk/core/user_behavior_analytics.py +851 -0
  89. moai_adk/core/version_sync.py +429 -0
  90. moai_adk/foundation/__init__.py +56 -0
  91. moai_adk/foundation/backend.py +1027 -0
  92. moai_adk/foundation/database.py +1115 -0
  93. moai_adk/foundation/devops.py +1585 -0
  94. moai_adk/foundation/ears.py +431 -0
  95. moai_adk/foundation/frontend.py +870 -0
  96. moai_adk/foundation/git/commit_templates.py +4 -12
  97. moai_adk/foundation/git.py +376 -0
  98. moai_adk/foundation/langs.py +484 -0
  99. moai_adk/foundation/ml_ops.py +1162 -0
  100. moai_adk/foundation/testing.py +1524 -0
  101. moai_adk/foundation/trust/trust_principles.py +23 -72
  102. moai_adk/foundation/trust/validation_checklist.py +57 -162
  103. moai_adk/project/__init__.py +0 -0
  104. moai_adk/project/configuration.py +1084 -0
  105. moai_adk/project/documentation.py +566 -0
  106. moai_adk/project/schema.py +447 -0
  107. moai_adk/statusline/alfred_detector.py +1 -3
  108. moai_adk/statusline/config.py +13 -4
  109. moai_adk/statusline/enhanced_output_style_detector.py +23 -15
  110. moai_adk/statusline/main.py +51 -15
  111. moai_adk/statusline/renderer.py +104 -48
  112. moai_adk/statusline/update_checker.py +3 -9
  113. moai_adk/statusline/version_reader.py +140 -46
  114. moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +549 -0
  115. moai_adk/templates/.claude/agents/moai/builder-agent.md +445 -0
  116. moai_adk/templates/.claude/agents/moai/builder-command.md +1132 -0
  117. moai_adk/templates/.claude/agents/moai/builder-skill.md +601 -0
  118. moai_adk/templates/.claude/agents/moai/expert-backend.md +831 -0
  119. moai_adk/templates/.claude/agents/moai/expert-database.md +774 -0
  120. moai_adk/templates/.claude/agents/moai/expert-debug.md +396 -0
  121. moai_adk/templates/.claude/agents/moai/expert-devops.md +711 -0
  122. moai_adk/templates/.claude/agents/moai/expert-frontend.md +666 -0
  123. moai_adk/templates/.claude/agents/moai/expert-security.md +474 -0
  124. moai_adk/templates/.claude/agents/moai/expert-uiux.md +1038 -0
  125. moai_adk/templates/.claude/agents/moai/manager-claude-code.md +429 -0
  126. moai_adk/templates/.claude/agents/moai/manager-docs.md +570 -0
  127. moai_adk/templates/.claude/agents/moai/manager-git.md +937 -0
  128. moai_adk/templates/.claude/agents/moai/manager-project.md +891 -0
  129. moai_adk/templates/.claude/agents/moai/manager-quality.md +598 -0
  130. moai_adk/templates/.claude/agents/moai/manager-spec.md +713 -0
  131. moai_adk/templates/.claude/agents/moai/manager-strategy.md +600 -0
  132. moai_adk/templates/.claude/agents/moai/manager-tdd.md +603 -0
  133. moai_adk/templates/.claude/agents/moai/mcp-context7.md +369 -0
  134. moai_adk/templates/.claude/agents/moai/mcp-figma.md +1567 -0
  135. moai_adk/templates/.claude/agents/moai/mcp-notion.md +749 -0
  136. moai_adk/templates/.claude/agents/moai/mcp-playwright.md +427 -0
  137. moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +994 -0
  138. moai_adk/templates/.claude/commands/moai/0-project.md +1143 -0
  139. moai_adk/templates/.claude/commands/moai/1-plan.md +1435 -0
  140. moai_adk/templates/.claude/commands/moai/2-run.md +883 -0
  141. moai_adk/templates/.claude/commands/moai/3-sync.md +993 -0
  142. moai_adk/templates/.claude/commands/moai/9-feedback.md +314 -0
  143. moai_adk/templates/.claude/hooks/__init__.py +8 -0
  144. moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
  145. moai_adk/templates/.claude/hooks/moai/lib/__init__.py +85 -0
  146. moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +244 -0
  147. moai_adk/templates/.claude/hooks/moai/lib/common.py +131 -0
  148. moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +446 -0
  149. moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
  150. moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
  151. moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +590 -0
  152. moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +317 -0
  153. moai_adk/templates/.claude/hooks/moai/lib/models.py +102 -0
  154. moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +28 -0
  155. moai_adk/templates/.claude/hooks/moai/lib/project.py +768 -0
  156. moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
  157. moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
  158. moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +530 -0
  159. moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +862 -0
  160. moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +921 -0
  161. moai_adk/templates/.claude/output-styles/moai/r2d2.md +380 -0
  162. moai_adk/templates/.claude/output-styles/moai/yoda.md +338 -0
  163. moai_adk/templates/.claude/settings.json +172 -0
  164. moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +247 -0
  165. moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +44 -0
  166. moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +130 -0
  167. moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
  168. moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +178 -0
  169. moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +147 -0
  170. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +319 -0
  171. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +320 -0
  172. moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
  173. moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
  174. moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
  175. moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
  176. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +496 -0
  177. moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +453 -0
  178. moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
  179. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
  180. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
  181. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/design-system-tokens.md +405 -0
  182. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
  183. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
  184. moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
  185. moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +491 -0
  186. moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +98 -0
  187. moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
  188. moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
  189. moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
  190. moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
  191. moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
  192. moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +201 -0
  193. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
  194. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +729 -0
  195. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +560 -0
  196. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
  197. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
  198. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
  199. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +113 -0
  200. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +238 -0
  201. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
  202. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
  203. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
  204. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
  205. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
  206. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
  207. moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +438 -0
  208. moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +515 -0
  209. moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
  210. moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +346 -0
  211. moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
  212. moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +757 -0
  213. moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
  214. moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
  215. moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
  216. moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +864 -0
  217. moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
  218. moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +981 -0
  219. moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +362 -0
  220. moai_adk/templates/.claude/skills/moai-foundation-quality/examples.md +1232 -0
  221. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/best-practices.md +261 -0
  222. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/integration-patterns.md +194 -0
  223. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/proactive-analysis.md +229 -0
  224. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/trust5-validation.md +169 -0
  225. moai_adk/templates/.claude/skills/moai-foundation-quality/reference.md +1266 -0
  226. moai_adk/templates/.claude/skills/moai-foundation-quality/scripts/quality-gate.sh +668 -0
  227. moai_adk/templates/.claude/skills/moai-foundation-quality/templates/github-actions-quality.yml +481 -0
  228. moai_adk/templates/.claude/skills/moai-foundation-quality/templates/quality-config.yaml +519 -0
  229. moai_adk/templates/.claude/skills/moai-integration-mcp/SKILL.md +352 -0
  230. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/README.md +52 -0
  231. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/error-handling.md +334 -0
  232. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/integration-patterns.md +310 -0
  233. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/security-authentication.md +256 -0
  234. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/server-architecture.md +253 -0
  235. moai_adk/templates/.claude/skills/moai-lang-unified/README.md +133 -0
  236. moai_adk/templates/.claude/skills/moai-lang-unified/SKILL.md +296 -0
  237. moai_adk/templates/.claude/skills/moai-lang-unified/examples.md +1269 -0
  238. moai_adk/templates/.claude/skills/moai-lang-unified/reference.md +331 -0
  239. moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +298 -0
  240. moai_adk/templates/.claude/skills/moai-library-mermaid/advanced-patterns.md +465 -0
  241. moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
  242. moai_adk/templates/.claude/skills/moai-library-mermaid/optimization.md +440 -0
  243. moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
  244. moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +316 -0
  245. moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +336 -0
  246. moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
  247. moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +17 -0
  248. moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +57 -0
  249. moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
  250. moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
  251. moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
  252. moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
  253. moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
  254. moai_adk/templates/.claude/skills/moai-library-nextra/optimization.md +303 -0
  255. moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +370 -0
  256. moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
  257. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
  258. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
  259. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
  260. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
  261. moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
  262. moai_adk/templates/.claude/skills/moai-platform-baas/README.md +186 -0
  263. moai_adk/templates/.claude/skills/moai-platform-baas/SKILL.md +290 -0
  264. moai_adk/templates/.claude/skills/moai-platform-baas/examples.md +1225 -0
  265. moai_adk/templates/.claude/skills/moai-platform-baas/reference.md +567 -0
  266. moai_adk/templates/.claude/skills/moai-platform-baas/scripts/provider-selector.py +323 -0
  267. moai_adk/templates/.claude/skills/moai-platform-baas/templates/stack-config.yaml +204 -0
  268. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +446 -0
  269. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/advanced-patterns.md +379 -0
  270. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/optimization.md +286 -0
  271. moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
  272. moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +387 -0
  273. moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +520 -0
  274. moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +574 -0
  275. moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +317 -0
  276. moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +663 -0
  277. moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +190 -0
  278. moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +135 -0
  279. moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +196 -0
  280. moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +17 -0
  281. moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +158 -0
  282. moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +340 -0
  283. moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +713 -0
  284. moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +538 -0
  285. moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +1336 -0
  286. moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +730 -0
  287. moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +608 -0
  288. moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +1005 -0
  289. moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
  290. moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1362 -0
  291. moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
  292. moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
  293. moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
  294. moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +71 -0
  295. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
  296. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
  297. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
  298. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
  299. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
  300. moai_adk/templates/.claude/skills/moai-workflow-project/test_integration_simple.py +436 -0
  301. moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +374 -0
  302. moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
  303. moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
  304. moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
  305. moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
  306. moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +453 -0
  307. moai_adk/templates/.claude/skills/moai-workflow-testing/advanced-patterns.md +576 -0
  308. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
  309. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
  310. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
  311. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
  312. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +220 -0
  313. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +845 -0
  314. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +1416 -0
  315. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +1234 -0
  316. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +1243 -0
  317. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +1260 -0
  318. moai_adk/templates/.claude/skills/moai-workflow-testing/optimization.md +505 -0
  319. moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
  320. moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
  321. moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
  322. moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
  323. moai_adk/templates/.claude/skills/moai-worktree/SKILL.md +410 -0
  324. moai_adk/templates/.claude/skills/moai-worktree/examples.md +606 -0
  325. moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +982 -0
  326. moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +778 -0
  327. moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +646 -0
  328. moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +782 -0
  329. moai_adk/templates/.claude/skills/moai-worktree/reference.md +357 -0
  330. moai_adk/templates/.git-hooks/pre-commit +103 -41
  331. moai_adk/templates/.git-hooks/pre-push +116 -21
  332. moai_adk/templates/.github/workflows/ci-universal.yml +513 -0
  333. moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
  334. moai_adk/templates/.gitignore +184 -44
  335. moai_adk/templates/.mcp.json +7 -9
  336. moai_adk/templates/.moai/cache/personalization.json +10 -0
  337. moai_adk/templates/.moai/config/config.yaml +344 -0
  338. moai_adk/templates/.moai/config/presets/manual.yaml +28 -0
  339. moai_adk/templates/.moai/config/presets/personal.yaml +30 -0
  340. moai_adk/templates/.moai/config/presets/team.yaml +33 -0
  341. moai_adk/templates/.moai/config/questions/_schema.yaml +79 -0
  342. moai_adk/templates/.moai/config/questions/tab1-user.yaml +108 -0
  343. moai_adk/templates/.moai/config/questions/tab2-project.yaml +122 -0
  344. moai_adk/templates/.moai/config/questions/tab3-git.yaml +542 -0
  345. moai_adk/templates/.moai/config/questions/tab4-quality.yaml +167 -0
  346. moai_adk/templates/.moai/config/questions/tab5-system.yaml +152 -0
  347. moai_adk/templates/.moai/config/sections/git-strategy.yaml +40 -0
  348. moai_adk/templates/.moai/config/sections/language.yaml +11 -0
  349. moai_adk/templates/.moai/config/sections/project.yaml +13 -0
  350. moai_adk/templates/.moai/config/sections/quality.yaml +15 -0
  351. moai_adk/templates/.moai/config/sections/system.yaml +14 -0
  352. moai_adk/templates/.moai/config/sections/user.yaml +5 -0
  353. moai_adk/templates/.moai/config/statusline-config.yaml +86 -0
  354. moai_adk/templates/.moai/scripts/setup-glm.py +136 -0
  355. moai_adk/templates/CLAUDE.md +382 -501
  356. moai_adk/utils/__init__.py +24 -1
  357. moai_adk/utils/banner.py +7 -10
  358. moai_adk/utils/common.py +16 -30
  359. moai_adk/utils/link_validator.py +4 -12
  360. moai_adk/utils/safe_file_reader.py +2 -6
  361. moai_adk/utils/timeout.py +160 -0
  362. moai_adk/utils/toon_utils.py +256 -0
  363. moai_adk/version.py +22 -0
  364. moai_adk-0.32.8.dist-info/METADATA +2478 -0
  365. moai_adk-0.32.8.dist-info/RECORD +396 -0
  366. {moai_adk-0.25.4.dist-info → moai_adk-0.32.8.dist-info}/WHEEL +1 -1
  367. {moai_adk-0.25.4.dist-info → moai_adk-0.32.8.dist-info}/entry_points.txt +1 -0
  368. moai_adk/cli/commands/backup.py +0 -82
  369. moai_adk/cli/commands/improve_user_experience.py +0 -348
  370. moai_adk/cli/commands/migrate.py +0 -158
  371. moai_adk/cli/commands/validate_links.py +0 -118
  372. moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -413
  373. moai_adk/templates/.github/workflows/moai-release-create.yml +0 -100
  374. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +0 -188
  375. moai_adk/utils/user_experience.py +0 -531
  376. moai_adk-0.25.4.dist-info/METADATA +0 -2279
  377. moai_adk-0.25.4.dist-info/RECORD +0 -112
  378. {moai_adk-0.25.4.dist-info → moai_adk-0.32.8.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1724 @@
1
+ """
2
+ Real-time Monitoring Dashboard
3
+
4
+ Phase 3: Enterprise-grade real-time monitoring dashboard with alerting,
5
+ analytics, and visualization capabilities for the hook system.
6
+
7
+ Key Features:
8
+ - Real-time performance monitoring and visualization
9
+ - Automated alerting with configurable thresholds
10
+ - Integration with external monitoring systems (Prometheus, Grafana, DataDog)
11
+ - Visual analytics for system health and performance
12
+ - REST API for monitoring data access
13
+ - WebSocket support for real-time updates
14
+ - Historical data analysis and trend prediction
15
+ - Multi-tenant dashboard support
16
+ - Custom alert rule engine
17
+ - Performance bottleneck detection
18
+ """
19
+
20
+ import asyncio
21
+ import logging
22
+ import statistics
23
+ import threading
24
+ import time
25
+ import uuid
26
+ from collections import defaultdict, deque
27
+ from dataclasses import dataclass, field
28
+ from datetime import datetime, timedelta
29
+ from enum import Enum
30
+ from typing import Any, Callable, Dict, List, Optional, Set, Union
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class MetricType(Enum):
36
+ """Types of metrics collected by the monitoring system"""
37
+
38
+ SYSTEM_PERFORMANCE = "system_performance"
39
+ HOOK_EXECUTION = "hook_execution"
40
+ ERROR_RATE = "error_rate"
41
+ RESPONSE_TIME = "response_time"
42
+ MEMORY_USAGE = "memory_usage"
43
+ CPU_USAGE = "cpu_usage"
44
+ NETWORK_IO = "network_io"
45
+ DISK_IO = "disk_io"
46
+ THROUGHPUT = "throughput"
47
+ AVAILABILITY = "availability"
48
+ CACHE_PERFORMANCE = "cache_performance"
49
+ DATABASE_PERFORMANCE = "database_performance"
50
+
51
+
52
+ class AlertSeverity(Enum):
53
+ """Alert severity levels"""
54
+
55
+ INFO = 1
56
+ WARNING = 2
57
+ ERROR = 3
58
+ CRITICAL = 4
59
+ EMERGENCY = 5
60
+
61
+
62
+ class DashboardType(Enum):
63
+ """Dashboard types"""
64
+
65
+ SYSTEM_OVERVIEW = "system_overview"
66
+ PERFORMANCE_ANALYTICS = "performance_analytics"
67
+ ERROR_MONITORING = "error_monitoring"
68
+ HOOK_ANALYSIS = "hook_analysis"
69
+ RESOURCE_USAGE = "resource_usage"
70
+ CUSTOM = "custom"
71
+
72
+
73
+ @dataclass
74
+ class MetricData:
75
+ """Single metric data point with enhanced metadata"""
76
+
77
+ timestamp: datetime
78
+ metric_type: MetricType
79
+ value: Union[int, float, str, bool]
80
+ tags: Dict[str, str] = field(default_factory=dict)
81
+ source: str = ""
82
+ metadata: Dict[str, Any] = field(default_factory=dict)
83
+ component: str = ""
84
+ environment: str = "production"
85
+ tenant_id: Optional[str] = None
86
+
87
+ def to_dict(self) -> Dict[str, Any]:
88
+ """Convert to dictionary for serialization"""
89
+ return {
90
+ "timestamp": self.timestamp.isoformat(),
91
+ "metric_type": self.metric_type.value,
92
+ "value": self.value,
93
+ "tags": self.tags,
94
+ "source": self.source,
95
+ "metadata": self.metadata,
96
+ "component": self.component,
97
+ "environment": self.environment,
98
+ "tenant_id": self.tenant_id,
99
+ }
100
+
101
+
102
+ @dataclass
103
+ class Alert:
104
+ """Enhanced alert definition with correlation and context"""
105
+
106
+ alert_id: str
107
+ severity: AlertSeverity
108
+ title: str
109
+ description: str
110
+ timestamp: datetime
111
+ metric_type: MetricType
112
+ threshold: float
113
+ current_value: float
114
+ source: str
115
+ component: str
116
+ tags: Dict[str, str] = field(default_factory=dict)
117
+ resolved: bool = False
118
+ resolved_at: Optional[datetime] = None
119
+ acknowledged: bool = False
120
+ acknowledged_at: Optional[datetime] = None
121
+ correlation_id: Optional[str] = None
122
+ context: Dict[str, Any] = field(default_factory=dict)
123
+ affected_services: List[str] = field(default_factory=list)
124
+ recovery_actions: List[str] = field(default_factory=list)
125
+ tenant_id: Optional[str] = None
126
+
127
+ def to_dict(self) -> Dict[str, Any]:
128
+ """Convert to dictionary for serialization"""
129
+ return {
130
+ "alert_id": self.alert_id,
131
+ "severity": self.severity.value,
132
+ "title": self.title,
133
+ "description": self.description,
134
+ "timestamp": self.timestamp.isoformat(),
135
+ "metric_type": self.metric_type.value,
136
+ "threshold": self.threshold,
137
+ "current_value": self.current_value,
138
+ "source": self.source,
139
+ "component": self.component,
140
+ "tags": self.tags,
141
+ "resolved": self.resolved,
142
+ "resolved_at": self.resolved_at.isoformat() if self.resolved_at else None,
143
+ "acknowledged": self.acknowledged,
144
+ "acknowledged_at": (self.acknowledged_at.isoformat() if self.acknowledged_at else None),
145
+ "correlation_id": self.correlation_id,
146
+ "context": self.context,
147
+ "affected_services": self.affected_services,
148
+ "recovery_actions": self.recovery_actions,
149
+ }
150
+
151
+
152
+ @dataclass
153
+ class DashboardWidget:
154
+ """Dashboard widget definition"""
155
+
156
+ widget_id: str
157
+ widget_type: str # "chart", "metric", "table", "alert", "heatmap"
158
+ title: str
159
+ position: Dict[str, Any] # {"x": 0, "y": 0, "width": 4, "height": 2}
160
+ config: Dict[str, Any] = field(default_factory=dict)
161
+ data_source: str = ""
162
+ refresh_interval_seconds: int = 30
163
+ metrics: List[str] = field(default_factory=list)
164
+ filters: Dict[str, Any] = field(default_factory=dict)
165
+
166
+
167
+ @dataclass
168
+ class Dashboard:
169
+ """Dashboard definition with widgets and layout"""
170
+
171
+ dashboard_id: str
172
+ name: str
173
+ description: str
174
+ dashboard_type: DashboardType
175
+ widgets: List[DashboardWidget] = field(default_factory=list)
176
+ layout: Dict[str, Any] = field(default_factory=dict)
177
+ filters: Dict[str, Any] = field(default_factory=dict)
178
+ owner: str = ""
179
+ tenant_id: Optional[str] = None
180
+ is_public: bool = False
181
+ created_at: datetime = field(default_factory=datetime.now)
182
+ updated_at: datetime = field(default_factory=datetime.now)
183
+
184
+ def to_dict(self) -> Dict[str, Any]:
185
+ """Convert to dictionary for serialization"""
186
+ return {
187
+ "dashboard_id": self.dashboard_id,
188
+ "name": self.name,
189
+ "description": self.description,
190
+ "dashboard_type": self.dashboard_type.value,
191
+ "widgets": [w.__dict__ for w in self.widgets],
192
+ "layout": self.layout,
193
+ "filters": self.filters,
194
+ "owner": self.owner,
195
+ "tenant_id": self.tenant_id,
196
+ "is_public": self.is_public,
197
+ "created_at": self.created_at.isoformat(),
198
+ "updated_at": self.updated_at.isoformat(),
199
+ }
200
+
201
+
202
+ class MetricsCollector:
203
+ """Advanced metrics collection with multi-tenant support"""
204
+
205
+ def __init__(self, buffer_size: int = 100000, retention_hours: int = 168): # 7 days
206
+ self.buffer_size = buffer_size
207
+ self.retention_hours = retention_hours
208
+ self.metrics_buffer: Dict[str, deque] = defaultdict(lambda: deque(maxlen=buffer_size))
209
+ self.aggregated_metrics: Dict[str, Dict[str, Any]] = defaultdict(dict)
210
+ self.tenant_metrics: Dict[str, Dict[str, deque]] = defaultdict(lambda: defaultdict(deque))
211
+ self._lock = threading.Lock()
212
+ self._last_cleanup = datetime.now()
213
+
214
+ def add_metric(self, metric: MetricData) -> None:
215
+ """Add a metric to the collection with tenant support"""
216
+ with self._lock:
217
+ # Global metrics buffer
218
+ key = f"{metric.metric_type.value}:{metric.component}"
219
+ self.metrics_buffer[key].append(metric)
220
+
221
+ # Tenant-specific metrics
222
+ if metric.tenant_id:
223
+ tenant_key = f"{metric.metric_type.value}:{metric.component}:{metric.tenant_id}"
224
+ self.tenant_metrics[metric.tenant_id][tenant_key].append(metric)
225
+
226
+ # Update aggregated statistics
227
+ self._update_aggregated_metrics(metric)
228
+ self._cleanup_old_metrics()
229
+
230
+ def _update_aggregated_metrics(self, metric: MetricData) -> None:
231
+ """Update aggregated statistics for a metric type"""
232
+ key = f"{metric.metric_type.value}:{metric.component}"
233
+
234
+ if key not in self.aggregated_metrics:
235
+ self.aggregated_metrics[key] = {
236
+ "count": 0,
237
+ "sum": 0,
238
+ "min": float("inf"),
239
+ "max": float("-inf"),
240
+ "values": [],
241
+ "last_updated": datetime.now(),
242
+ }
243
+
244
+ agg = self.aggregated_metrics[key]
245
+
246
+ if isinstance(metric.value, (int, float)):
247
+ agg["count"] += 1
248
+ agg["sum"] += metric.value
249
+ agg["min"] = min(agg["min"], metric.value)
250
+ agg["max"] = max(agg["max"], metric.value)
251
+ agg["values"].append(metric.value)
252
+ agg["last_updated"] = datetime.now()
253
+
254
+ # Keep only recent values for statistics
255
+ if len(agg["values"]) > 10000:
256
+ agg["values"] = agg["values"][-10000:]
257
+
258
+ def _cleanup_old_metrics(self) -> None:
259
+ """Remove metrics older than retention period"""
260
+ now = datetime.now()
261
+ if (now - self._last_cleanup).seconds < 300: # Cleanup every 5 minutes
262
+ return
263
+
264
+ cutoff_time = now - timedelta(hours=self.retention_hours)
265
+
266
+ # Cleanup global metrics
267
+ for key, buffer in self.metrics_buffer.items():
268
+ while buffer and buffer[0].timestamp < cutoff_time:
269
+ buffer.popleft()
270
+
271
+ # Cleanup tenant metrics
272
+ for tenant_id, tenant_buffers in self.tenant_metrics.items():
273
+ for key, buffer in tenant_buffers.items():
274
+ while buffer and buffer[0].timestamp < cutoff_time:
275
+ buffer.popleft()
276
+
277
+ self._last_cleanup = now
278
+
279
+ def get_metrics(
280
+ self,
281
+ metric_type: Optional[MetricType] = None,
282
+ component: Optional[str] = None,
283
+ tenant_id: Optional[str] = None,
284
+ start_time: Optional[datetime] = None,
285
+ end_time: Optional[datetime] = None,
286
+ limit: Optional[int] = None,
287
+ tags: Optional[Dict[str, str]] = None,
288
+ ) -> List[MetricData]:
289
+ """Get metrics with comprehensive filtering"""
290
+ with self._lock:
291
+ # Choose the right metrics source
292
+ source_metrics: List[MetricData] = []
293
+ if tenant_id:
294
+ for buffer in self.tenant_metrics.get(tenant_id, {}).values():
295
+ source_metrics.extend(buffer)
296
+ else:
297
+ for buffer in self.metrics_buffer.values():
298
+ source_metrics.extend(buffer)
299
+
300
+ # Apply filters
301
+ metrics = source_metrics
302
+
303
+ # Filter by metric type
304
+ if metric_type:
305
+ metrics = [m for m in metrics if m.metric_type == metric_type]
306
+
307
+ # Filter by component
308
+ if component:
309
+ metrics = [m for m in metrics if m.component == component]
310
+
311
+ # Filter by time range
312
+ if start_time:
313
+ metrics = [m for m in metrics if m.timestamp >= start_time]
314
+ if end_time:
315
+ metrics = [m for m in metrics if m.timestamp <= end_time]
316
+
317
+ # Filter by tags
318
+ if tags:
319
+ metrics = [m for m in metrics if all(m.tags.get(k) == v for k, v in tags.items())]
320
+
321
+ # Sort by timestamp (newest first)
322
+ metrics.sort(key=lambda m: m.timestamp, reverse=True)
323
+
324
+ # Apply limit
325
+ if limit:
326
+ metrics = metrics[:limit]
327
+
328
+ return metrics
329
+
330
+ def get_statistics(
331
+ self,
332
+ metric_type: MetricType,
333
+ component: Optional[str] = None,
334
+ minutes: int = 60,
335
+ tenant_id: Optional[str] = None,
336
+ ) -> Dict[str, Any]:
337
+ """Get statistical summary for a metric type"""
338
+ with self._lock:
339
+ key = f"{metric_type.value}:{component}" if component else metric_type.value
340
+
341
+ # Choose the right aggregated metrics
342
+ if tenant_id:
343
+ agg: Dict[str, Any] = {}
344
+ # Calculate tenant-specific stats
345
+ tenant_metrics = self.get_metrics(metric_type, component, tenant_id)
346
+ if tenant_metrics:
347
+ values: List[float] = [float(m.value) for m in tenant_metrics if isinstance(m.value, (int, float))]
348
+ if values:
349
+ agg = {
350
+ "count": len(values),
351
+ "average": statistics.mean(values),
352
+ "median": statistics.median(values),
353
+ "min": min(values),
354
+ "max": max(values),
355
+ "std_dev": (statistics.stdev(values) if len(values) > 1 else 0),
356
+ "p95": self._percentile(values, 95),
357
+ "p99": self._percentile(values, 99),
358
+ }
359
+ else:
360
+ agg = self.aggregated_metrics.get(key, {})
361
+
362
+ if not agg or agg.get("count", 0) == 0:
363
+ return {
364
+ "count": 0,
365
+ "average": None,
366
+ "min": None,
367
+ "max": None,
368
+ "median": None,
369
+ "std_dev": None,
370
+ }
371
+
372
+ agg_values_raw = agg.get("values", [])
373
+ values_list: List[float] = agg_values_raw if isinstance(agg_values_raw, list) else []
374
+ if not values_list:
375
+ return {
376
+ "count": agg.get("count", 0),
377
+ "average": agg.get("sum", 0) / max(agg.get("count", 1), 1),
378
+ "min": agg.get("min"),
379
+ "max": agg.get("max"),
380
+ "median": None,
381
+ "std_dev": 0,
382
+ "p95": agg.get("max"),
383
+ "p99": agg.get("max"),
384
+ }
385
+
386
+ try:
387
+ last_updated_raw = agg.get("last_updated", datetime.now())
388
+ if isinstance(last_updated_raw, datetime):
389
+ last_updated_str = last_updated_raw.isoformat()
390
+ else:
391
+ last_updated_str = datetime.now().isoformat()
392
+ return {
393
+ "count": len(values_list),
394
+ "average": statistics.mean(values_list),
395
+ "median": statistics.median(values_list),
396
+ "min": min(values_list),
397
+ "max": max(values_list),
398
+ "std_dev": (statistics.stdev(values_list) if len(values_list) > 1 else 0),
399
+ "p95": self._percentile(values_list, 95),
400
+ "p99": self._percentile(values_list, 99),
401
+ "last_updated": last_updated_str,
402
+ }
403
+ except (statistics.StatisticsError, IndexError):
404
+ return {
405
+ "count": len(values_list),
406
+ "average": statistics.mean(values_list),
407
+ "median": statistics.median(values_list),
408
+ "min": min(values_list),
409
+ "max": max(values_list),
410
+ "std_dev": 0,
411
+ "p95": max(values_list),
412
+ "p99": max(values_list),
413
+ "last_updated": datetime.now().isoformat(),
414
+ }
415
+
416
+ def _percentile(self, values: List[float], percentile: int) -> float:
417
+ """Calculate percentile value"""
418
+ if not values:
419
+ return 0.0
420
+ sorted_values = sorted(values)
421
+ index = (percentile / 100) * (len(sorted_values) - 1)
422
+ if index.is_integer():
423
+ return sorted_values[int(index)]
424
+ else:
425
+ lower = sorted_values[int(index)]
426
+ upper = sorted_values[int(index) + 1]
427
+ return lower + (upper - lower) * (index - int(index))
428
+
429
+
430
+ class AlertManager:
431
+ """Advanced alert management with correlation and multi-tenant support"""
432
+
433
+ def __init__(self, metrics_collector: MetricsCollector):
434
+ self.metrics_collector = metrics_collector
435
+ self.alert_rules: List[Dict[str, Any]] = []
436
+ self.active_alerts: Dict[str, Alert] = {}
437
+ self.alert_history: List[Alert] = []
438
+ self.alert_callbacks: List[Callable[[Alert], None]] = []
439
+ self.tenant_alerts: Dict[str, Dict[str, Alert]] = defaultdict(dict)
440
+ self._lock = threading.Lock()
441
+
442
+ def add_alert_rule(
443
+ self,
444
+ name: str,
445
+ metric_type: MetricType,
446
+ threshold: float,
447
+ operator: str = "gt", # gt, lt, eq, ne, gte, lte
448
+ severity: AlertSeverity = AlertSeverity.WARNING,
449
+ window_minutes: int = 5,
450
+ consecutive_violations: int = 1,
451
+ tags: Optional[Dict[str, str]] = None,
452
+ description: Optional[str] = None,
453
+ tenant_id: Optional[str] = None,
454
+ component: Optional[str] = None,
455
+ cooldown_minutes: int = 15,
456
+ ) -> None:
457
+ """Add an alert rule with enhanced configuration"""
458
+ rule = {
459
+ "name": name,
460
+ "metric_type": metric_type,
461
+ "threshold": threshold,
462
+ "operator": operator,
463
+ "severity": severity,
464
+ "window_minutes": window_minutes,
465
+ "consecutive_violations": consecutive_violations,
466
+ "tags": tags or {},
467
+ "description": description or f"Alert when {metric_type.value} {operator} {threshold}",
468
+ "violation_count": 0,
469
+ "last_check": None,
470
+ "last_triggered": None,
471
+ "enabled": True,
472
+ "tenant_id": tenant_id,
473
+ "component": component,
474
+ "cooldown_minutes": cooldown_minutes,
475
+ }
476
+
477
+ with self._lock:
478
+ self.alert_rules.append(rule)
479
+
480
+ def check_alerts(self) -> List[Alert]:
481
+ """Check all alert rules and generate alerts for violations"""
482
+ triggered_alerts = []
483
+
484
+ with self._lock:
485
+ for rule in self.alert_rules:
486
+ if not rule["enabled"]:
487
+ continue
488
+
489
+ # Check cooldown period
490
+ if rule["last_triggered"]:
491
+ time_since_last = datetime.now() - rule["last_triggered"]
492
+ if time_since_last.total_seconds() < rule["cooldown_minutes"] * 60:
493
+ continue
494
+
495
+ # Get recent metrics for this rule
496
+ recent_metrics = self.metrics_collector.get_metrics(
497
+ metric_type=rule["metric_type"],
498
+ component=rule.get("component"),
499
+ tenant_id=rule.get("tenant_id"),
500
+ start_time=datetime.now() - timedelta(minutes=rule["window_minutes"]),
501
+ tags=rule.get("tags"),
502
+ )
503
+
504
+ if not recent_metrics:
505
+ continue
506
+
507
+ # Check for violations
508
+ violations = 0
509
+ latest_value = None
510
+
511
+ for metric in recent_metrics:
512
+ if isinstance(metric.value, (int, float)):
513
+ if self._evaluate_condition(metric.value, rule["threshold"], rule["operator"]):
514
+ violations += 1
515
+ latest_value = metric.value
516
+
517
+ # Trigger alert if threshold exceeded
518
+ if violations >= rule["consecutive_violations"]:
519
+ alert_id = f"{rule['name']}_{int(time.time())}"
520
+
521
+ alert = Alert(
522
+ alert_id=alert_id,
523
+ severity=rule["severity"],
524
+ title=f"{rule['name']} Alert Triggered",
525
+ description=rule["description"],
526
+ timestamp=datetime.now(),
527
+ metric_type=rule["metric_type"],
528
+ threshold=rule["threshold"],
529
+ current_value=latest_value or 0,
530
+ source="monitoring_system",
531
+ component=rule.get("component", "unknown"),
532
+ tags=rule.get("tags", {}),
533
+ tenant_id=rule.get("tenant_id"),
534
+ )
535
+
536
+ # Store alert
537
+ self.active_alerts[alert_id] = alert
538
+ self.alert_history.append(alert)
539
+
540
+ if alert.tenant_id:
541
+ self.tenant_alerts[alert.tenant_id][alert_id] = alert
542
+
543
+ triggered_alerts.append(alert)
544
+
545
+ # Update rule state
546
+ rule["violation_count"] = violations
547
+ rule["last_check"] = datetime.now()
548
+ rule["last_triggered"] = datetime.now()
549
+
550
+ # Trigger callbacks
551
+ for callback in self.alert_callbacks:
552
+ try:
553
+ callback(alert)
554
+ except Exception as e:
555
+ logger.error(f"Error in alert callback: {e}")
556
+
557
+ rule["violation_count"] = violations
558
+ rule["last_check"] = datetime.now()
559
+
560
+ # Check for resolved alerts
561
+ self._check_resolved_alerts()
562
+
563
+ return triggered_alerts
564
+
565
+ def _check_resolved_alerts(self) -> None:
566
+ """Check if any active alerts have been resolved"""
567
+ for alert_id, alert in list(self.active_alerts.items()):
568
+ # Check if alert condition is no longer met
569
+ rule = next((r for r in self.alert_rules if r["name"] in alert_id), None)
570
+ if rule:
571
+ recent_metrics = self.metrics_collector.get_metrics(
572
+ metric_type=alert.metric_type,
573
+ component=alert.component,
574
+ tenant_id=alert.tenant_id,
575
+ start_time=datetime.now() - timedelta(minutes=1), # Check last minute
576
+ )
577
+
578
+ if recent_metrics:
579
+ latest_value = None
580
+ for metric in recent_metrics:
581
+ if isinstance(metric.value, (int, float)):
582
+ if not self._evaluate_condition(metric.value, alert.threshold, rule["operator"]):
583
+ latest_value = metric.value
584
+ break
585
+
586
+ if latest_value is not None:
587
+ # Alert is resolved
588
+ alert.resolved = True
589
+ alert.resolved_at = datetime.now()
590
+ self.alert_history.append(alert)
591
+ del self.active_alerts[alert_id]
592
+
593
+ if alert.tenant_id and alert_id in self.tenant_alerts.get(alert.tenant_id, {}):
594
+ del self.tenant_alerts[alert.tenant_id][alert_id]
595
+
596
+ def _evaluate_condition(self, value: float, threshold: float, operator: str) -> bool:
597
+ """Evaluate alert condition"""
598
+ if operator == "gt":
599
+ return value > threshold
600
+ elif operator == "lt":
601
+ return value < threshold
602
+ elif operator == "eq":
603
+ return value == threshold
604
+ elif operator == "ne":
605
+ return value != threshold
606
+ elif operator == "gte":
607
+ return value >= threshold
608
+ elif operator == "lte":
609
+ return value <= threshold
610
+ else:
611
+ return False
612
+
613
+ def add_alert_callback(self, callback: Callable[[Alert], None]) -> None:
614
+ """Add a callback function to be triggered when alerts fire"""
615
+ self.alert_callbacks.append(callback)
616
+
617
+ def acknowledge_alert(self, alert_id: str, tenant_id: Optional[str] = None) -> bool:
618
+ """Acknowledge an alert"""
619
+ with self._lock:
620
+ if tenant_id:
621
+ alerts = self.tenant_alerts.get(tenant_id, {})
622
+ if alert_id in alerts:
623
+ alerts[alert_id].acknowledged = True
624
+ alerts[alert_id].acknowledged_at = datetime.now()
625
+ return True
626
+ else:
627
+ if alert_id in self.active_alerts:
628
+ self.active_alerts[alert_id].acknowledged = True
629
+ self.active_alerts[alert_id].acknowledged_at = datetime.now()
630
+ return True
631
+ return False
632
+
633
+ def get_active_alerts(
634
+ self,
635
+ severity: Optional[AlertSeverity] = None,
636
+ tenant_id: Optional[str] = None,
637
+ component: Optional[str] = None,
638
+ ) -> List[Alert]:
639
+ """Get currently active alerts with filtering"""
640
+ with self._lock:
641
+ if tenant_id:
642
+ alerts = list(self.tenant_alerts.get(tenant_id, {}).values())
643
+ else:
644
+ alerts = list(self.active_alerts.values())
645
+
646
+ # Apply filters
647
+ if severity:
648
+ alerts = [a for a in alerts if a.severity == severity]
649
+ if component:
650
+ alerts = [a for a in alerts if a.component == component]
651
+
652
+ return sorted(alerts, key=lambda a: (a.severity.value, a.timestamp), reverse=True)
653
+
654
+ def get_alert_history(
655
+ self,
656
+ hours: int = 24,
657
+ tenant_id: Optional[str] = None,
658
+ ) -> List[Alert]:
659
+ """Get alert history"""
660
+ cutoff_time = datetime.now() - timedelta(hours=hours)
661
+
662
+ if tenant_id:
663
+ tenant_history = []
664
+ for alert in self.alert_history:
665
+ if alert.tenant_id == tenant_id and alert.timestamp >= cutoff_time:
666
+ tenant_history.append(alert)
667
+ return tenant_history
668
+ else:
669
+ return [a for a in self.alert_history if a.timestamp >= cutoff_time]
670
+
671
+ def get_alert_statistics(
672
+ self,
673
+ hours: int = 24,
674
+ tenant_id: Optional[str] = None,
675
+ ) -> Dict[str, Any]:
676
+ """Get alert statistics"""
677
+ alerts = self.get_alert_history(hours, tenant_id)
678
+
679
+ # Count by severity
680
+ by_severity: Dict[str, int] = defaultdict(int)
681
+ by_component: Dict[str, int] = defaultdict(int)
682
+ by_hour: Dict[str, int] = defaultdict(int)
683
+
684
+ for alert in alerts:
685
+ by_severity[str(alert.severity.value)] += 1
686
+ by_component[alert.component] += 1
687
+ hour_key = alert.timestamp.strftime("%Y-%m-%d %H:00")
688
+ by_hour[hour_key] += 1
689
+
690
+ return {
691
+ "total_alerts": len(alerts),
692
+ "by_severity": dict(by_severity),
693
+ "by_component": dict(by_component),
694
+ "by_hour": dict(by_hour),
695
+ "resolved_count": sum(1 for a in alerts if a.resolved),
696
+ "acknowledged_count": sum(1 for a in alerts if a.acknowledged),
697
+ "resolution_rate": sum(1 for a in alerts if a.resolved) / max(len(alerts), 1),
698
+ "period_hours": hours,
699
+ }
700
+
701
+
702
+ class DashboardManager:
703
+ """Dashboard management with multi-tenant support"""
704
+
705
+ def __init__(self):
706
+ self.dashboards: Dict[str, Dashboard] = {}
707
+ self.tenant_dashboards: Dict[str, Dict[str, Dashboard]] = defaultdict(dict)
708
+ self.default_dashboards: Dict[str, Dashboard] = {}
709
+ self._lock = threading.Lock()
710
+
711
+ # Create default dashboards
712
+ self._create_default_dashboards()
713
+
714
+ def create_dashboard(
715
+ self,
716
+ name: str,
717
+ description: str,
718
+ dashboard_type: DashboardType,
719
+ widgets: List[DashboardWidget],
720
+ owner: str = "",
721
+ tenant_id: Optional[str] = None,
722
+ is_public: bool = False,
723
+ layout: Optional[Dict[str, Any]] = None,
724
+ ) -> str:
725
+ """Create a new dashboard"""
726
+ dashboard_id = str(uuid.uuid4())
727
+
728
+ dashboard = Dashboard(
729
+ dashboard_id=dashboard_id,
730
+ name=name,
731
+ description=description,
732
+ dashboard_type=dashboard_type,
733
+ widgets=widgets,
734
+ layout=layout or {"grid": {"cols": 12, "rows": 8}},
735
+ owner=owner,
736
+ tenant_id=tenant_id,
737
+ is_public=is_public,
738
+ )
739
+
740
+ with self._lock:
741
+ if tenant_id:
742
+ self.tenant_dashboards[tenant_id][dashboard_id] = dashboard
743
+ else:
744
+ self.dashboards[dashboard_id] = dashboard
745
+
746
+ logger.info(f"Created dashboard: {name} ({dashboard_id})")
747
+ return dashboard_id
748
+
749
+ def get_dashboard(self, dashboard_id: str, tenant_id: Optional[str] = None) -> Optional[Dashboard]:
750
+ """Get a dashboard by ID"""
751
+ with self._lock:
752
+ if tenant_id:
753
+ return self.tenant_dashboards.get(tenant_id, {}).get(dashboard_id)
754
+ else:
755
+ return self.dashboards.get(dashboard_id) or self.default_dashboards.get(dashboard_id)
756
+
757
+ def list_dashboards(
758
+ self,
759
+ tenant_id: Optional[str] = None,
760
+ dashboard_type: Optional[DashboardType] = None,
761
+ owner: Optional[str] = None,
762
+ is_public: Optional[bool] = None,
763
+ ) -> List[Dashboard]:
764
+ """List dashboards with filtering"""
765
+ with self._lock:
766
+ dashboards: List[Dashboard] = []
767
+
768
+ # Collect dashboards
769
+ if tenant_id:
770
+ dashboards.extend(self.tenant_dashboards.get(tenant_id, {}).values())
771
+ else:
772
+ dashboards.extend(self.dashboards.values())
773
+
774
+ # Add public dashboards
775
+ dashboards.extend([d for d in self.dashboards.values() if d.is_public])
776
+
777
+ # Apply filters
778
+ if dashboard_type:
779
+ dashboards = [d for d in dashboards if d.dashboard_type == dashboard_type]
780
+ if owner:
781
+ dashboards = [d for d in dashboards if d.owner == owner]
782
+ if is_public is not None:
783
+ dashboards = [d for d in dashboards if d.is_public == is_public]
784
+
785
+ return dashboards
786
+
787
+ def update_dashboard(
788
+ self,
789
+ dashboard_id: str,
790
+ updates: Dict[str, Any],
791
+ tenant_id: Optional[str] = None,
792
+ ) -> bool:
793
+ """Update a dashboard"""
794
+ with self._lock:
795
+ if tenant_id:
796
+ dashboard = self.tenant_dashboards.get(tenant_id, {}).get(dashboard_id)
797
+ else:
798
+ dashboard = self.dashboards.get(dashboard_id)
799
+
800
+ if not dashboard:
801
+ return False
802
+
803
+ # Update fields
804
+ for key, value in updates.items():
805
+ if hasattr(dashboard, key):
806
+ setattr(dashboard, key, value)
807
+ elif key in dashboard.__dict__:
808
+ dashboard.__dict__[key] = value
809
+
810
+ dashboard.updated_at = datetime.now()
811
+ return True
812
+
813
+ def delete_dashboard(self, dashboard_id: str, tenant_id: Optional[str] = None) -> bool:
814
+ """Delete a dashboard"""
815
+ with self._lock:
816
+ if tenant_id:
817
+ if dashboard_id in self.tenant_dashboards.get(tenant_id, {}):
818
+ del self.tenant_dashboards[tenant_id][dashboard_id]
819
+ return True
820
+ else:
821
+ if dashboard_id in self.dashboards:
822
+ del self.dashboards[dashboard_id]
823
+ return True
824
+ if dashboard_id in self.default_dashboards:
825
+ # Don't delete default dashboards
826
+ return False
827
+ return False
828
+
829
+ def _create_default_dashboards(self):
830
+ """Create default dashboards"""
831
+ # System Overview Dashboard
832
+ system_overview_widgets = [
833
+ DashboardWidget(
834
+ widget_id="sys_health",
835
+ widget_type="metric",
836
+ title="System Health",
837
+ position={"x": 0, "y": 0, "width": 4, "height": 2},
838
+ config={"metric": "health_score", "format": "percentage"},
839
+ ),
840
+ DashboardWidget(
841
+ widget_id="active_alerts",
842
+ widget_type="metric",
843
+ title="Active Alerts",
844
+ position={"x": 4, "y": 0, "width": 4, "height": 2},
845
+ config={"metric": "active_alerts", "format": "number"},
846
+ ),
847
+ DashboardWidget(
848
+ widget_id="uptime",
849
+ widget_type="metric",
850
+ title="System Uptime",
851
+ position={"x": 8, "y": 0, "width": 4, "height": 2},
852
+ config={"metric": "uptime", "format": "duration"},
853
+ ),
854
+ DashboardWidget(
855
+ widget_id="cpu_chart",
856
+ widget_type="chart",
857
+ title="CPU Usage",
858
+ position={"x": 0, "y": 2, "width": 6, "height": 3},
859
+ config={"chart_type": "line", "time_range": "1h"},
860
+ metrics=["cpu_usage"],
861
+ ),
862
+ DashboardWidget(
863
+ widget_id="memory_chart",
864
+ widget_type="chart",
865
+ title="Memory Usage",
866
+ position={"x": 6, "y": 2, "width": 6, "height": 3},
867
+ config={"chart_type": "line", "time_range": "1h"},
868
+ metrics=["memory_usage"],
869
+ ),
870
+ DashboardWidget(
871
+ widget_id="alert_table",
872
+ widget_type="table",
873
+ title="Recent Alerts",
874
+ position={"x": 0, "y": 5, "width": 12, "height": 3},
875
+ config={"limit": 10, "sort": "timestamp"},
876
+ ),
877
+ ]
878
+
879
+ system_dashboard = Dashboard(
880
+ dashboard_id="system_overview",
881
+ name="System Overview",
882
+ description="Overview of system health and performance",
883
+ dashboard_type=DashboardType.SYSTEM_OVERVIEW,
884
+ widgets=system_overview_widgets,
885
+ owner="system",
886
+ is_public=True,
887
+ )
888
+
889
+ self.default_dashboards["system_overview"] = system_dashboard
890
+
891
+ # Hook Analysis Dashboard
892
+ hook_analysis_widgets = [
893
+ DashboardWidget(
894
+ widget_id="hook_execution_chart",
895
+ widget_type="chart",
896
+ title="Hook Execution Rate",
897
+ position={"x": 0, "y": 0, "width": 6, "height": 3},
898
+ config={"chart_type": "line", "time_range": "24h"},
899
+ metrics=["hook_execution_rate"],
900
+ ),
901
+ DashboardWidget(
902
+ widget_id="hook_success_rate",
903
+ widget_type="metric",
904
+ title="Hook Success Rate",
905
+ position={"x": 6, "y": 0, "width": 6, "height": 3},
906
+ config={"metric": "hook_success_rate", "format": "percentage"},
907
+ ),
908
+ DashboardWidget(
909
+ widget_id="slow_hooks",
910
+ widget_type="table",
911
+ title="Slowest Hooks",
912
+ position={"x": 0, "y": 3, "width": 12, "height": 4},
913
+ config={"limit": 15, "sort": "execution_time", "order": "desc"},
914
+ ),
915
+ DashboardWidget(
916
+ widget_id="error_by_hook",
917
+ widget_type="chart",
918
+ title="Error Rate by Hook",
919
+ position={"x": 0, "y": 7, "width": 12, "height": 3},
920
+ config={"chart_type": "bar", "time_range": "24h"},
921
+ ),
922
+ ]
923
+
924
+ hook_dashboard = Dashboard(
925
+ dashboard_id="hook_analysis",
926
+ name="Hook Analysis",
927
+ description="Detailed analysis of hook execution performance",
928
+ dashboard_type=DashboardType.HOOK_ANALYSIS,
929
+ widgets=hook_analysis_widgets,
930
+ owner="system",
931
+ is_public=True,
932
+ )
933
+
934
+ self.default_dashboards["hook_analysis"] = hook_dashboard
935
+
936
+ logger.info("Created default dashboards")
937
+
938
+
939
+ class RealtimeMonitoringDashboard:
940
+ """
941
+ Real-time Monitoring Dashboard System
942
+
943
+ Enterprise-grade real-time monitoring dashboard with alerting,
944
+ analytics, and visualization capabilities.
945
+ """
946
+
947
+ def __init__(
948
+ self,
949
+ metrics_buffer_size: int = 100000,
950
+ retention_hours: int = 168, # 7 days
951
+ alert_check_interval: int = 30, # seconds
952
+ enable_websocket: bool = True,
953
+ enable_external_integration: bool = True,
954
+ ):
955
+ """Initialize Real-time Monitoring Dashboard
956
+
957
+ Args:
958
+ metrics_buffer_size: Size of metrics buffer
959
+ retention_hours: Hours to retain metrics
960
+ alert_check_interval: Interval between alert checks
961
+ enable_websocket: Enable WebSocket support for real-time updates
962
+ enable_external_integration: Enable external monitoring system integration
963
+ """
964
+ self.metrics_buffer_size = metrics_buffer_size
965
+ self.retention_hours = retention_hours
966
+ self.alert_check_interval = alert_check_interval
967
+ self.enable_websocket = enable_websocket
968
+ self.enable_external_integration = enable_external_integration
969
+
970
+ # Initialize components
971
+ self.metrics_collector = MetricsCollector(metrics_buffer_size, retention_hours)
972
+ self.alert_manager = AlertManager(self.metrics_collector)
973
+ self.dashboard_manager = DashboardManager()
974
+
975
+ # System state
976
+ self._running = False
977
+ self._startup_time = datetime.now()
978
+
979
+ # WebSocket connections
980
+ self.websocket_connections: Set[Any] = set()
981
+ self.websocket_lock = threading.Lock()
982
+
983
+ # Background tasks
984
+ self._monitoring_thread: Optional[threading.Thread] = None
985
+ self._alert_thread: Optional[threading.Thread] = None
986
+
987
+ # External integrations
988
+ self.external_integrations: Dict[str, Any] = {}
989
+
990
+ # Setup default alerts
991
+ self._setup_default_alerts()
992
+
993
+ logger.info("Real-time Monitoring Dashboard initialized")
994
+
995
+ def _setup_default_alerts(self):
996
+ """Setup default alert rules"""
997
+ # CPU usage alert
998
+ self.alert_manager.add_alert_rule(
999
+ name="High CPU Usage",
1000
+ metric_type=MetricType.CPU_USAGE,
1001
+ threshold=80.0,
1002
+ operator="gt",
1003
+ severity=AlertSeverity.ERROR,
1004
+ window_minutes=5,
1005
+ consecutive_violations=2,
1006
+ tags={"component": "system"},
1007
+ description="CPU usage exceeds 80% for 5 minutes",
1008
+ )
1009
+
1010
+ # Memory usage alert
1011
+ self.alert_manager.add_alert_rule(
1012
+ name="High Memory Usage",
1013
+ metric_type=MetricType.MEMORY_USAGE,
1014
+ threshold=85.0,
1015
+ operator="gt",
1016
+ severity=AlertSeverity.ERROR,
1017
+ window_minutes=5,
1018
+ consecutive_violations=2,
1019
+ tags={"component": "system"},
1020
+ description="Memory usage exceeds 85% for 5 minutes",
1021
+ )
1022
+
1023
+ # Error rate alert
1024
+ self.alert_manager.add_alert_rule(
1025
+ name="High Error Rate",
1026
+ metric_type=MetricType.ERROR_RATE,
1027
+ threshold=5.0,
1028
+ operator="gt",
1029
+ severity=AlertSeverity.CRITICAL,
1030
+ window_minutes=2,
1031
+ consecutive_violations=1,
1032
+ tags={"component": "system"},
1033
+ description="Error rate exceeds 5% in 2 minutes",
1034
+ )
1035
+
1036
+ # Hook execution time alert
1037
+ self.alert_manager.add_alert_rule(
1038
+ name="Slow Hook Execution",
1039
+ metric_type=MetricType.RESPONSE_TIME,
1040
+ threshold=5000.0, # 5 seconds
1041
+ operator="gt",
1042
+ severity=AlertSeverity.WARNING,
1043
+ window_minutes=10,
1044
+ consecutive_violations=3,
1045
+ tags={"component": "hooks"},
1046
+ description="Hook execution time exceeds 5 seconds",
1047
+ )
1048
+
1049
+ logger.info("Default alert rules configured")
1050
+
1051
+ async def start(self) -> None:
1052
+ """Start the monitoring dashboard system"""
1053
+ if self._running:
1054
+ return
1055
+
1056
+ logger.info("Starting Real-time Monitoring Dashboard...")
1057
+
1058
+ try:
1059
+ # Start background monitoring
1060
+ self._start_background_monitoring()
1061
+
1062
+ # Start alert checking
1063
+ self._start_alert_monitoring()
1064
+
1065
+ # Start WebSocket server if enabled
1066
+ if self.enable_websocket:
1067
+ await self._start_websocket_server()
1068
+
1069
+ # Initialize external integrations
1070
+ if self.enable_external_integration:
1071
+ await self._initialize_external_integrations()
1072
+
1073
+ self._running = True
1074
+ logger.info("Real-time Monitoring Dashboard started successfully")
1075
+
1076
+ except Exception as e:
1077
+ logger.error(f"Error starting monitoring dashboard: {e}")
1078
+ raise
1079
+
1080
+ def stop(self) -> None:
1081
+ """Stop the monitoring dashboard system"""
1082
+ if not self._running:
1083
+ return
1084
+
1085
+ logger.info("Stopping Real-time Monitoring Dashboard...")
1086
+
1087
+ try:
1088
+ # Stop background threads
1089
+ self._running = False
1090
+ if self._monitoring_thread:
1091
+ self._monitoring_thread.join(timeout=5)
1092
+ if self._alert_thread:
1093
+ self._alert_thread.join(timeout=5)
1094
+
1095
+ # Close WebSocket connections
1096
+ with self.websocket_lock:
1097
+ for conn in self.websocket_connections:
1098
+ try:
1099
+ # Close connection
1100
+ pass # Implementation depends on WebSocket library
1101
+ except Exception:
1102
+ pass
1103
+ self.websocket_connections.clear()
1104
+
1105
+ logger.info("Real-time Monitoring Dashboard stopped")
1106
+
1107
+ except Exception as e:
1108
+ logger.error(f"Error stopping monitoring dashboard: {e}")
1109
+
1110
+ def _start_background_monitoring(self) -> None:
1111
+ """Start background metrics collection"""
1112
+
1113
+ def monitor_loop():
1114
+ while self._running:
1115
+ try:
1116
+ self._collect_system_metrics()
1117
+ time.sleep(30) # Collect metrics every 30 seconds
1118
+ except Exception as e:
1119
+ logger.error(f"Error in monitoring loop: {e}")
1120
+ time.sleep(30)
1121
+
1122
+ self._monitoring_thread = threading.Thread(target=monitor_loop, daemon=True)
1123
+ self._monitoring_thread.start()
1124
+
1125
+ def _start_alert_monitoring(self) -> None:
1126
+ """Start alert checking"""
1127
+
1128
+ def alert_loop():
1129
+ while self._running:
1130
+ try:
1131
+ alerts = self.alert_manager.check_alerts()
1132
+ if alerts:
1133
+ for alert in alerts:
1134
+ logger.warning(f"Alert triggered: {alert.title} - {alert.current_value}")
1135
+ # Broadcast to WebSocket clients
1136
+ self._broadcast_alert(alert)
1137
+
1138
+ time.sleep(self.alert_check_interval)
1139
+ except Exception as e:
1140
+ logger.error(f"Error in alert loop: {e}")
1141
+ time.sleep(self.alert_check_interval)
1142
+
1143
+ self._alert_thread = threading.Thread(target=alert_loop, daemon=True)
1144
+ self._alert_thread.start()
1145
+
1146
+ async def _start_websocket_server(self) -> None:
1147
+ """Start WebSocket server for real-time updates"""
1148
+ # Implementation depends on WebSocket library (websockets, FastAPI WebSocket, etc.)
1149
+ logger.info("WebSocket server support enabled")
1150
+ pass
1151
+
1152
+ async def _initialize_external_integrations(self) -> None:
1153
+ """Initialize external monitoring system integrations"""
1154
+ # Prometheus integration
1155
+ try:
1156
+ await self._setup_prometheus_integration()
1157
+ except Exception as e:
1158
+ logger.warning(f"Failed to setup Prometheus integration: {e}")
1159
+
1160
+ # DataDog integration
1161
+ try:
1162
+ await self._setup_datadog_integration()
1163
+ except Exception as e:
1164
+ logger.warning(f"Failed to setup DataDog integration: {e}")
1165
+
1166
+ logger.info("External integrations initialized")
1167
+
1168
+ async def _setup_prometheus_integration(self) -> None:
1169
+ """Setup Prometheus integration"""
1170
+ # Implementation would setup Prometheus metrics endpoint
1171
+ pass
1172
+
1173
+ async def _setup_datadog_integration(self) -> None:
1174
+ """Setup DataDog integration"""
1175
+ # Implementation would setup DataDog API client
1176
+ pass
1177
+
1178
+ def _collect_system_metrics(self) -> None:
1179
+ """Collect system performance metrics"""
1180
+ try:
1181
+ import psutil
1182
+
1183
+ # CPU Usage
1184
+ cpu_percent = psutil.cpu_percent(interval=1)
1185
+ self.add_metric(
1186
+ MetricType.CPU_USAGE,
1187
+ cpu_percent,
1188
+ tags={"component": "system"},
1189
+ source="psutil",
1190
+ )
1191
+
1192
+ # Memory Usage
1193
+ memory = psutil.virtual_memory()
1194
+ self.add_metric(
1195
+ MetricType.MEMORY_USAGE,
1196
+ memory.percent,
1197
+ tags={"component": "system"},
1198
+ source="psutil",
1199
+ )
1200
+
1201
+ # Python process memory
1202
+ process = psutil.Process()
1203
+ process_memory = process.memory_info()
1204
+ self.add_metric(
1205
+ MetricType.MEMORY_USAGE,
1206
+ process_memory.rss / (1024**2), # MB
1207
+ tags={"component": "python_process"},
1208
+ source="psutil",
1209
+ )
1210
+
1211
+ # System load
1212
+ try:
1213
+ load_avg = psutil.getloadavg()
1214
+ self.add_metric(
1215
+ MetricType.SYSTEM_PERFORMANCE,
1216
+ load_avg[0], # 1-minute load average
1217
+ tags={"component": "system", "metric": "load_1min"},
1218
+ source="psutil",
1219
+ )
1220
+ except (AttributeError, OSError):
1221
+ pass # Not available on all systems
1222
+
1223
+ except Exception as e:
1224
+ logger.error(f"Error collecting system metrics: {e}")
1225
+
1226
+ def add_metric(
1227
+ self,
1228
+ metric_type: MetricType,
1229
+ value: Union[int, float],
1230
+ tags: Optional[Dict[str, str]] = None,
1231
+ source: str = "custom",
1232
+ component: str = "",
1233
+ tenant_id: Optional[str] = None,
1234
+ metadata: Optional[Dict[str, Any]] = None,
1235
+ ) -> None:
1236
+ """Add a metric to the monitoring system"""
1237
+ metric = MetricData(
1238
+ timestamp=datetime.now(),
1239
+ metric_type=metric_type,
1240
+ value=value,
1241
+ tags=tags or {},
1242
+ source=source,
1243
+ component=component,
1244
+ tenant_id=tenant_id,
1245
+ metadata=metadata or {},
1246
+ )
1247
+
1248
+ self.metrics_collector.add_metric(metric)
1249
+
1250
+ def get_dashboard_data(
1251
+ self,
1252
+ dashboard_id: str,
1253
+ tenant_id: Optional[str] = None,
1254
+ time_range: Optional[str] = None, # "1h", "24h", "7d"
1255
+ filters: Optional[Dict[str, Any]] = None,
1256
+ ) -> Dict[str, Any]:
1257
+ """Get dashboard data for visualization"""
1258
+ try:
1259
+ # Get dashboard
1260
+ dashboard = self.dashboard_manager.get_dashboard(dashboard_id, tenant_id)
1261
+ if not dashboard:
1262
+ return {"error": "Dashboard not found"}
1263
+
1264
+ # Calculate time range
1265
+ end_time = datetime.now()
1266
+ if time_range:
1267
+ if time_range == "1h":
1268
+ start_time = end_time - timedelta(hours=1)
1269
+ elif time_range == "24h":
1270
+ start_time = end_time - timedelta(hours=24)
1271
+ elif time_range == "7d":
1272
+ start_time = end_time - timedelta(days=7)
1273
+ else:
1274
+ start_time = end_time - timedelta(hours=1) # Default
1275
+ else:
1276
+ start_time = end_time - timedelta(hours=1)
1277
+
1278
+ # Collect data for each widget
1279
+ widgets_data = {}
1280
+ for widget in dashboard.widgets:
1281
+ try:
1282
+ widget_data = self._get_widget_data(widget, start_time, end_time, tenant_id, filters)
1283
+ widgets_data[widget.widget_id] = widget_data
1284
+ except Exception as e:
1285
+ logger.error(f"Error getting data for widget {widget.widget_id}: {e}")
1286
+ widgets_data[widget.widget_id] = {"error": str(e)}
1287
+
1288
+ return {
1289
+ "dashboard": dashboard.to_dict(),
1290
+ "widgets_data": widgets_data,
1291
+ "time_range": time_range or "1h",
1292
+ "generated_at": datetime.now().isoformat(),
1293
+ }
1294
+
1295
+ except Exception as e:
1296
+ logger.error(f"Error getting dashboard data: {e}")
1297
+ return {"error": str(e), "generated_at": datetime.now().isoformat()}
1298
+
1299
+ def _get_widget_data(
1300
+ self,
1301
+ widget: DashboardWidget,
1302
+ start_time: datetime,
1303
+ end_time: datetime,
1304
+ tenant_id: Optional[str] = None,
1305
+ filters: Optional[Dict[str, Any]] = None,
1306
+ ) -> Dict[str, Any]:
1307
+ """Get data for a specific widget"""
1308
+ if widget.widget_type == "metric":
1309
+ return self._get_metric_widget_data(widget, tenant_id)
1310
+ elif widget.widget_type == "chart":
1311
+ return self._get_chart_widget_data(widget, start_time, end_time, tenant_id, filters)
1312
+ elif widget.widget_type == "table":
1313
+ return self._get_table_widget_data(widget, start_time, end_time, tenant_id, filters)
1314
+ elif widget.widget_type == "alert":
1315
+ return self._get_alert_widget_data(widget, tenant_id)
1316
+ else:
1317
+ return {"error": f"Unsupported widget type: {widget.widget_type}"}
1318
+
1319
+ def _get_metric_widget_data(self, widget: DashboardWidget, tenant_id: Optional[str] = None) -> Dict[str, Any]:
1320
+ """Get data for metric widget"""
1321
+ metric_name = widget.config.get("metric")
1322
+ if not metric_name:
1323
+ return {"error": "No metric specified"}
1324
+
1325
+ # Get latest metrics
1326
+ metric_type = None
1327
+ if metric_name == "cpu_usage":
1328
+ metric_type = MetricType.CPU_USAGE
1329
+ elif metric_name == "memory_usage":
1330
+ metric_type = MetricType.MEMORY_USAGE
1331
+ elif metric_name == "health_score":
1332
+ metric_type = MetricType.AVAILABILITY
1333
+ else:
1334
+ return {"error": f"Unknown metric: {metric_name}"}
1335
+
1336
+ recent_metrics = self.metrics_collector.get_metrics(metric_type=metric_type, tenant_id=tenant_id, limit=1)
1337
+
1338
+ if not recent_metrics:
1339
+ return {"value": 0, "status": "no_data"}
1340
+
1341
+ latest_metric = recent_metrics[0]
1342
+ format_type = widget.config.get("format", "number")
1343
+
1344
+ if format_type == "percentage":
1345
+ value = f"{latest_metric.value:.1f}%"
1346
+ elif format_type == "duration":
1347
+ value = f"{latest_metric.value:.0f}s"
1348
+ else:
1349
+ value = str(latest_metric.value)
1350
+
1351
+ return {
1352
+ "value": latest_metric.value,
1353
+ "formatted_value": value,
1354
+ "timestamp": latest_metric.timestamp.isoformat(),
1355
+ "format": format_type,
1356
+ }
1357
+
1358
+ def _get_chart_widget_data(
1359
+ self,
1360
+ widget: DashboardWidget,
1361
+ start_time: datetime,
1362
+ end_time: datetime,
1363
+ tenant_id: Optional[str] = None,
1364
+ filters: Optional[Dict[str, Any]] = None,
1365
+ ) -> Dict[str, Any]:
1366
+ """Get data for chart widget"""
1367
+ chart_type = widget.config.get("chart_type", "line")
1368
+ metrics = widget.metrics or []
1369
+
1370
+ chart_data = {
1371
+ "type": chart_type,
1372
+ "data": [],
1373
+ "labels": [],
1374
+ }
1375
+
1376
+ for metric_name in metrics:
1377
+ # Map metric names to MetricType
1378
+ metric_type = self._map_metric_name(metric_name)
1379
+ if metric_type:
1380
+ metric_data = self.metrics_collector.get_metrics(
1381
+ metric_type=metric_type,
1382
+ tenant_id=tenant_id,
1383
+ start_time=start_time,
1384
+ end_time=end_time,
1385
+ limit=100,
1386
+ )
1387
+
1388
+ series_data = []
1389
+ for metric in metric_data:
1390
+ series_data.append(
1391
+ {
1392
+ "timestamp": metric.timestamp.isoformat(),
1393
+ "value": metric.value,
1394
+ }
1395
+ )
1396
+
1397
+ chart_data["data"].append(
1398
+ {
1399
+ "name": metric_name,
1400
+ "series": series_data,
1401
+ }
1402
+ )
1403
+
1404
+ return chart_data
1405
+
1406
+ def _get_table_widget_data(
1407
+ self,
1408
+ widget: DashboardWidget,
1409
+ start_time: datetime,
1410
+ end_time: datetime,
1411
+ tenant_id: Optional[str] = None,
1412
+ filters: Optional[Dict[str, Any]] = None,
1413
+ ) -> Dict[str, Any]:
1414
+ """Get data for table widget"""
1415
+ if widget.widget_id == "alert_table":
1416
+ alerts = self.alert_manager.get_alert_history(hours=24, tenant_id=tenant_id)
1417
+
1418
+ table_data = []
1419
+ for alert in alerts:
1420
+ table_data.append(
1421
+ {
1422
+ "timestamp": alert.timestamp.isoformat(),
1423
+ "severity": alert.severity.value,
1424
+ "title": alert.title,
1425
+ "description": alert.description,
1426
+ "component": alert.component,
1427
+ "resolved": alert.resolved,
1428
+ }
1429
+ )
1430
+
1431
+ return {
1432
+ "columns": [
1433
+ "timestamp",
1434
+ "severity",
1435
+ "title",
1436
+ "description",
1437
+ "component",
1438
+ "resolved",
1439
+ ],
1440
+ "data": table_data,
1441
+ }
1442
+
1443
+ return {"error": "Unknown table type"}
1444
+
1445
+ def _get_alert_widget_data(self, widget: DashboardWidget, tenant_id: Optional[str] = None) -> Dict[str, Any]:
1446
+ """Get data for alert widget"""
1447
+ active_alerts = self.alert_manager.get_active_alerts(tenant_id=tenant_id)
1448
+
1449
+ return {
1450
+ "active_count": len(active_alerts),
1451
+ "severity_breakdown": {
1452
+ severity.value: len([a for a in active_alerts if a.severity == severity]) for severity in AlertSeverity
1453
+ },
1454
+ "recent_alerts": [
1455
+ {
1456
+ "id": alert.alert_id,
1457
+ "title": alert.title,
1458
+ "severity": alert.severity.value,
1459
+ "timestamp": alert.timestamp.isoformat(),
1460
+ }
1461
+ for alert in sorted(active_alerts, key=lambda a: a.timestamp, reverse=True)[:10]
1462
+ ],
1463
+ }
1464
+
1465
+ def _map_metric_name(self, metric_name: str) -> Optional[MetricType]:
1466
+ """Map metric name to MetricType enum"""
1467
+ mapping = {
1468
+ "cpu_usage": MetricType.CPU_USAGE,
1469
+ "memory_usage": MetricType.MEMORY_USAGE,
1470
+ "hook_execution_rate": MetricType.THROUGHPUT,
1471
+ "hook_success_rate": MetricType.AVAILABILITY,
1472
+ "response_time": MetricType.RESPONSE_TIME,
1473
+ "error_rate": MetricType.ERROR_RATE,
1474
+ "network_io": MetricType.NETWORK_IO,
1475
+ "disk_io": MetricType.DISK_IO,
1476
+ "cache_performance": MetricType.CACHE_PERFORMANCE,
1477
+ "database_performance": MetricType.DATABASE_PERFORMANCE,
1478
+ }
1479
+
1480
+ return mapping.get(metric_name)
1481
+
1482
+ def _broadcast_alert(self, alert: Alert) -> None:
1483
+ """Broadcast alert to WebSocket clients"""
1484
+ if not self.enable_websocket:
1485
+ return
1486
+
1487
+ {
1488
+ "type": "alert",
1489
+ "data": alert.to_dict(),
1490
+ "timestamp": datetime.now().isoformat(),
1491
+ }
1492
+
1493
+ # Implementation would send message to all connected WebSocket clients
1494
+ logger.info(f"Broadcasting alert: {alert.title}")
1495
+
1496
+ def get_system_status(self) -> Dict[str, Any]:
1497
+ """Get comprehensive system status"""
1498
+ return {
1499
+ "status": "running" if self._running else "stopped",
1500
+ "uptime_seconds": (datetime.now() - self._startup_time).total_seconds(),
1501
+ "metrics_collected": len(self.metrics_collector.metrics_buffer),
1502
+ "active_alerts": len(self.alert_manager.active_alerts),
1503
+ "total_dashboards": len(self.dashboard_manager.dashboards) + len(self.dashboard_manager.default_dashboards),
1504
+ "websocket_connections": len(self.websocket_connections),
1505
+ "external_integrations": list(self.external_integrations.keys()),
1506
+ "last_update": datetime.now().isoformat(),
1507
+ }
1508
+
1509
+ def create_custom_dashboard(
1510
+ self,
1511
+ name: str,
1512
+ description: str,
1513
+ widgets: List[Dict[str, Any]],
1514
+ owner: str = "",
1515
+ tenant_id: Optional[str] = None,
1516
+ is_public: bool = False,
1517
+ ) -> str:
1518
+ """Create a custom dashboard from widget definitions"""
1519
+ dashboard_widgets = []
1520
+
1521
+ for widget_def in widgets:
1522
+ widget = DashboardWidget(
1523
+ widget_id=widget_def.get("widget_id", str(uuid.uuid4())),
1524
+ widget_type=widget_def.get("widget_type", "metric"),
1525
+ title=widget_def.get("title", ""),
1526
+ position=widget_def.get("position", {"x": 0, "y": 0, "width": 4, "height": 2}),
1527
+ config=widget_def.get("config", {}),
1528
+ metrics=widget_def.get("metrics", []),
1529
+ )
1530
+ dashboard_widgets.append(widget)
1531
+
1532
+ return self.dashboard_manager.create_dashboard(
1533
+ name=name,
1534
+ description=description,
1535
+ dashboard_type=DashboardType.CUSTOM,
1536
+ widgets=dashboard_widgets,
1537
+ owner=owner,
1538
+ tenant_id=tenant_id,
1539
+ is_public=is_public,
1540
+ )
1541
+
1542
+
1543
+ # Global instance for easy access
1544
+ _monitoring_dashboard: Optional[RealtimeMonitoringDashboard] = None
1545
+
1546
+
1547
+ def get_monitoring_dashboard(
1548
+ metrics_buffer_size: int = 100000,
1549
+ retention_hours: int = 168,
1550
+ alert_check_interval: int = 30,
1551
+ enable_websocket: bool = True,
1552
+ enable_external_integration: bool = True,
1553
+ ) -> RealtimeMonitoringDashboard:
1554
+ """Get or create global monitoring dashboard instance"""
1555
+ global _monitoring_dashboard
1556
+ if _monitoring_dashboard is None:
1557
+ _monitoring_dashboard = RealtimeMonitoringDashboard(
1558
+ metrics_buffer_size=metrics_buffer_size,
1559
+ retention_hours=retention_hours,
1560
+ alert_check_interval=alert_check_interval,
1561
+ enable_websocket=enable_websocket,
1562
+ enable_external_integration=enable_external_integration,
1563
+ )
1564
+ return _monitoring_dashboard
1565
+
1566
+
1567
+ # Convenience functions
1568
+ async def start_monitoring() -> None:
1569
+ """Start the monitoring dashboard"""
1570
+ dashboard = get_monitoring_dashboard()
1571
+ await dashboard.start()
1572
+
1573
+
1574
+ def stop_monitoring() -> None:
1575
+ """Stop the monitoring dashboard"""
1576
+ dashboard = get_monitoring_dashboard()
1577
+ dashboard.stop()
1578
+
1579
+
1580
+ def add_system_metric(
1581
+ metric_type: MetricType,
1582
+ value: Union[int, float],
1583
+ component: str = "",
1584
+ tags: Optional[Dict[str, str]] = None,
1585
+ ) -> None:
1586
+ """Add a system metric"""
1587
+ dashboard = get_monitoring_dashboard()
1588
+ dashboard.add_metric(metric_type, value, tags, component=component)
1589
+
1590
+
1591
+ def add_hook_metric(
1592
+ hook_path: str,
1593
+ execution_time_ms: float,
1594
+ success: bool,
1595
+ tenant_id: Optional[str] = None,
1596
+ ) -> None:
1597
+ """Add hook execution metric"""
1598
+ dashboard = get_monitoring_dashboard()
1599
+
1600
+ # Add execution time metric
1601
+ dashboard.add_metric(
1602
+ MetricType.RESPONSE_TIME,
1603
+ execution_time_ms,
1604
+ tags={"hook_path": hook_path, "success": str(success)},
1605
+ component="hooks",
1606
+ tenant_id=tenant_id,
1607
+ )
1608
+
1609
+ # Add success rate metric
1610
+ dashboard.add_metric(
1611
+ MetricType.AVAILABILITY,
1612
+ 1.0 if success else 0.0,
1613
+ tags={"hook_path": hook_path},
1614
+ component="hooks",
1615
+ tenant_id=tenant_id,
1616
+ )
1617
+
1618
+
1619
+ if __name__ == "__main__":
1620
+ # Example usage
1621
+ async def main():
1622
+ print("šŸ“Š Starting Real-time Monitoring Dashboard...")
1623
+
1624
+ # Initialize monitoring dashboard
1625
+ dashboard = RealtimeMonitoringDashboard(
1626
+ metrics_buffer_size=10000,
1627
+ retention_hours=24,
1628
+ enable_websocket=False, # Disable for demo
1629
+ enable_external_integration=False,
1630
+ )
1631
+
1632
+ try:
1633
+ # Start the system
1634
+ await dashboard.start()
1635
+
1636
+ # Add some test metrics
1637
+ print("\nšŸ“ˆ Adding test metrics...")
1638
+ for i in range(20):
1639
+ dashboard.add_metric(
1640
+ MetricType.CPU_USAGE,
1641
+ 20 + (i % 80), # CPU usage from 20% to 100%
1642
+ tags={"component": "system", "instance": "demo"},
1643
+ source="demo",
1644
+ )
1645
+
1646
+ dashboard.add_metric(
1647
+ MetricType.MEMORY_USAGE,
1648
+ 30 + (i % 70), # Memory usage from 30% to 100%
1649
+ tags={"component": "system"},
1650
+ source="demo",
1651
+ )
1652
+
1653
+ dashboard.add_hook_metric(
1654
+ f"test_hook_{i % 5}.py",
1655
+ 100 + (i * 50), # Execution time from 100ms to 1000ms
1656
+ i % 4 != 0, # 75% success rate
1657
+ tenant_id="demo_tenant" if i % 2 == 0 else None,
1658
+ )
1659
+
1660
+ await asyncio.sleep(0.1)
1661
+
1662
+ # Let metrics process
1663
+ print("\nā³ Processing metrics and checking alerts...")
1664
+ await asyncio.sleep(5)
1665
+
1666
+ # Get system status
1667
+ status = dashboard.get_system_status()
1668
+ print("\nšŸ“Š System Status:")
1669
+ print(f" Status: {status['status']}")
1670
+ print(f" Uptime: {status['uptime_seconds']:.1f}s")
1671
+ print(f" Metrics collected: {status['metrics_collected']}")
1672
+ print(f" Active alerts: {status['active_alerts']}")
1673
+ print(f" Total dashboards: {status['total_dashboards']}")
1674
+
1675
+ # Get dashboard data
1676
+ dashboard_data = dashboard.get_dashboard_data("system_overview")
1677
+ print("\nšŸ“± Dashboard Data:")
1678
+ print(f" Dashboard: {dashboard_data['dashboard']['name']}")
1679
+ print(f" Widgets: {len(dashboard_data.get('widgets_data', {}))}")
1680
+ print(f" Generated at: {dashboard_data.get('generated_at')}")
1681
+
1682
+ # Get metrics statistics
1683
+ cpu_stats = dashboard.metrics_collector.get_statistics(MetricType.CPU_USAGE, minutes=10)
1684
+ memory_stats = dashboard.metrics_collector.get_statistics(MetricType.MEMORY_USAGE, minutes=10)
1685
+ print("\nšŸ“ˆ Metrics Statistics (last 10 minutes):")
1686
+ cpu_avg = cpu_stats.get("average", 0)
1687
+ cpu_max = cpu_stats.get("max", 0)
1688
+ cpu_count = cpu_stats.get("count", 0)
1689
+ print(f" CPU Usage - Avg: {cpu_avg:.1f}%, Max: {cpu_max:.1f}%, Count: {cpu_count}")
1690
+ mem_avg = memory_stats.get("average", 0)
1691
+ mem_max = memory_stats.get("max", 0)
1692
+ mem_count = memory_stats.get("count", 0)
1693
+ print(f" Memory Usage - Avg: {mem_avg:.1f}%, Max: {mem_max:.1f}%, Count: {mem_count}")
1694
+
1695
+ # Get alert statistics
1696
+ alert_stats = dashboard.alert_manager.get_alert_statistics(hours=1)
1697
+ print("\n🚨 Alert Statistics (last 1 hour):")
1698
+ print(f" Total alerts: {alert_stats['total_alerts']}")
1699
+ print(f" Resolved: {alert_stats['resolved_count']}")
1700
+ print(f" Resolution rate: {alert_stats['resolution_rate']:.1%}")
1701
+ print(f" By severity: {alert_stats['by_severity']}")
1702
+
1703
+ # List available dashboards
1704
+ dashboards = dashboard.dashboard_manager.list_dashboards()
1705
+ print("\nšŸ“‹ Available Dashboards:")
1706
+ for dashboard_info in dashboards:
1707
+ print(f" - {dashboard_info.name} ({dashboard_info.dashboard_type.value})")
1708
+
1709
+ print("\nāœ… Real-time Monitoring Dashboard demo completed successfully!")
1710
+
1711
+ except Exception as e:
1712
+ print(f"\nāŒ Demo failed: {str(e)}")
1713
+ import traceback
1714
+
1715
+ traceback.print_exc()
1716
+
1717
+ finally:
1718
+ # Stop the system
1719
+ print("\nšŸ›‘ Stopping monitoring dashboard...")
1720
+ dashboard.stop()
1721
+ print("āœ… System stopped")
1722
+
1723
+ # Run the demo
1724
+ asyncio.run(main())