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.
- moai_adk/__init__.py +2 -5
- moai_adk/__main__.py +114 -82
- moai_adk/cli/__init__.py +6 -1
- moai_adk/cli/commands/__init__.py +1 -3
- moai_adk/cli/commands/analyze.py +5 -16
- moai_adk/cli/commands/doctor.py +6 -18
- moai_adk/cli/commands/init.py +56 -125
- moai_adk/cli/commands/language.py +14 -35
- moai_adk/cli/commands/status.py +9 -15
- moai_adk/cli/commands/update.py +1555 -190
- moai_adk/cli/prompts/init_prompts.py +112 -56
- moai_adk/cli/spec_status.py +263 -0
- moai_adk/cli/ui/__init__.py +44 -0
- moai_adk/cli/ui/progress.py +422 -0
- moai_adk/cli/ui/prompts.py +389 -0
- moai_adk/cli/ui/theme.py +129 -0
- moai_adk/cli/worktree/__init__.py +27 -0
- moai_adk/cli/worktree/__main__.py +31 -0
- moai_adk/cli/worktree/cli.py +672 -0
- moai_adk/cli/worktree/exceptions.py +89 -0
- moai_adk/cli/worktree/manager.py +490 -0
- moai_adk/cli/worktree/models.py +65 -0
- moai_adk/cli/worktree/registry.py +128 -0
- moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
- moai_adk/core/analysis/session_analyzer.py +17 -56
- moai_adk/core/claude_integration.py +26 -54
- moai_adk/core/command_helpers.py +10 -10
- moai_adk/core/comprehensive_monitoring_system.py +1183 -0
- moai_adk/core/config/auto_spec_config.py +5 -11
- moai_adk/core/config/migration.py +19 -9
- moai_adk/core/config/unified.py +436 -0
- moai_adk/core/context_manager.py +6 -12
- moai_adk/core/enterprise_features.py +1404 -0
- moai_adk/core/error_recovery_system.py +725 -112
- moai_adk/core/event_driven_hook_system.py +1371 -0
- moai_adk/core/git/__init__.py +8 -0
- moai_adk/core/git/branch_manager.py +3 -11
- moai_adk/core/git/checkpoint.py +1 -3
- moai_adk/core/git/conflict_detector.py +413 -0
- moai_adk/core/git/manager.py +91 -1
- moai_adk/core/hooks/post_tool_auto_spec_completion.py +56 -80
- moai_adk/core/input_validation_middleware.py +1006 -0
- moai_adk/core/integration/engine.py +6 -18
- moai_adk/core/integration/integration_tester.py +10 -9
- moai_adk/core/integration/utils.py +1 -1
- moai_adk/core/issue_creator.py +10 -28
- moai_adk/core/jit_context_loader.py +956 -0
- moai_adk/core/jit_enhanced_hook_manager.py +1987 -0
- moai_adk/core/language_config_resolver.py +485 -0
- moai_adk/core/language_validator.py +28 -41
- moai_adk/core/mcp/setup.py +15 -12
- moai_adk/core/merge/__init__.py +9 -0
- moai_adk/core/merge/analyzer.py +481 -0
- moai_adk/core/migration/alfred_to_moai_migrator.py +383 -0
- moai_adk/core/migration/backup_manager.py +78 -9
- moai_adk/core/migration/custom_element_scanner.py +358 -0
- moai_adk/core/migration/file_migrator.py +8 -17
- moai_adk/core/migration/interactive_checkbox_ui.py +488 -0
- moai_adk/core/migration/selective_restorer.py +470 -0
- moai_adk/core/migration/template_utils.py +74 -0
- moai_adk/core/migration/user_selection_ui.py +338 -0
- moai_adk/core/migration/version_detector.py +6 -10
- moai_adk/core/migration/version_migrator.py +3 -3
- moai_adk/core/performance/cache_system.py +8 -10
- moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
- moai_adk/core/project/checker.py +2 -4
- moai_adk/core/project/detector.py +1 -3
- moai_adk/core/project/initializer.py +135 -23
- moai_adk/core/project/phase_executor.py +54 -81
- moai_adk/core/project/validator.py +6 -12
- moai_adk/core/quality/trust_checker.py +9 -27
- moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
- moai_adk/core/robust_json_parser.py +611 -0
- moai_adk/core/rollback_manager.py +73 -148
- moai_adk/core/session_manager.py +10 -26
- moai_adk/core/skill_loading_system.py +579 -0
- moai_adk/core/spec/confidence_scoring.py +31 -100
- moai_adk/core/spec/ears_template_engine.py +351 -286
- moai_adk/core/spec/quality_validator.py +35 -69
- moai_adk/core/spec_status_manager.py +64 -74
- moai_adk/core/template/backup.py +45 -20
- moai_adk/core/template/config.py +112 -39
- moai_adk/core/template/merger.py +11 -19
- moai_adk/core/template/processor.py +253 -149
- moai_adk/core/template_engine.py +73 -40
- moai_adk/core/template_variable_synchronizer.py +417 -0
- moai_adk/core/unified_permission_manager.py +745 -0
- moai_adk/core/user_behavior_analytics.py +851 -0
- moai_adk/core/version_sync.py +429 -0
- moai_adk/foundation/__init__.py +56 -0
- moai_adk/foundation/backend.py +1027 -0
- moai_adk/foundation/database.py +1115 -0
- moai_adk/foundation/devops.py +1585 -0
- moai_adk/foundation/ears.py +431 -0
- moai_adk/foundation/frontend.py +870 -0
- moai_adk/foundation/git/commit_templates.py +4 -12
- moai_adk/foundation/git.py +376 -0
- moai_adk/foundation/langs.py +484 -0
- moai_adk/foundation/ml_ops.py +1162 -0
- moai_adk/foundation/testing.py +1524 -0
- moai_adk/foundation/trust/trust_principles.py +23 -72
- moai_adk/foundation/trust/validation_checklist.py +57 -162
- moai_adk/project/__init__.py +0 -0
- moai_adk/project/configuration.py +1084 -0
- moai_adk/project/documentation.py +566 -0
- moai_adk/project/schema.py +447 -0
- moai_adk/statusline/alfred_detector.py +1 -3
- moai_adk/statusline/config.py +13 -4
- moai_adk/statusline/enhanced_output_style_detector.py +23 -15
- moai_adk/statusline/main.py +51 -15
- moai_adk/statusline/renderer.py +104 -48
- moai_adk/statusline/update_checker.py +3 -9
- moai_adk/statusline/version_reader.py +140 -46
- moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +549 -0
- moai_adk/templates/.claude/agents/moai/builder-agent.md +445 -0
- moai_adk/templates/.claude/agents/moai/builder-command.md +1132 -0
- moai_adk/templates/.claude/agents/moai/builder-skill.md +601 -0
- moai_adk/templates/.claude/agents/moai/expert-backend.md +831 -0
- moai_adk/templates/.claude/agents/moai/expert-database.md +774 -0
- moai_adk/templates/.claude/agents/moai/expert-debug.md +396 -0
- moai_adk/templates/.claude/agents/moai/expert-devops.md +711 -0
- moai_adk/templates/.claude/agents/moai/expert-frontend.md +666 -0
- moai_adk/templates/.claude/agents/moai/expert-security.md +474 -0
- moai_adk/templates/.claude/agents/moai/expert-uiux.md +1038 -0
- moai_adk/templates/.claude/agents/moai/manager-claude-code.md +429 -0
- moai_adk/templates/.claude/agents/moai/manager-docs.md +570 -0
- moai_adk/templates/.claude/agents/moai/manager-git.md +937 -0
- moai_adk/templates/.claude/agents/moai/manager-project.md +891 -0
- moai_adk/templates/.claude/agents/moai/manager-quality.md +598 -0
- moai_adk/templates/.claude/agents/moai/manager-spec.md +713 -0
- moai_adk/templates/.claude/agents/moai/manager-strategy.md +600 -0
- moai_adk/templates/.claude/agents/moai/manager-tdd.md +603 -0
- moai_adk/templates/.claude/agents/moai/mcp-context7.md +369 -0
- moai_adk/templates/.claude/agents/moai/mcp-figma.md +1567 -0
- moai_adk/templates/.claude/agents/moai/mcp-notion.md +749 -0
- moai_adk/templates/.claude/agents/moai/mcp-playwright.md +427 -0
- moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +994 -0
- moai_adk/templates/.claude/commands/moai/0-project.md +1143 -0
- moai_adk/templates/.claude/commands/moai/1-plan.md +1435 -0
- moai_adk/templates/.claude/commands/moai/2-run.md +883 -0
- moai_adk/templates/.claude/commands/moai/3-sync.md +993 -0
- moai_adk/templates/.claude/commands/moai/9-feedback.md +314 -0
- moai_adk/templates/.claude/hooks/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
- moai_adk/templates/.claude/hooks/moai/lib/__init__.py +85 -0
- moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +244 -0
- moai_adk/templates/.claude/hooks/moai/lib/common.py +131 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +446 -0
- moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
- moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
- moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +590 -0
- moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +317 -0
- moai_adk/templates/.claude/hooks/moai/lib/models.py +102 -0
- moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +28 -0
- moai_adk/templates/.claude/hooks/moai/lib/project.py +768 -0
- moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
- moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
- moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +530 -0
- moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +862 -0
- moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +921 -0
- moai_adk/templates/.claude/output-styles/moai/r2d2.md +380 -0
- moai_adk/templates/.claude/output-styles/moai/yoda.md +338 -0
- moai_adk/templates/.claude/settings.json +172 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +247 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +44 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +130 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +178 -0
- moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +147 -0
- moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +319 -0
- moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +320 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
- moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
- moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +496 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +453 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/design-system-tokens.md +405 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
- moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
- moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +491 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +98 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
- moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +201 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +560 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +113 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +238 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
- moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
- moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +438 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +515 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +346 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +757 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +864 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
- moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +981 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +362 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/examples.md +1232 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/best-practices.md +261 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/integration-patterns.md +194 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/proactive-analysis.md +229 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/modules/trust5-validation.md +169 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/reference.md +1266 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/scripts/quality-gate.sh +668 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/github-actions-quality.yml +481 -0
- moai_adk/templates/.claude/skills/moai-foundation-quality/templates/quality-config.yaml +519 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/SKILL.md +352 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/README.md +52 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/error-handling.md +334 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/integration-patterns.md +310 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/security-authentication.md +256 -0
- moai_adk/templates/.claude/skills/moai-integration-mcp/modules/server-architecture.md +253 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/README.md +133 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/SKILL.md +296 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/examples.md +1269 -0
- moai_adk/templates/.claude/skills/moai-lang-unified/reference.md +331 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +298 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/advanced-patterns.md +465 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/optimization.md +440 -0
- moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +316 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +336 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +17 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +57 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
- moai_adk/templates/.claude/skills/moai-library-nextra/optimization.md +303 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +370 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
- moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/README.md +186 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/SKILL.md +290 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/examples.md +1225 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/reference.md +567 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/scripts/provider-selector.py +323 -0
- moai_adk/templates/.claude/skills/moai-platform-baas/templates/stack-config.yaml +204 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +446 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/advanced-patterns.md +379 -0
- moai_adk/templates/.claude/skills/moai-workflow-jit-docs/optimization.md +286 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +387 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +520 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +574 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +317 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +663 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +190 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +135 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +196 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +17 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +158 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +340 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +713 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +538 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +1336 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +730 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +608 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +1005 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1362 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +71 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
- moai_adk/templates/.claude/skills/moai-workflow-project/test_integration_simple.py +436 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +374 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
- moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +453 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/advanced-patterns.md +576 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +220 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +845 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +1416 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +1234 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +1243 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +1260 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/optimization.md +505 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
- moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
- moai_adk/templates/.claude/skills/moai-worktree/SKILL.md +410 -0
- moai_adk/templates/.claude/skills/moai-worktree/examples.md +606 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +982 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +778 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +646 -0
- moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +782 -0
- moai_adk/templates/.claude/skills/moai-worktree/reference.md +357 -0
- moai_adk/templates/.git-hooks/pre-commit +103 -41
- moai_adk/templates/.git-hooks/pre-push +116 -21
- moai_adk/templates/.github/workflows/ci-universal.yml +513 -0
- moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
- moai_adk/templates/.gitignore +184 -44
- moai_adk/templates/.mcp.json +7 -9
- moai_adk/templates/.moai/cache/personalization.json +10 -0
- moai_adk/templates/.moai/config/config.yaml +344 -0
- moai_adk/templates/.moai/config/presets/manual.yaml +28 -0
- moai_adk/templates/.moai/config/presets/personal.yaml +30 -0
- moai_adk/templates/.moai/config/presets/team.yaml +33 -0
- moai_adk/templates/.moai/config/questions/_schema.yaml +79 -0
- moai_adk/templates/.moai/config/questions/tab1-user.yaml +108 -0
- moai_adk/templates/.moai/config/questions/tab2-project.yaml +122 -0
- moai_adk/templates/.moai/config/questions/tab3-git.yaml +542 -0
- moai_adk/templates/.moai/config/questions/tab4-quality.yaml +167 -0
- moai_adk/templates/.moai/config/questions/tab5-system.yaml +152 -0
- moai_adk/templates/.moai/config/sections/git-strategy.yaml +40 -0
- moai_adk/templates/.moai/config/sections/language.yaml +11 -0
- moai_adk/templates/.moai/config/sections/project.yaml +13 -0
- moai_adk/templates/.moai/config/sections/quality.yaml +15 -0
- moai_adk/templates/.moai/config/sections/system.yaml +14 -0
- moai_adk/templates/.moai/config/sections/user.yaml +5 -0
- moai_adk/templates/.moai/config/statusline-config.yaml +86 -0
- moai_adk/templates/.moai/scripts/setup-glm.py +136 -0
- moai_adk/templates/CLAUDE.md +382 -501
- moai_adk/utils/__init__.py +24 -1
- moai_adk/utils/banner.py +7 -10
- moai_adk/utils/common.py +16 -30
- moai_adk/utils/link_validator.py +4 -12
- moai_adk/utils/safe_file_reader.py +2 -6
- moai_adk/utils/timeout.py +160 -0
- moai_adk/utils/toon_utils.py +256 -0
- moai_adk/version.py +22 -0
- moai_adk-0.32.8.dist-info/METADATA +2478 -0
- moai_adk-0.32.8.dist-info/RECORD +396 -0
- {moai_adk-0.25.4.dist-info → moai_adk-0.32.8.dist-info}/WHEEL +1 -1
- {moai_adk-0.25.4.dist-info → moai_adk-0.32.8.dist-info}/entry_points.txt +1 -0
- moai_adk/cli/commands/backup.py +0 -82
- moai_adk/cli/commands/improve_user_experience.py +0 -348
- moai_adk/cli/commands/migrate.py +0 -158
- moai_adk/cli/commands/validate_links.py +0 -118
- moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -413
- moai_adk/templates/.github/workflows/moai-release-create.yml +0 -100
- moai_adk/templates/.github/workflows/moai-release-pipeline.yml +0 -188
- moai_adk/utils/user_experience.py +0 -531
- moai_adk-0.25.4.dist-info/METADATA +0 -2279
- moai_adk-0.25.4.dist-info/RECORD +0 -112
- {moai_adk-0.25.4.dist-info → moai_adk-0.32.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1183 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Comprehensive Monitoring System
|
|
3
|
+
|
|
4
|
+
Real-time monitoring, analytics, and predictive analysis for MoAI-ADK
|
|
5
|
+
with automated alerting and optimization capabilities.
|
|
6
|
+
|
|
7
|
+
Key Features:
|
|
8
|
+
- Real-time metrics collection and analysis
|
|
9
|
+
- User behavior analytics and pattern recognition
|
|
10
|
+
- Predictive analytics and trend analysis
|
|
11
|
+
- Automated alerting system
|
|
12
|
+
- System health monitoring
|
|
13
|
+
- Performance optimization recommendations
|
|
14
|
+
- Real-time dashboard interface
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import statistics
|
|
20
|
+
import threading
|
|
21
|
+
import time
|
|
22
|
+
from collections import defaultdict, deque
|
|
23
|
+
from dataclasses import dataclass, field
|
|
24
|
+
from datetime import datetime, timedelta
|
|
25
|
+
from enum import Enum
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
28
|
+
|
|
29
|
+
import psutil
|
|
30
|
+
|
|
31
|
+
# Set up logging
|
|
32
|
+
logging.basicConfig(level=logging.INFO)
|
|
33
|
+
logger = logging.getLogger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MetricType(Enum):
|
|
37
|
+
"""Types of metrics collected by the monitoring system"""
|
|
38
|
+
|
|
39
|
+
SYSTEM_PERFORMANCE = "system_performance"
|
|
40
|
+
USER_BEHAVIOR = "user_behavior"
|
|
41
|
+
TOKEN_USAGE = "token_usage"
|
|
42
|
+
ERROR_RATE = "error_rate"
|
|
43
|
+
RESPONSE_TIME = "response_time"
|
|
44
|
+
MEMORY_USAGE = "memory_usage"
|
|
45
|
+
CPU_USAGE = "cpu_usage"
|
|
46
|
+
THROUGHPUT = "throughput"
|
|
47
|
+
AVAILABILITY = "availability"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AlertSeverity(Enum):
|
|
51
|
+
"""Alert severity levels"""
|
|
52
|
+
|
|
53
|
+
LOW = 1
|
|
54
|
+
MEDIUM = 2
|
|
55
|
+
HIGH = 3
|
|
56
|
+
CRITICAL = 4
|
|
57
|
+
EMERGENCY = 5
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class HealthStatus(Enum):
|
|
61
|
+
"""System health status"""
|
|
62
|
+
|
|
63
|
+
HEALTHY = "healthy"
|
|
64
|
+
WARNING = "warning"
|
|
65
|
+
DEGRADED = "degraded"
|
|
66
|
+
CRITICAL = "critical"
|
|
67
|
+
DOWN = "down"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class MetricData:
|
|
72
|
+
"""Single metric data point"""
|
|
73
|
+
|
|
74
|
+
timestamp: datetime
|
|
75
|
+
metric_type: MetricType
|
|
76
|
+
value: Union[int, float, str, bool]
|
|
77
|
+
tags: Dict[str, str] = field(default_factory=dict)
|
|
78
|
+
source: str = ""
|
|
79
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
80
|
+
|
|
81
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
82
|
+
"""Convert to dictionary for serialization"""
|
|
83
|
+
return {
|
|
84
|
+
"timestamp": self.timestamp.isoformat(),
|
|
85
|
+
"metric_type": self.metric_type.value,
|
|
86
|
+
"value": self.value,
|
|
87
|
+
"tags": self.tags,
|
|
88
|
+
"source": self.source,
|
|
89
|
+
"metadata": self.metadata,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class Alert:
|
|
95
|
+
"""Alert definition and data"""
|
|
96
|
+
|
|
97
|
+
alert_id: str
|
|
98
|
+
severity: AlertSeverity
|
|
99
|
+
title: str
|
|
100
|
+
description: str
|
|
101
|
+
timestamp: datetime
|
|
102
|
+
metric_type: MetricType
|
|
103
|
+
threshold: float
|
|
104
|
+
current_value: float
|
|
105
|
+
source: str
|
|
106
|
+
tags: Dict[str, str] = field(default_factory=dict)
|
|
107
|
+
resolved: bool = False
|
|
108
|
+
resolved_at: Optional[datetime] = None
|
|
109
|
+
acknowledged: bool = False
|
|
110
|
+
acknowledged_at: Optional[datetime] = None
|
|
111
|
+
|
|
112
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
113
|
+
"""Convert to dictionary for serialization"""
|
|
114
|
+
return {
|
|
115
|
+
"alert_id": self.alert_id,
|
|
116
|
+
"severity": self.severity.value,
|
|
117
|
+
"title": self.title,
|
|
118
|
+
"description": self.description,
|
|
119
|
+
"timestamp": self.timestamp.isoformat(),
|
|
120
|
+
"metric_type": self.metric_type.value,
|
|
121
|
+
"threshold": self.threshold,
|
|
122
|
+
"current_value": self.current_value,
|
|
123
|
+
"source": self.source,
|
|
124
|
+
"tags": self.tags,
|
|
125
|
+
"resolved": self.resolved,
|
|
126
|
+
"resolved_at": self.resolved_at.isoformat() if self.resolved_at else None,
|
|
127
|
+
"acknowledged": self.acknowledged,
|
|
128
|
+
"acknowledged_at": (self.acknowledged_at.isoformat() if self.acknowledged_at else None),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@dataclass
|
|
133
|
+
class SystemHealth:
|
|
134
|
+
"""System health status information"""
|
|
135
|
+
|
|
136
|
+
status: HealthStatus
|
|
137
|
+
timestamp: datetime
|
|
138
|
+
overall_score: float # 0-100
|
|
139
|
+
component_scores: Dict[str, float] = field(default_factory=dict)
|
|
140
|
+
active_alerts: List[str] = field(default_factory=list)
|
|
141
|
+
recent_metrics: Dict[str, float] = field(default_factory=dict)
|
|
142
|
+
uptime_percentage: float = 100.0
|
|
143
|
+
last_check: Optional[datetime] = None
|
|
144
|
+
|
|
145
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
146
|
+
"""Convert to dictionary for serialization"""
|
|
147
|
+
return {
|
|
148
|
+
"status": self.status.value,
|
|
149
|
+
"timestamp": self.timestamp.isoformat(),
|
|
150
|
+
"overall_score": self.overall_score,
|
|
151
|
+
"component_scores": self.component_scores,
|
|
152
|
+
"active_alerts": self.active_alerts,
|
|
153
|
+
"recent_metrics": self.recent_metrics,
|
|
154
|
+
"uptime_percentage": self.uptime_percentage,
|
|
155
|
+
"last_check": self.last_check.isoformat() if self.last_check else None,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class MetricsCollector:
|
|
160
|
+
"""Collects and manages system metrics"""
|
|
161
|
+
|
|
162
|
+
def __init__(self, buffer_size: int = 10000, retention_hours: int = 24):
|
|
163
|
+
self.buffer_size = buffer_size
|
|
164
|
+
self.retention_hours = retention_hours
|
|
165
|
+
self.metrics_buffer: Dict[MetricType, deque] = defaultdict(lambda: deque(maxlen=buffer_size))
|
|
166
|
+
self.aggregated_metrics: Dict[MetricType, Dict[str, Any]] = defaultdict(dict)
|
|
167
|
+
self._lock = threading.Lock()
|
|
168
|
+
self._last_cleanup = datetime.now()
|
|
169
|
+
|
|
170
|
+
def add_metric(self, metric: MetricData) -> None:
|
|
171
|
+
"""Add a metric to the collection"""
|
|
172
|
+
with self._lock:
|
|
173
|
+
self.metrics_buffer[metric.metric_type].append(metric)
|
|
174
|
+
self._update_aggregated_metrics(metric)
|
|
175
|
+
self._cleanup_old_metrics()
|
|
176
|
+
|
|
177
|
+
def _update_aggregated_metrics(self, metric: MetricData) -> None:
|
|
178
|
+
"""Update aggregated statistics for a metric type"""
|
|
179
|
+
if metric.metric_type not in self.aggregated_metrics:
|
|
180
|
+
self.aggregated_metrics[metric.metric_type] = {
|
|
181
|
+
"count": 0,
|
|
182
|
+
"sum": 0,
|
|
183
|
+
"min": float("inf"),
|
|
184
|
+
"max": float("-inf"),
|
|
185
|
+
"values": [],
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
agg = self.aggregated_metrics[metric.metric_type]
|
|
189
|
+
|
|
190
|
+
if isinstance(metric.value, (int, float)):
|
|
191
|
+
agg["count"] += 1
|
|
192
|
+
agg["sum"] += metric.value
|
|
193
|
+
agg["min"] = min(agg["min"], metric.value)
|
|
194
|
+
agg["max"] = max(agg["max"], metric.value)
|
|
195
|
+
agg["values"].append(metric.value)
|
|
196
|
+
|
|
197
|
+
# Keep only recent values for statistics
|
|
198
|
+
if len(agg["values"]) > 1000:
|
|
199
|
+
agg["values"] = agg["values"][-1000:]
|
|
200
|
+
|
|
201
|
+
def _cleanup_old_metrics(self) -> None:
|
|
202
|
+
"""Remove metrics older than retention period"""
|
|
203
|
+
now = datetime.now()
|
|
204
|
+
if (now - self._last_cleanup).seconds < 300: # Cleanup every 5 minutes
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
cutoff_time = now - timedelta(hours=self.retention_hours)
|
|
208
|
+
|
|
209
|
+
for metric_type in self.metrics_buffer:
|
|
210
|
+
while self.metrics_buffer[metric_type] and self.metrics_buffer[metric_type][0].timestamp < cutoff_time:
|
|
211
|
+
self.metrics_buffer[metric_type].popleft()
|
|
212
|
+
|
|
213
|
+
self._last_cleanup = now
|
|
214
|
+
|
|
215
|
+
def get_metrics(
|
|
216
|
+
self,
|
|
217
|
+
metric_type: Optional[MetricType] = None,
|
|
218
|
+
start_time: Optional[datetime] = None,
|
|
219
|
+
end_time: Optional[datetime] = None,
|
|
220
|
+
limit: Optional[int] = None,
|
|
221
|
+
) -> List[MetricData]:
|
|
222
|
+
"""Get metrics with optional filtering"""
|
|
223
|
+
with self._lock:
|
|
224
|
+
if metric_type:
|
|
225
|
+
metrics = list(self.metrics_buffer[metric_type])
|
|
226
|
+
else:
|
|
227
|
+
metrics = []
|
|
228
|
+
for mlist in self.metrics_buffer.values():
|
|
229
|
+
metrics.extend(mlist)
|
|
230
|
+
|
|
231
|
+
# Filter by time range
|
|
232
|
+
if start_time:
|
|
233
|
+
metrics = [m for m in metrics if m.timestamp >= start_time]
|
|
234
|
+
if end_time:
|
|
235
|
+
metrics = [m for m in metrics if m.timestamp <= end_time]
|
|
236
|
+
|
|
237
|
+
# Sort by timestamp (newest first)
|
|
238
|
+
metrics.sort(key=lambda m: m.timestamp, reverse=True)
|
|
239
|
+
|
|
240
|
+
# Apply limit
|
|
241
|
+
if limit:
|
|
242
|
+
metrics = metrics[:limit]
|
|
243
|
+
|
|
244
|
+
return metrics
|
|
245
|
+
|
|
246
|
+
def get_statistics(self, metric_type: MetricType, minutes: int = 60) -> Dict[str, Any]:
|
|
247
|
+
"""Get statistical summary for a metric type"""
|
|
248
|
+
with self._lock:
|
|
249
|
+
agg = self.aggregated_metrics.get(metric_type, {})
|
|
250
|
+
|
|
251
|
+
if not agg or agg["count"] == 0:
|
|
252
|
+
return {
|
|
253
|
+
"count": 0,
|
|
254
|
+
"average": None,
|
|
255
|
+
"min": None,
|
|
256
|
+
"max": None,
|
|
257
|
+
"median": None,
|
|
258
|
+
"std_dev": None,
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
values = agg["values"]
|
|
262
|
+
if not values:
|
|
263
|
+
return {
|
|
264
|
+
"count": agg["count"],
|
|
265
|
+
"average": agg["sum"] / agg["count"],
|
|
266
|
+
"min": agg["min"],
|
|
267
|
+
"max": agg["max"],
|
|
268
|
+
"median": None,
|
|
269
|
+
"std_dev": None,
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
try:
|
|
273
|
+
return {
|
|
274
|
+
"count": len(values),
|
|
275
|
+
"average": statistics.mean(values),
|
|
276
|
+
"median": statistics.median(values),
|
|
277
|
+
"min": min(values),
|
|
278
|
+
"max": max(values),
|
|
279
|
+
"std_dev": statistics.stdev(values) if len(values) > 1 else 0,
|
|
280
|
+
"p95": (statistics.quantiles(values, n=20)[18] if len(values) > 20 else max(values)),
|
|
281
|
+
"p99": (statistics.quantiles(values, n=100)[98] if len(values) > 100 else max(values)),
|
|
282
|
+
}
|
|
283
|
+
except (statistics.StatisticsError, IndexError):
|
|
284
|
+
return {
|
|
285
|
+
"count": len(values),
|
|
286
|
+
"average": statistics.mean(values),
|
|
287
|
+
"median": statistics.median(values),
|
|
288
|
+
"min": min(values),
|
|
289
|
+
"max": max(values),
|
|
290
|
+
"std_dev": 0,
|
|
291
|
+
"p95": max(values),
|
|
292
|
+
"p99": max(values),
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
class AlertManager:
|
|
297
|
+
"""Manages alert rules, detection, and notification"""
|
|
298
|
+
|
|
299
|
+
def __init__(self, metrics_collector: MetricsCollector):
|
|
300
|
+
self.metrics_collector = metrics_collector
|
|
301
|
+
self.alert_rules: List[Dict[str, Any]] = []
|
|
302
|
+
self.active_alerts: Dict[str, Alert] = {}
|
|
303
|
+
self.alert_history: List[Alert] = []
|
|
304
|
+
self.alert_callbacks: List[Callable[[Alert], None]] = []
|
|
305
|
+
self._lock = threading.Lock()
|
|
306
|
+
|
|
307
|
+
def add_alert_rule(
|
|
308
|
+
self,
|
|
309
|
+
name: str,
|
|
310
|
+
metric_type: MetricType,
|
|
311
|
+
threshold: float,
|
|
312
|
+
operator: str = "gt", # gt, lt, eq, ne
|
|
313
|
+
severity: AlertSeverity = AlertSeverity.MEDIUM,
|
|
314
|
+
window_minutes: int = 5,
|
|
315
|
+
consecutive_violations: int = 1,
|
|
316
|
+
tags: Optional[Dict[str, str]] = None,
|
|
317
|
+
description: Optional[str] = None,
|
|
318
|
+
) -> None:
|
|
319
|
+
"""Add an alert rule"""
|
|
320
|
+
rule = {
|
|
321
|
+
"name": name,
|
|
322
|
+
"metric_type": metric_type,
|
|
323
|
+
"threshold": threshold,
|
|
324
|
+
"operator": operator,
|
|
325
|
+
"severity": severity,
|
|
326
|
+
"window_minutes": window_minutes,
|
|
327
|
+
"consecutive_violations": consecutive_violations,
|
|
328
|
+
"tags": tags or {},
|
|
329
|
+
"description": description or f"Alert when {metric_type.value} {operator} {threshold}",
|
|
330
|
+
"violation_count": 0,
|
|
331
|
+
"last_check": None,
|
|
332
|
+
"enabled": True,
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
with self._lock:
|
|
336
|
+
self.alert_rules.append(rule)
|
|
337
|
+
|
|
338
|
+
def check_alerts(self) -> List[Alert]:
|
|
339
|
+
"""Check all alert rules and generate alerts for violations"""
|
|
340
|
+
triggered_alerts = []
|
|
341
|
+
|
|
342
|
+
with self._lock:
|
|
343
|
+
for rule in self.alert_rules:
|
|
344
|
+
if not rule["enabled"]:
|
|
345
|
+
continue
|
|
346
|
+
|
|
347
|
+
# Get recent metrics for this rule
|
|
348
|
+
recent_metrics = self.metrics_collector.get_metrics(
|
|
349
|
+
metric_type=rule["metric_type"],
|
|
350
|
+
start_time=datetime.now() - timedelta(minutes=rule["window_minutes"]),
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
if not recent_metrics:
|
|
354
|
+
continue
|
|
355
|
+
|
|
356
|
+
# Check for violations
|
|
357
|
+
violations = 0
|
|
358
|
+
latest_value = None
|
|
359
|
+
|
|
360
|
+
for metric in recent_metrics:
|
|
361
|
+
if isinstance(metric.value, (int, float)):
|
|
362
|
+
if self._evaluate_condition(metric.value, rule["threshold"], rule["operator"]):
|
|
363
|
+
violations += 1
|
|
364
|
+
latest_value = metric.value
|
|
365
|
+
|
|
366
|
+
# Trigger alert if threshold exceeded
|
|
367
|
+
if violations >= rule["consecutive_violations"]:
|
|
368
|
+
alert_id = f"{rule['name']}_{int(time.time())}"
|
|
369
|
+
|
|
370
|
+
alert = Alert(
|
|
371
|
+
alert_id=alert_id,
|
|
372
|
+
severity=rule["severity"],
|
|
373
|
+
title=f"{rule['name']} Alert Triggered",
|
|
374
|
+
description=rule["description"],
|
|
375
|
+
timestamp=datetime.now(),
|
|
376
|
+
metric_type=rule["metric_type"],
|
|
377
|
+
threshold=rule["threshold"],
|
|
378
|
+
current_value=latest_value or 0,
|
|
379
|
+
source="monitoring_system",
|
|
380
|
+
tags=rule["tags"],
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
self.active_alerts[alert_id] = alert
|
|
384
|
+
self.alert_history.append(alert)
|
|
385
|
+
triggered_alerts.append(alert)
|
|
386
|
+
|
|
387
|
+
# Trigger callbacks
|
|
388
|
+
for callback in self.alert_callbacks:
|
|
389
|
+
try:
|
|
390
|
+
callback(alert)
|
|
391
|
+
except Exception as e:
|
|
392
|
+
logger.error(f"Error in alert callback: {e}")
|
|
393
|
+
|
|
394
|
+
rule["violation_count"] = violations
|
|
395
|
+
rule["last_check"] = datetime.now()
|
|
396
|
+
|
|
397
|
+
# Check for resolved alerts
|
|
398
|
+
resolved_alerts = []
|
|
399
|
+
for alert_id, alert in list(self.active_alerts.items()):
|
|
400
|
+
# Check if alert condition is no longer met
|
|
401
|
+
rule = next((r for r in self.alert_rules if r["name"] in alert_id), None)
|
|
402
|
+
if rule:
|
|
403
|
+
recent_metrics = self.metrics_collector.get_metrics(
|
|
404
|
+
metric_type=rule["metric_type"],
|
|
405
|
+
start_time=datetime.now() - timedelta(minutes=1), # Check last minute
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
if recent_metrics:
|
|
409
|
+
latest_value = None
|
|
410
|
+
for metric in recent_metrics:
|
|
411
|
+
if isinstance(metric.value, (int, float)):
|
|
412
|
+
if not self._evaluate_condition(metric.value, rule["threshold"], rule["operator"]):
|
|
413
|
+
latest_value = metric.value
|
|
414
|
+
break
|
|
415
|
+
|
|
416
|
+
if latest_value is not None:
|
|
417
|
+
# Alert is resolved
|
|
418
|
+
alert.resolved = True
|
|
419
|
+
alert.resolved_at = datetime.now()
|
|
420
|
+
resolved_alerts.append(alert)
|
|
421
|
+
del self.active_alerts[alert_id]
|
|
422
|
+
|
|
423
|
+
return triggered_alerts
|
|
424
|
+
|
|
425
|
+
def _evaluate_condition(self, value: float, threshold: float, operator: str) -> bool:
|
|
426
|
+
"""Evaluate alert condition"""
|
|
427
|
+
if operator == "gt":
|
|
428
|
+
return value > threshold
|
|
429
|
+
elif operator == "lt":
|
|
430
|
+
return value < threshold
|
|
431
|
+
elif operator == "eq":
|
|
432
|
+
return value == threshold
|
|
433
|
+
elif operator == "ne":
|
|
434
|
+
return value != threshold
|
|
435
|
+
elif operator == "gte":
|
|
436
|
+
return value >= threshold
|
|
437
|
+
elif operator == "lte":
|
|
438
|
+
return value <= threshold
|
|
439
|
+
else:
|
|
440
|
+
return False
|
|
441
|
+
|
|
442
|
+
def add_alert_callback(self, callback: Callable[[Alert], None]) -> None:
|
|
443
|
+
"""Add a callback function to be triggered when alerts fire"""
|
|
444
|
+
self.alert_callbacks.append(callback)
|
|
445
|
+
|
|
446
|
+
def acknowledge_alert(self, alert_id: str) -> bool:
|
|
447
|
+
"""Acknowledge an alert"""
|
|
448
|
+
with self._lock:
|
|
449
|
+
if alert_id in self.active_alerts:
|
|
450
|
+
self.active_alerts[alert_id].acknowledged = True
|
|
451
|
+
self.active_alerts[alert_id].acknowledged_at = datetime.now()
|
|
452
|
+
return True
|
|
453
|
+
return False
|
|
454
|
+
|
|
455
|
+
def get_active_alerts(self, severity: Optional[AlertSeverity] = None) -> List[Alert]:
|
|
456
|
+
"""Get currently active alerts"""
|
|
457
|
+
alerts = list(self.active_alerts.values())
|
|
458
|
+
if severity:
|
|
459
|
+
alerts = [a for a in alerts if a.severity == severity]
|
|
460
|
+
return sorted(alerts, key=lambda a: (a.severity.value, a.timestamp), reverse=True)
|
|
461
|
+
|
|
462
|
+
def get_alert_history(self, hours: int = 24) -> List[Alert]:
|
|
463
|
+
"""Get alert history"""
|
|
464
|
+
cutoff_time = datetime.now() - timedelta(hours=hours)
|
|
465
|
+
return [a for a in self.alert_history if a.timestamp >= cutoff_time]
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
class PredictiveAnalytics:
|
|
469
|
+
"""Predictive analytics for system performance and user behavior"""
|
|
470
|
+
|
|
471
|
+
def __init__(self, metrics_collector: MetricsCollector):
|
|
472
|
+
self.metrics_collector = metrics_collector
|
|
473
|
+
self.models: Dict[str, Dict[str, Any]] = {}
|
|
474
|
+
self.predictions: Dict[str, Dict[str, Any]] = {}
|
|
475
|
+
|
|
476
|
+
def predict_metric_trend(
|
|
477
|
+
self,
|
|
478
|
+
metric_type: MetricType,
|
|
479
|
+
hours_ahead: int = 1,
|
|
480
|
+
confidence_threshold: float = 0.7,
|
|
481
|
+
) -> Dict[str, Any]:
|
|
482
|
+
"""Predict metric values for specified hours ahead"""
|
|
483
|
+
try:
|
|
484
|
+
# Get historical data
|
|
485
|
+
historical_metrics = self.metrics_collector.get_metrics(
|
|
486
|
+
metric_type=metric_type, start_time=datetime.now() - timedelta(hours=24)
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
if len(historical_metrics) < 10:
|
|
490
|
+
return {
|
|
491
|
+
"prediction": None,
|
|
492
|
+
"confidence": 0.0,
|
|
493
|
+
"reason": "Insufficient historical data",
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
# Extract numeric values
|
|
497
|
+
values = []
|
|
498
|
+
timestamps = []
|
|
499
|
+
for metric in historical_metrics:
|
|
500
|
+
if isinstance(metric.value, (int, float)):
|
|
501
|
+
values.append(metric.value)
|
|
502
|
+
timestamps.append(metric.timestamp)
|
|
503
|
+
|
|
504
|
+
if len(values) < 10:
|
|
505
|
+
return {
|
|
506
|
+
"prediction": None,
|
|
507
|
+
"confidence": 0.0,
|
|
508
|
+
"reason": "Insufficient numeric data points",
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
# Simple linear regression for prediction
|
|
512
|
+
import numpy as np
|
|
513
|
+
|
|
514
|
+
# Convert timestamps to numeric values (hours ago)
|
|
515
|
+
now = datetime.now()
|
|
516
|
+
x = np.array([(now - ts).total_seconds() / 3600 for ts in timestamps])
|
|
517
|
+
y = np.array(values)
|
|
518
|
+
|
|
519
|
+
# Fit linear model
|
|
520
|
+
coeffs = np.polyfit(x, y, 1)
|
|
521
|
+
|
|
522
|
+
# Predict future values
|
|
523
|
+
future_x = np.array([-h for h in range(1, hours_ahead + 1)])
|
|
524
|
+
future_y = np.polyval(coeffs, future_x)
|
|
525
|
+
|
|
526
|
+
# Calculate confidence based on R-squared
|
|
527
|
+
y_pred = np.polyval(coeffs, x)
|
|
528
|
+
ss_res = np.sum((y - y_pred) ** 2)
|
|
529
|
+
ss_tot = np.sum((y - np.mean(y)) ** 2)
|
|
530
|
+
r_squared = 1 - (ss_res / ss_tot) if ss_tot != 0 else 0
|
|
531
|
+
|
|
532
|
+
confidence = max(0, r_squared)
|
|
533
|
+
|
|
534
|
+
return {
|
|
535
|
+
"prediction": {
|
|
536
|
+
"future_values": future_y.tolist(),
|
|
537
|
+
"trend": ("increasing" if coeffs[0] > 0 else "decreasing" if coeffs[0] < 0 else "stable"),
|
|
538
|
+
"slope": coeffs[0],
|
|
539
|
+
},
|
|
540
|
+
"confidence": confidence,
|
|
541
|
+
"data_points": len(values),
|
|
542
|
+
"model_type": "linear_regression",
|
|
543
|
+
"reason": f"Linear regression on {len(values)} data points with R²={r_squared:.3f}",
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
except Exception as e:
|
|
547
|
+
logger.error(f"Error in predictive analytics: {e}")
|
|
548
|
+
return {
|
|
549
|
+
"prediction": None,
|
|
550
|
+
"confidence": 0.0,
|
|
551
|
+
"reason": f"Analysis error: {str(e)}",
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
def detect_anomalies(
|
|
555
|
+
self,
|
|
556
|
+
metric_type: MetricType,
|
|
557
|
+
z_score_threshold: float = 2.0,
|
|
558
|
+
window_minutes: int = 60,
|
|
559
|
+
) -> Dict[str, Any]:
|
|
560
|
+
"""Detect anomalies in metric data using statistical methods"""
|
|
561
|
+
try:
|
|
562
|
+
recent_metrics = self.metrics_collector.get_metrics(
|
|
563
|
+
metric_type=metric_type,
|
|
564
|
+
start_time=datetime.now() - timedelta(minutes=window_minutes),
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
values = []
|
|
568
|
+
for metric in recent_metrics:
|
|
569
|
+
if isinstance(metric.value, (int, float)):
|
|
570
|
+
values.append(metric.value)
|
|
571
|
+
|
|
572
|
+
if len(values) < 5:
|
|
573
|
+
return {
|
|
574
|
+
"anomalies": [],
|
|
575
|
+
"statistics": {},
|
|
576
|
+
"reason": "Insufficient data for anomaly detection",
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
import numpy as np
|
|
580
|
+
|
|
581
|
+
values_array = np.array(values)
|
|
582
|
+
mean = np.mean(values_array)
|
|
583
|
+
std = np.std(values_array)
|
|
584
|
+
|
|
585
|
+
if std == 0:
|
|
586
|
+
return {
|
|
587
|
+
"anomalies": [],
|
|
588
|
+
"statistics": {"mean": mean, "std": std},
|
|
589
|
+
"reason": "No variance in data",
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
# Detect anomalies using Z-score
|
|
593
|
+
z_scores = np.abs((values_array - mean) / std)
|
|
594
|
+
anomaly_indices = np.where(z_scores > z_score_threshold)[0]
|
|
595
|
+
|
|
596
|
+
anomalies = []
|
|
597
|
+
for i, idx in enumerate(anomaly_indices):
|
|
598
|
+
metric = recent_metrics[idx]
|
|
599
|
+
anomalies.append(
|
|
600
|
+
{
|
|
601
|
+
"timestamp": metric.timestamp.isoformat(),
|
|
602
|
+
"value": metric.value,
|
|
603
|
+
"z_score": float(z_scores[idx]),
|
|
604
|
+
"deviation": float(values[idx] - mean),
|
|
605
|
+
}
|
|
606
|
+
)
|
|
607
|
+
|
|
608
|
+
return {
|
|
609
|
+
"anomalies": anomalies,
|
|
610
|
+
"statistics": {
|
|
611
|
+
"mean": float(mean),
|
|
612
|
+
"std": float(std),
|
|
613
|
+
"min": float(np.min(values_array)),
|
|
614
|
+
"max": float(np.max(values_array)),
|
|
615
|
+
"count": len(values),
|
|
616
|
+
},
|
|
617
|
+
"threshold": z_score_threshold,
|
|
618
|
+
"reason": f"Found {len(anomalies)} anomalies using Z-score > {z_score_threshold}",
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
except Exception as e:
|
|
622
|
+
logger.error(f"Error in anomaly detection: {e}")
|
|
623
|
+
return {
|
|
624
|
+
"anomalies": [],
|
|
625
|
+
"statistics": {},
|
|
626
|
+
"reason": f"Analysis error: {str(e)}",
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
class PerformanceMonitor:
|
|
631
|
+
"""System performance monitoring"""
|
|
632
|
+
|
|
633
|
+
def __init__(self):
|
|
634
|
+
self.start_time = datetime.now()
|
|
635
|
+
self.metrics_collector = MetricsCollector()
|
|
636
|
+
self.alert_manager = AlertManager(self.metrics_collector)
|
|
637
|
+
self.predictive_analytics = PredictiveAnalytics(self.metrics_collector)
|
|
638
|
+
self._running = False
|
|
639
|
+
self._monitor_thread: Optional[threading.Thread] = None
|
|
640
|
+
self._monitor_interval = 30 # seconds
|
|
641
|
+
|
|
642
|
+
def start(self) -> None:
|
|
643
|
+
"""Start performance monitoring"""
|
|
644
|
+
if self._running:
|
|
645
|
+
return
|
|
646
|
+
|
|
647
|
+
self._running = True
|
|
648
|
+
self._monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
|
|
649
|
+
self._monitor_thread.start()
|
|
650
|
+
logger.info("Performance monitoring started")
|
|
651
|
+
|
|
652
|
+
def stop(self) -> None:
|
|
653
|
+
"""Stop performance monitoring"""
|
|
654
|
+
self._running = False
|
|
655
|
+
if self._monitor_thread:
|
|
656
|
+
self._monitor_thread.join(timeout=5)
|
|
657
|
+
logger.info("Performance monitoring stopped")
|
|
658
|
+
|
|
659
|
+
def _monitor_loop(self) -> None:
|
|
660
|
+
"""Main monitoring loop"""
|
|
661
|
+
while self._running:
|
|
662
|
+
try:
|
|
663
|
+
self._collect_system_metrics()
|
|
664
|
+
self._check_alerts()
|
|
665
|
+
time.sleep(self._monitor_interval)
|
|
666
|
+
except Exception as e:
|
|
667
|
+
logger.error(f"Error in monitoring loop: {e}")
|
|
668
|
+
time.sleep(self._monitor_interval)
|
|
669
|
+
|
|
670
|
+
def _collect_system_metrics(self) -> None:
|
|
671
|
+
"""Collect system performance metrics"""
|
|
672
|
+
try:
|
|
673
|
+
# CPU Usage
|
|
674
|
+
cpu_percent = psutil.cpu_percent(interval=1)
|
|
675
|
+
self.metrics_collector.add_metric(
|
|
676
|
+
MetricData(
|
|
677
|
+
timestamp=datetime.now(),
|
|
678
|
+
metric_type=MetricType.CPU_USAGE,
|
|
679
|
+
value=cpu_percent,
|
|
680
|
+
tags={"component": "system"},
|
|
681
|
+
source="psutil",
|
|
682
|
+
)
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
# Memory Usage
|
|
686
|
+
memory = psutil.virtual_memory()
|
|
687
|
+
self.metrics_collector.add_metric(
|
|
688
|
+
MetricData(
|
|
689
|
+
timestamp=datetime.now(),
|
|
690
|
+
metric_type=MetricType.MEMORY_USAGE,
|
|
691
|
+
value=memory.percent,
|
|
692
|
+
tags={"component": "system", "total_gb": memory.total / (1024**3)},
|
|
693
|
+
source="psutil",
|
|
694
|
+
)
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
# Python process memory
|
|
698
|
+
process = psutil.Process()
|
|
699
|
+
process_memory = process.memory_info()
|
|
700
|
+
self.metrics_collector.add_metric(
|
|
701
|
+
MetricData(
|
|
702
|
+
timestamp=datetime.now(),
|
|
703
|
+
metric_type=MetricType.MEMORY_USAGE,
|
|
704
|
+
value=process_memory.rss / (1024**2), # MB
|
|
705
|
+
tags={"component": "python_process"},
|
|
706
|
+
source="psutil",
|
|
707
|
+
)
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
# System load
|
|
711
|
+
load_avg = psutil.getloadavg()
|
|
712
|
+
self.metrics_collector.add_metric(
|
|
713
|
+
MetricData(
|
|
714
|
+
timestamp=datetime.now(),
|
|
715
|
+
metric_type=MetricType.SYSTEM_PERFORMANCE,
|
|
716
|
+
value=load_avg[0], # 1-minute load average
|
|
717
|
+
tags={"component": "system", "metric": "load_1min"},
|
|
718
|
+
source="psutil",
|
|
719
|
+
)
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
except Exception as e:
|
|
723
|
+
logger.error(f"Error collecting system metrics: {e}")
|
|
724
|
+
|
|
725
|
+
def _check_alerts(self) -> None:
|
|
726
|
+
"""Check for alerts"""
|
|
727
|
+
try:
|
|
728
|
+
alerts = self.alert_manager.check_alerts()
|
|
729
|
+
if alerts:
|
|
730
|
+
for alert in alerts:
|
|
731
|
+
logger.warning(f"Alert triggered: {alert.title} - {alert.current_value}")
|
|
732
|
+
|
|
733
|
+
except Exception as e:
|
|
734
|
+
logger.error(f"Error checking alerts: {e}")
|
|
735
|
+
|
|
736
|
+
def add_custom_metric(
|
|
737
|
+
self,
|
|
738
|
+
metric_type: MetricType,
|
|
739
|
+
value: Union[int, float],
|
|
740
|
+
tags: Optional[Dict[str, str]] = None,
|
|
741
|
+
source: str = "custom",
|
|
742
|
+
) -> None:
|
|
743
|
+
"""Add a custom metric"""
|
|
744
|
+
self.metrics_collector.add_metric(
|
|
745
|
+
MetricData(
|
|
746
|
+
timestamp=datetime.now(),
|
|
747
|
+
metric_type=metric_type,
|
|
748
|
+
value=value,
|
|
749
|
+
tags=tags or {},
|
|
750
|
+
source=source,
|
|
751
|
+
)
|
|
752
|
+
)
|
|
753
|
+
|
|
754
|
+
def get_system_health(self) -> SystemHealth:
|
|
755
|
+
"""Get overall system health status"""
|
|
756
|
+
try:
|
|
757
|
+
# Calculate component scores
|
|
758
|
+
component_scores = {}
|
|
759
|
+
|
|
760
|
+
# CPU health
|
|
761
|
+
cpu_metrics = self.metrics_collector.get_metrics(
|
|
762
|
+
MetricType.CPU_USAGE, start_time=datetime.now() - timedelta(minutes=5)
|
|
763
|
+
)
|
|
764
|
+
if cpu_metrics:
|
|
765
|
+
cpu_values = [m.value for m in cpu_metrics if isinstance(m.value, (int, float))]
|
|
766
|
+
if cpu_values:
|
|
767
|
+
avg_cpu = statistics.mean(cpu_values)
|
|
768
|
+
cpu_score = max(0, 100 - avg_cpu) # Lower CPU usage = higher score
|
|
769
|
+
component_scores["cpu"] = cpu_score
|
|
770
|
+
|
|
771
|
+
# Memory health
|
|
772
|
+
memory_metrics = self.metrics_collector.get_metrics(
|
|
773
|
+
MetricType.MEMORY_USAGE,
|
|
774
|
+
start_time=datetime.now() - timedelta(minutes=5),
|
|
775
|
+
)
|
|
776
|
+
if memory_metrics:
|
|
777
|
+
memory_values = [m.value for m in memory_metrics if isinstance(m.value, (int, float))]
|
|
778
|
+
if memory_values:
|
|
779
|
+
avg_memory = statistics.mean(memory_values)
|
|
780
|
+
memory_score = max(0, 100 - avg_memory) # Lower memory usage = higher score
|
|
781
|
+
component_scores["memory"] = memory_score
|
|
782
|
+
|
|
783
|
+
# Error rate health
|
|
784
|
+
error_metrics = self.metrics_collector.get_metrics(
|
|
785
|
+
MetricType.ERROR_RATE, start_time=datetime.now() - timedelta(minutes=10)
|
|
786
|
+
)
|
|
787
|
+
if error_metrics:
|
|
788
|
+
error_values = [m.value for m in error_metrics if isinstance(m.value, (int, float))]
|
|
789
|
+
if error_values:
|
|
790
|
+
avg_error = statistics.mean(error_values)
|
|
791
|
+
error_score = max(0, 100 - avg_error * 10) # Lower error rate = higher score
|
|
792
|
+
component_scores["error_rate"] = error_score
|
|
793
|
+
|
|
794
|
+
# Calculate overall score
|
|
795
|
+
if component_scores:
|
|
796
|
+
overall_score = statistics.mean(component_scores.values())
|
|
797
|
+
else:
|
|
798
|
+
overall_score = 100.0
|
|
799
|
+
|
|
800
|
+
# Determine health status
|
|
801
|
+
if overall_score >= 90:
|
|
802
|
+
status = HealthStatus.HEALTHY
|
|
803
|
+
elif overall_score >= 70:
|
|
804
|
+
status = HealthStatus.WARNING
|
|
805
|
+
elif overall_score >= 50:
|
|
806
|
+
status = HealthStatus.DEGRADED
|
|
807
|
+
elif overall_score >= 30:
|
|
808
|
+
status = HealthStatus.CRITICAL
|
|
809
|
+
else:
|
|
810
|
+
status = HealthStatus.DOWN
|
|
811
|
+
|
|
812
|
+
# Get active alerts
|
|
813
|
+
active_alerts = list(self.alert_manager.active_alerts.keys())
|
|
814
|
+
|
|
815
|
+
# Get recent metrics summary
|
|
816
|
+
recent_metrics = {}
|
|
817
|
+
for metric_type in [
|
|
818
|
+
MetricType.CPU_USAGE,
|
|
819
|
+
MetricType.MEMORY_USAGE,
|
|
820
|
+
MetricType.ERROR_RATE,
|
|
821
|
+
]:
|
|
822
|
+
recent_metric = self.metrics_collector.get_metrics(metric_type, limit=1)
|
|
823
|
+
if recent_metric and isinstance(recent_metric[0].value, (int, float)):
|
|
824
|
+
recent_metrics[metric_type.value] = recent_metric[0].value
|
|
825
|
+
|
|
826
|
+
return SystemHealth(
|
|
827
|
+
status=status,
|
|
828
|
+
timestamp=datetime.now(),
|
|
829
|
+
overall_score=overall_score,
|
|
830
|
+
component_scores=component_scores,
|
|
831
|
+
active_alerts=active_alerts,
|
|
832
|
+
recent_metrics=recent_metrics,
|
|
833
|
+
last_check=datetime.now(),
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
except Exception as e:
|
|
837
|
+
logger.error(f"Error calculating system health: {e}")
|
|
838
|
+
return SystemHealth(
|
|
839
|
+
status=HealthStatus.DOWN,
|
|
840
|
+
timestamp=datetime.now(),
|
|
841
|
+
overall_score=0.0,
|
|
842
|
+
last_check=datetime.now(),
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
def setup_default_alerts(self) -> None:
|
|
846
|
+
"""Setup default alert rules"""
|
|
847
|
+
# CPU usage alert
|
|
848
|
+
self.alert_manager.add_alert_rule(
|
|
849
|
+
name="High CPU Usage",
|
|
850
|
+
metric_type=MetricType.CPU_USAGE,
|
|
851
|
+
threshold=80.0,
|
|
852
|
+
operator="gt",
|
|
853
|
+
severity=AlertSeverity.HIGH,
|
|
854
|
+
window_minutes=5,
|
|
855
|
+
consecutive_violations=2,
|
|
856
|
+
tags={"component": "cpu"},
|
|
857
|
+
)
|
|
858
|
+
|
|
859
|
+
# Memory usage alert
|
|
860
|
+
self.alert_manager.add_alert_rule(
|
|
861
|
+
name="High Memory Usage",
|
|
862
|
+
metric_type=MetricType.MEMORY_USAGE,
|
|
863
|
+
threshold=85.0,
|
|
864
|
+
operator="gt",
|
|
865
|
+
severity=AlertSeverity.HIGH,
|
|
866
|
+
window_minutes=5,
|
|
867
|
+
consecutive_violations=2,
|
|
868
|
+
tags={"component": "memory"},
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
# Error rate alert
|
|
872
|
+
self.alert_manager.add_alert_rule(
|
|
873
|
+
name="High Error Rate",
|
|
874
|
+
metric_type=MetricType.ERROR_RATE,
|
|
875
|
+
threshold=5.0,
|
|
876
|
+
operator="gt",
|
|
877
|
+
severity=AlertSeverity.CRITICAL,
|
|
878
|
+
window_minutes=2,
|
|
879
|
+
consecutive_violations=1,
|
|
880
|
+
tags={"component": "errors"},
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
logger.info("Default alert rules configured")
|
|
884
|
+
|
|
885
|
+
|
|
886
|
+
class ComprehensiveMonitoringSystem:
|
|
887
|
+
"""Main monitoring system orchestrator"""
|
|
888
|
+
|
|
889
|
+
def __init__(self, config_file: Optional[Path] = None):
|
|
890
|
+
self.config_file = config_file or Path.cwd() / ".moai" / "config" / "monitoring.json"
|
|
891
|
+
self.config = self._load_config()
|
|
892
|
+
|
|
893
|
+
# Initialize components
|
|
894
|
+
self.metrics_collector = MetricsCollector(
|
|
895
|
+
buffer_size=self.config.get("buffer_size", 10000),
|
|
896
|
+
retention_hours=self.config.get("retention_hours", 24),
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
self.alert_manager = AlertManager(self.metrics_collector)
|
|
900
|
+
self.predictive_analytics = PredictiveAnalytics(self.metrics_collector)
|
|
901
|
+
self.performance_monitor = PerformanceMonitor()
|
|
902
|
+
|
|
903
|
+
# Initialize monitoring status
|
|
904
|
+
self._running = False
|
|
905
|
+
self._startup_time = datetime.now()
|
|
906
|
+
|
|
907
|
+
def _load_config(self) -> Dict[str, Any]:
|
|
908
|
+
"""Load monitoring configuration"""
|
|
909
|
+
default_config = {
|
|
910
|
+
"buffer_size": 10000,
|
|
911
|
+
"retention_hours": 24,
|
|
912
|
+
"monitor_interval": 30,
|
|
913
|
+
"alert_check_interval": 60,
|
|
914
|
+
"predictive_analysis_hours": 24,
|
|
915
|
+
"health_check_interval": 300,
|
|
916
|
+
"enable_predictions": True,
|
|
917
|
+
"enable_anomaly_detection": True,
|
|
918
|
+
"auto_optimization": False,
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
if self.config_file.exists():
|
|
922
|
+
try:
|
|
923
|
+
with open(self.config_file, "r") as f:
|
|
924
|
+
config = json.load(f)
|
|
925
|
+
default_config.update(config)
|
|
926
|
+
except Exception as e:
|
|
927
|
+
logger.error(f"Error loading monitoring config: {e}")
|
|
928
|
+
|
|
929
|
+
return default_config
|
|
930
|
+
|
|
931
|
+
def start(self) -> None:
|
|
932
|
+
"""Start the monitoring system"""
|
|
933
|
+
if self._running:
|
|
934
|
+
return
|
|
935
|
+
|
|
936
|
+
logger.info("Starting Comprehensive Monitoring System")
|
|
937
|
+
|
|
938
|
+
# Start performance monitoring
|
|
939
|
+
self.performance_monitor.start()
|
|
940
|
+
|
|
941
|
+
# Setup default alerts
|
|
942
|
+
self.performance_monitor.setup_default_alerts()
|
|
943
|
+
|
|
944
|
+
# Setup alert callbacks
|
|
945
|
+
self.alert_manager.add_alert_callback(self._handle_alert)
|
|
946
|
+
|
|
947
|
+
self._running = True
|
|
948
|
+
logger.info("Comprehensive Monitoring System started successfully")
|
|
949
|
+
|
|
950
|
+
def stop(self) -> None:
|
|
951
|
+
"""Stop the monitoring system"""
|
|
952
|
+
if not self._running:
|
|
953
|
+
return
|
|
954
|
+
|
|
955
|
+
logger.info("Stopping Comprehensive Monitoring System")
|
|
956
|
+
|
|
957
|
+
self.performance_monitor.stop()
|
|
958
|
+
self._running = False
|
|
959
|
+
|
|
960
|
+
logger.info("Comprehensive Monitoring System stopped")
|
|
961
|
+
|
|
962
|
+
def _handle_alert(self, alert: Alert) -> None:
|
|
963
|
+
"""Handle triggered alerts"""
|
|
964
|
+
logger.warning(f"ALERT: {alert.title} - {alert.description}")
|
|
965
|
+
|
|
966
|
+
# Here you could add additional alert handling:
|
|
967
|
+
# - Send notifications
|
|
968
|
+
# - Trigger automated responses
|
|
969
|
+
# - Log to external systems
|
|
970
|
+
# - Send to monitoring dashboard
|
|
971
|
+
|
|
972
|
+
def add_metric(
|
|
973
|
+
self,
|
|
974
|
+
metric_type: MetricType,
|
|
975
|
+
value: Union[int, float],
|
|
976
|
+
tags: Optional[Dict[str, str]] = None,
|
|
977
|
+
source: str = "user",
|
|
978
|
+
) -> None:
|
|
979
|
+
"""Add a custom metric"""
|
|
980
|
+
self.performance_monitor.add_custom_metric(metric_type, value, tags, source)
|
|
981
|
+
|
|
982
|
+
def get_dashboard_data(self) -> Dict[str, Any]:
|
|
983
|
+
"""Get data for monitoring dashboard"""
|
|
984
|
+
try:
|
|
985
|
+
# System health
|
|
986
|
+
health = self.performance_monitor.get_system_health()
|
|
987
|
+
|
|
988
|
+
# Active alerts
|
|
989
|
+
active_alerts = self.alert_manager.get_active_alerts()
|
|
990
|
+
|
|
991
|
+
# Recent metrics summary
|
|
992
|
+
recent_metrics = {}
|
|
993
|
+
for metric_type in [
|
|
994
|
+
MetricType.CPU_USAGE,
|
|
995
|
+
MetricType.MEMORY_USAGE,
|
|
996
|
+
MetricType.ERROR_RATE,
|
|
997
|
+
MetricType.RESPONSE_TIME,
|
|
998
|
+
]:
|
|
999
|
+
stats = self.metrics_collector.get_statistics(metric_type, minutes=60)
|
|
1000
|
+
if stats["count"] > 0:
|
|
1001
|
+
recent_metrics[metric_type.value] = stats
|
|
1002
|
+
|
|
1003
|
+
# Predictions
|
|
1004
|
+
predictions = {}
|
|
1005
|
+
if self.config.get("enable_predictions", True):
|
|
1006
|
+
for metric_type in [MetricType.CPU_USAGE, MetricType.MEMORY_USAGE]:
|
|
1007
|
+
pred = self.predictive_analytics.predict_metric_trend(metric_type, hours_ahead=1)
|
|
1008
|
+
if pred["confidence"] > 0.5:
|
|
1009
|
+
predictions[metric_type.value] = pred
|
|
1010
|
+
|
|
1011
|
+
return {
|
|
1012
|
+
"health": health.to_dict(),
|
|
1013
|
+
"active_alerts": [alert.to_dict() for alert in active_alerts],
|
|
1014
|
+
"recent_metrics": recent_metrics,
|
|
1015
|
+
"predictions": predictions,
|
|
1016
|
+
"uptime_seconds": (datetime.now() - self._startup_time).total_seconds(),
|
|
1017
|
+
"last_update": datetime.now().isoformat(),
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
except Exception as e:
|
|
1021
|
+
logger.error(f"Error getting dashboard data: {e}")
|
|
1022
|
+
return {"error": str(e), "last_update": datetime.now().isoformat()}
|
|
1023
|
+
|
|
1024
|
+
def get_analytics_report(self, hours: int = 24) -> Dict[str, Any]:
|
|
1025
|
+
"""Generate comprehensive analytics report"""
|
|
1026
|
+
try:
|
|
1027
|
+
# Overall metrics summary
|
|
1028
|
+
summary = {}
|
|
1029
|
+
for metric_type in MetricType:
|
|
1030
|
+
stats = self.metrics_collector.get_statistics(metric_type, minutes=hours * 60)
|
|
1031
|
+
if stats["count"] > 0:
|
|
1032
|
+
summary[metric_type.value] = stats
|
|
1033
|
+
|
|
1034
|
+
# Anomaly detection
|
|
1035
|
+
anomalies = {}
|
|
1036
|
+
if self.config.get("enable_anomaly_detection", True):
|
|
1037
|
+
for metric_type in [
|
|
1038
|
+
MetricType.CPU_USAGE,
|
|
1039
|
+
MetricType.MEMORY_USAGE,
|
|
1040
|
+
MetricType.ERROR_RATE,
|
|
1041
|
+
]:
|
|
1042
|
+
anomaly_result = self.predictive_analytics.detect_anomalies(metric_type)
|
|
1043
|
+
if anomaly_result["anomalies"]:
|
|
1044
|
+
anomalies[metric_type.value] = anomaly_result
|
|
1045
|
+
|
|
1046
|
+
# Alert summary
|
|
1047
|
+
alert_history = self.alert_manager.get_alert_history(hours=hours)
|
|
1048
|
+
by_severity: Dict[str, int] = {}
|
|
1049
|
+
by_metric_type: Dict[str, int] = {}
|
|
1050
|
+
|
|
1051
|
+
for alert in alert_history:
|
|
1052
|
+
severity_key = alert.severity.name
|
|
1053
|
+
by_severity[severity_key] = by_severity.get(severity_key, 0) + 1
|
|
1054
|
+
|
|
1055
|
+
metric_key = alert.metric_type.value
|
|
1056
|
+
by_metric_type[metric_key] = by_metric_type.get(metric_key, 0) + 1
|
|
1057
|
+
|
|
1058
|
+
alert_summary = {
|
|
1059
|
+
"total_alerts": len(alert_history),
|
|
1060
|
+
"by_severity": by_severity,
|
|
1061
|
+
"by_metric_type": by_metric_type,
|
|
1062
|
+
"resolved_count": sum(1 for a in alert_history if a.resolved),
|
|
1063
|
+
"acknowledged_count": sum(1 for a in alert_history if a.acknowledged),
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
return {
|
|
1067
|
+
"report_period_hours": hours,
|
|
1068
|
+
"generated_at": datetime.now().isoformat(),
|
|
1069
|
+
"metrics_summary": summary,
|
|
1070
|
+
"anomalies": anomalies,
|
|
1071
|
+
"alert_summary": alert_summary,
|
|
1072
|
+
"system_health": self.performance_monitor.get_system_health().to_dict(),
|
|
1073
|
+
"recommendations": self._generate_recommendations(summary, anomalies),
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
except Exception as e:
|
|
1077
|
+
logger.error(f"Error generating analytics report: {e}")
|
|
1078
|
+
return {"error": str(e), "generated_at": datetime.now().isoformat()}
|
|
1079
|
+
|
|
1080
|
+
def _generate_recommendations(self, metrics_summary: Dict[str, Any], anomalies: Dict[str, Any]) -> List[str]:
|
|
1081
|
+
"""Generate optimization recommendations based on metrics and anomalies"""
|
|
1082
|
+
recommendations = []
|
|
1083
|
+
|
|
1084
|
+
# CPU recommendations
|
|
1085
|
+
if MetricType.CPU_USAGE.value in metrics_summary:
|
|
1086
|
+
cpu_stats = metrics_summary[MetricType.CPU_USAGE.value]
|
|
1087
|
+
if cpu_stats["average"] > 70:
|
|
1088
|
+
recommendations.append("High CPU usage detected. Consider optimizing code or scaling resources.")
|
|
1089
|
+
|
|
1090
|
+
# Memory recommendations
|
|
1091
|
+
if MetricType.MEMORY_USAGE.value in metrics_summary:
|
|
1092
|
+
memory_stats = metrics_summary[MetricType.MEMORY_USAGE.value]
|
|
1093
|
+
if memory_stats["average"] > 80:
|
|
1094
|
+
recommendations.append(
|
|
1095
|
+
"High memory usage detected. Consider memory optimization or increasing available memory."
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
# Error rate recommendations
|
|
1099
|
+
if MetricType.ERROR_RATE.value in metrics_summary:
|
|
1100
|
+
error_stats = metrics_summary[MetricType.ERROR_RATE.value]
|
|
1101
|
+
if error_stats["average"] > 5:
|
|
1102
|
+
recommendations.append(
|
|
1103
|
+
"High error rate detected. Review error logs and implement better error handling."
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
# Anomaly recommendations
|
|
1107
|
+
if anomalies:
|
|
1108
|
+
recommendations.append(
|
|
1109
|
+
"Anomalies detected in system metrics. Review the detailed anomaly report for specific issues."
|
|
1110
|
+
)
|
|
1111
|
+
|
|
1112
|
+
return recommendations
|
|
1113
|
+
|
|
1114
|
+
|
|
1115
|
+
# Global instance for easy access
|
|
1116
|
+
_monitoring_system: Optional[ComprehensiveMonitoringSystem] = None
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
def get_monitoring_system() -> ComprehensiveMonitoringSystem:
|
|
1120
|
+
"""Get or create global monitoring system instance"""
|
|
1121
|
+
global _monitoring_system
|
|
1122
|
+
if _monitoring_system is None:
|
|
1123
|
+
_monitoring_system = ComprehensiveMonitoringSystem()
|
|
1124
|
+
return _monitoring_system
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
# Convenience functions
|
|
1128
|
+
def start_monitoring() -> None:
|
|
1129
|
+
"""Start the monitoring system"""
|
|
1130
|
+
system = get_monitoring_system()
|
|
1131
|
+
system.start()
|
|
1132
|
+
|
|
1133
|
+
|
|
1134
|
+
def stop_monitoring() -> None:
|
|
1135
|
+
"""Stop the monitoring system"""
|
|
1136
|
+
system = get_monitoring_system()
|
|
1137
|
+
system.stop()
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
def add_metric(
|
|
1141
|
+
metric_type: MetricType,
|
|
1142
|
+
value: Union[int, float],
|
|
1143
|
+
tags: Optional[Dict[str, str]] = None,
|
|
1144
|
+
source: str = "user",
|
|
1145
|
+
) -> None:
|
|
1146
|
+
"""Add a custom metric"""
|
|
1147
|
+
system = get_monitoring_system()
|
|
1148
|
+
system.add_metric(metric_type, value, tags, source)
|
|
1149
|
+
|
|
1150
|
+
|
|
1151
|
+
def get_dashboard_data() -> Dict[str, Any]:
|
|
1152
|
+
"""Get monitoring dashboard data"""
|
|
1153
|
+
system = get_monitoring_system()
|
|
1154
|
+
return system.get_dashboard_data()
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
if __name__ == "__main__":
|
|
1158
|
+
# Example usage
|
|
1159
|
+
print("Starting Comprehensive Monitoring System...")
|
|
1160
|
+
|
|
1161
|
+
monitoring = ComprehensiveMonitoringSystem()
|
|
1162
|
+
monitoring.start()
|
|
1163
|
+
|
|
1164
|
+
try:
|
|
1165
|
+
# Simulate some metrics
|
|
1166
|
+
for i in range(10):
|
|
1167
|
+
monitoring.add_metric(MetricType.CPU_USAGE, 50 + i * 3)
|
|
1168
|
+
monitoring.add_metric(MetricType.MEMORY_USAGE, 60 + i * 2)
|
|
1169
|
+
time.sleep(1)
|
|
1170
|
+
|
|
1171
|
+
# Get dashboard data
|
|
1172
|
+
dashboard_data = monitoring.get_dashboard_data()
|
|
1173
|
+
print(f"System Health: {dashboard_data['health']['status']}")
|
|
1174
|
+
print(f"Overall Score: {dashboard_data['health']['overall_score']}")
|
|
1175
|
+
print(f"Active Alerts: {len(dashboard_data['active_alerts'])}")
|
|
1176
|
+
|
|
1177
|
+
# Generate analytics report
|
|
1178
|
+
report = monitoring.get_analytics_report(hours=1)
|
|
1179
|
+
print(f"Analytics Report: {len(report['metrics_summary'])} metric types tracked")
|
|
1180
|
+
|
|
1181
|
+
finally:
|
|
1182
|
+
monitoring.stop()
|
|
1183
|
+
print("Monitoring stopped.")
|