tapps-agents 3.5.41__py3-none-any.whl → 3.6.0__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.
- tapps_agents/__init__.py +2 -2
- tapps_agents/agents/__init__.py +22 -22
- tapps_agents/agents/analyst/__init__.py +5 -5
- tapps_agents/agents/architect/__init__.py +5 -5
- tapps_agents/agents/architect/agent.py +1033 -1033
- tapps_agents/agents/architect/pattern_detector.py +75 -75
- tapps_agents/agents/cleanup/__init__.py +7 -7
- tapps_agents/agents/cleanup/agent.py +445 -445
- tapps_agents/agents/debugger/__init__.py +7 -7
- tapps_agents/agents/debugger/agent.py +310 -310
- tapps_agents/agents/debugger/error_analyzer.py +437 -437
- tapps_agents/agents/designer/__init__.py +5 -5
- tapps_agents/agents/designer/agent.py +786 -786
- tapps_agents/agents/designer/visual_designer.py +638 -638
- tapps_agents/agents/documenter/__init__.py +7 -7
- tapps_agents/agents/documenter/agent.py +531 -531
- tapps_agents/agents/documenter/doc_generator.py +472 -472
- tapps_agents/agents/documenter/doc_validator.py +393 -393
- tapps_agents/agents/documenter/framework_doc_updater.py +493 -493
- tapps_agents/agents/enhancer/__init__.py +7 -7
- tapps_agents/agents/evaluator/__init__.py +7 -7
- tapps_agents/agents/evaluator/agent.py +443 -443
- tapps_agents/agents/evaluator/priority_evaluator.py +641 -641
- tapps_agents/agents/evaluator/quality_analyzer.py +147 -147
- tapps_agents/agents/evaluator/report_generator.py +344 -344
- tapps_agents/agents/evaluator/usage_analyzer.py +192 -192
- tapps_agents/agents/evaluator/workflow_analyzer.py +189 -189
- tapps_agents/agents/implementer/__init__.py +7 -7
- tapps_agents/agents/implementer/agent.py +798 -798
- tapps_agents/agents/implementer/auto_fix.py +1119 -1119
- tapps_agents/agents/implementer/code_generator.py +73 -73
- tapps_agents/agents/improver/__init__.py +1 -1
- tapps_agents/agents/improver/agent.py +753 -753
- tapps_agents/agents/ops/__init__.py +1 -1
- tapps_agents/agents/ops/agent.py +619 -619
- tapps_agents/agents/ops/dependency_analyzer.py +600 -600
- tapps_agents/agents/orchestrator/__init__.py +5 -5
- tapps_agents/agents/orchestrator/agent.py +522 -522
- tapps_agents/agents/planner/__init__.py +7 -7
- tapps_agents/agents/planner/agent.py +1127 -1127
- tapps_agents/agents/reviewer/__init__.py +24 -24
- tapps_agents/agents/reviewer/agent.py +3513 -3513
- tapps_agents/agents/reviewer/aggregator.py +213 -213
- tapps_agents/agents/reviewer/batch_review.py +448 -448
- tapps_agents/agents/reviewer/cache.py +443 -443
- tapps_agents/agents/reviewer/context7_enhancer.py +630 -630
- tapps_agents/agents/reviewer/context_detector.py +203 -203
- tapps_agents/agents/reviewer/docker_compose_validator.py +158 -158
- tapps_agents/agents/reviewer/dockerfile_validator.py +176 -176
- tapps_agents/agents/reviewer/error_handling.py +126 -126
- tapps_agents/agents/reviewer/feedback_generator.py +490 -490
- tapps_agents/agents/reviewer/influxdb_validator.py +316 -316
- tapps_agents/agents/reviewer/issue_tracking.py +169 -169
- tapps_agents/agents/reviewer/library_detector.py +295 -295
- tapps_agents/agents/reviewer/library_patterns.py +268 -268
- tapps_agents/agents/reviewer/maintainability_scorer.py +593 -593
- tapps_agents/agents/reviewer/metric_strategies.py +276 -276
- tapps_agents/agents/reviewer/mqtt_validator.py +160 -160
- tapps_agents/agents/reviewer/output_enhancer.py +105 -105
- tapps_agents/agents/reviewer/pattern_detector.py +241 -241
- tapps_agents/agents/reviewer/performance_scorer.py +357 -357
- tapps_agents/agents/reviewer/phased_review.py +516 -516
- tapps_agents/agents/reviewer/progressive_review.py +435 -435
- tapps_agents/agents/reviewer/react_scorer.py +331 -331
- tapps_agents/agents/reviewer/score_constants.py +228 -228
- tapps_agents/agents/reviewer/score_validator.py +507 -507
- tapps_agents/agents/reviewer/scorer_registry.py +373 -373
- tapps_agents/agents/reviewer/scoring.py +1566 -1566
- tapps_agents/agents/reviewer/service_discovery.py +534 -534
- tapps_agents/agents/reviewer/tools/__init__.py +41 -41
- tapps_agents/agents/reviewer/tools/parallel_executor.py +581 -581
- tapps_agents/agents/reviewer/tools/ruff_grouping.py +250 -250
- tapps_agents/agents/reviewer/tools/scoped_mypy.py +284 -284
- tapps_agents/agents/reviewer/typescript_scorer.py +1142 -1142
- tapps_agents/agents/reviewer/validation.py +208 -208
- tapps_agents/agents/reviewer/websocket_validator.py +132 -132
- tapps_agents/agents/tester/__init__.py +7 -7
- tapps_agents/agents/tester/accessibility_auditor.py +309 -309
- tapps_agents/agents/tester/agent.py +1080 -1080
- tapps_agents/agents/tester/batch_generator.py +54 -54
- tapps_agents/agents/tester/context_learner.py +51 -51
- tapps_agents/agents/tester/coverage_analyzer.py +386 -386
- tapps_agents/agents/tester/coverage_test_generator.py +290 -290
- tapps_agents/agents/tester/debug_enhancer.py +238 -238
- tapps_agents/agents/tester/device_emulator.py +241 -241
- tapps_agents/agents/tester/integration_generator.py +62 -62
- tapps_agents/agents/tester/network_recorder.py +300 -300
- tapps_agents/agents/tester/performance_monitor.py +320 -320
- tapps_agents/agents/tester/test_fixer.py +316 -316
- tapps_agents/agents/tester/test_generator.py +632 -632
- tapps_agents/agents/tester/trace_manager.py +234 -234
- tapps_agents/agents/tester/visual_regression.py +291 -291
- tapps_agents/analysis/pattern_detector.py +36 -36
- tapps_agents/beads/hydration.py +213 -213
- tapps_agents/beads/parse.py +32 -32
- tapps_agents/beads/specs.py +206 -206
- tapps_agents/cli/__init__.py +9 -9
- tapps_agents/cli/__main__.py +8 -8
- tapps_agents/cli/base.py +478 -478
- tapps_agents/cli/command_classifier.py +72 -72
- tapps_agents/cli/commands/__init__.py +2 -2
- tapps_agents/cli/commands/analyst.py +173 -173
- tapps_agents/cli/commands/architect.py +109 -109
- tapps_agents/cli/commands/cleanup_agent.py +92 -92
- tapps_agents/cli/commands/common.py +126 -126
- tapps_agents/cli/commands/debugger.py +90 -90
- tapps_agents/cli/commands/designer.py +112 -112
- tapps_agents/cli/commands/documenter.py +136 -136
- tapps_agents/cli/commands/enhancer.py +110 -110
- tapps_agents/cli/commands/evaluator.py +255 -255
- tapps_agents/cli/commands/health.py +665 -665
- tapps_agents/cli/commands/implementer.py +301 -301
- tapps_agents/cli/commands/improver.py +91 -91
- tapps_agents/cli/commands/knowledge.py +111 -111
- tapps_agents/cli/commands/learning.py +172 -172
- tapps_agents/cli/commands/observability.py +283 -283
- tapps_agents/cli/commands/ops.py +135 -135
- tapps_agents/cli/commands/orchestrator.py +116 -116
- tapps_agents/cli/commands/planner.py +237 -237
- tapps_agents/cli/commands/reviewer.py +1872 -1872
- tapps_agents/cli/commands/status.py +285 -285
- tapps_agents/cli/commands/task.py +227 -227
- tapps_agents/cli/commands/tester.py +191 -191
- tapps_agents/cli/commands/top_level.py +3586 -3586
- tapps_agents/cli/feedback.py +936 -936
- tapps_agents/cli/formatters.py +608 -608
- tapps_agents/cli/help/__init__.py +7 -7
- tapps_agents/cli/help/static_help.py +425 -425
- tapps_agents/cli/network_detection.py +110 -110
- tapps_agents/cli/output_compactor.py +274 -274
- tapps_agents/cli/parsers/__init__.py +2 -2
- tapps_agents/cli/parsers/analyst.py +186 -186
- tapps_agents/cli/parsers/architect.py +167 -167
- tapps_agents/cli/parsers/cleanup_agent.py +228 -228
- tapps_agents/cli/parsers/debugger.py +116 -116
- tapps_agents/cli/parsers/designer.py +182 -182
- tapps_agents/cli/parsers/documenter.py +134 -134
- tapps_agents/cli/parsers/enhancer.py +113 -113
- tapps_agents/cli/parsers/evaluator.py +213 -213
- tapps_agents/cli/parsers/implementer.py +168 -168
- tapps_agents/cli/parsers/improver.py +132 -132
- tapps_agents/cli/parsers/ops.py +159 -159
- tapps_agents/cli/parsers/orchestrator.py +98 -98
- tapps_agents/cli/parsers/planner.py +145 -145
- tapps_agents/cli/parsers/reviewer.py +462 -462
- tapps_agents/cli/parsers/tester.py +124 -124
- tapps_agents/cli/progress_heartbeat.py +254 -254
- tapps_agents/cli/streaming_progress.py +336 -336
- tapps_agents/cli/utils/__init__.py +6 -6
- tapps_agents/cli/utils/agent_lifecycle.py +48 -48
- tapps_agents/cli/utils/error_formatter.py +82 -82
- tapps_agents/cli/utils/error_recovery.py +188 -188
- tapps_agents/cli/utils/output_handler.py +59 -59
- tapps_agents/cli/utils/prompt_enhancer.py +319 -319
- tapps_agents/cli/validators/__init__.py +9 -9
- tapps_agents/cli/validators/command_validator.py +81 -81
- tapps_agents/context7/__init__.py +112 -112
- tapps_agents/context7/agent_integration.py +869 -869
- tapps_agents/context7/analytics.py +382 -382
- tapps_agents/context7/analytics_dashboard.py +299 -299
- tapps_agents/context7/async_cache.py +681 -681
- tapps_agents/context7/backup_client.py +958 -958
- tapps_agents/context7/cache_locking.py +194 -194
- tapps_agents/context7/cache_metadata.py +214 -214
- tapps_agents/context7/cache_prewarm.py +488 -488
- tapps_agents/context7/cache_structure.py +168 -168
- tapps_agents/context7/cache_warming.py +604 -604
- tapps_agents/context7/circuit_breaker.py +376 -376
- tapps_agents/context7/cleanup.py +461 -461
- tapps_agents/context7/commands.py +858 -858
- tapps_agents/context7/credential_validation.py +276 -276
- tapps_agents/context7/cross_reference_resolver.py +168 -168
- tapps_agents/context7/cross_references.py +424 -424
- tapps_agents/context7/doc_manager.py +225 -225
- tapps_agents/context7/fuzzy_matcher.py +369 -369
- tapps_agents/context7/kb_cache.py +404 -404
- tapps_agents/context7/language_detector.py +219 -219
- tapps_agents/context7/library_detector.py +725 -725
- tapps_agents/context7/lookup.py +738 -738
- tapps_agents/context7/metadata.py +258 -258
- tapps_agents/context7/refresh_queue.py +300 -300
- tapps_agents/context7/security.py +373 -373
- tapps_agents/context7/staleness_policies.py +278 -278
- tapps_agents/context7/tiles_integration.py +47 -47
- tapps_agents/continuous_bug_fix/__init__.py +20 -20
- tapps_agents/continuous_bug_fix/bug_finder.py +306 -306
- tapps_agents/continuous_bug_fix/bug_fix_coordinator.py +177 -177
- tapps_agents/continuous_bug_fix/commit_manager.py +178 -178
- tapps_agents/continuous_bug_fix/continuous_bug_fixer.py +322 -322
- tapps_agents/continuous_bug_fix/proactive_bug_finder.py +285 -285
- tapps_agents/core/__init__.py +298 -298
- tapps_agents/core/adaptive_cache_config.py +432 -432
- tapps_agents/core/agent_base.py +647 -647
- tapps_agents/core/agent_cache.py +466 -466
- tapps_agents/core/agent_learning.py +1865 -1865
- tapps_agents/core/analytics_dashboard.py +563 -563
- tapps_agents/core/analytics_enhancements.py +597 -597
- tapps_agents/core/anonymization.py +274 -274
- tapps_agents/core/artifact_context_builder.py +293 -0
- tapps_agents/core/ast_parser.py +228 -228
- tapps_agents/core/async_file_ops.py +402 -402
- tapps_agents/core/best_practice_consultant.py +299 -299
- tapps_agents/core/brownfield_analyzer.py +299 -299
- tapps_agents/core/brownfield_review.py +541 -541
- tapps_agents/core/browser_controller.py +513 -513
- tapps_agents/core/capability_registry.py +418 -418
- tapps_agents/core/change_impact_analyzer.py +190 -190
- tapps_agents/core/checkpoint_manager.py +377 -377
- tapps_agents/core/code_generator.py +329 -329
- tapps_agents/core/code_validator.py +276 -276
- tapps_agents/core/command_registry.py +327 -327
- tapps_agents/core/config.py +33 -0
- tapps_agents/core/context_gathering/__init__.py +2 -2
- tapps_agents/core/context_gathering/repository_explorer.py +28 -28
- tapps_agents/core/context_intelligence/__init__.py +2 -2
- tapps_agents/core/context_intelligence/relevance_scorer.py +24 -24
- tapps_agents/core/context_intelligence/token_budget_manager.py +27 -27
- tapps_agents/core/context_manager.py +240 -240
- tapps_agents/core/cursor_feedback_monitor.py +146 -146
- tapps_agents/core/cursor_verification.py +290 -290
- tapps_agents/core/customization_loader.py +280 -280
- tapps_agents/core/customization_schema.py +260 -260
- tapps_agents/core/customization_template.py +238 -238
- tapps_agents/core/debug_logger.py +124 -124
- tapps_agents/core/design_validator.py +298 -298
- tapps_agents/core/diagram_generator.py +226 -226
- tapps_agents/core/docker_utils.py +232 -232
- tapps_agents/core/document_generator.py +617 -617
- tapps_agents/core/domain_detector.py +30 -30
- tapps_agents/core/error_envelope.py +454 -454
- tapps_agents/core/error_handler.py +270 -270
- tapps_agents/core/estimation_tracker.py +189 -189
- tapps_agents/core/eval_prompt_engine.py +116 -116
- tapps_agents/core/evaluation_base.py +119 -119
- tapps_agents/core/evaluation_models.py +320 -320
- tapps_agents/core/evaluation_orchestrator.py +225 -225
- tapps_agents/core/evaluators/__init__.py +7 -7
- tapps_agents/core/evaluators/architectural_evaluator.py +205 -205
- tapps_agents/core/evaluators/behavioral_evaluator.py +160 -160
- tapps_agents/core/evaluators/performance_profile_evaluator.py +160 -160
- tapps_agents/core/evaluators/security_posture_evaluator.py +148 -148
- tapps_agents/core/evaluators/spec_compliance_evaluator.py +181 -181
- tapps_agents/core/exceptions.py +107 -107
- tapps_agents/core/expert_config_generator.py +293 -293
- tapps_agents/core/export_schema.py +202 -202
- tapps_agents/core/external_feedback_models.py +102 -102
- tapps_agents/core/external_feedback_storage.py +213 -213
- tapps_agents/core/fallback_strategy.py +314 -314
- tapps_agents/core/feedback_analyzer.py +162 -162
- tapps_agents/core/feedback_collector.py +178 -178
- tapps_agents/core/git_operations.py +445 -445
- tapps_agents/core/hardware_profiler.py +151 -151
- tapps_agents/core/instructions.py +324 -324
- tapps_agents/core/io_guardrails.py +69 -69
- tapps_agents/core/issue_manifest.py +249 -249
- tapps_agents/core/issue_schema.py +139 -139
- tapps_agents/core/json_utils.py +128 -128
- tapps_agents/core/knowledge_graph.py +446 -446
- tapps_agents/core/language_detector.py +296 -296
- tapps_agents/core/learning_confidence.py +242 -242
- tapps_agents/core/learning_dashboard.py +246 -246
- tapps_agents/core/learning_decision.py +384 -384
- tapps_agents/core/learning_explainability.py +578 -578
- tapps_agents/core/learning_export.py +287 -287
- tapps_agents/core/learning_integration.py +228 -228
- tapps_agents/core/llm_behavior.py +232 -232
- tapps_agents/core/long_duration_support.py +786 -786
- tapps_agents/core/mcp_setup.py +106 -106
- tapps_agents/core/memory_integration.py +396 -396
- tapps_agents/core/meta_learning.py +666 -666
- tapps_agents/core/module_path_sanitizer.py +199 -199
- tapps_agents/core/multi_agent_orchestrator.py +382 -382
- tapps_agents/core/network_errors.py +125 -125
- tapps_agents/core/nfr_validator.py +336 -336
- tapps_agents/core/offline_mode.py +158 -158
- tapps_agents/core/output_contracts.py +300 -300
- tapps_agents/core/output_formatter.py +300 -300
- tapps_agents/core/path_normalizer.py +174 -174
- tapps_agents/core/path_validator.py +322 -322
- tapps_agents/core/pattern_library.py +250 -250
- tapps_agents/core/performance_benchmark.py +301 -301
- tapps_agents/core/performance_monitor.py +184 -184
- tapps_agents/core/playwright_mcp_controller.py +771 -771
- tapps_agents/core/policy_loader.py +135 -135
- tapps_agents/core/progress.py +166 -166
- tapps_agents/core/project_profile.py +354 -354
- tapps_agents/core/project_type_detector.py +454 -454
- tapps_agents/core/prompt_base.py +223 -223
- tapps_agents/core/prompt_learning/__init__.py +2 -2
- tapps_agents/core/prompt_learning/learning_loop.py +24 -24
- tapps_agents/core/prompt_learning/project_prompt_store.py +25 -25
- tapps_agents/core/prompt_learning/skills_prompt_analyzer.py +35 -35
- tapps_agents/core/prompt_optimization/__init__.py +6 -6
- tapps_agents/core/prompt_optimization/ab_tester.py +114 -114
- tapps_agents/core/prompt_optimization/correlation_analyzer.py +160 -160
- tapps_agents/core/prompt_optimization/progressive_refiner.py +129 -129
- tapps_agents/core/prompt_optimization/prompt_library.py +37 -37
- tapps_agents/core/requirements_evaluator.py +431 -431
- tapps_agents/core/resource_aware_executor.py +449 -449
- tapps_agents/core/resource_monitor.py +343 -343
- tapps_agents/core/resume_handler.py +298 -298
- tapps_agents/core/retry_handler.py +197 -197
- tapps_agents/core/review_checklists.py +479 -479
- tapps_agents/core/role_loader.py +201 -201
- tapps_agents/core/role_template_loader.py +201 -201
- tapps_agents/core/runtime_mode.py +60 -60
- tapps_agents/core/security_scanner.py +342 -342
- tapps_agents/core/skill_agent_registry.py +194 -194
- tapps_agents/core/skill_integration.py +208 -208
- tapps_agents/core/skill_loader.py +492 -492
- tapps_agents/core/skill_template.py +341 -341
- tapps_agents/core/skill_validator.py +478 -478
- tapps_agents/core/stack_analyzer.py +35 -35
- tapps_agents/core/startup.py +174 -174
- tapps_agents/core/storage_manager.py +397 -397
- tapps_agents/core/storage_models.py +166 -166
- tapps_agents/core/story_evaluator.py +410 -410
- tapps_agents/core/subprocess_utils.py +170 -170
- tapps_agents/core/task_duration.py +296 -296
- tapps_agents/core/task_memory.py +582 -582
- tapps_agents/core/task_state.py +226 -226
- tapps_agents/core/tech_stack_priorities.py +208 -208
- tapps_agents/core/temp_directory.py +194 -194
- tapps_agents/core/template_merger.py +600 -600
- tapps_agents/core/template_selector.py +280 -280
- tapps_agents/core/test_generator.py +286 -286
- tapps_agents/core/tiered_context.py +253 -253
- tapps_agents/core/token_monitor.py +345 -345
- tapps_agents/core/traceability.py +254 -254
- tapps_agents/core/trajectory_tracker.py +50 -50
- tapps_agents/core/unicode_safe.py +143 -143
- tapps_agents/core/unified_cache_config.py +170 -170
- tapps_agents/core/unified_state.py +324 -324
- tapps_agents/core/validate_cursor_setup.py +237 -237
- tapps_agents/core/validation_registry.py +136 -136
- tapps_agents/core/validators/__init__.py +4 -4
- tapps_agents/core/validators/python_validator.py +87 -87
- tapps_agents/core/verification_agent.py +90 -90
- tapps_agents/core/visual_feedback.py +644 -644
- tapps_agents/core/workflow_validator.py +197 -197
- tapps_agents/core/worktree.py +367 -367
- tapps_agents/docker/__init__.py +10 -10
- tapps_agents/docker/analyzer.py +186 -186
- tapps_agents/docker/debugger.py +229 -229
- tapps_agents/docker/error_patterns.py +216 -216
- tapps_agents/epic/__init__.py +22 -22
- tapps_agents/epic/beads_sync.py +115 -115
- tapps_agents/epic/markdown_sync.py +105 -105
- tapps_agents/epic/models.py +96 -96
- tapps_agents/experts/__init__.py +163 -163
- tapps_agents/experts/agent_integration.py +243 -243
- tapps_agents/experts/auto_generator.py +331 -331
- tapps_agents/experts/base_expert.py +536 -536
- tapps_agents/experts/builtin_registry.py +261 -261
- tapps_agents/experts/business_metrics.py +565 -565
- tapps_agents/experts/cache.py +266 -266
- tapps_agents/experts/confidence_breakdown.py +306 -306
- tapps_agents/experts/confidence_calculator.py +336 -336
- tapps_agents/experts/confidence_metrics.py +236 -236
- tapps_agents/experts/domain_config.py +311 -311
- tapps_agents/experts/domain_detector.py +550 -550
- tapps_agents/experts/domain_utils.py +84 -84
- tapps_agents/experts/expert_config.py +113 -113
- tapps_agents/experts/expert_engine.py +465 -465
- tapps_agents/experts/expert_registry.py +744 -744
- tapps_agents/experts/expert_synthesizer.py +70 -70
- tapps_agents/experts/governance.py +197 -197
- tapps_agents/experts/history_logger.py +312 -312
- tapps_agents/experts/knowledge/README.md +180 -180
- tapps_agents/experts/knowledge/accessibility/accessible-forms.md +331 -331
- tapps_agents/experts/knowledge/accessibility/aria-patterns.md +344 -344
- tapps_agents/experts/knowledge/accessibility/color-contrast.md +285 -285
- tapps_agents/experts/knowledge/accessibility/keyboard-navigation.md +332 -332
- tapps_agents/experts/knowledge/accessibility/screen-readers.md +282 -282
- tapps_agents/experts/knowledge/accessibility/semantic-html.md +355 -355
- tapps_agents/experts/knowledge/accessibility/testing-accessibility.md +369 -369
- tapps_agents/experts/knowledge/accessibility/wcag-2.1.md +296 -296
- tapps_agents/experts/knowledge/accessibility/wcag-2.2.md +211 -211
- tapps_agents/experts/knowledge/agent-learning/best-practices.md +715 -715
- tapps_agents/experts/knowledge/agent-learning/pattern-extraction.md +282 -282
- tapps_agents/experts/knowledge/agent-learning/prompt-optimization.md +320 -320
- tapps_agents/experts/knowledge/ai-frameworks/model-optimization.md +90 -90
- tapps_agents/experts/knowledge/ai-frameworks/openvino-patterns.md +260 -260
- tapps_agents/experts/knowledge/api-design-integration/api-gateway-patterns.md +309 -309
- tapps_agents/experts/knowledge/api-design-integration/api-security-patterns.md +521 -521
- tapps_agents/experts/knowledge/api-design-integration/api-versioning.md +421 -421
- tapps_agents/experts/knowledge/api-design-integration/async-protocol-patterns.md +61 -61
- tapps_agents/experts/knowledge/api-design-integration/contract-testing.md +221 -221
- tapps_agents/experts/knowledge/api-design-integration/external-api-integration.md +489 -489
- tapps_agents/experts/knowledge/api-design-integration/fastapi-patterns.md +360 -360
- tapps_agents/experts/knowledge/api-design-integration/fastapi-testing.md +262 -262
- tapps_agents/experts/knowledge/api-design-integration/graphql-patterns.md +582 -582
- tapps_agents/experts/knowledge/api-design-integration/grpc-best-practices.md +499 -499
- tapps_agents/experts/knowledge/api-design-integration/mqtt-patterns.md +455 -455
- tapps_agents/experts/knowledge/api-design-integration/rate-limiting.md +507 -507
- tapps_agents/experts/knowledge/api-design-integration/restful-api-design.md +618 -618
- tapps_agents/experts/knowledge/api-design-integration/websocket-patterns.md +480 -480
- tapps_agents/experts/knowledge/cloud-infrastructure/cloud-native-patterns.md +175 -175
- tapps_agents/experts/knowledge/cloud-infrastructure/container-health-checks.md +261 -261
- tapps_agents/experts/knowledge/cloud-infrastructure/containerization.md +222 -222
- tapps_agents/experts/knowledge/cloud-infrastructure/cost-optimization.md +122 -122
- tapps_agents/experts/knowledge/cloud-infrastructure/disaster-recovery.md +153 -153
- tapps_agents/experts/knowledge/cloud-infrastructure/dockerfile-patterns.md +285 -285
- tapps_agents/experts/knowledge/cloud-infrastructure/infrastructure-as-code.md +187 -187
- tapps_agents/experts/knowledge/cloud-infrastructure/kubernetes-patterns.md +253 -253
- tapps_agents/experts/knowledge/cloud-infrastructure/multi-cloud-strategies.md +155 -155
- tapps_agents/experts/knowledge/cloud-infrastructure/serverless-architecture.md +200 -200
- tapps_agents/experts/knowledge/code-quality-analysis/README.md +16 -16
- tapps_agents/experts/knowledge/code-quality-analysis/code-metrics.md +137 -137
- tapps_agents/experts/knowledge/code-quality-analysis/complexity-analysis.md +181 -181
- tapps_agents/experts/knowledge/code-quality-analysis/technical-debt-patterns.md +191 -191
- tapps_agents/experts/knowledge/data-privacy-compliance/anonymization.md +313 -313
- tapps_agents/experts/knowledge/data-privacy-compliance/ccpa.md +255 -255
- tapps_agents/experts/knowledge/data-privacy-compliance/consent-management.md +282 -282
- tapps_agents/experts/knowledge/data-privacy-compliance/data-minimization.md +275 -275
- tapps_agents/experts/knowledge/data-privacy-compliance/data-retention.md +297 -297
- tapps_agents/experts/knowledge/data-privacy-compliance/data-subject-rights.md +383 -383
- tapps_agents/experts/knowledge/data-privacy-compliance/encryption-privacy.md +285 -285
- tapps_agents/experts/knowledge/data-privacy-compliance/gdpr.md +344 -344
- tapps_agents/experts/knowledge/data-privacy-compliance/hipaa.md +385 -385
- tapps_agents/experts/knowledge/data-privacy-compliance/privacy-by-design.md +280 -280
- tapps_agents/experts/knowledge/database-data-management/acid-vs-cap.md +164 -164
- tapps_agents/experts/knowledge/database-data-management/backup-and-recovery.md +182 -182
- tapps_agents/experts/knowledge/database-data-management/data-modeling.md +172 -172
- tapps_agents/experts/knowledge/database-data-management/database-design.md +187 -187
- tapps_agents/experts/knowledge/database-data-management/flux-query-optimization.md +342 -342
- tapps_agents/experts/knowledge/database-data-management/influxdb-connection-patterns.md +432 -432
- tapps_agents/experts/knowledge/database-data-management/influxdb-patterns.md +442 -442
- tapps_agents/experts/knowledge/database-data-management/migration-strategies.md +216 -216
- tapps_agents/experts/knowledge/database-data-management/nosql-patterns.md +259 -259
- tapps_agents/experts/knowledge/database-data-management/scalability-patterns.md +184 -184
- tapps_agents/experts/knowledge/database-data-management/sql-optimization.md +175 -175
- tapps_agents/experts/knowledge/database-data-management/time-series-modeling.md +444 -444
- tapps_agents/experts/knowledge/development-workflow/README.md +16 -16
- tapps_agents/experts/knowledge/development-workflow/automation-best-practices.md +216 -216
- tapps_agents/experts/knowledge/development-workflow/build-strategies.md +198 -198
- tapps_agents/experts/knowledge/development-workflow/deployment-patterns.md +205 -205
- tapps_agents/experts/knowledge/development-workflow/git-workflows.md +205 -205
- tapps_agents/experts/knowledge/documentation-knowledge-management/README.md +16 -16
- tapps_agents/experts/knowledge/documentation-knowledge-management/api-documentation-patterns.md +231 -231
- tapps_agents/experts/knowledge/documentation-knowledge-management/documentation-standards.md +191 -191
- tapps_agents/experts/knowledge/documentation-knowledge-management/knowledge-management.md +171 -171
- tapps_agents/experts/knowledge/documentation-knowledge-management/technical-writing-guide.md +192 -192
- tapps_agents/experts/knowledge/observability-monitoring/alerting-patterns.md +461 -461
- tapps_agents/experts/knowledge/observability-monitoring/apm-tools.md +459 -459
- tapps_agents/experts/knowledge/observability-monitoring/distributed-tracing.md +367 -367
- tapps_agents/experts/knowledge/observability-monitoring/logging-strategies.md +478 -478
- tapps_agents/experts/knowledge/observability-monitoring/metrics-and-monitoring.md +510 -510
- tapps_agents/experts/knowledge/observability-monitoring/observability-best-practices.md +492 -492
- tapps_agents/experts/knowledge/observability-monitoring/open-telemetry.md +573 -573
- tapps_agents/experts/knowledge/observability-monitoring/slo-sli-sla.md +419 -419
- tapps_agents/experts/knowledge/performance/anti-patterns.md +284 -284
- tapps_agents/experts/knowledge/performance/api-performance.md +256 -256
- tapps_agents/experts/knowledge/performance/caching.md +327 -327
- tapps_agents/experts/knowledge/performance/database-performance.md +252 -252
- tapps_agents/experts/knowledge/performance/optimization-patterns.md +327 -327
- tapps_agents/experts/knowledge/performance/profiling.md +297 -297
- tapps_agents/experts/knowledge/performance/resource-management.md +293 -293
- tapps_agents/experts/knowledge/performance/scalability.md +306 -306
- tapps_agents/experts/knowledge/security/owasp-top10.md +209 -209
- tapps_agents/experts/knowledge/security/secure-coding-practices.md +207 -207
- tapps_agents/experts/knowledge/security/threat-modeling.md +220 -220
- tapps_agents/experts/knowledge/security/vulnerability-patterns.md +342 -342
- tapps_agents/experts/knowledge/software-architecture/docker-compose-patterns.md +314 -314
- tapps_agents/experts/knowledge/software-architecture/microservices-patterns.md +379 -379
- tapps_agents/experts/knowledge/software-architecture/service-communication.md +316 -316
- tapps_agents/experts/knowledge/testing/best-practices.md +310 -310
- tapps_agents/experts/knowledge/testing/coverage-analysis.md +293 -293
- tapps_agents/experts/knowledge/testing/mocking.md +256 -256
- tapps_agents/experts/knowledge/testing/test-automation.md +276 -276
- tapps_agents/experts/knowledge/testing/test-data.md +271 -271
- tapps_agents/experts/knowledge/testing/test-design-patterns.md +280 -280
- tapps_agents/experts/knowledge/testing/test-maintenance.md +236 -236
- tapps_agents/experts/knowledge/testing/test-strategies.md +311 -311
- tapps_agents/experts/knowledge/user-experience/information-architecture.md +325 -325
- tapps_agents/experts/knowledge/user-experience/interaction-design.md +363 -363
- tapps_agents/experts/knowledge/user-experience/prototyping.md +293 -293
- tapps_agents/experts/knowledge/user-experience/usability-heuristics.md +337 -337
- tapps_agents/experts/knowledge/user-experience/usability-testing.md +311 -311
- tapps_agents/experts/knowledge/user-experience/user-journeys.md +296 -296
- tapps_agents/experts/knowledge/user-experience/user-research.md +373 -373
- tapps_agents/experts/knowledge/user-experience/ux-principles.md +340 -340
- tapps_agents/experts/knowledge_freshness.py +321 -321
- tapps_agents/experts/knowledge_ingestion.py +438 -438
- tapps_agents/experts/knowledge_need_detector.py +93 -93
- tapps_agents/experts/knowledge_validator.py +382 -382
- tapps_agents/experts/observability.py +440 -440
- tapps_agents/experts/passive_notifier.py +238 -238
- tapps_agents/experts/proactive_orchestrator.py +32 -32
- tapps_agents/experts/rag_chunker.py +205 -205
- tapps_agents/experts/rag_embedder.py +152 -152
- tapps_agents/experts/rag_evaluation.py +299 -299
- tapps_agents/experts/rag_index.py +303 -303
- tapps_agents/experts/rag_metrics.py +293 -293
- tapps_agents/experts/rag_safety.py +263 -263
- tapps_agents/experts/report_generator.py +296 -296
- tapps_agents/experts/setup_wizard.py +441 -441
- tapps_agents/experts/simple_rag.py +431 -431
- tapps_agents/experts/vector_rag.py +354 -354
- tapps_agents/experts/weight_distributor.py +304 -304
- tapps_agents/health/__init__.py +24 -24
- tapps_agents/health/base.py +75 -75
- tapps_agents/health/checks/__init__.py +22 -22
- tapps_agents/health/checks/automation.py +127 -127
- tapps_agents/health/checks/context7_cache.py +210 -210
- tapps_agents/health/checks/environment.py +116 -116
- tapps_agents/health/checks/execution.py +170 -170
- tapps_agents/health/checks/knowledge_base.py +187 -187
- tapps_agents/health/checks/outcomes.py +324 -324
- tapps_agents/health/collector.py +280 -280
- tapps_agents/health/dashboard.py +137 -137
- tapps_agents/health/metrics.py +151 -151
- tapps_agents/health/orchestrator.py +271 -271
- tapps_agents/health/registry.py +166 -166
- tapps_agents/hooks/__init__.py +33 -33
- tapps_agents/hooks/config.py +140 -140
- tapps_agents/hooks/events.py +135 -135
- tapps_agents/hooks/executor.py +128 -128
- tapps_agents/hooks/manager.py +143 -143
- tapps_agents/integration/__init__.py +8 -8
- tapps_agents/integration/service_integrator.py +121 -121
- tapps_agents/integrations/__init__.py +10 -10
- tapps_agents/integrations/clawdbot.py +525 -525
- tapps_agents/integrations/memory_bridge.py +356 -356
- tapps_agents/mcp/__init__.py +18 -18
- tapps_agents/mcp/gateway.py +112 -112
- tapps_agents/mcp/servers/__init__.py +13 -13
- tapps_agents/mcp/servers/analysis.py +204 -204
- tapps_agents/mcp/servers/context7.py +198 -198
- tapps_agents/mcp/servers/filesystem.py +218 -218
- tapps_agents/mcp/servers/git.py +201 -201
- tapps_agents/mcp/tool_registry.py +115 -115
- tapps_agents/quality/__init__.py +54 -54
- tapps_agents/quality/coverage_analyzer.py +379 -379
- tapps_agents/quality/enforcement.py +82 -82
- tapps_agents/quality/gates/__init__.py +37 -37
- tapps_agents/quality/gates/approval_gate.py +255 -255
- tapps_agents/quality/gates/base.py +84 -84
- tapps_agents/quality/gates/exceptions.py +43 -43
- tapps_agents/quality/gates/policy_gate.py +195 -195
- tapps_agents/quality/gates/registry.py +239 -239
- tapps_agents/quality/gates/security_gate.py +156 -156
- tapps_agents/quality/quality_gates.py +369 -369
- tapps_agents/quality/secret_scanner.py +335 -335
- tapps_agents/session/__init__.py +19 -19
- tapps_agents/session/manager.py +256 -256
- tapps_agents/simple_mode/__init__.py +66 -66
- tapps_agents/simple_mode/agent_contracts.py +357 -357
- tapps_agents/simple_mode/beads_hooks.py +151 -151
- tapps_agents/simple_mode/code_snippet_handler.py +382 -382
- tapps_agents/simple_mode/documentation_manager.py +395 -395
- tapps_agents/simple_mode/documentation_reader.py +187 -187
- tapps_agents/simple_mode/file_inference.py +292 -292
- tapps_agents/simple_mode/framework_change_detector.py +268 -268
- tapps_agents/simple_mode/intent_parser.py +510 -510
- tapps_agents/simple_mode/learning_progression.py +358 -358
- tapps_agents/simple_mode/nl_handler.py +700 -700
- tapps_agents/simple_mode/onboarding.py +253 -253
- tapps_agents/simple_mode/orchestrators/__init__.py +38 -38
- tapps_agents/simple_mode/orchestrators/base.py +185 -185
- tapps_agents/simple_mode/orchestrators/breakdown_orchestrator.py +49 -49
- tapps_agents/simple_mode/orchestrators/brownfield_orchestrator.py +135 -135
- tapps_agents/simple_mode/orchestrators/build_orchestrator.py +2700 -2667
- tapps_agents/simple_mode/orchestrators/deliverable_checklist.py +349 -349
- tapps_agents/simple_mode/orchestrators/enhance_orchestrator.py +53 -53
- tapps_agents/simple_mode/orchestrators/epic_orchestrator.py +122 -122
- tapps_agents/simple_mode/orchestrators/explore_orchestrator.py +184 -184
- tapps_agents/simple_mode/orchestrators/fix_orchestrator.py +723 -723
- tapps_agents/simple_mode/orchestrators/plan_analysis_orchestrator.py +206 -206
- tapps_agents/simple_mode/orchestrators/pr_orchestrator.py +237 -237
- tapps_agents/simple_mode/orchestrators/refactor_orchestrator.py +222 -222
- tapps_agents/simple_mode/orchestrators/requirements_tracer.py +262 -262
- tapps_agents/simple_mode/orchestrators/resume_orchestrator.py +210 -210
- tapps_agents/simple_mode/orchestrators/review_orchestrator.py +161 -161
- tapps_agents/simple_mode/orchestrators/test_orchestrator.py +82 -82
- tapps_agents/simple_mode/output_aggregator.py +340 -340
- tapps_agents/simple_mode/result_formatters.py +598 -598
- tapps_agents/simple_mode/step_dependencies.py +382 -382
- tapps_agents/simple_mode/step_results.py +276 -276
- tapps_agents/simple_mode/streaming.py +388 -388
- tapps_agents/simple_mode/variations.py +129 -129
- tapps_agents/simple_mode/visual_feedback.py +238 -238
- tapps_agents/simple_mode/zero_config.py +274 -274
- tapps_agents/suggestions/__init__.py +8 -8
- tapps_agents/suggestions/inline_suggester.py +52 -52
- tapps_agents/templates/__init__.py +8 -8
- tapps_agents/templates/microservice_generator.py +274 -274
- tapps_agents/utils/env_validator.py +291 -291
- tapps_agents/workflow/__init__.py +171 -171
- tapps_agents/workflow/acceptance_verifier.py +132 -132
- tapps_agents/workflow/agent_handlers/__init__.py +41 -41
- tapps_agents/workflow/agent_handlers/analyst_handler.py +75 -75
- tapps_agents/workflow/agent_handlers/architect_handler.py +107 -107
- tapps_agents/workflow/agent_handlers/base.py +84 -84
- tapps_agents/workflow/agent_handlers/debugger_handler.py +100 -100
- tapps_agents/workflow/agent_handlers/designer_handler.py +110 -110
- tapps_agents/workflow/agent_handlers/documenter_handler.py +94 -94
- tapps_agents/workflow/agent_handlers/implementer_handler.py +235 -235
- tapps_agents/workflow/agent_handlers/ops_handler.py +62 -62
- tapps_agents/workflow/agent_handlers/orchestrator_handler.py +43 -43
- tapps_agents/workflow/agent_handlers/planner_handler.py +98 -98
- tapps_agents/workflow/agent_handlers/registry.py +119 -119
- tapps_agents/workflow/agent_handlers/reviewer_handler.py +119 -119
- tapps_agents/workflow/agent_handlers/tester_handler.py +69 -69
- tapps_agents/workflow/analytics_accessor.py +337 -337
- tapps_agents/workflow/analytics_alerts.py +416 -416
- tapps_agents/workflow/analytics_dashboard_cursor.py +281 -281
- tapps_agents/workflow/analytics_dual_write.py +103 -103
- tapps_agents/workflow/analytics_integration.py +119 -119
- tapps_agents/workflow/analytics_query_parser.py +278 -278
- tapps_agents/workflow/analytics_visualizer.py +259 -259
- tapps_agents/workflow/artifact_helper.py +204 -204
- tapps_agents/workflow/audit_logger.py +263 -263
- tapps_agents/workflow/auto_execution_config.py +340 -340
- tapps_agents/workflow/auto_progression.py +586 -586
- tapps_agents/workflow/branch_cleanup.py +349 -349
- tapps_agents/workflow/checkpoint.py +256 -256
- tapps_agents/workflow/checkpoint_manager.py +178 -178
- tapps_agents/workflow/code_artifact.py +179 -179
- tapps_agents/workflow/common_enums.py +96 -96
- tapps_agents/workflow/confirmation_handler.py +130 -130
- tapps_agents/workflow/context_analyzer.py +222 -222
- tapps_agents/workflow/context_artifact.py +230 -230
- tapps_agents/workflow/cursor_chat.py +94 -94
- tapps_agents/workflow/cursor_executor.py +2337 -2337
- tapps_agents/workflow/cursor_skill_helper.py +516 -516
- tapps_agents/workflow/dependency_resolver.py +244 -244
- tapps_agents/workflow/design_artifact.py +156 -156
- tapps_agents/workflow/detector.py +751 -751
- tapps_agents/workflow/direct_execution_fallback.py +301 -301
- tapps_agents/workflow/docs_artifact.py +168 -168
- tapps_agents/workflow/enforcer.py +389 -389
- tapps_agents/workflow/enhancement_artifact.py +142 -142
- tapps_agents/workflow/error_recovery.py +806 -806
- tapps_agents/workflow/event_bus.py +183 -183
- tapps_agents/workflow/event_log.py +612 -612
- tapps_agents/workflow/events.py +63 -63
- tapps_agents/workflow/exceptions.py +43 -43
- tapps_agents/workflow/execution_graph.py +498 -498
- tapps_agents/workflow/execution_plan.py +126 -126
- tapps_agents/workflow/file_utils.py +186 -186
- tapps_agents/workflow/gate_evaluator.py +182 -182
- tapps_agents/workflow/gate_integration.py +200 -200
- tapps_agents/workflow/graph_visualizer.py +130 -130
- tapps_agents/workflow/health_checker.py +206 -206
- tapps_agents/workflow/logging_helper.py +243 -243
- tapps_agents/workflow/manifest.py +582 -582
- tapps_agents/workflow/marker_writer.py +250 -250
- tapps_agents/workflow/message_formatter.py +188 -188
- tapps_agents/workflow/messaging.py +325 -325
- tapps_agents/workflow/metadata_models.py +91 -91
- tapps_agents/workflow/metrics_integration.py +226 -226
- tapps_agents/workflow/migration_utils.py +116 -116
- tapps_agents/workflow/models.py +148 -148
- tapps_agents/workflow/nlp_config.py +198 -198
- tapps_agents/workflow/nlp_error_handler.py +207 -207
- tapps_agents/workflow/nlp_executor.py +163 -163
- tapps_agents/workflow/nlp_parser.py +528 -528
- tapps_agents/workflow/observability_dashboard.py +451 -451
- tapps_agents/workflow/observer.py +170 -170
- tapps_agents/workflow/ops_artifact.py +257 -257
- tapps_agents/workflow/output_passing.py +214 -214
- tapps_agents/workflow/parallel_executor.py +463 -463
- tapps_agents/workflow/planning_artifact.py +179 -179
- tapps_agents/workflow/preset_loader.py +285 -285
- tapps_agents/workflow/preset_recommender.py +270 -270
- tapps_agents/workflow/progress_logger.py +145 -145
- tapps_agents/workflow/progress_manager.py +303 -303
- tapps_agents/workflow/progress_monitor.py +186 -186
- tapps_agents/workflow/progress_updates.py +423 -423
- tapps_agents/workflow/quality_artifact.py +158 -158
- tapps_agents/workflow/quality_loopback.py +101 -101
- tapps_agents/workflow/recommender.py +387 -387
- tapps_agents/workflow/remediation_loop.py +166 -166
- tapps_agents/workflow/result_aggregator.py +300 -300
- tapps_agents/workflow/review_artifact.py +185 -185
- tapps_agents/workflow/schema_validator.py +522 -522
- tapps_agents/workflow/session_handoff.py +178 -178
- tapps_agents/workflow/skill_invoker.py +648 -648
- tapps_agents/workflow/state_manager.py +756 -756
- tapps_agents/workflow/state_persistence_config.py +331 -331
- tapps_agents/workflow/status_monitor.py +449 -449
- tapps_agents/workflow/step_checkpoint.py +314 -314
- tapps_agents/workflow/step_details.py +201 -201
- tapps_agents/workflow/story_models.py +147 -147
- tapps_agents/workflow/streaming.py +416 -416
- tapps_agents/workflow/suggestion_engine.py +552 -552
- tapps_agents/workflow/testing_artifact.py +186 -186
- tapps_agents/workflow/timeline.py +158 -158
- tapps_agents/workflow/token_integration.py +209 -209
- tapps_agents/workflow/validation.py +217 -217
- tapps_agents/workflow/visual_feedback.py +391 -391
- tapps_agents/workflow/workflow_chain.py +95 -95
- tapps_agents/workflow/workflow_summary.py +219 -219
- tapps_agents/workflow/worktree_manager.py +724 -724
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/METADATA +672 -672
- tapps_agents-3.6.0.dist-info/RECORD +758 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/licenses/LICENSE +22 -22
- tapps_agents/health/checks/outcomes.backup_20260204_064058.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064256.py +0 -324
- tapps_agents/health/checks/outcomes.backup_20260204_064600.py +0 -324
- tapps_agents-3.5.41.dist-info/RECORD +0 -760
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/WHEEL +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/entry_points.txt +0 -0
- {tapps_agents-3.5.41.dist-info → tapps_agents-3.6.0.dist-info}/top_level.txt +0 -0
|
@@ -1,597 +1,597 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Enhanced Analytics Dashboard and Access Layer
|
|
3
|
-
|
|
4
|
-
High-quality enhancements to analytics collection and reporting:
|
|
5
|
-
- Date range filtering
|
|
6
|
-
- Enhanced aggregation methods
|
|
7
|
-
- Performance optimizations
|
|
8
|
-
- Better error handling
|
|
9
|
-
- Metrics validation
|
|
10
|
-
- Query optimization
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
|
-
import json
|
|
16
|
-
import logging
|
|
17
|
-
from collections import defaultdict
|
|
18
|
-
from dataclasses import asdict, dataclass
|
|
19
|
-
from datetime import UTC, datetime, timedelta
|
|
20
|
-
from pathlib import Path
|
|
21
|
-
from typing import Any
|
|
22
|
-
|
|
23
|
-
logger = logging.getLogger(__name__)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@dataclass
|
|
27
|
-
class DateRange:
|
|
28
|
-
"""Date range for filtering metrics."""
|
|
29
|
-
|
|
30
|
-
start: datetime | None = None
|
|
31
|
-
end: datetime | None = None
|
|
32
|
-
|
|
33
|
-
def contains(self, timestamp: datetime) -> bool:
|
|
34
|
-
"""Check if timestamp is within range."""
|
|
35
|
-
if self.start and timestamp < self.start:
|
|
36
|
-
return False
|
|
37
|
-
if self.end and timestamp > self.end:
|
|
38
|
-
return False
|
|
39
|
-
return True
|
|
40
|
-
|
|
41
|
-
def to_dict(self) -> dict[str, Any]:
|
|
42
|
-
"""Convert to dictionary."""
|
|
43
|
-
return {
|
|
44
|
-
"start": self.start.isoformat() if self.start else None,
|
|
45
|
-
"end": self.end.isoformat() if self.end else None,
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class EnhancedAnalyticsCollector:
|
|
50
|
-
"""
|
|
51
|
-
Enhanced analytics collector with improved performance and filtering.
|
|
52
|
-
|
|
53
|
-
Features:
|
|
54
|
-
- Date range filtering
|
|
55
|
-
- Enhanced aggregation
|
|
56
|
-
- Performance optimizations
|
|
57
|
-
- Better error handling
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
def __init__(self, analytics_dir: Path | None = None):
|
|
61
|
-
"""
|
|
62
|
-
Initialize enhanced analytics collector.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
analytics_dir: Directory to store analytics data (defaults to .tapps-agents/analytics)
|
|
66
|
-
"""
|
|
67
|
-
if analytics_dir is None:
|
|
68
|
-
analytics_dir = Path(".tapps-agents/analytics")
|
|
69
|
-
|
|
70
|
-
self.analytics_dir = Path(analytics_dir)
|
|
71
|
-
self.analytics_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
-
|
|
73
|
-
self.metrics_file = self.analytics_dir / "metrics.json"
|
|
74
|
-
self.history_dir = self.analytics_dir / "history"
|
|
75
|
-
self.history_dir.mkdir(parents=True, exist_ok=True)
|
|
76
|
-
|
|
77
|
-
def record_agent_execution(
|
|
78
|
-
self,
|
|
79
|
-
agent_id: str,
|
|
80
|
-
agent_name: str,
|
|
81
|
-
duration: float,
|
|
82
|
-
success: bool,
|
|
83
|
-
timestamp: datetime | None = None,
|
|
84
|
-
metadata: dict[str, Any] | None = None,
|
|
85
|
-
):
|
|
86
|
-
"""
|
|
87
|
-
Record an agent execution with enhanced error handling.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
agent_id: Agent identifier
|
|
91
|
-
agent_name: Agent name
|
|
92
|
-
duration: Execution duration in seconds
|
|
93
|
-
success: Whether execution was successful
|
|
94
|
-
timestamp: Execution timestamp (defaults to now)
|
|
95
|
-
metadata: Optional metadata dictionary
|
|
96
|
-
"""
|
|
97
|
-
if timestamp is None:
|
|
98
|
-
timestamp = datetime.now(UTC)
|
|
99
|
-
|
|
100
|
-
# Validate inputs
|
|
101
|
-
if duration < 0:
|
|
102
|
-
logger.warning(f"Negative duration for agent {agent_id}: {duration}")
|
|
103
|
-
duration = 0.0
|
|
104
|
-
|
|
105
|
-
record = {
|
|
106
|
-
"agent_id": agent_id,
|
|
107
|
-
"agent_name": agent_name,
|
|
108
|
-
"duration": duration,
|
|
109
|
-
"success": success,
|
|
110
|
-
"timestamp": timestamp.isoformat(),
|
|
111
|
-
"metadata": metadata or {},
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
# Append to history with error handling
|
|
115
|
-
try:
|
|
116
|
-
history_file = (
|
|
117
|
-
self.history_dir / f"agents-{timestamp.strftime('%Y-%m-%d')}.jsonl"
|
|
118
|
-
)
|
|
119
|
-
with open(history_file, "a", encoding="utf-8") as f:
|
|
120
|
-
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
121
|
-
logger.debug(f"Recorded agent execution: {agent_id}")
|
|
122
|
-
except Exception as e:
|
|
123
|
-
logger.error(f"Failed to record agent execution {agent_id}: {e}", exc_info=True)
|
|
124
|
-
|
|
125
|
-
def record_workflow_execution(
|
|
126
|
-
self,
|
|
127
|
-
workflow_id: str,
|
|
128
|
-
workflow_name: str,
|
|
129
|
-
duration: float,
|
|
130
|
-
steps: int,
|
|
131
|
-
success: bool,
|
|
132
|
-
timestamp: datetime | None = None,
|
|
133
|
-
metadata: dict[str, Any] | None = None,
|
|
134
|
-
):
|
|
135
|
-
"""
|
|
136
|
-
Record a workflow execution with enhanced error handling.
|
|
137
|
-
|
|
138
|
-
Args:
|
|
139
|
-
workflow_id: Workflow identifier
|
|
140
|
-
workflow_name: Workflow name
|
|
141
|
-
duration: Execution duration in seconds
|
|
142
|
-
steps: Number of steps executed
|
|
143
|
-
success: Whether execution was successful
|
|
144
|
-
timestamp: Execution timestamp (defaults to now)
|
|
145
|
-
metadata: Optional metadata dictionary
|
|
146
|
-
"""
|
|
147
|
-
if timestamp is None:
|
|
148
|
-
timestamp = datetime.now(UTC)
|
|
149
|
-
|
|
150
|
-
# Validate inputs
|
|
151
|
-
if duration < 0:
|
|
152
|
-
logger.warning(f"Negative duration for workflow {workflow_id}: {duration}")
|
|
153
|
-
duration = 0.0
|
|
154
|
-
if steps < 0:
|
|
155
|
-
logger.warning(f"Negative steps for workflow {workflow_id}: {steps}")
|
|
156
|
-
steps = 0
|
|
157
|
-
|
|
158
|
-
record = {
|
|
159
|
-
"workflow_id": workflow_id,
|
|
160
|
-
"workflow_name": workflow_name,
|
|
161
|
-
"duration": duration,
|
|
162
|
-
"steps": steps,
|
|
163
|
-
"success": success,
|
|
164
|
-
"timestamp": timestamp.isoformat(),
|
|
165
|
-
"metadata": metadata or {},
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
# Append to history with error handling
|
|
169
|
-
try:
|
|
170
|
-
history_file = (
|
|
171
|
-
self.history_dir / f"workflows-{timestamp.strftime('%Y-%m-%d')}.jsonl"
|
|
172
|
-
)
|
|
173
|
-
with open(history_file, "a", encoding="utf-8") as f:
|
|
174
|
-
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
175
|
-
logger.debug(f"Recorded workflow execution: {workflow_id}")
|
|
176
|
-
except Exception as e:
|
|
177
|
-
logger.error(f"Failed to record workflow execution {workflow_id}: {e}", exc_info=True)
|
|
178
|
-
|
|
179
|
-
def get_agent_metrics(
|
|
180
|
-
self,
|
|
181
|
-
agent_id: str | None = None,
|
|
182
|
-
days: int = 30,
|
|
183
|
-
date_range: DateRange | None = None,
|
|
184
|
-
) -> list[dict[str, Any]]:
|
|
185
|
-
"""
|
|
186
|
-
Get agent performance metrics with date range filtering.
|
|
187
|
-
|
|
188
|
-
Args:
|
|
189
|
-
agent_id: Optional agent ID to filter by
|
|
190
|
-
days: Number of days to look back (ignored if date_range provided)
|
|
191
|
-
date_range: Optional date range filter
|
|
192
|
-
|
|
193
|
-
Returns:
|
|
194
|
-
List of agent metrics dictionaries
|
|
195
|
-
"""
|
|
196
|
-
# Use date_range if provided, otherwise calculate from days
|
|
197
|
-
if date_range:
|
|
198
|
-
start_date = date_range.start
|
|
199
|
-
end_date = date_range.end
|
|
200
|
-
else:
|
|
201
|
-
end_date = datetime.now(UTC)
|
|
202
|
-
start_date = end_date - timedelta(days=days)
|
|
203
|
-
|
|
204
|
-
agent_data: dict[str, dict[str, Any]] = defaultdict(
|
|
205
|
-
lambda: {
|
|
206
|
-
"total": 0,
|
|
207
|
-
"successful": 0,
|
|
208
|
-
"failed": 0,
|
|
209
|
-
"durations": [],
|
|
210
|
-
"last_execution": None,
|
|
211
|
-
"agent_name": None,
|
|
212
|
-
}
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
# Calculate date range for file filtering
|
|
216
|
-
current_date = start_date
|
|
217
|
-
files_processed = 0
|
|
218
|
-
|
|
219
|
-
while current_date <= end_date:
|
|
220
|
-
date_str = current_date.strftime("%Y-%m-%d")
|
|
221
|
-
history_file = self.history_dir / f"agents-{date_str}.jsonl"
|
|
222
|
-
|
|
223
|
-
if history_file.exists():
|
|
224
|
-
try:
|
|
225
|
-
with open(history_file, encoding="utf-8") as f:
|
|
226
|
-
for line in f:
|
|
227
|
-
try:
|
|
228
|
-
record = json.loads(line.strip())
|
|
229
|
-
|
|
230
|
-
# Parse timestamp
|
|
231
|
-
record_timestamp = datetime.fromisoformat(
|
|
232
|
-
record["timestamp"].replace("Z", "+00:00")
|
|
233
|
-
)
|
|
234
|
-
|
|
235
|
-
# Apply date range filter
|
|
236
|
-
if date_range and not date_range.contains(record_timestamp):
|
|
237
|
-
continue
|
|
238
|
-
|
|
239
|
-
# Apply agent filter
|
|
240
|
-
if agent_id and record["agent_id"] != agent_id:
|
|
241
|
-
continue
|
|
242
|
-
|
|
243
|
-
agent_key = record["agent_id"]
|
|
244
|
-
agent_data[agent_key]["agent_name"] = record.get(
|
|
245
|
-
"agent_name", agent_key
|
|
246
|
-
)
|
|
247
|
-
agent_data[agent_key]["total"] += 1
|
|
248
|
-
|
|
249
|
-
if record["success"]:
|
|
250
|
-
agent_data[agent_key]["successful"] += 1
|
|
251
|
-
else:
|
|
252
|
-
agent_data[agent_key]["failed"] += 1
|
|
253
|
-
|
|
254
|
-
agent_data[agent_key]["durations"].append(record["duration"])
|
|
255
|
-
|
|
256
|
-
if (
|
|
257
|
-
agent_data[agent_key]["last_execution"] is None
|
|
258
|
-
or record_timestamp > agent_data[agent_key]["last_execution"]
|
|
259
|
-
):
|
|
260
|
-
agent_data[agent_key]["last_execution"] = record_timestamp
|
|
261
|
-
|
|
262
|
-
except Exception as e:
|
|
263
|
-
logger.debug(f"Failed to parse agent record: {e}")
|
|
264
|
-
continue
|
|
265
|
-
|
|
266
|
-
files_processed += 1
|
|
267
|
-
except Exception as e:
|
|
268
|
-
logger.warning(f"Failed to read {history_file}: {e}")
|
|
269
|
-
|
|
270
|
-
current_date += timedelta(days=1)
|
|
271
|
-
|
|
272
|
-
# Convert to metrics
|
|
273
|
-
metrics = []
|
|
274
|
-
for agent_id_key, data in agent_data.items():
|
|
275
|
-
durations = data["durations"]
|
|
276
|
-
total_duration = sum(durations)
|
|
277
|
-
avg_duration = total_duration / len(durations) if durations else 0.0
|
|
278
|
-
|
|
279
|
-
metrics.append({
|
|
280
|
-
"agent_id": agent_id_key,
|
|
281
|
-
"agent_name": data["agent_name"] or agent_id_key,
|
|
282
|
-
"total_executions": data["total"],
|
|
283
|
-
"successful_executions": data["successful"],
|
|
284
|
-
"failed_executions": data["failed"],
|
|
285
|
-
"average_duration": avg_duration,
|
|
286
|
-
"min_duration": min(durations) if durations else 0.0,
|
|
287
|
-
"max_duration": max(durations) if durations else 0.0,
|
|
288
|
-
"total_duration": total_duration,
|
|
289
|
-
"last_execution": data["last_execution"].isoformat() if data["last_execution"] else None,
|
|
290
|
-
"success_rate": (
|
|
291
|
-
data["successful"] / data["total"] if data["total"] > 0 else 0.0
|
|
292
|
-
),
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
return metrics
|
|
296
|
-
|
|
297
|
-
def get_workflow_metrics(
|
|
298
|
-
self,
|
|
299
|
-
workflow_id: str | None = None,
|
|
300
|
-
days: int = 30,
|
|
301
|
-
date_range: DateRange | None = None,
|
|
302
|
-
) -> list[dict[str, Any]]:
|
|
303
|
-
"""
|
|
304
|
-
Get workflow performance metrics with date range filtering.
|
|
305
|
-
|
|
306
|
-
Args:
|
|
307
|
-
workflow_id: Optional workflow ID to filter by
|
|
308
|
-
days: Number of days to look back (ignored if date_range provided)
|
|
309
|
-
date_range: Optional date range filter
|
|
310
|
-
|
|
311
|
-
Returns:
|
|
312
|
-
List of workflow metrics dictionaries
|
|
313
|
-
"""
|
|
314
|
-
# Use date_range if provided, otherwise calculate from days
|
|
315
|
-
if date_range:
|
|
316
|
-
start_date = date_range.start
|
|
317
|
-
end_date = date_range.end
|
|
318
|
-
else:
|
|
319
|
-
end_date = datetime.now(UTC)
|
|
320
|
-
start_date = end_date - timedelta(days=days)
|
|
321
|
-
|
|
322
|
-
workflow_data: dict[str, dict[str, Any]] = defaultdict(
|
|
323
|
-
lambda: {
|
|
324
|
-
"total": 0,
|
|
325
|
-
"successful": 0,
|
|
326
|
-
"failed": 0,
|
|
327
|
-
"durations": [],
|
|
328
|
-
"steps": [],
|
|
329
|
-
"last_execution": None,
|
|
330
|
-
"workflow_name": None,
|
|
331
|
-
}
|
|
332
|
-
)
|
|
333
|
-
|
|
334
|
-
# Calculate date range for file filtering
|
|
335
|
-
current_date = start_date
|
|
336
|
-
|
|
337
|
-
while current_date <= end_date:
|
|
338
|
-
date_str = current_date.strftime("%Y-%m-%d")
|
|
339
|
-
history_file = self.history_dir / f"workflows-{date_str}.jsonl"
|
|
340
|
-
|
|
341
|
-
if history_file.exists():
|
|
342
|
-
try:
|
|
343
|
-
with open(history_file, encoding="utf-8") as f:
|
|
344
|
-
for line in f:
|
|
345
|
-
try:
|
|
346
|
-
record = json.loads(line.strip())
|
|
347
|
-
|
|
348
|
-
# Parse timestamp
|
|
349
|
-
record_timestamp = datetime.fromisoformat(
|
|
350
|
-
record["timestamp"].replace("Z", "+00:00")
|
|
351
|
-
)
|
|
352
|
-
|
|
353
|
-
# Apply date range filter
|
|
354
|
-
if date_range and not date_range.contains(record_timestamp):
|
|
355
|
-
continue
|
|
356
|
-
|
|
357
|
-
# Apply workflow filter
|
|
358
|
-
if workflow_id and record["workflow_id"] != workflow_id:
|
|
359
|
-
continue
|
|
360
|
-
|
|
361
|
-
workflow_key = record["workflow_id"]
|
|
362
|
-
workflow_data[workflow_key]["workflow_name"] = record.get(
|
|
363
|
-
"workflow_name", workflow_key
|
|
364
|
-
)
|
|
365
|
-
workflow_data[workflow_key]["total"] += 1
|
|
366
|
-
|
|
367
|
-
if record["success"]:
|
|
368
|
-
workflow_data[workflow_key]["successful"] += 1
|
|
369
|
-
else:
|
|
370
|
-
workflow_data[workflow_key]["failed"] += 1
|
|
371
|
-
|
|
372
|
-
workflow_data[workflow_key]["durations"].append(record["duration"])
|
|
373
|
-
workflow_data[workflow_key]["steps"].append(record["steps"])
|
|
374
|
-
|
|
375
|
-
if (
|
|
376
|
-
workflow_data[workflow_key]["last_execution"] is None
|
|
377
|
-
or record_timestamp > workflow_data[workflow_key]["last_execution"]
|
|
378
|
-
):
|
|
379
|
-
workflow_data[workflow_key]["last_execution"] = record_timestamp
|
|
380
|
-
|
|
381
|
-
except Exception as e:
|
|
382
|
-
logger.debug(f"Failed to parse workflow record: {e}")
|
|
383
|
-
continue
|
|
384
|
-
except Exception as e:
|
|
385
|
-
logger.warning(f"Failed to read {history_file}: {e}")
|
|
386
|
-
|
|
387
|
-
current_date += timedelta(days=1)
|
|
388
|
-
|
|
389
|
-
# Convert to metrics
|
|
390
|
-
metrics = []
|
|
391
|
-
for workflow_id_key, data in workflow_data.items():
|
|
392
|
-
durations = data["durations"]
|
|
393
|
-
avg_duration = sum(durations) / len(durations) if durations else 0.0
|
|
394
|
-
avg_steps = (
|
|
395
|
-
sum(data["steps"]) / len(data["steps"]) if data["steps"] else 0.0
|
|
396
|
-
)
|
|
397
|
-
|
|
398
|
-
metrics.append({
|
|
399
|
-
"workflow_id": workflow_id_key,
|
|
400
|
-
"workflow_name": data["workflow_name"] or workflow_id_key,
|
|
401
|
-
"total_executions": data["total"],
|
|
402
|
-
"successful_executions": data["successful"],
|
|
403
|
-
"failed_executions": data["failed"],
|
|
404
|
-
"average_duration": avg_duration,
|
|
405
|
-
"average_steps": avg_steps,
|
|
406
|
-
"last_execution": data["last_execution"].isoformat() if data["last_execution"] else None,
|
|
407
|
-
"success_rate": (
|
|
408
|
-
data["successful"] / data["total"] if data["total"] > 0 else 0.0
|
|
409
|
-
),
|
|
410
|
-
})
|
|
411
|
-
|
|
412
|
-
return metrics
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
class MetricsAggregator:
|
|
416
|
-
"""
|
|
417
|
-
Enhanced metrics aggregation with multiple aggregation types.
|
|
418
|
-
|
|
419
|
-
Supports:
|
|
420
|
-
- Summary statistics
|
|
421
|
-
- Percentiles
|
|
422
|
-
- Time-series aggregation
|
|
423
|
-
- Grouped aggregation
|
|
424
|
-
"""
|
|
425
|
-
|
|
426
|
-
@staticmethod
|
|
427
|
-
def aggregate(
|
|
428
|
-
metrics: list[dict[str, Any]],
|
|
429
|
-
aggregation_type: str = "summary",
|
|
430
|
-
group_by: str | None = None,
|
|
431
|
-
) -> dict[str, Any]:
|
|
432
|
-
"""
|
|
433
|
-
Aggregate metrics with enhanced options.
|
|
434
|
-
|
|
435
|
-
Args:
|
|
436
|
-
metrics: List of metric dictionaries
|
|
437
|
-
aggregation_type: Type of aggregation (summary, percentiles, time_series)
|
|
438
|
-
group_by: Optional field to group by (e.g., "agent_id", "workflow_id")
|
|
439
|
-
|
|
440
|
-
Returns:
|
|
441
|
-
Aggregated metrics dictionary
|
|
442
|
-
"""
|
|
443
|
-
if not metrics:
|
|
444
|
-
return {"count": 0}
|
|
445
|
-
|
|
446
|
-
if group_by:
|
|
447
|
-
return MetricsAggregator._grouped_aggregate(metrics, aggregation_type, group_by)
|
|
448
|
-
|
|
449
|
-
if aggregation_type == "summary":
|
|
450
|
-
return MetricsAggregator._summary_aggregate(metrics)
|
|
451
|
-
elif aggregation_type == "percentiles":
|
|
452
|
-
return MetricsAggregator._percentile_aggregate(metrics)
|
|
453
|
-
elif aggregation_type == "time_series":
|
|
454
|
-
return MetricsAggregator._time_series_aggregate(metrics)
|
|
455
|
-
else:
|
|
456
|
-
return {"count": len(metrics)}
|
|
457
|
-
|
|
458
|
-
@staticmethod
|
|
459
|
-
def _summary_aggregate(metrics: list[dict[str, Any]]) -> dict[str, Any]:
|
|
460
|
-
"""Calculate summary statistics."""
|
|
461
|
-
total_executions = sum(m.get("total_executions", 0) for m in metrics)
|
|
462
|
-
total_successful = sum(m.get("successful_executions", 0) for m in metrics)
|
|
463
|
-
total_failed = sum(m.get("failed_executions", 0) for m in metrics)
|
|
464
|
-
|
|
465
|
-
durations = []
|
|
466
|
-
for m in metrics:
|
|
467
|
-
if "average_duration" in m:
|
|
468
|
-
durations.append(m["average_duration"])
|
|
469
|
-
|
|
470
|
-
durations_sorted = sorted(durations) if durations else []
|
|
471
|
-
|
|
472
|
-
return {
|
|
473
|
-
"count": len(metrics),
|
|
474
|
-
"total_executions": total_executions,
|
|
475
|
-
"total_successful": total_successful,
|
|
476
|
-
"total_failed": total_failed,
|
|
477
|
-
"success_rate": (
|
|
478
|
-
total_successful / total_executions if total_executions > 0 else 0.0
|
|
479
|
-
),
|
|
480
|
-
"average_duration": sum(durations) / len(durations) if durations else 0.0,
|
|
481
|
-
"median_duration": (
|
|
482
|
-
durations_sorted[len(durations_sorted) // 2]
|
|
483
|
-
if durations_sorted
|
|
484
|
-
else 0.0
|
|
485
|
-
),
|
|
486
|
-
"min_duration": min(durations) if durations else 0.0,
|
|
487
|
-
"max_duration": max(durations) if durations else 0.0,
|
|
488
|
-
"p25_duration": (
|
|
489
|
-
durations_sorted[len(durations_sorted) // 4]
|
|
490
|
-
if len(durations_sorted) >= 4
|
|
491
|
-
else 0.0
|
|
492
|
-
),
|
|
493
|
-
"p75_duration": (
|
|
494
|
-
durations_sorted[len(durations_sorted) * 3 // 4]
|
|
495
|
-
if len(durations_sorted) >= 4
|
|
496
|
-
else 0.0
|
|
497
|
-
),
|
|
498
|
-
"p95_duration": (
|
|
499
|
-
durations_sorted[int(len(durations_sorted) * 0.95)]
|
|
500
|
-
if len(durations_sorted) >= 20
|
|
501
|
-
else 0.0
|
|
502
|
-
),
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
@staticmethod
|
|
506
|
-
def _percentile_aggregate(metrics: list[dict[str, Any]]) -> dict[str, Any]:
|
|
507
|
-
"""Calculate percentile statistics."""
|
|
508
|
-
durations = []
|
|
509
|
-
success_rates = []
|
|
510
|
-
|
|
511
|
-
for m in metrics:
|
|
512
|
-
if "average_duration" in m:
|
|
513
|
-
durations.append(m["average_duration"])
|
|
514
|
-
if "success_rate" in m:
|
|
515
|
-
success_rates.append(m["success_rate"])
|
|
516
|
-
|
|
517
|
-
durations_sorted = sorted(durations) if durations else []
|
|
518
|
-
success_rates_sorted = sorted(success_rates) if success_rates else []
|
|
519
|
-
|
|
520
|
-
def percentile(data: list[float], p: float) -> float:
|
|
521
|
-
"""Calculate percentile."""
|
|
522
|
-
if not data:
|
|
523
|
-
return 0.0
|
|
524
|
-
idx = int(len(data) * p)
|
|
525
|
-
return data[min(idx, len(data) - 1)]
|
|
526
|
-
|
|
527
|
-
return {
|
|
528
|
-
"count": len(metrics),
|
|
529
|
-
"duration_percentiles": {
|
|
530
|
-
"p50": percentile(durations_sorted, 0.50),
|
|
531
|
-
"p75": percentile(durations_sorted, 0.75),
|
|
532
|
-
"p90": percentile(durations_sorted, 0.90),
|
|
533
|
-
"p95": percentile(durations_sorted, 0.95),
|
|
534
|
-
"p99": percentile(durations_sorted, 0.99),
|
|
535
|
-
},
|
|
536
|
-
"success_rate_percentiles": {
|
|
537
|
-
"p50": percentile(success_rates_sorted, 0.50),
|
|
538
|
-
"p75": percentile(success_rates_sorted, 0.75),
|
|
539
|
-
"p90": percentile(success_rates_sorted, 0.90),
|
|
540
|
-
"p95": percentile(success_rates_sorted, 0.95),
|
|
541
|
-
"p99": percentile(success_rates_sorted, 0.99),
|
|
542
|
-
},
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
@staticmethod
|
|
546
|
-
def _time_series_aggregate(metrics: list[dict[str, Any]]) -> dict[str, Any]:
|
|
547
|
-
"""Aggregate metrics by time intervals."""
|
|
548
|
-
# Group by day
|
|
549
|
-
by_day: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
|
550
|
-
|
|
551
|
-
for m in metrics:
|
|
552
|
-
if "last_execution" in m and m["last_execution"]:
|
|
553
|
-
try:
|
|
554
|
-
exec_time = datetime.fromisoformat(m["last_execution"].replace("Z", "+00:00"))
|
|
555
|
-
day_key = exec_time.strftime("%Y-%m-%d")
|
|
556
|
-
by_day[day_key].append(m)
|
|
557
|
-
except Exception:
|
|
558
|
-
continue
|
|
559
|
-
|
|
560
|
-
time_series = []
|
|
561
|
-
for day, day_metrics in sorted(by_day.items()):
|
|
562
|
-
summary = MetricsAggregator._summary_aggregate(day_metrics)
|
|
563
|
-
time_series.append({
|
|
564
|
-
"date": day,
|
|
565
|
-
**summary,
|
|
566
|
-
})
|
|
567
|
-
|
|
568
|
-
return {
|
|
569
|
-
"count": len(metrics),
|
|
570
|
-
"time_series": time_series,
|
|
571
|
-
"days": len(time_series),
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
@staticmethod
|
|
575
|
-
def _grouped_aggregate(
|
|
576
|
-
metrics: list[dict[str, Any]],
|
|
577
|
-
aggregation_type: str,
|
|
578
|
-
group_by: str,
|
|
579
|
-
) -> dict[str, Any]:
|
|
580
|
-
"""Aggregate metrics grouped by a field."""
|
|
581
|
-
groups: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
|
582
|
-
|
|
583
|
-
for m in metrics:
|
|
584
|
-
group_key = m.get(group_by, "unknown")
|
|
585
|
-
groups[group_key].append(m)
|
|
586
|
-
|
|
587
|
-
grouped_results = {}
|
|
588
|
-
for group_key, group_metrics in groups.items():
|
|
589
|
-
grouped_results[group_key] = MetricsAggregator.aggregate(
|
|
590
|
-
group_metrics, aggregation_type
|
|
591
|
-
)
|
|
592
|
-
|
|
593
|
-
return {
|
|
594
|
-
"count": len(metrics),
|
|
595
|
-
"groups": len(groups),
|
|
596
|
-
"grouped": grouped_results,
|
|
597
|
-
}
|
|
1
|
+
"""
|
|
2
|
+
Enhanced Analytics Dashboard and Access Layer
|
|
3
|
+
|
|
4
|
+
High-quality enhancements to analytics collection and reporting:
|
|
5
|
+
- Date range filtering
|
|
6
|
+
- Enhanced aggregation methods
|
|
7
|
+
- Performance optimizations
|
|
8
|
+
- Better error handling
|
|
9
|
+
- Metrics validation
|
|
10
|
+
- Query optimization
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
from collections import defaultdict
|
|
18
|
+
from dataclasses import asdict, dataclass
|
|
19
|
+
from datetime import UTC, datetime, timedelta
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class DateRange:
|
|
28
|
+
"""Date range for filtering metrics."""
|
|
29
|
+
|
|
30
|
+
start: datetime | None = None
|
|
31
|
+
end: datetime | None = None
|
|
32
|
+
|
|
33
|
+
def contains(self, timestamp: datetime) -> bool:
|
|
34
|
+
"""Check if timestamp is within range."""
|
|
35
|
+
if self.start and timestamp < self.start:
|
|
36
|
+
return False
|
|
37
|
+
if self.end and timestamp > self.end:
|
|
38
|
+
return False
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
def to_dict(self) -> dict[str, Any]:
|
|
42
|
+
"""Convert to dictionary."""
|
|
43
|
+
return {
|
|
44
|
+
"start": self.start.isoformat() if self.start else None,
|
|
45
|
+
"end": self.end.isoformat() if self.end else None,
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class EnhancedAnalyticsCollector:
|
|
50
|
+
"""
|
|
51
|
+
Enhanced analytics collector with improved performance and filtering.
|
|
52
|
+
|
|
53
|
+
Features:
|
|
54
|
+
- Date range filtering
|
|
55
|
+
- Enhanced aggregation
|
|
56
|
+
- Performance optimizations
|
|
57
|
+
- Better error handling
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, analytics_dir: Path | None = None):
|
|
61
|
+
"""
|
|
62
|
+
Initialize enhanced analytics collector.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
analytics_dir: Directory to store analytics data (defaults to .tapps-agents/analytics)
|
|
66
|
+
"""
|
|
67
|
+
if analytics_dir is None:
|
|
68
|
+
analytics_dir = Path(".tapps-agents/analytics")
|
|
69
|
+
|
|
70
|
+
self.analytics_dir = Path(analytics_dir)
|
|
71
|
+
self.analytics_dir.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
|
|
73
|
+
self.metrics_file = self.analytics_dir / "metrics.json"
|
|
74
|
+
self.history_dir = self.analytics_dir / "history"
|
|
75
|
+
self.history_dir.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
|
|
77
|
+
def record_agent_execution(
|
|
78
|
+
self,
|
|
79
|
+
agent_id: str,
|
|
80
|
+
agent_name: str,
|
|
81
|
+
duration: float,
|
|
82
|
+
success: bool,
|
|
83
|
+
timestamp: datetime | None = None,
|
|
84
|
+
metadata: dict[str, Any] | None = None,
|
|
85
|
+
):
|
|
86
|
+
"""
|
|
87
|
+
Record an agent execution with enhanced error handling.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
agent_id: Agent identifier
|
|
91
|
+
agent_name: Agent name
|
|
92
|
+
duration: Execution duration in seconds
|
|
93
|
+
success: Whether execution was successful
|
|
94
|
+
timestamp: Execution timestamp (defaults to now)
|
|
95
|
+
metadata: Optional metadata dictionary
|
|
96
|
+
"""
|
|
97
|
+
if timestamp is None:
|
|
98
|
+
timestamp = datetime.now(UTC)
|
|
99
|
+
|
|
100
|
+
# Validate inputs
|
|
101
|
+
if duration < 0:
|
|
102
|
+
logger.warning(f"Negative duration for agent {agent_id}: {duration}")
|
|
103
|
+
duration = 0.0
|
|
104
|
+
|
|
105
|
+
record = {
|
|
106
|
+
"agent_id": agent_id,
|
|
107
|
+
"agent_name": agent_name,
|
|
108
|
+
"duration": duration,
|
|
109
|
+
"success": success,
|
|
110
|
+
"timestamp": timestamp.isoformat(),
|
|
111
|
+
"metadata": metadata or {},
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# Append to history with error handling
|
|
115
|
+
try:
|
|
116
|
+
history_file = (
|
|
117
|
+
self.history_dir / f"agents-{timestamp.strftime('%Y-%m-%d')}.jsonl"
|
|
118
|
+
)
|
|
119
|
+
with open(history_file, "a", encoding="utf-8") as f:
|
|
120
|
+
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
121
|
+
logger.debug(f"Recorded agent execution: {agent_id}")
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"Failed to record agent execution {agent_id}: {e}", exc_info=True)
|
|
124
|
+
|
|
125
|
+
def record_workflow_execution(
|
|
126
|
+
self,
|
|
127
|
+
workflow_id: str,
|
|
128
|
+
workflow_name: str,
|
|
129
|
+
duration: float,
|
|
130
|
+
steps: int,
|
|
131
|
+
success: bool,
|
|
132
|
+
timestamp: datetime | None = None,
|
|
133
|
+
metadata: dict[str, Any] | None = None,
|
|
134
|
+
):
|
|
135
|
+
"""
|
|
136
|
+
Record a workflow execution with enhanced error handling.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
workflow_id: Workflow identifier
|
|
140
|
+
workflow_name: Workflow name
|
|
141
|
+
duration: Execution duration in seconds
|
|
142
|
+
steps: Number of steps executed
|
|
143
|
+
success: Whether execution was successful
|
|
144
|
+
timestamp: Execution timestamp (defaults to now)
|
|
145
|
+
metadata: Optional metadata dictionary
|
|
146
|
+
"""
|
|
147
|
+
if timestamp is None:
|
|
148
|
+
timestamp = datetime.now(UTC)
|
|
149
|
+
|
|
150
|
+
# Validate inputs
|
|
151
|
+
if duration < 0:
|
|
152
|
+
logger.warning(f"Negative duration for workflow {workflow_id}: {duration}")
|
|
153
|
+
duration = 0.0
|
|
154
|
+
if steps < 0:
|
|
155
|
+
logger.warning(f"Negative steps for workflow {workflow_id}: {steps}")
|
|
156
|
+
steps = 0
|
|
157
|
+
|
|
158
|
+
record = {
|
|
159
|
+
"workflow_id": workflow_id,
|
|
160
|
+
"workflow_name": workflow_name,
|
|
161
|
+
"duration": duration,
|
|
162
|
+
"steps": steps,
|
|
163
|
+
"success": success,
|
|
164
|
+
"timestamp": timestamp.isoformat(),
|
|
165
|
+
"metadata": metadata or {},
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# Append to history with error handling
|
|
169
|
+
try:
|
|
170
|
+
history_file = (
|
|
171
|
+
self.history_dir / f"workflows-{timestamp.strftime('%Y-%m-%d')}.jsonl"
|
|
172
|
+
)
|
|
173
|
+
with open(history_file, "a", encoding="utf-8") as f:
|
|
174
|
+
f.write(json.dumps(record, ensure_ascii=False) + "\n")
|
|
175
|
+
logger.debug(f"Recorded workflow execution: {workflow_id}")
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(f"Failed to record workflow execution {workflow_id}: {e}", exc_info=True)
|
|
178
|
+
|
|
179
|
+
def get_agent_metrics(
|
|
180
|
+
self,
|
|
181
|
+
agent_id: str | None = None,
|
|
182
|
+
days: int = 30,
|
|
183
|
+
date_range: DateRange | None = None,
|
|
184
|
+
) -> list[dict[str, Any]]:
|
|
185
|
+
"""
|
|
186
|
+
Get agent performance metrics with date range filtering.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
agent_id: Optional agent ID to filter by
|
|
190
|
+
days: Number of days to look back (ignored if date_range provided)
|
|
191
|
+
date_range: Optional date range filter
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
List of agent metrics dictionaries
|
|
195
|
+
"""
|
|
196
|
+
# Use date_range if provided, otherwise calculate from days
|
|
197
|
+
if date_range:
|
|
198
|
+
start_date = date_range.start
|
|
199
|
+
end_date = date_range.end
|
|
200
|
+
else:
|
|
201
|
+
end_date = datetime.now(UTC)
|
|
202
|
+
start_date = end_date - timedelta(days=days)
|
|
203
|
+
|
|
204
|
+
agent_data: dict[str, dict[str, Any]] = defaultdict(
|
|
205
|
+
lambda: {
|
|
206
|
+
"total": 0,
|
|
207
|
+
"successful": 0,
|
|
208
|
+
"failed": 0,
|
|
209
|
+
"durations": [],
|
|
210
|
+
"last_execution": None,
|
|
211
|
+
"agent_name": None,
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
# Calculate date range for file filtering
|
|
216
|
+
current_date = start_date
|
|
217
|
+
files_processed = 0
|
|
218
|
+
|
|
219
|
+
while current_date <= end_date:
|
|
220
|
+
date_str = current_date.strftime("%Y-%m-%d")
|
|
221
|
+
history_file = self.history_dir / f"agents-{date_str}.jsonl"
|
|
222
|
+
|
|
223
|
+
if history_file.exists():
|
|
224
|
+
try:
|
|
225
|
+
with open(history_file, encoding="utf-8") as f:
|
|
226
|
+
for line in f:
|
|
227
|
+
try:
|
|
228
|
+
record = json.loads(line.strip())
|
|
229
|
+
|
|
230
|
+
# Parse timestamp
|
|
231
|
+
record_timestamp = datetime.fromisoformat(
|
|
232
|
+
record["timestamp"].replace("Z", "+00:00")
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Apply date range filter
|
|
236
|
+
if date_range and not date_range.contains(record_timestamp):
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# Apply agent filter
|
|
240
|
+
if agent_id and record["agent_id"] != agent_id:
|
|
241
|
+
continue
|
|
242
|
+
|
|
243
|
+
agent_key = record["agent_id"]
|
|
244
|
+
agent_data[agent_key]["agent_name"] = record.get(
|
|
245
|
+
"agent_name", agent_key
|
|
246
|
+
)
|
|
247
|
+
agent_data[agent_key]["total"] += 1
|
|
248
|
+
|
|
249
|
+
if record["success"]:
|
|
250
|
+
agent_data[agent_key]["successful"] += 1
|
|
251
|
+
else:
|
|
252
|
+
agent_data[agent_key]["failed"] += 1
|
|
253
|
+
|
|
254
|
+
agent_data[agent_key]["durations"].append(record["duration"])
|
|
255
|
+
|
|
256
|
+
if (
|
|
257
|
+
agent_data[agent_key]["last_execution"] is None
|
|
258
|
+
or record_timestamp > agent_data[agent_key]["last_execution"]
|
|
259
|
+
):
|
|
260
|
+
agent_data[agent_key]["last_execution"] = record_timestamp
|
|
261
|
+
|
|
262
|
+
except Exception as e:
|
|
263
|
+
logger.debug(f"Failed to parse agent record: {e}")
|
|
264
|
+
continue
|
|
265
|
+
|
|
266
|
+
files_processed += 1
|
|
267
|
+
except Exception as e:
|
|
268
|
+
logger.warning(f"Failed to read {history_file}: {e}")
|
|
269
|
+
|
|
270
|
+
current_date += timedelta(days=1)
|
|
271
|
+
|
|
272
|
+
# Convert to metrics
|
|
273
|
+
metrics = []
|
|
274
|
+
for agent_id_key, data in agent_data.items():
|
|
275
|
+
durations = data["durations"]
|
|
276
|
+
total_duration = sum(durations)
|
|
277
|
+
avg_duration = total_duration / len(durations) if durations else 0.0
|
|
278
|
+
|
|
279
|
+
metrics.append({
|
|
280
|
+
"agent_id": agent_id_key,
|
|
281
|
+
"agent_name": data["agent_name"] or agent_id_key,
|
|
282
|
+
"total_executions": data["total"],
|
|
283
|
+
"successful_executions": data["successful"],
|
|
284
|
+
"failed_executions": data["failed"],
|
|
285
|
+
"average_duration": avg_duration,
|
|
286
|
+
"min_duration": min(durations) if durations else 0.0,
|
|
287
|
+
"max_duration": max(durations) if durations else 0.0,
|
|
288
|
+
"total_duration": total_duration,
|
|
289
|
+
"last_execution": data["last_execution"].isoformat() if data["last_execution"] else None,
|
|
290
|
+
"success_rate": (
|
|
291
|
+
data["successful"] / data["total"] if data["total"] > 0 else 0.0
|
|
292
|
+
),
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
return metrics
|
|
296
|
+
|
|
297
|
+
def get_workflow_metrics(
|
|
298
|
+
self,
|
|
299
|
+
workflow_id: str | None = None,
|
|
300
|
+
days: int = 30,
|
|
301
|
+
date_range: DateRange | None = None,
|
|
302
|
+
) -> list[dict[str, Any]]:
|
|
303
|
+
"""
|
|
304
|
+
Get workflow performance metrics with date range filtering.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
workflow_id: Optional workflow ID to filter by
|
|
308
|
+
days: Number of days to look back (ignored if date_range provided)
|
|
309
|
+
date_range: Optional date range filter
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
List of workflow metrics dictionaries
|
|
313
|
+
"""
|
|
314
|
+
# Use date_range if provided, otherwise calculate from days
|
|
315
|
+
if date_range:
|
|
316
|
+
start_date = date_range.start
|
|
317
|
+
end_date = date_range.end
|
|
318
|
+
else:
|
|
319
|
+
end_date = datetime.now(UTC)
|
|
320
|
+
start_date = end_date - timedelta(days=days)
|
|
321
|
+
|
|
322
|
+
workflow_data: dict[str, dict[str, Any]] = defaultdict(
|
|
323
|
+
lambda: {
|
|
324
|
+
"total": 0,
|
|
325
|
+
"successful": 0,
|
|
326
|
+
"failed": 0,
|
|
327
|
+
"durations": [],
|
|
328
|
+
"steps": [],
|
|
329
|
+
"last_execution": None,
|
|
330
|
+
"workflow_name": None,
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
# Calculate date range for file filtering
|
|
335
|
+
current_date = start_date
|
|
336
|
+
|
|
337
|
+
while current_date <= end_date:
|
|
338
|
+
date_str = current_date.strftime("%Y-%m-%d")
|
|
339
|
+
history_file = self.history_dir / f"workflows-{date_str}.jsonl"
|
|
340
|
+
|
|
341
|
+
if history_file.exists():
|
|
342
|
+
try:
|
|
343
|
+
with open(history_file, encoding="utf-8") as f:
|
|
344
|
+
for line in f:
|
|
345
|
+
try:
|
|
346
|
+
record = json.loads(line.strip())
|
|
347
|
+
|
|
348
|
+
# Parse timestamp
|
|
349
|
+
record_timestamp = datetime.fromisoformat(
|
|
350
|
+
record["timestamp"].replace("Z", "+00:00")
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Apply date range filter
|
|
354
|
+
if date_range and not date_range.contains(record_timestamp):
|
|
355
|
+
continue
|
|
356
|
+
|
|
357
|
+
# Apply workflow filter
|
|
358
|
+
if workflow_id and record["workflow_id"] != workflow_id:
|
|
359
|
+
continue
|
|
360
|
+
|
|
361
|
+
workflow_key = record["workflow_id"]
|
|
362
|
+
workflow_data[workflow_key]["workflow_name"] = record.get(
|
|
363
|
+
"workflow_name", workflow_key
|
|
364
|
+
)
|
|
365
|
+
workflow_data[workflow_key]["total"] += 1
|
|
366
|
+
|
|
367
|
+
if record["success"]:
|
|
368
|
+
workflow_data[workflow_key]["successful"] += 1
|
|
369
|
+
else:
|
|
370
|
+
workflow_data[workflow_key]["failed"] += 1
|
|
371
|
+
|
|
372
|
+
workflow_data[workflow_key]["durations"].append(record["duration"])
|
|
373
|
+
workflow_data[workflow_key]["steps"].append(record["steps"])
|
|
374
|
+
|
|
375
|
+
if (
|
|
376
|
+
workflow_data[workflow_key]["last_execution"] is None
|
|
377
|
+
or record_timestamp > workflow_data[workflow_key]["last_execution"]
|
|
378
|
+
):
|
|
379
|
+
workflow_data[workflow_key]["last_execution"] = record_timestamp
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
logger.debug(f"Failed to parse workflow record: {e}")
|
|
383
|
+
continue
|
|
384
|
+
except Exception as e:
|
|
385
|
+
logger.warning(f"Failed to read {history_file}: {e}")
|
|
386
|
+
|
|
387
|
+
current_date += timedelta(days=1)
|
|
388
|
+
|
|
389
|
+
# Convert to metrics
|
|
390
|
+
metrics = []
|
|
391
|
+
for workflow_id_key, data in workflow_data.items():
|
|
392
|
+
durations = data["durations"]
|
|
393
|
+
avg_duration = sum(durations) / len(durations) if durations else 0.0
|
|
394
|
+
avg_steps = (
|
|
395
|
+
sum(data["steps"]) / len(data["steps"]) if data["steps"] else 0.0
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
metrics.append({
|
|
399
|
+
"workflow_id": workflow_id_key,
|
|
400
|
+
"workflow_name": data["workflow_name"] or workflow_id_key,
|
|
401
|
+
"total_executions": data["total"],
|
|
402
|
+
"successful_executions": data["successful"],
|
|
403
|
+
"failed_executions": data["failed"],
|
|
404
|
+
"average_duration": avg_duration,
|
|
405
|
+
"average_steps": avg_steps,
|
|
406
|
+
"last_execution": data["last_execution"].isoformat() if data["last_execution"] else None,
|
|
407
|
+
"success_rate": (
|
|
408
|
+
data["successful"] / data["total"] if data["total"] > 0 else 0.0
|
|
409
|
+
),
|
|
410
|
+
})
|
|
411
|
+
|
|
412
|
+
return metrics
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
class MetricsAggregator:
|
|
416
|
+
"""
|
|
417
|
+
Enhanced metrics aggregation with multiple aggregation types.
|
|
418
|
+
|
|
419
|
+
Supports:
|
|
420
|
+
- Summary statistics
|
|
421
|
+
- Percentiles
|
|
422
|
+
- Time-series aggregation
|
|
423
|
+
- Grouped aggregation
|
|
424
|
+
"""
|
|
425
|
+
|
|
426
|
+
@staticmethod
|
|
427
|
+
def aggregate(
|
|
428
|
+
metrics: list[dict[str, Any]],
|
|
429
|
+
aggregation_type: str = "summary",
|
|
430
|
+
group_by: str | None = None,
|
|
431
|
+
) -> dict[str, Any]:
|
|
432
|
+
"""
|
|
433
|
+
Aggregate metrics with enhanced options.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
metrics: List of metric dictionaries
|
|
437
|
+
aggregation_type: Type of aggregation (summary, percentiles, time_series)
|
|
438
|
+
group_by: Optional field to group by (e.g., "agent_id", "workflow_id")
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
Aggregated metrics dictionary
|
|
442
|
+
"""
|
|
443
|
+
if not metrics:
|
|
444
|
+
return {"count": 0}
|
|
445
|
+
|
|
446
|
+
if group_by:
|
|
447
|
+
return MetricsAggregator._grouped_aggregate(metrics, aggregation_type, group_by)
|
|
448
|
+
|
|
449
|
+
if aggregation_type == "summary":
|
|
450
|
+
return MetricsAggregator._summary_aggregate(metrics)
|
|
451
|
+
elif aggregation_type == "percentiles":
|
|
452
|
+
return MetricsAggregator._percentile_aggregate(metrics)
|
|
453
|
+
elif aggregation_type == "time_series":
|
|
454
|
+
return MetricsAggregator._time_series_aggregate(metrics)
|
|
455
|
+
else:
|
|
456
|
+
return {"count": len(metrics)}
|
|
457
|
+
|
|
458
|
+
@staticmethod
|
|
459
|
+
def _summary_aggregate(metrics: list[dict[str, Any]]) -> dict[str, Any]:
|
|
460
|
+
"""Calculate summary statistics."""
|
|
461
|
+
total_executions = sum(m.get("total_executions", 0) for m in metrics)
|
|
462
|
+
total_successful = sum(m.get("successful_executions", 0) for m in metrics)
|
|
463
|
+
total_failed = sum(m.get("failed_executions", 0) for m in metrics)
|
|
464
|
+
|
|
465
|
+
durations = []
|
|
466
|
+
for m in metrics:
|
|
467
|
+
if "average_duration" in m:
|
|
468
|
+
durations.append(m["average_duration"])
|
|
469
|
+
|
|
470
|
+
durations_sorted = sorted(durations) if durations else []
|
|
471
|
+
|
|
472
|
+
return {
|
|
473
|
+
"count": len(metrics),
|
|
474
|
+
"total_executions": total_executions,
|
|
475
|
+
"total_successful": total_successful,
|
|
476
|
+
"total_failed": total_failed,
|
|
477
|
+
"success_rate": (
|
|
478
|
+
total_successful / total_executions if total_executions > 0 else 0.0
|
|
479
|
+
),
|
|
480
|
+
"average_duration": sum(durations) / len(durations) if durations else 0.0,
|
|
481
|
+
"median_duration": (
|
|
482
|
+
durations_sorted[len(durations_sorted) // 2]
|
|
483
|
+
if durations_sorted
|
|
484
|
+
else 0.0
|
|
485
|
+
),
|
|
486
|
+
"min_duration": min(durations) if durations else 0.0,
|
|
487
|
+
"max_duration": max(durations) if durations else 0.0,
|
|
488
|
+
"p25_duration": (
|
|
489
|
+
durations_sorted[len(durations_sorted) // 4]
|
|
490
|
+
if len(durations_sorted) >= 4
|
|
491
|
+
else 0.0
|
|
492
|
+
),
|
|
493
|
+
"p75_duration": (
|
|
494
|
+
durations_sorted[len(durations_sorted) * 3 // 4]
|
|
495
|
+
if len(durations_sorted) >= 4
|
|
496
|
+
else 0.0
|
|
497
|
+
),
|
|
498
|
+
"p95_duration": (
|
|
499
|
+
durations_sorted[int(len(durations_sorted) * 0.95)]
|
|
500
|
+
if len(durations_sorted) >= 20
|
|
501
|
+
else 0.0
|
|
502
|
+
),
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
@staticmethod
|
|
506
|
+
def _percentile_aggregate(metrics: list[dict[str, Any]]) -> dict[str, Any]:
|
|
507
|
+
"""Calculate percentile statistics."""
|
|
508
|
+
durations = []
|
|
509
|
+
success_rates = []
|
|
510
|
+
|
|
511
|
+
for m in metrics:
|
|
512
|
+
if "average_duration" in m:
|
|
513
|
+
durations.append(m["average_duration"])
|
|
514
|
+
if "success_rate" in m:
|
|
515
|
+
success_rates.append(m["success_rate"])
|
|
516
|
+
|
|
517
|
+
durations_sorted = sorted(durations) if durations else []
|
|
518
|
+
success_rates_sorted = sorted(success_rates) if success_rates else []
|
|
519
|
+
|
|
520
|
+
def percentile(data: list[float], p: float) -> float:
|
|
521
|
+
"""Calculate percentile."""
|
|
522
|
+
if not data:
|
|
523
|
+
return 0.0
|
|
524
|
+
idx = int(len(data) * p)
|
|
525
|
+
return data[min(idx, len(data) - 1)]
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
"count": len(metrics),
|
|
529
|
+
"duration_percentiles": {
|
|
530
|
+
"p50": percentile(durations_sorted, 0.50),
|
|
531
|
+
"p75": percentile(durations_sorted, 0.75),
|
|
532
|
+
"p90": percentile(durations_sorted, 0.90),
|
|
533
|
+
"p95": percentile(durations_sorted, 0.95),
|
|
534
|
+
"p99": percentile(durations_sorted, 0.99),
|
|
535
|
+
},
|
|
536
|
+
"success_rate_percentiles": {
|
|
537
|
+
"p50": percentile(success_rates_sorted, 0.50),
|
|
538
|
+
"p75": percentile(success_rates_sorted, 0.75),
|
|
539
|
+
"p90": percentile(success_rates_sorted, 0.90),
|
|
540
|
+
"p95": percentile(success_rates_sorted, 0.95),
|
|
541
|
+
"p99": percentile(success_rates_sorted, 0.99),
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
@staticmethod
|
|
546
|
+
def _time_series_aggregate(metrics: list[dict[str, Any]]) -> dict[str, Any]:
|
|
547
|
+
"""Aggregate metrics by time intervals."""
|
|
548
|
+
# Group by day
|
|
549
|
+
by_day: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
|
550
|
+
|
|
551
|
+
for m in metrics:
|
|
552
|
+
if "last_execution" in m and m["last_execution"]:
|
|
553
|
+
try:
|
|
554
|
+
exec_time = datetime.fromisoformat(m["last_execution"].replace("Z", "+00:00"))
|
|
555
|
+
day_key = exec_time.strftime("%Y-%m-%d")
|
|
556
|
+
by_day[day_key].append(m)
|
|
557
|
+
except Exception:
|
|
558
|
+
continue
|
|
559
|
+
|
|
560
|
+
time_series = []
|
|
561
|
+
for day, day_metrics in sorted(by_day.items()):
|
|
562
|
+
summary = MetricsAggregator._summary_aggregate(day_metrics)
|
|
563
|
+
time_series.append({
|
|
564
|
+
"date": day,
|
|
565
|
+
**summary,
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
return {
|
|
569
|
+
"count": len(metrics),
|
|
570
|
+
"time_series": time_series,
|
|
571
|
+
"days": len(time_series),
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
@staticmethod
|
|
575
|
+
def _grouped_aggregate(
|
|
576
|
+
metrics: list[dict[str, Any]],
|
|
577
|
+
aggregation_type: str,
|
|
578
|
+
group_by: str,
|
|
579
|
+
) -> dict[str, Any]:
|
|
580
|
+
"""Aggregate metrics grouped by a field."""
|
|
581
|
+
groups: dict[str, list[dict[str, Any]]] = defaultdict(list)
|
|
582
|
+
|
|
583
|
+
for m in metrics:
|
|
584
|
+
group_key = m.get(group_by, "unknown")
|
|
585
|
+
groups[group_key].append(m)
|
|
586
|
+
|
|
587
|
+
grouped_results = {}
|
|
588
|
+
for group_key, group_metrics in groups.items():
|
|
589
|
+
grouped_results[group_key] = MetricsAggregator.aggregate(
|
|
590
|
+
group_metrics, aggregation_type
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
"count": len(metrics),
|
|
595
|
+
"groups": len(groups),
|
|
596
|
+
"grouped": grouped_results,
|
|
597
|
+
}
|