attune-ai 2.0.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.
- attune/__init__.py +358 -0
- attune/adaptive/__init__.py +13 -0
- attune/adaptive/task_complexity.py +127 -0
- attune/agent_monitoring.py +414 -0
- attune/cache/__init__.py +117 -0
- attune/cache/base.py +166 -0
- attune/cache/dependency_manager.py +256 -0
- attune/cache/hash_only.py +251 -0
- attune/cache/hybrid.py +457 -0
- attune/cache/storage.py +285 -0
- attune/cache_monitor.py +356 -0
- attune/cache_stats.py +298 -0
- attune/cli/__init__.py +152 -0
- attune/cli/__main__.py +12 -0
- attune/cli/commands/__init__.py +1 -0
- attune/cli/commands/batch.py +264 -0
- attune/cli/commands/cache.py +248 -0
- attune/cli/commands/help.py +331 -0
- attune/cli/commands/info.py +140 -0
- attune/cli/commands/inspect.py +436 -0
- attune/cli/commands/inspection.py +57 -0
- attune/cli/commands/memory.py +48 -0
- attune/cli/commands/metrics.py +92 -0
- attune/cli/commands/orchestrate.py +184 -0
- attune/cli/commands/patterns.py +207 -0
- attune/cli/commands/profiling.py +202 -0
- attune/cli/commands/provider.py +98 -0
- attune/cli/commands/routing.py +285 -0
- attune/cli/commands/setup.py +96 -0
- attune/cli/commands/status.py +235 -0
- attune/cli/commands/sync.py +166 -0
- attune/cli/commands/tier.py +121 -0
- attune/cli/commands/utilities.py +114 -0
- attune/cli/commands/workflow.py +579 -0
- attune/cli/core.py +32 -0
- attune/cli/parsers/__init__.py +68 -0
- attune/cli/parsers/batch.py +118 -0
- attune/cli/parsers/cache.py +65 -0
- attune/cli/parsers/help.py +41 -0
- attune/cli/parsers/info.py +26 -0
- attune/cli/parsers/inspect.py +66 -0
- attune/cli/parsers/metrics.py +42 -0
- attune/cli/parsers/orchestrate.py +61 -0
- attune/cli/parsers/patterns.py +54 -0
- attune/cli/parsers/provider.py +40 -0
- attune/cli/parsers/routing.py +110 -0
- attune/cli/parsers/setup.py +42 -0
- attune/cli/parsers/status.py +47 -0
- attune/cli/parsers/sync.py +31 -0
- attune/cli/parsers/tier.py +33 -0
- attune/cli/parsers/workflow.py +77 -0
- attune/cli/utils/__init__.py +1 -0
- attune/cli/utils/data.py +242 -0
- attune/cli/utils/helpers.py +68 -0
- attune/cli_legacy.py +3957 -0
- attune/cli_minimal.py +1159 -0
- attune/cli_router.py +437 -0
- attune/cli_unified.py +814 -0
- attune/config/__init__.py +66 -0
- attune/config/xml_config.py +286 -0
- attune/config.py +545 -0
- attune/coordination.py +870 -0
- attune/core.py +1511 -0
- attune/core_modules/__init__.py +15 -0
- attune/cost_tracker.py +626 -0
- attune/dashboard/__init__.py +41 -0
- attune/dashboard/app.py +512 -0
- attune/dashboard/simple_server.py +435 -0
- attune/dashboard/standalone_server.py +547 -0
- attune/discovery.py +306 -0
- attune/emergence.py +306 -0
- attune/exceptions.py +123 -0
- attune/feedback_loops.py +373 -0
- attune/hot_reload/README.md +473 -0
- attune/hot_reload/__init__.py +62 -0
- attune/hot_reload/config.py +83 -0
- attune/hot_reload/integration.py +229 -0
- attune/hot_reload/reloader.py +298 -0
- attune/hot_reload/watcher.py +183 -0
- attune/hot_reload/websocket.py +177 -0
- attune/levels.py +577 -0
- attune/leverage_points.py +441 -0
- attune/logging_config.py +261 -0
- attune/mcp/__init__.py +10 -0
- attune/mcp/server.py +506 -0
- attune/memory/__init__.py +237 -0
- attune/memory/claude_memory.py +469 -0
- attune/memory/config.py +224 -0
- attune/memory/control_panel.py +1290 -0
- attune/memory/control_panel_support.py +145 -0
- attune/memory/cross_session.py +845 -0
- attune/memory/edges.py +179 -0
- attune/memory/encryption.py +159 -0
- attune/memory/file_session.py +770 -0
- attune/memory/graph.py +570 -0
- attune/memory/long_term.py +913 -0
- attune/memory/long_term_types.py +99 -0
- attune/memory/mixins/__init__.py +25 -0
- attune/memory/mixins/backend_init_mixin.py +249 -0
- attune/memory/mixins/capabilities_mixin.py +208 -0
- attune/memory/mixins/handoff_mixin.py +208 -0
- attune/memory/mixins/lifecycle_mixin.py +49 -0
- attune/memory/mixins/long_term_mixin.py +352 -0
- attune/memory/mixins/promotion_mixin.py +109 -0
- attune/memory/mixins/short_term_mixin.py +182 -0
- attune/memory/nodes.py +179 -0
- attune/memory/redis_bootstrap.py +540 -0
- attune/memory/security/__init__.py +31 -0
- attune/memory/security/audit_logger.py +932 -0
- attune/memory/security/pii_scrubber.py +640 -0
- attune/memory/security/secrets_detector.py +678 -0
- attune/memory/short_term.py +2192 -0
- attune/memory/simple_storage.py +302 -0
- attune/memory/storage/__init__.py +15 -0
- attune/memory/storage_backend.py +167 -0
- attune/memory/summary_index.py +583 -0
- attune/memory/types.py +446 -0
- attune/memory/unified.py +182 -0
- attune/meta_workflows/__init__.py +74 -0
- attune/meta_workflows/agent_creator.py +248 -0
- attune/meta_workflows/builtin_templates.py +567 -0
- attune/meta_workflows/cli_commands/__init__.py +56 -0
- attune/meta_workflows/cli_commands/agent_commands.py +321 -0
- attune/meta_workflows/cli_commands/analytics_commands.py +442 -0
- attune/meta_workflows/cli_commands/config_commands.py +232 -0
- attune/meta_workflows/cli_commands/memory_commands.py +182 -0
- attune/meta_workflows/cli_commands/template_commands.py +354 -0
- attune/meta_workflows/cli_commands/workflow_commands.py +382 -0
- attune/meta_workflows/cli_meta_workflows.py +59 -0
- attune/meta_workflows/form_engine.py +292 -0
- attune/meta_workflows/intent_detector.py +409 -0
- attune/meta_workflows/models.py +569 -0
- attune/meta_workflows/pattern_learner.py +738 -0
- attune/meta_workflows/plan_generator.py +384 -0
- attune/meta_workflows/session_context.py +397 -0
- attune/meta_workflows/template_registry.py +229 -0
- attune/meta_workflows/workflow.py +984 -0
- attune/metrics/__init__.py +12 -0
- attune/metrics/collector.py +31 -0
- attune/metrics/prompt_metrics.py +194 -0
- attune/models/__init__.py +172 -0
- attune/models/__main__.py +13 -0
- attune/models/adaptive_routing.py +437 -0
- attune/models/auth_cli.py +444 -0
- attune/models/auth_strategy.py +450 -0
- attune/models/cli.py +655 -0
- attune/models/empathy_executor.py +354 -0
- attune/models/executor.py +257 -0
- attune/models/fallback.py +762 -0
- attune/models/provider_config.py +282 -0
- attune/models/registry.py +472 -0
- attune/models/tasks.py +359 -0
- attune/models/telemetry/__init__.py +71 -0
- attune/models/telemetry/analytics.py +594 -0
- attune/models/telemetry/backend.py +196 -0
- attune/models/telemetry/data_models.py +431 -0
- attune/models/telemetry/storage.py +489 -0
- attune/models/token_estimator.py +420 -0
- attune/models/validation.py +280 -0
- attune/monitoring/__init__.py +52 -0
- attune/monitoring/alerts.py +946 -0
- attune/monitoring/alerts_cli.py +448 -0
- attune/monitoring/multi_backend.py +271 -0
- attune/monitoring/otel_backend.py +362 -0
- attune/optimization/__init__.py +19 -0
- attune/optimization/context_optimizer.py +272 -0
- attune/orchestration/__init__.py +67 -0
- attune/orchestration/agent_templates.py +707 -0
- attune/orchestration/config_store.py +499 -0
- attune/orchestration/execution_strategies.py +2111 -0
- attune/orchestration/meta_orchestrator.py +1168 -0
- attune/orchestration/pattern_learner.py +696 -0
- attune/orchestration/real_tools.py +931 -0
- attune/pattern_cache.py +187 -0
- attune/pattern_library.py +542 -0
- attune/patterns/debugging/all_patterns.json +81 -0
- attune/patterns/debugging/workflow_20260107_1770825e.json +77 -0
- attune/patterns/refactoring_memory.json +89 -0
- attune/persistence.py +564 -0
- attune/platform_utils.py +265 -0
- attune/plugins/__init__.py +28 -0
- attune/plugins/base.py +361 -0
- attune/plugins/registry.py +268 -0
- attune/project_index/__init__.py +32 -0
- attune/project_index/cli.py +335 -0
- attune/project_index/index.py +667 -0
- attune/project_index/models.py +504 -0
- attune/project_index/reports.py +474 -0
- attune/project_index/scanner.py +777 -0
- attune/project_index/scanner_parallel.py +291 -0
- attune/prompts/__init__.py +61 -0
- attune/prompts/config.py +77 -0
- attune/prompts/context.py +177 -0
- attune/prompts/parser.py +285 -0
- attune/prompts/registry.py +313 -0
- attune/prompts/templates.py +208 -0
- attune/redis_config.py +302 -0
- attune/redis_memory.py +799 -0
- attune/resilience/__init__.py +56 -0
- attune/resilience/circuit_breaker.py +256 -0
- attune/resilience/fallback.py +179 -0
- attune/resilience/health.py +300 -0
- attune/resilience/retry.py +209 -0
- attune/resilience/timeout.py +135 -0
- attune/routing/__init__.py +43 -0
- attune/routing/chain_executor.py +433 -0
- attune/routing/classifier.py +217 -0
- attune/routing/smart_router.py +234 -0
- attune/routing/workflow_registry.py +343 -0
- attune/scaffolding/README.md +589 -0
- attune/scaffolding/__init__.py +35 -0
- attune/scaffolding/__main__.py +14 -0
- attune/scaffolding/cli.py +240 -0
- attune/scaffolding/templates/base_wizard.py.jinja2 +121 -0
- attune/scaffolding/templates/coach_wizard.py.jinja2 +321 -0
- attune/scaffolding/templates/domain_wizard.py.jinja2 +408 -0
- attune/scaffolding/templates/linear_flow_wizard.py.jinja2 +203 -0
- attune/socratic/__init__.py +256 -0
- attune/socratic/ab_testing.py +958 -0
- attune/socratic/blueprint.py +533 -0
- attune/socratic/cli.py +703 -0
- attune/socratic/collaboration.py +1114 -0
- attune/socratic/domain_templates.py +924 -0
- attune/socratic/embeddings.py +738 -0
- attune/socratic/engine.py +794 -0
- attune/socratic/explainer.py +682 -0
- attune/socratic/feedback.py +772 -0
- attune/socratic/forms.py +629 -0
- attune/socratic/generator.py +732 -0
- attune/socratic/llm_analyzer.py +637 -0
- attune/socratic/mcp_server.py +702 -0
- attune/socratic/session.py +312 -0
- attune/socratic/storage.py +667 -0
- attune/socratic/success.py +730 -0
- attune/socratic/visual_editor.py +860 -0
- attune/socratic/web_ui.py +958 -0
- attune/telemetry/__init__.py +39 -0
- attune/telemetry/agent_coordination.py +475 -0
- attune/telemetry/agent_tracking.py +367 -0
- attune/telemetry/approval_gates.py +545 -0
- attune/telemetry/cli.py +1231 -0
- attune/telemetry/commands/__init__.py +14 -0
- attune/telemetry/commands/dashboard_commands.py +696 -0
- attune/telemetry/event_streaming.py +409 -0
- attune/telemetry/feedback_loop.py +567 -0
- attune/telemetry/usage_tracker.py +591 -0
- attune/templates.py +754 -0
- attune/test_generator/__init__.py +38 -0
- attune/test_generator/__main__.py +14 -0
- attune/test_generator/cli.py +234 -0
- attune/test_generator/generator.py +355 -0
- attune/test_generator/risk_analyzer.py +216 -0
- attune/test_generator/templates/unit_test.py.jinja2 +272 -0
- attune/tier_recommender.py +384 -0
- attune/tools.py +183 -0
- attune/trust/__init__.py +28 -0
- attune/trust/circuit_breaker.py +579 -0
- attune/trust_building.py +527 -0
- attune/validation/__init__.py +19 -0
- attune/validation/xml_validator.py +281 -0
- attune/vscode_bridge.py +173 -0
- attune/workflow_commands.py +780 -0
- attune/workflow_patterns/__init__.py +33 -0
- attune/workflow_patterns/behavior.py +249 -0
- attune/workflow_patterns/core.py +76 -0
- attune/workflow_patterns/output.py +99 -0
- attune/workflow_patterns/registry.py +255 -0
- attune/workflow_patterns/structural.py +288 -0
- attune/workflows/__init__.py +539 -0
- attune/workflows/autonomous_test_gen.py +1268 -0
- attune/workflows/base.py +2667 -0
- attune/workflows/batch_processing.py +342 -0
- attune/workflows/bug_predict.py +1084 -0
- attune/workflows/builder.py +273 -0
- attune/workflows/caching.py +253 -0
- attune/workflows/code_review.py +1048 -0
- attune/workflows/code_review_adapters.py +312 -0
- attune/workflows/code_review_pipeline.py +722 -0
- attune/workflows/config.py +645 -0
- attune/workflows/dependency_check.py +644 -0
- attune/workflows/document_gen/__init__.py +25 -0
- attune/workflows/document_gen/config.py +30 -0
- attune/workflows/document_gen/report_formatter.py +162 -0
- attune/workflows/document_gen/workflow.py +1426 -0
- attune/workflows/document_manager.py +216 -0
- attune/workflows/document_manager_README.md +134 -0
- attune/workflows/documentation_orchestrator.py +1205 -0
- attune/workflows/history.py +510 -0
- attune/workflows/keyboard_shortcuts/__init__.py +39 -0
- attune/workflows/keyboard_shortcuts/generators.py +391 -0
- attune/workflows/keyboard_shortcuts/parsers.py +416 -0
- attune/workflows/keyboard_shortcuts/prompts.py +295 -0
- attune/workflows/keyboard_shortcuts/schema.py +193 -0
- attune/workflows/keyboard_shortcuts/workflow.py +509 -0
- attune/workflows/llm_base.py +363 -0
- attune/workflows/manage_docs.py +87 -0
- attune/workflows/manage_docs_README.md +134 -0
- attune/workflows/manage_documentation.py +821 -0
- attune/workflows/new_sample_workflow1.py +149 -0
- attune/workflows/new_sample_workflow1_README.md +150 -0
- attune/workflows/orchestrated_health_check.py +849 -0
- attune/workflows/orchestrated_release_prep.py +600 -0
- attune/workflows/output.py +413 -0
- attune/workflows/perf_audit.py +863 -0
- attune/workflows/pr_review.py +762 -0
- attune/workflows/progress.py +785 -0
- attune/workflows/progress_server.py +322 -0
- attune/workflows/progressive/README 2.md +454 -0
- attune/workflows/progressive/README.md +454 -0
- attune/workflows/progressive/__init__.py +82 -0
- attune/workflows/progressive/cli.py +219 -0
- attune/workflows/progressive/core.py +488 -0
- attune/workflows/progressive/orchestrator.py +723 -0
- attune/workflows/progressive/reports.py +520 -0
- attune/workflows/progressive/telemetry.py +274 -0
- attune/workflows/progressive/test_gen.py +495 -0
- attune/workflows/progressive/workflow.py +589 -0
- attune/workflows/refactor_plan.py +694 -0
- attune/workflows/release_prep.py +895 -0
- attune/workflows/release_prep_crew.py +969 -0
- attune/workflows/research_synthesis.py +404 -0
- attune/workflows/routing.py +168 -0
- attune/workflows/secure_release.py +593 -0
- attune/workflows/security_adapters.py +297 -0
- attune/workflows/security_audit.py +1329 -0
- attune/workflows/security_audit_phase3.py +355 -0
- attune/workflows/seo_optimization.py +633 -0
- attune/workflows/step_config.py +234 -0
- attune/workflows/telemetry_mixin.py +269 -0
- attune/workflows/test5.py +125 -0
- attune/workflows/test5_README.md +158 -0
- attune/workflows/test_coverage_boost_crew.py +849 -0
- attune/workflows/test_gen/__init__.py +52 -0
- attune/workflows/test_gen/ast_analyzer.py +249 -0
- attune/workflows/test_gen/config.py +88 -0
- attune/workflows/test_gen/data_models.py +38 -0
- attune/workflows/test_gen/report_formatter.py +289 -0
- attune/workflows/test_gen/test_templates.py +381 -0
- attune/workflows/test_gen/workflow.py +655 -0
- attune/workflows/test_gen.py +54 -0
- attune/workflows/test_gen_behavioral.py +477 -0
- attune/workflows/test_gen_parallel.py +341 -0
- attune/workflows/test_lifecycle.py +526 -0
- attune/workflows/test_maintenance.py +627 -0
- attune/workflows/test_maintenance_cli.py +590 -0
- attune/workflows/test_maintenance_crew.py +840 -0
- attune/workflows/test_runner.py +622 -0
- attune/workflows/tier_tracking.py +531 -0
- attune/workflows/xml_enhanced_crew.py +285 -0
- attune_ai-2.0.0.dist-info/METADATA +1026 -0
- attune_ai-2.0.0.dist-info/RECORD +457 -0
- attune_ai-2.0.0.dist-info/WHEEL +5 -0
- attune_ai-2.0.0.dist-info/entry_points.txt +26 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE +201 -0
- attune_ai-2.0.0.dist-info/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +101 -0
- attune_ai-2.0.0.dist-info/top_level.txt +5 -0
- attune_healthcare/__init__.py +13 -0
- attune_healthcare/monitors/__init__.py +9 -0
- attune_healthcare/monitors/clinical_protocol_monitor.py +315 -0
- attune_healthcare/monitors/monitoring/__init__.py +44 -0
- attune_healthcare/monitors/monitoring/protocol_checker.py +300 -0
- attune_healthcare/monitors/monitoring/protocol_loader.py +214 -0
- attune_healthcare/monitors/monitoring/sensor_parsers.py +306 -0
- attune_healthcare/monitors/monitoring/trajectory_analyzer.py +389 -0
- attune_llm/README.md +553 -0
- attune_llm/__init__.py +28 -0
- attune_llm/agent_factory/__init__.py +53 -0
- attune_llm/agent_factory/adapters/__init__.py +85 -0
- attune_llm/agent_factory/adapters/autogen_adapter.py +312 -0
- attune_llm/agent_factory/adapters/crewai_adapter.py +483 -0
- attune_llm/agent_factory/adapters/haystack_adapter.py +298 -0
- attune_llm/agent_factory/adapters/langchain_adapter.py +362 -0
- attune_llm/agent_factory/adapters/langgraph_adapter.py +333 -0
- attune_llm/agent_factory/adapters/native.py +228 -0
- attune_llm/agent_factory/adapters/wizard_adapter.py +423 -0
- attune_llm/agent_factory/base.py +305 -0
- attune_llm/agent_factory/crews/__init__.py +67 -0
- attune_llm/agent_factory/crews/code_review.py +1113 -0
- attune_llm/agent_factory/crews/health_check.py +1262 -0
- attune_llm/agent_factory/crews/refactoring.py +1128 -0
- attune_llm/agent_factory/crews/security_audit.py +1018 -0
- attune_llm/agent_factory/decorators.py +287 -0
- attune_llm/agent_factory/factory.py +558 -0
- attune_llm/agent_factory/framework.py +193 -0
- attune_llm/agent_factory/memory_integration.py +328 -0
- attune_llm/agent_factory/resilient.py +320 -0
- attune_llm/agents_md/__init__.py +22 -0
- attune_llm/agents_md/loader.py +218 -0
- attune_llm/agents_md/parser.py +271 -0
- attune_llm/agents_md/registry.py +307 -0
- attune_llm/claude_memory.py +466 -0
- attune_llm/cli/__init__.py +8 -0
- attune_llm/cli/sync_claude.py +487 -0
- attune_llm/code_health.py +1313 -0
- attune_llm/commands/__init__.py +51 -0
- attune_llm/commands/context.py +375 -0
- attune_llm/commands/loader.py +301 -0
- attune_llm/commands/models.py +231 -0
- attune_llm/commands/parser.py +371 -0
- attune_llm/commands/registry.py +429 -0
- attune_llm/config/__init__.py +29 -0
- attune_llm/config/unified.py +291 -0
- attune_llm/context/__init__.py +22 -0
- attune_llm/context/compaction.py +455 -0
- attune_llm/context/manager.py +434 -0
- attune_llm/contextual_patterns.py +361 -0
- attune_llm/core.py +907 -0
- attune_llm/git_pattern_extractor.py +435 -0
- attune_llm/hooks/__init__.py +24 -0
- attune_llm/hooks/config.py +306 -0
- attune_llm/hooks/executor.py +289 -0
- attune_llm/hooks/registry.py +302 -0
- attune_llm/hooks/scripts/__init__.py +39 -0
- attune_llm/hooks/scripts/evaluate_session.py +201 -0
- attune_llm/hooks/scripts/first_time_init.py +285 -0
- attune_llm/hooks/scripts/pre_compact.py +207 -0
- attune_llm/hooks/scripts/session_end.py +183 -0
- attune_llm/hooks/scripts/session_start.py +163 -0
- attune_llm/hooks/scripts/suggest_compact.py +225 -0
- attune_llm/learning/__init__.py +30 -0
- attune_llm/learning/evaluator.py +438 -0
- attune_llm/learning/extractor.py +514 -0
- attune_llm/learning/storage.py +560 -0
- attune_llm/levels.py +227 -0
- attune_llm/pattern_confidence.py +414 -0
- attune_llm/pattern_resolver.py +272 -0
- attune_llm/pattern_summary.py +350 -0
- attune_llm/providers.py +967 -0
- attune_llm/routing/__init__.py +32 -0
- attune_llm/routing/model_router.py +362 -0
- attune_llm/security/IMPLEMENTATION_SUMMARY.md +413 -0
- attune_llm/security/PHASE2_COMPLETE.md +384 -0
- attune_llm/security/PHASE2_SECRETS_DETECTOR_COMPLETE.md +271 -0
- attune_llm/security/QUICK_REFERENCE.md +316 -0
- attune_llm/security/README.md +262 -0
- attune_llm/security/__init__.py +62 -0
- attune_llm/security/audit_logger.py +929 -0
- attune_llm/security/audit_logger_example.py +152 -0
- attune_llm/security/pii_scrubber.py +640 -0
- attune_llm/security/secrets_detector.py +678 -0
- attune_llm/security/secrets_detector_example.py +304 -0
- attune_llm/security/secure_memdocs.py +1192 -0
- attune_llm/security/secure_memdocs_example.py +278 -0
- attune_llm/session_status.py +745 -0
- attune_llm/state.py +246 -0
- attune_llm/utils/__init__.py +5 -0
- attune_llm/utils/tokens.py +349 -0
- attune_software/SOFTWARE_PLUGIN_README.md +57 -0
- attune_software/__init__.py +13 -0
- attune_software/cli/__init__.py +120 -0
- attune_software/cli/inspect.py +362 -0
- attune_software/cli.py +574 -0
- attune_software/plugin.py +188 -0
- workflow_scaffolding/__init__.py +11 -0
- workflow_scaffolding/__main__.py +12 -0
- workflow_scaffolding/cli.py +206 -0
- workflow_scaffolding/generator.py +265 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0",
|
|
3
|
+
"updated_at": "2026-01-07T06:30:43.930352",
|
|
4
|
+
"node_count": 5,
|
|
5
|
+
"edge_count": 0,
|
|
6
|
+
"nodes": [
|
|
7
|
+
{
|
|
8
|
+
"id": "refactoring_analysis_20260107063043_663dbce03854",
|
|
9
|
+
"type": "pattern",
|
|
10
|
+
"name": "refactor:workflows/bug_predict.py",
|
|
11
|
+
"description": "Found 1 refactoring opportunities:\n - 1 medium impact\n\nTop categories:\n - other: 1",
|
|
12
|
+
"source_wizard": "refactoring_crew",
|
|
13
|
+
"source_file": "",
|
|
14
|
+
"source_line": null,
|
|
15
|
+
"severity": "",
|
|
16
|
+
"confidence": 1.0,
|
|
17
|
+
"metadata": {},
|
|
18
|
+
"tags": [],
|
|
19
|
+
"created_at": "2026-01-07T06:30:43.460922",
|
|
20
|
+
"updated_at": "2026-01-07T06:30:43.460924",
|
|
21
|
+
"status": "open"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "refactoring_analysis_20260107063043_fc92406de1d5",
|
|
25
|
+
"type": "pattern",
|
|
26
|
+
"name": "refactor:monitoring/alerts_cli.py",
|
|
27
|
+
"description": "Found 1 refactoring opportunities:\n - 1 medium impact\n\nTop categories:\n - other: 1",
|
|
28
|
+
"source_wizard": "refactoring_crew",
|
|
29
|
+
"source_file": "",
|
|
30
|
+
"source_line": null,
|
|
31
|
+
"severity": "",
|
|
32
|
+
"confidence": 1.0,
|
|
33
|
+
"metadata": {},
|
|
34
|
+
"tags": [],
|
|
35
|
+
"created_at": "2026-01-07T06:30:43.593291",
|
|
36
|
+
"updated_at": "2026-01-07T06:30:43.593291",
|
|
37
|
+
"status": "open"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"id": "refactoring_analysis_20260107063043_5cce2d4230a4",
|
|
41
|
+
"type": "pattern",
|
|
42
|
+
"name": "refactor:memory/edges.py",
|
|
43
|
+
"description": "Found 1 refactoring opportunities:\n - 1 medium impact\n\nTop categories:\n - other: 1",
|
|
44
|
+
"source_wizard": "refactoring_crew",
|
|
45
|
+
"source_file": "",
|
|
46
|
+
"source_line": null,
|
|
47
|
+
"severity": "",
|
|
48
|
+
"confidence": 1.0,
|
|
49
|
+
"metadata": {},
|
|
50
|
+
"tags": [],
|
|
51
|
+
"created_at": "2026-01-07T06:30:43.682805",
|
|
52
|
+
"updated_at": "2026-01-07T06:30:43.682806",
|
|
53
|
+
"status": "open"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "refactoring_analysis_20260107063043_0ef136ce11fb",
|
|
57
|
+
"type": "pattern",
|
|
58
|
+
"name": "refactor:workflows/refactor_plan.py",
|
|
59
|
+
"description": "Found 1 refactoring opportunities:\n - 1 medium impact\n\nTop categories:\n - other: 1",
|
|
60
|
+
"source_wizard": "refactoring_crew",
|
|
61
|
+
"source_file": "",
|
|
62
|
+
"source_line": null,
|
|
63
|
+
"severity": "",
|
|
64
|
+
"confidence": 1.0,
|
|
65
|
+
"metadata": {},
|
|
66
|
+
"tags": [],
|
|
67
|
+
"created_at": "2026-01-07T06:30:43.796584",
|
|
68
|
+
"updated_at": "2026-01-07T06:30:43.796586",
|
|
69
|
+
"status": "open"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"id": "refactoring_analysis_20260107063043_3c6673a7bb17",
|
|
73
|
+
"type": "pattern",
|
|
74
|
+
"name": "refactor:workflows/new_sample_workflow1.py",
|
|
75
|
+
"description": "Found 1 refactoring opportunities:\n - 1 medium impact\n\nTop categories:\n - other: 1",
|
|
76
|
+
"source_wizard": "refactoring_crew",
|
|
77
|
+
"source_file": "",
|
|
78
|
+
"source_line": null,
|
|
79
|
+
"severity": "",
|
|
80
|
+
"confidence": 1.0,
|
|
81
|
+
"metadata": {},
|
|
82
|
+
"tags": [],
|
|
83
|
+
"created_at": "2026-01-07T06:30:43.930183",
|
|
84
|
+
"updated_at": "2026-01-07T06:30:43.930183",
|
|
85
|
+
"status": "open"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"edges": []
|
|
89
|
+
}
|
attune/persistence.py
ADDED
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
"""Persistence Layer for Empathy Framework
|
|
2
|
+
|
|
3
|
+
Provides:
|
|
4
|
+
- Pattern library save/load (JSON, SQLite)
|
|
5
|
+
- Collaboration state persistence
|
|
6
|
+
- Metrics and telemetry tracking
|
|
7
|
+
|
|
8
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
9
|
+
Licensed under Fair Source 0.9
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import sqlite3
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from .config import _validate_file_path
|
|
19
|
+
from .core import CollaborationState
|
|
20
|
+
from .pattern_library import Pattern, PatternLibrary
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PatternPersistence:
|
|
24
|
+
"""Save and load PatternLibrary to/from files
|
|
25
|
+
|
|
26
|
+
Supports:
|
|
27
|
+
- JSON format (human-readable, good for backups)
|
|
28
|
+
- SQLite format (queryable, good for production)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def save_to_json(library: PatternLibrary, filepath: str):
|
|
33
|
+
"""Save pattern library to JSON file
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
library: PatternLibrary instance to save
|
|
37
|
+
filepath: Path to JSON file
|
|
38
|
+
|
|
39
|
+
Example:
|
|
40
|
+
>>> library = PatternLibrary()
|
|
41
|
+
>>> PatternPersistence.save_to_json(library, "patterns.json")
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
patterns_list: list[dict[str, Any]] = []
|
|
45
|
+
data: dict[str, Any] = {
|
|
46
|
+
"patterns": patterns_list,
|
|
47
|
+
"agent_contributions": library.agent_contributions,
|
|
48
|
+
"metadata": {
|
|
49
|
+
"saved_at": datetime.now().isoformat(),
|
|
50
|
+
"pattern_count": len(library.patterns),
|
|
51
|
+
"version": "1.0",
|
|
52
|
+
},
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Serialize each pattern
|
|
56
|
+
for _pattern_id, pattern in library.patterns.items():
|
|
57
|
+
patterns_list.append(
|
|
58
|
+
{
|
|
59
|
+
"id": pattern.id,
|
|
60
|
+
"agent_id": pattern.agent_id,
|
|
61
|
+
"pattern_type": pattern.pattern_type,
|
|
62
|
+
"name": pattern.name,
|
|
63
|
+
"description": pattern.description,
|
|
64
|
+
"context": pattern.context,
|
|
65
|
+
"code": pattern.code,
|
|
66
|
+
"confidence": pattern.confidence,
|
|
67
|
+
"usage_count": pattern.usage_count,
|
|
68
|
+
"success_count": pattern.success_count,
|
|
69
|
+
"failure_count": pattern.failure_count,
|
|
70
|
+
"tags": pattern.tags,
|
|
71
|
+
"discovered_at": pattern.discovered_at.isoformat(),
|
|
72
|
+
"last_used": pattern.last_used.isoformat() if pattern.last_used else None,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Write to file
|
|
77
|
+
validated_path = _validate_file_path(filepath)
|
|
78
|
+
with open(validated_path, "w") as f:
|
|
79
|
+
json.dump(data, f, indent=2)
|
|
80
|
+
|
|
81
|
+
@staticmethod
|
|
82
|
+
def load_from_json(filepath: str) -> PatternLibrary:
|
|
83
|
+
"""Load pattern library from JSON file
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
filepath: Path to JSON file
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
PatternLibrary instance
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
FileNotFoundError: If file doesn't exist
|
|
93
|
+
json.JSONDecodeError: If file is not valid JSON
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> library = PatternPersistence.load_from_json("patterns.json")
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
with open(filepath) as f:
|
|
100
|
+
data = json.load(f)
|
|
101
|
+
|
|
102
|
+
library = PatternLibrary()
|
|
103
|
+
|
|
104
|
+
# Restore patterns
|
|
105
|
+
for pattern_data in data["patterns"]:
|
|
106
|
+
pattern = Pattern(
|
|
107
|
+
id=pattern_data["id"],
|
|
108
|
+
agent_id=pattern_data["agent_id"],
|
|
109
|
+
pattern_type=pattern_data["pattern_type"],
|
|
110
|
+
name=pattern_data["name"],
|
|
111
|
+
description=pattern_data["description"],
|
|
112
|
+
context=pattern_data.get("context", {}),
|
|
113
|
+
code=pattern_data.get("code"),
|
|
114
|
+
confidence=pattern_data.get("confidence", 0.5),
|
|
115
|
+
usage_count=pattern_data.get("usage_count", 0),
|
|
116
|
+
success_count=pattern_data.get("success_count", 0),
|
|
117
|
+
failure_count=pattern_data.get("failure_count", 0),
|
|
118
|
+
tags=pattern_data.get("tags", []),
|
|
119
|
+
discovered_at=datetime.fromisoformat(pattern_data["discovered_at"]),
|
|
120
|
+
last_used=(
|
|
121
|
+
datetime.fromisoformat(pattern_data["last_used"])
|
|
122
|
+
if pattern_data.get("last_used")
|
|
123
|
+
else None
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
library.contribute_pattern(pattern.agent_id, pattern)
|
|
127
|
+
|
|
128
|
+
# Restore agent_contributions index
|
|
129
|
+
library.agent_contributions = data.get("agent_contributions", {})
|
|
130
|
+
|
|
131
|
+
return library
|
|
132
|
+
|
|
133
|
+
@staticmethod
|
|
134
|
+
def save_to_sqlite(library: PatternLibrary, db_path: str):
|
|
135
|
+
"""Save pattern library to SQLite database
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
library: PatternLibrary instance to save
|
|
139
|
+
db_path: Path to SQLite database file
|
|
140
|
+
|
|
141
|
+
Creates tables:
|
|
142
|
+
- patterns: Core pattern data
|
|
143
|
+
- pattern_usage: Usage history
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
>>> library = PatternLibrary()
|
|
147
|
+
>>> PatternPersistence.save_to_sqlite(library, "patterns.db")
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
conn = sqlite3.connect(db_path)
|
|
151
|
+
cursor = conn.cursor()
|
|
152
|
+
|
|
153
|
+
# Create tables
|
|
154
|
+
cursor.execute(
|
|
155
|
+
"""
|
|
156
|
+
CREATE TABLE IF NOT EXISTS patterns (
|
|
157
|
+
id TEXT PRIMARY KEY,
|
|
158
|
+
agent_id TEXT NOT NULL,
|
|
159
|
+
pattern_type TEXT NOT NULL,
|
|
160
|
+
name TEXT NOT NULL,
|
|
161
|
+
description TEXT,
|
|
162
|
+
context TEXT,
|
|
163
|
+
code TEXT,
|
|
164
|
+
confidence REAL DEFAULT 0.5,
|
|
165
|
+
usage_count INTEGER DEFAULT 0,
|
|
166
|
+
success_count INTEGER DEFAULT 0,
|
|
167
|
+
failure_count INTEGER DEFAULT 0,
|
|
168
|
+
tags TEXT,
|
|
169
|
+
discovered_at TIMESTAMP,
|
|
170
|
+
last_used TIMESTAMP,
|
|
171
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
172
|
+
)
|
|
173
|
+
""",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
cursor.execute(
|
|
177
|
+
"""
|
|
178
|
+
CREATE TABLE IF NOT EXISTS pattern_usage (
|
|
179
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
180
|
+
pattern_id TEXT NOT NULL,
|
|
181
|
+
agent_id TEXT NOT NULL,
|
|
182
|
+
success BOOLEAN NOT NULL,
|
|
183
|
+
used_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
184
|
+
FOREIGN KEY (pattern_id) REFERENCES patterns(id)
|
|
185
|
+
)
|
|
186
|
+
""",
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# Insert or update patterns
|
|
190
|
+
for pattern in library.patterns.values():
|
|
191
|
+
cursor.execute(
|
|
192
|
+
"""
|
|
193
|
+
INSERT OR REPLACE INTO patterns (
|
|
194
|
+
id, agent_id, pattern_type, name, description, context,
|
|
195
|
+
code, confidence, usage_count, success_count, failure_count,
|
|
196
|
+
tags, discovered_at, last_used, updated_at
|
|
197
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
198
|
+
""",
|
|
199
|
+
(
|
|
200
|
+
pattern.id,
|
|
201
|
+
pattern.agent_id,
|
|
202
|
+
pattern.pattern_type,
|
|
203
|
+
pattern.name,
|
|
204
|
+
pattern.description,
|
|
205
|
+
json.dumps(pattern.context),
|
|
206
|
+
pattern.code,
|
|
207
|
+
pattern.confidence,
|
|
208
|
+
pattern.usage_count,
|
|
209
|
+
pattern.success_count,
|
|
210
|
+
pattern.failure_count,
|
|
211
|
+
json.dumps(pattern.tags),
|
|
212
|
+
pattern.discovered_at.isoformat(),
|
|
213
|
+
pattern.last_used.isoformat() if pattern.last_used else None,
|
|
214
|
+
),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
conn.commit()
|
|
218
|
+
conn.close()
|
|
219
|
+
|
|
220
|
+
@staticmethod
|
|
221
|
+
def load_from_sqlite(db_path: str) -> PatternLibrary:
|
|
222
|
+
"""Load pattern library from SQLite database
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
db_path: Path to SQLite database file
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
PatternLibrary instance
|
|
229
|
+
|
|
230
|
+
Example:
|
|
231
|
+
>>> library = PatternPersistence.load_from_sqlite("patterns.db")
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
conn = sqlite3.connect(db_path)
|
|
235
|
+
conn.row_factory = sqlite3.Row # Access columns by name
|
|
236
|
+
cursor = conn.cursor()
|
|
237
|
+
|
|
238
|
+
library = PatternLibrary()
|
|
239
|
+
|
|
240
|
+
# Load patterns
|
|
241
|
+
cursor.execute("SELECT * FROM patterns")
|
|
242
|
+
rows = cursor.fetchall()
|
|
243
|
+
|
|
244
|
+
for row in rows:
|
|
245
|
+
pattern = Pattern(
|
|
246
|
+
id=row["id"],
|
|
247
|
+
agent_id=row["agent_id"],
|
|
248
|
+
pattern_type=row["pattern_type"],
|
|
249
|
+
name=row["name"],
|
|
250
|
+
description=row["description"],
|
|
251
|
+
context=json.loads(row["context"]),
|
|
252
|
+
code=row["code"],
|
|
253
|
+
confidence=row["confidence"],
|
|
254
|
+
usage_count=row["usage_count"],
|
|
255
|
+
success_count=row["success_count"],
|
|
256
|
+
failure_count=row["failure_count"],
|
|
257
|
+
tags=json.loads(row["tags"]),
|
|
258
|
+
discovered_at=datetime.fromisoformat(row["discovered_at"]),
|
|
259
|
+
last_used=datetime.fromisoformat(row["last_used"]) if row["last_used"] else None,
|
|
260
|
+
)
|
|
261
|
+
library.contribute_pattern(pattern.agent_id, pattern)
|
|
262
|
+
|
|
263
|
+
conn.close()
|
|
264
|
+
return library
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class StateManager:
|
|
268
|
+
"""Persist collaboration state across sessions
|
|
269
|
+
|
|
270
|
+
Enables:
|
|
271
|
+
- Long-term trust tracking
|
|
272
|
+
- Historical analytics
|
|
273
|
+
- User personalization
|
|
274
|
+
"""
|
|
275
|
+
|
|
276
|
+
def __init__(self, storage_path: str = "./empathy_state"):
|
|
277
|
+
self.storage_path = Path(storage_path)
|
|
278
|
+
self.storage_path.mkdir(exist_ok=True, parents=True)
|
|
279
|
+
|
|
280
|
+
def save_state(self, user_id: str, state: CollaborationState):
|
|
281
|
+
"""Save user's collaboration state to JSON
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
user_id: User identifier
|
|
285
|
+
state: CollaborationState instance
|
|
286
|
+
|
|
287
|
+
Example:
|
|
288
|
+
>>> manager = StateManager()
|
|
289
|
+
>>> manager.save_state("user123", empathy.collaboration_state)
|
|
290
|
+
|
|
291
|
+
"""
|
|
292
|
+
filepath = self.storage_path / f"{user_id}.json"
|
|
293
|
+
|
|
294
|
+
data = {
|
|
295
|
+
"user_id": user_id,
|
|
296
|
+
"trust_level": state.trust_level,
|
|
297
|
+
"total_interactions": state.total_interactions,
|
|
298
|
+
"successful_interventions": state.successful_interventions,
|
|
299
|
+
"failed_interventions": state.failed_interventions,
|
|
300
|
+
"session_start": state.session_start.isoformat(),
|
|
301
|
+
"trust_trajectory": state.trust_trajectory,
|
|
302
|
+
"shared_context": state.shared_context,
|
|
303
|
+
"saved_at": datetime.now().isoformat(),
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
validated_path = _validate_file_path(str(filepath))
|
|
307
|
+
with open(validated_path, "w") as f:
|
|
308
|
+
json.dump(data, f, indent=2)
|
|
309
|
+
|
|
310
|
+
def load_state(self, user_id: str) -> CollaborationState | None:
|
|
311
|
+
"""Load user's previous state
|
|
312
|
+
|
|
313
|
+
Args:
|
|
314
|
+
user_id: User identifier
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
CollaborationState if found, None otherwise
|
|
318
|
+
|
|
319
|
+
Example:
|
|
320
|
+
>>> manager = StateManager()
|
|
321
|
+
>>> state = manager.load_state("user123")
|
|
322
|
+
>>> if state:
|
|
323
|
+
... empathy = EmpathyOS(user_id="user123", target_level=4)
|
|
324
|
+
... empathy.collaboration_state = state
|
|
325
|
+
|
|
326
|
+
"""
|
|
327
|
+
filepath = self.storage_path / f"{user_id}.json"
|
|
328
|
+
|
|
329
|
+
if not filepath.exists():
|
|
330
|
+
return None
|
|
331
|
+
|
|
332
|
+
try:
|
|
333
|
+
with open(filepath) as f:
|
|
334
|
+
data = json.load(f)
|
|
335
|
+
|
|
336
|
+
state = CollaborationState()
|
|
337
|
+
state.trust_level = data["trust_level"]
|
|
338
|
+
state.total_interactions = data["total_interactions"]
|
|
339
|
+
state.successful_interventions = data["successful_interventions"]
|
|
340
|
+
state.failed_interventions = data["failed_interventions"]
|
|
341
|
+
state.session_start = datetime.fromisoformat(data["session_start"])
|
|
342
|
+
state.trust_trajectory = data.get("trust_trajectory", [])
|
|
343
|
+
state.shared_context = data.get("shared_context", {})
|
|
344
|
+
|
|
345
|
+
return state
|
|
346
|
+
|
|
347
|
+
except (json.JSONDecodeError, KeyError, ValueError):
|
|
348
|
+
# Corrupted file - return None
|
|
349
|
+
return None
|
|
350
|
+
|
|
351
|
+
def list_users(self) -> list[str]:
|
|
352
|
+
"""List all users with saved state
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
List of user IDs
|
|
356
|
+
|
|
357
|
+
Example:
|
|
358
|
+
>>> manager = StateManager()
|
|
359
|
+
>>> users = manager.list_users()
|
|
360
|
+
>>> print(f"Found {len(users)} users")
|
|
361
|
+
|
|
362
|
+
"""
|
|
363
|
+
return [p.stem for p in self.storage_path.glob("*.json")]
|
|
364
|
+
|
|
365
|
+
def delete_state(self, user_id: str) -> bool:
|
|
366
|
+
"""Delete user's saved state
|
|
367
|
+
|
|
368
|
+
Args:
|
|
369
|
+
user_id: User identifier
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
True if deleted, False if didn't exist
|
|
373
|
+
|
|
374
|
+
Example:
|
|
375
|
+
>>> manager = StateManager()
|
|
376
|
+
>>> deleted = manager.delete_state("user123")
|
|
377
|
+
|
|
378
|
+
"""
|
|
379
|
+
filepath = self.storage_path / f"{user_id}.json"
|
|
380
|
+
|
|
381
|
+
if filepath.exists():
|
|
382
|
+
filepath.unlink()
|
|
383
|
+
return True
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
class MetricsCollector:
|
|
388
|
+
"""Collect and persist empathy framework metrics
|
|
389
|
+
|
|
390
|
+
Tracks:
|
|
391
|
+
- Empathy level usage
|
|
392
|
+
- Success rates by level
|
|
393
|
+
- Average response times
|
|
394
|
+
- Trust trajectory trends
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
def __init__(self, db_path: str = "./metrics.db"):
|
|
398
|
+
self.db_path = db_path
|
|
399
|
+
self._init_database()
|
|
400
|
+
|
|
401
|
+
def _init_database(self):
|
|
402
|
+
"""Initialize SQLite database for metrics"""
|
|
403
|
+
conn = sqlite3.connect(self.db_path)
|
|
404
|
+
cursor = conn.cursor()
|
|
405
|
+
|
|
406
|
+
cursor.execute(
|
|
407
|
+
"""
|
|
408
|
+
CREATE TABLE IF NOT EXISTS metrics (
|
|
409
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
410
|
+
user_id TEXT NOT NULL,
|
|
411
|
+
empathy_level INTEGER NOT NULL,
|
|
412
|
+
success BOOLEAN NOT NULL,
|
|
413
|
+
response_time_ms REAL,
|
|
414
|
+
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
415
|
+
metadata TEXT
|
|
416
|
+
)
|
|
417
|
+
""",
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
cursor.execute(
|
|
421
|
+
"""
|
|
422
|
+
CREATE INDEX IF NOT EXISTS idx_user_level
|
|
423
|
+
ON metrics(user_id, empathy_level)
|
|
424
|
+
""",
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
cursor.execute(
|
|
428
|
+
"""
|
|
429
|
+
CREATE INDEX IF NOT EXISTS idx_timestamp
|
|
430
|
+
ON metrics(timestamp)
|
|
431
|
+
""",
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
conn.commit()
|
|
435
|
+
conn.close()
|
|
436
|
+
|
|
437
|
+
def record_metric(
|
|
438
|
+
self,
|
|
439
|
+
user_id: str,
|
|
440
|
+
empathy_level: int,
|
|
441
|
+
success: bool,
|
|
442
|
+
response_time_ms: float,
|
|
443
|
+
metadata: dict | None = None,
|
|
444
|
+
):
|
|
445
|
+
"""Record a single metric event
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
user_id: User identifier
|
|
449
|
+
empathy_level: 1-5 empathy level used
|
|
450
|
+
success: Whether the operation succeeded
|
|
451
|
+
response_time_ms: Response time in milliseconds
|
|
452
|
+
metadata: Optional additional data
|
|
453
|
+
|
|
454
|
+
Example:
|
|
455
|
+
>>> collector = MetricsCollector()
|
|
456
|
+
>>> collector.record_metric(
|
|
457
|
+
... user_id="user123",
|
|
458
|
+
... empathy_level=4,
|
|
459
|
+
... success=True,
|
|
460
|
+
... response_time_ms=250.5,
|
|
461
|
+
... metadata={"bottlenecks_predicted": 3}
|
|
462
|
+
... )
|
|
463
|
+
|
|
464
|
+
"""
|
|
465
|
+
conn = sqlite3.connect(self.db_path)
|
|
466
|
+
cursor = conn.cursor()
|
|
467
|
+
|
|
468
|
+
cursor.execute(
|
|
469
|
+
"""
|
|
470
|
+
INSERT INTO metrics (
|
|
471
|
+
user_id, empathy_level, success, response_time_ms, metadata
|
|
472
|
+
) VALUES (?, ?, ?, ?, ?)
|
|
473
|
+
""",
|
|
474
|
+
(
|
|
475
|
+
user_id,
|
|
476
|
+
empathy_level,
|
|
477
|
+
success,
|
|
478
|
+
response_time_ms,
|
|
479
|
+
json.dumps(metadata) if metadata else None,
|
|
480
|
+
),
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
conn.commit()
|
|
484
|
+
conn.close()
|
|
485
|
+
|
|
486
|
+
def get_user_stats(self, user_id: str) -> dict:
|
|
487
|
+
"""Get aggregated statistics for a user
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
user_id: User identifier
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
Dict with statistics
|
|
494
|
+
|
|
495
|
+
Example:
|
|
496
|
+
>>> collector = MetricsCollector()
|
|
497
|
+
>>> stats = collector.get_user_stats("user123")
|
|
498
|
+
>>> print(f"Success rate: {stats['success_rate']:.1%}")
|
|
499
|
+
|
|
500
|
+
"""
|
|
501
|
+
conn = sqlite3.connect(self.db_path)
|
|
502
|
+
conn.row_factory = sqlite3.Row
|
|
503
|
+
cursor = conn.cursor()
|
|
504
|
+
|
|
505
|
+
cursor.execute(
|
|
506
|
+
"""
|
|
507
|
+
SELECT
|
|
508
|
+
COUNT(*) as total_operations,
|
|
509
|
+
SUM(CASE WHEN success THEN 1 ELSE 0 END) as successes,
|
|
510
|
+
AVG(response_time_ms) as avg_response_time,
|
|
511
|
+
MIN(timestamp) as first_use,
|
|
512
|
+
MAX(timestamp) as last_use
|
|
513
|
+
FROM metrics
|
|
514
|
+
WHERE user_id = ?
|
|
515
|
+
""",
|
|
516
|
+
(user_id,),
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
row = cursor.fetchone()
|
|
520
|
+
|
|
521
|
+
if not row or row["total_operations"] == 0:
|
|
522
|
+
conn.close()
|
|
523
|
+
return {
|
|
524
|
+
"total_operations": 0,
|
|
525
|
+
"success_rate": 0.0,
|
|
526
|
+
"avg_response_time_ms": 0.0,
|
|
527
|
+
"first_use": None,
|
|
528
|
+
"last_use": None,
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
# Get per-level breakdown
|
|
532
|
+
cursor.execute(
|
|
533
|
+
"""
|
|
534
|
+
SELECT
|
|
535
|
+
empathy_level,
|
|
536
|
+
COUNT(*) as operations,
|
|
537
|
+
SUM(CASE WHEN success THEN 1 ELSE 0 END) as successes
|
|
538
|
+
FROM metrics
|
|
539
|
+
WHERE user_id = ?
|
|
540
|
+
GROUP BY empathy_level
|
|
541
|
+
ORDER BY empathy_level
|
|
542
|
+
""",
|
|
543
|
+
(user_id,),
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
level_stats = {}
|
|
547
|
+
for level_row in cursor.fetchall():
|
|
548
|
+
level = level_row["empathy_level"]
|
|
549
|
+
ops = level_row["operations"]
|
|
550
|
+
level_stats[f"level_{level}"] = {
|
|
551
|
+
"operations": ops,
|
|
552
|
+
"success_rate": level_row["successes"] / ops if ops > 0 else 0.0,
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
conn.close()
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
"total_operations": row["total_operations"],
|
|
559
|
+
"success_rate": row["successes"] / row["total_operations"],
|
|
560
|
+
"avg_response_time_ms": row["avg_response_time"],
|
|
561
|
+
"first_use": row["first_use"],
|
|
562
|
+
"last_use": row["last_use"],
|
|
563
|
+
"by_level": level_stats,
|
|
564
|
+
}
|