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,196 @@
|
|
|
1
|
+
"""Telemetry backend interface.
|
|
2
|
+
|
|
3
|
+
Abstract base class for telemetry storage backends.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from typing import Protocol
|
|
11
|
+
|
|
12
|
+
from .data_models import (
|
|
13
|
+
AgentAssignmentRecord,
|
|
14
|
+
CoverageRecord,
|
|
15
|
+
FileTestRecord,
|
|
16
|
+
LLMCallRecord,
|
|
17
|
+
TaskRoutingRecord,
|
|
18
|
+
TestExecutionRecord,
|
|
19
|
+
WorkflowRunRecord,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _parse_timestamp(timestamp_str: str) -> datetime:
|
|
24
|
+
"""Parse ISO format timestamp, handling 'Z' suffix for Python 3.10 compatibility.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
timestamp_str: ISO format timestamp string, possibly with 'Z' suffix
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Parsed datetime object (timezone-naive UTC)
|
|
31
|
+
"""
|
|
32
|
+
# Python 3.10's fromisoformat() doesn't handle 'Z' suffix
|
|
33
|
+
if timestamp_str.endswith("Z"):
|
|
34
|
+
timestamp_str = timestamp_str[:-1]
|
|
35
|
+
|
|
36
|
+
dt = datetime.fromisoformat(timestamp_str)
|
|
37
|
+
|
|
38
|
+
# Convert to naive UTC if timezone-aware
|
|
39
|
+
if dt.tzinfo is not None:
|
|
40
|
+
dt = dt.replace(tzinfo=None)
|
|
41
|
+
|
|
42
|
+
return dt
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class TelemetryBackend(Protocol):
|
|
47
|
+
"""Protocol for telemetry storage backends.
|
|
48
|
+
|
|
49
|
+
Implementations can store telemetry data in different backends:
|
|
50
|
+
- JSONL files (default, via TelemetryStore)
|
|
51
|
+
- Database (PostgreSQL, SQLite, etc.)
|
|
52
|
+
- Cloud services (DataDog, New Relic, etc.)
|
|
53
|
+
- Custom backends
|
|
54
|
+
|
|
55
|
+
Supports both core telemetry (LLM calls, workflows) and Tier 1
|
|
56
|
+
automation monitoring (task routing, tests, coverage, assignments).
|
|
57
|
+
|
|
58
|
+
Example implementing a custom backend:
|
|
59
|
+
>>> class DatabaseBackend:
|
|
60
|
+
... def log_call(self, record: LLMCallRecord) -> None:
|
|
61
|
+
... # Insert into database
|
|
62
|
+
... pass
|
|
63
|
+
...
|
|
64
|
+
... def log_workflow(self, record: WorkflowRunRecord) -> None:
|
|
65
|
+
... # Insert into database
|
|
66
|
+
... pass
|
|
67
|
+
...
|
|
68
|
+
... def get_calls(self, since=None, workflow_name=None, limit=1000):
|
|
69
|
+
... # Query database
|
|
70
|
+
... return []
|
|
71
|
+
...
|
|
72
|
+
... def get_workflows(self, since=None, workflow_name=None, limit=100):
|
|
73
|
+
... # Query database
|
|
74
|
+
... return []
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
def log_call(self, record: LLMCallRecord) -> None:
|
|
78
|
+
"""Log an LLM call record."""
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
def log_workflow(self, record: WorkflowRunRecord) -> None:
|
|
82
|
+
"""Log a workflow run record."""
|
|
83
|
+
...
|
|
84
|
+
|
|
85
|
+
def get_calls(
|
|
86
|
+
self,
|
|
87
|
+
since: datetime | None = None,
|
|
88
|
+
workflow_name: str | None = None,
|
|
89
|
+
limit: int = 1000,
|
|
90
|
+
) -> list[LLMCallRecord]:
|
|
91
|
+
"""Get LLM call records with optional filters."""
|
|
92
|
+
...
|
|
93
|
+
|
|
94
|
+
def get_workflows(
|
|
95
|
+
self,
|
|
96
|
+
since: datetime | None = None,
|
|
97
|
+
workflow_name: str | None = None,
|
|
98
|
+
limit: int = 100,
|
|
99
|
+
) -> list[WorkflowRunRecord]:
|
|
100
|
+
"""Get workflow run records with optional filters."""
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
# Tier 1 automation monitoring methods
|
|
104
|
+
def log_task_routing(self, record: TaskRoutingRecord) -> None:
|
|
105
|
+
"""Log a task routing decision."""
|
|
106
|
+
...
|
|
107
|
+
|
|
108
|
+
def log_test_execution(self, record: TestExecutionRecord) -> None:
|
|
109
|
+
"""Log a test execution."""
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
def log_coverage(self, record: CoverageRecord) -> None:
|
|
113
|
+
"""Log coverage metrics."""
|
|
114
|
+
...
|
|
115
|
+
|
|
116
|
+
def log_agent_assignment(self, record: AgentAssignmentRecord) -> None:
|
|
117
|
+
"""Log an agent assignment."""
|
|
118
|
+
...
|
|
119
|
+
|
|
120
|
+
def get_task_routings(
|
|
121
|
+
self,
|
|
122
|
+
since: datetime | None = None,
|
|
123
|
+
status: str | None = None,
|
|
124
|
+
limit: int = 1000,
|
|
125
|
+
) -> list[TaskRoutingRecord]:
|
|
126
|
+
"""Get task routing records with optional filters."""
|
|
127
|
+
...
|
|
128
|
+
|
|
129
|
+
def get_test_executions(
|
|
130
|
+
self,
|
|
131
|
+
since: datetime | None = None,
|
|
132
|
+
success_only: bool = False,
|
|
133
|
+
limit: int = 100,
|
|
134
|
+
) -> list[TestExecutionRecord]:
|
|
135
|
+
"""Get test execution records with optional filters."""
|
|
136
|
+
...
|
|
137
|
+
|
|
138
|
+
def get_coverage_history(
|
|
139
|
+
self,
|
|
140
|
+
since: datetime | None = None,
|
|
141
|
+
limit: int = 100,
|
|
142
|
+
) -> list[CoverageRecord]:
|
|
143
|
+
"""Get coverage history records."""
|
|
144
|
+
...
|
|
145
|
+
|
|
146
|
+
def get_agent_assignments(
|
|
147
|
+
self,
|
|
148
|
+
since: datetime | None = None,
|
|
149
|
+
automated_only: bool = True,
|
|
150
|
+
limit: int = 1000,
|
|
151
|
+
) -> list[AgentAssignmentRecord]:
|
|
152
|
+
"""Get agent assignment records with optional filters."""
|
|
153
|
+
...
|
|
154
|
+
|
|
155
|
+
# Per-file test tracking methods
|
|
156
|
+
def log_file_test(self, record: "FileTestRecord") -> None:
|
|
157
|
+
"""Log a per-file test execution record."""
|
|
158
|
+
...
|
|
159
|
+
|
|
160
|
+
def get_file_tests(
|
|
161
|
+
self,
|
|
162
|
+
file_path: str | None = None,
|
|
163
|
+
since: datetime | None = None,
|
|
164
|
+
result_filter: str | None = None,
|
|
165
|
+
limit: int = 1000,
|
|
166
|
+
) -> list["FileTestRecord"]:
|
|
167
|
+
"""Get per-file test records with optional filters."""
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
def get_latest_file_test(self, file_path: str) -> "FileTestRecord | None":
|
|
171
|
+
"""Get the most recent test record for a specific file."""
|
|
172
|
+
...
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _parse_timestamp(timestamp_str: str) -> datetime:
|
|
176
|
+
"""Parse ISO format timestamp, handling 'Z' suffix for Python 3.10 compatibility.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
timestamp_str: ISO format timestamp string, possibly with 'Z' suffix
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Parsed datetime object (timezone-naive UTC)
|
|
183
|
+
"""
|
|
184
|
+
# Python 3.10's fromisoformat() doesn't handle 'Z' suffix
|
|
185
|
+
if timestamp_str.endswith("Z"):
|
|
186
|
+
timestamp_str = timestamp_str[:-1]
|
|
187
|
+
|
|
188
|
+
dt = datetime.fromisoformat(timestamp_str)
|
|
189
|
+
|
|
190
|
+
# Convert to naive UTC if timezone-aware
|
|
191
|
+
if dt.tzinfo is not None:
|
|
192
|
+
dt = dt.replace(tzinfo=None)
|
|
193
|
+
|
|
194
|
+
return dt
|
|
195
|
+
|
|
196
|
+
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
"""Telemetry data models.
|
|
2
|
+
|
|
3
|
+
Data classes for tracking LLM calls, workflows, tests, and agent assignments.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import asdict, dataclass, field
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class LLMCallRecord:
|
|
15
|
+
"""Record of a single LLM API call.
|
|
16
|
+
|
|
17
|
+
Captures all relevant metrics for cost tracking, performance analysis,
|
|
18
|
+
and debugging.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
# Identification
|
|
22
|
+
call_id: str
|
|
23
|
+
timestamp: str # ISO format
|
|
24
|
+
|
|
25
|
+
# Context
|
|
26
|
+
workflow_name: str | None = None
|
|
27
|
+
step_name: str | None = None
|
|
28
|
+
user_id: str | None = None
|
|
29
|
+
session_id: str | None = None
|
|
30
|
+
|
|
31
|
+
# Task routing
|
|
32
|
+
task_type: str = "unknown"
|
|
33
|
+
provider: str = "anthropic"
|
|
34
|
+
tier: str = "capable"
|
|
35
|
+
model_id: str = ""
|
|
36
|
+
|
|
37
|
+
# Token usage
|
|
38
|
+
input_tokens: int = 0
|
|
39
|
+
output_tokens: int = 0
|
|
40
|
+
|
|
41
|
+
# Cost (in USD)
|
|
42
|
+
estimated_cost: float = 0.0
|
|
43
|
+
actual_cost: float | None = None
|
|
44
|
+
|
|
45
|
+
# Performance
|
|
46
|
+
latency_ms: int = 0
|
|
47
|
+
|
|
48
|
+
# Fallback and resilience tracking
|
|
49
|
+
fallback_used: bool = False
|
|
50
|
+
fallback_chain: list[str] = field(default_factory=list)
|
|
51
|
+
original_provider: str | None = None
|
|
52
|
+
original_model: str | None = None
|
|
53
|
+
retry_count: int = 0 # Number of retries before success
|
|
54
|
+
circuit_breaker_state: str | None = None # "closed", "open", "half-open"
|
|
55
|
+
|
|
56
|
+
# Error tracking
|
|
57
|
+
success: bool = True
|
|
58
|
+
error_type: str | None = None
|
|
59
|
+
error_message: str | None = None
|
|
60
|
+
|
|
61
|
+
# Additional metadata
|
|
62
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
63
|
+
|
|
64
|
+
def to_dict(self) -> dict[str, Any]:
|
|
65
|
+
"""Convert to dictionary for JSON serialization."""
|
|
66
|
+
return asdict(self)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def from_dict(cls, data: dict[str, Any]) -> "LLMCallRecord":
|
|
70
|
+
"""Create from dictionary."""
|
|
71
|
+
return cls(**data)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass
|
|
75
|
+
class WorkflowStageRecord:
|
|
76
|
+
"""Record of a single workflow stage execution."""
|
|
77
|
+
|
|
78
|
+
stage_name: str
|
|
79
|
+
tier: str
|
|
80
|
+
model_id: str
|
|
81
|
+
input_tokens: int = 0
|
|
82
|
+
output_tokens: int = 0
|
|
83
|
+
cost: float = 0.0
|
|
84
|
+
latency_ms: int = 0
|
|
85
|
+
success: bool = True
|
|
86
|
+
skipped: bool = False
|
|
87
|
+
skip_reason: str | None = None
|
|
88
|
+
error: str | None = None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass
|
|
92
|
+
class WorkflowRunRecord:
|
|
93
|
+
"""Record of a complete workflow execution.
|
|
94
|
+
|
|
95
|
+
Aggregates stage-level metrics and provides workflow-level analytics.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
# Identification
|
|
99
|
+
run_id: str
|
|
100
|
+
workflow_name: str
|
|
101
|
+
started_at: str # ISO format
|
|
102
|
+
completed_at: str | None = None
|
|
103
|
+
|
|
104
|
+
# Context
|
|
105
|
+
user_id: str | None = None
|
|
106
|
+
session_id: str | None = None
|
|
107
|
+
|
|
108
|
+
# Stages
|
|
109
|
+
stages: list[WorkflowStageRecord] = field(default_factory=list)
|
|
110
|
+
|
|
111
|
+
# Aggregated metrics
|
|
112
|
+
total_input_tokens: int = 0
|
|
113
|
+
total_output_tokens: int = 0
|
|
114
|
+
total_cost: float = 0.0
|
|
115
|
+
baseline_cost: float = 0.0 # If all stages used premium
|
|
116
|
+
savings: float = 0.0
|
|
117
|
+
savings_percent: float = 0.0
|
|
118
|
+
|
|
119
|
+
# Performance
|
|
120
|
+
total_duration_ms: int = 0
|
|
121
|
+
|
|
122
|
+
# Status
|
|
123
|
+
success: bool = True
|
|
124
|
+
error: str | None = None
|
|
125
|
+
|
|
126
|
+
# Provider usage
|
|
127
|
+
providers_used: list[str] = field(default_factory=list)
|
|
128
|
+
tiers_used: list[str] = field(default_factory=list)
|
|
129
|
+
|
|
130
|
+
def to_dict(self) -> dict[str, Any]:
|
|
131
|
+
"""Convert to dictionary for JSON serialization."""
|
|
132
|
+
data = asdict(self)
|
|
133
|
+
data["stages"] = [asdict(s) for s in self.stages]
|
|
134
|
+
return data
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def from_dict(cls, data: dict[str, Any]) -> "WorkflowRunRecord":
|
|
138
|
+
"""Create from dictionary."""
|
|
139
|
+
stages = [WorkflowStageRecord(**s) for s in data.pop("stages", [])]
|
|
140
|
+
return cls(stages=stages, **data)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass
|
|
144
|
+
class TaskRoutingRecord:
|
|
145
|
+
"""Record of task routing decision for Tier 1 automation.
|
|
146
|
+
|
|
147
|
+
Tracks which agent/workflow handles each task, routing strategy,
|
|
148
|
+
and execution outcome for automation monitoring.
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
# Identification (required)
|
|
152
|
+
routing_id: str
|
|
153
|
+
timestamp: str # ISO format
|
|
154
|
+
|
|
155
|
+
# Task context (required)
|
|
156
|
+
task_description: str
|
|
157
|
+
task_type: str # "code_review", "test_gen", "bug_fix", "refactor", etc.
|
|
158
|
+
task_complexity: str # "simple", "moderate", "complex"
|
|
159
|
+
|
|
160
|
+
# Routing decision (required)
|
|
161
|
+
assigned_agent: str # "test_gen_workflow", "code_review_workflow", etc.
|
|
162
|
+
assigned_tier: str # "cheap", "capable", "premium"
|
|
163
|
+
routing_strategy: str # "rule_based", "ml_predicted", "manual_override"
|
|
164
|
+
|
|
165
|
+
# Optional fields with defaults
|
|
166
|
+
task_dependencies: list[str] = field(default_factory=list) # Task IDs this depends on
|
|
167
|
+
confidence_score: float = 1.0 # 0.0-1.0 for ML predictions
|
|
168
|
+
|
|
169
|
+
# Execution tracking
|
|
170
|
+
status: str = "pending" # "pending", "running", "completed", "failed"
|
|
171
|
+
started_at: str | None = None
|
|
172
|
+
completed_at: str | None = None
|
|
173
|
+
|
|
174
|
+
# Outcome
|
|
175
|
+
success: bool = False
|
|
176
|
+
quality_score: float | None = None # 0.0-1.0 if applicable
|
|
177
|
+
retry_count: int = 0
|
|
178
|
+
error_type: str | None = None
|
|
179
|
+
error_message: str | None = None
|
|
180
|
+
|
|
181
|
+
# Cost tracking
|
|
182
|
+
estimated_cost: float = 0.0
|
|
183
|
+
actual_cost: float | None = None
|
|
184
|
+
|
|
185
|
+
# Metadata
|
|
186
|
+
user_id: str | None = None
|
|
187
|
+
session_id: str | None = None
|
|
188
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
189
|
+
|
|
190
|
+
def to_dict(self) -> dict[str, Any]:
|
|
191
|
+
"""Convert to dictionary for JSON serialization."""
|
|
192
|
+
return asdict(self)
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def from_dict(cls, data: dict[str, Any]) -> "TaskRoutingRecord":
|
|
196
|
+
"""Create from dictionary."""
|
|
197
|
+
return cls(**data)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass
|
|
201
|
+
class TestExecutionRecord:
|
|
202
|
+
"""Record of test execution for Tier 1 QA automation.
|
|
203
|
+
|
|
204
|
+
Tracks test execution results, coverage metrics, and failure details
|
|
205
|
+
for quality assurance monitoring.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
# Identification (required)
|
|
209
|
+
execution_id: str
|
|
210
|
+
timestamp: str # ISO format
|
|
211
|
+
|
|
212
|
+
# Test context (required)
|
|
213
|
+
test_suite: str # "unit", "integration", "e2e", "all"
|
|
214
|
+
|
|
215
|
+
# Optional fields with defaults
|
|
216
|
+
test_files: list[str] = field(default_factory=list) # Specific test files executed
|
|
217
|
+
triggered_by: str = "manual" # "workflow", "manual", "ci", "pre_commit"
|
|
218
|
+
|
|
219
|
+
# Execution details
|
|
220
|
+
command: str = ""
|
|
221
|
+
working_directory: str = ""
|
|
222
|
+
duration_seconds: float = 0.0
|
|
223
|
+
|
|
224
|
+
# Results
|
|
225
|
+
total_tests: int = 0
|
|
226
|
+
passed: int = 0
|
|
227
|
+
failed: int = 0
|
|
228
|
+
skipped: int = 0
|
|
229
|
+
errors: int = 0
|
|
230
|
+
|
|
231
|
+
# Coverage (if available)
|
|
232
|
+
coverage_percentage: float | None = None
|
|
233
|
+
coverage_report_path: str | None = None
|
|
234
|
+
|
|
235
|
+
# Failures
|
|
236
|
+
failed_tests: list[dict[str, Any]] = field(
|
|
237
|
+
default_factory=list
|
|
238
|
+
) # [{name, file, error, traceback}]
|
|
239
|
+
|
|
240
|
+
# Status
|
|
241
|
+
success: bool = False # True if all tests passed
|
|
242
|
+
exit_code: int = 0
|
|
243
|
+
|
|
244
|
+
# Metadata
|
|
245
|
+
workflow_id: str | None = None # Link to workflow that triggered this
|
|
246
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
247
|
+
|
|
248
|
+
def to_dict(self) -> dict[str, Any]:
|
|
249
|
+
"""Convert to dictionary for JSON serialization."""
|
|
250
|
+
return asdict(self)
|
|
251
|
+
|
|
252
|
+
@classmethod
|
|
253
|
+
def from_dict(cls, data: dict[str, Any]) -> "TestExecutionRecord":
|
|
254
|
+
"""Create from dictionary."""
|
|
255
|
+
return cls(**data)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
@dataclass
|
|
259
|
+
class CoverageRecord:
|
|
260
|
+
"""Record of test coverage metrics for Tier 1 QA monitoring.
|
|
261
|
+
|
|
262
|
+
Tracks coverage percentage, trends, and critical gaps for
|
|
263
|
+
continuous quality improvement.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
# Identification (required)
|
|
267
|
+
record_id: str
|
|
268
|
+
timestamp: str # ISO format
|
|
269
|
+
|
|
270
|
+
# Coverage metrics (required)
|
|
271
|
+
overall_percentage: float
|
|
272
|
+
lines_total: int
|
|
273
|
+
lines_covered: int
|
|
274
|
+
|
|
275
|
+
# Optional fields with defaults
|
|
276
|
+
branches_total: int = 0
|
|
277
|
+
branches_covered: int = 0
|
|
278
|
+
|
|
279
|
+
# File-level breakdown
|
|
280
|
+
files_total: int = 0
|
|
281
|
+
files_well_covered: int = 0 # >= 80%
|
|
282
|
+
files_critical: int = 0 # < 50%
|
|
283
|
+
untested_files: list[str] = field(default_factory=list)
|
|
284
|
+
|
|
285
|
+
# Critical gaps
|
|
286
|
+
critical_gaps: list[dict[str, Any]] = field(
|
|
287
|
+
default_factory=list
|
|
288
|
+
) # [{file, coverage, priority}]
|
|
289
|
+
|
|
290
|
+
# Trend data
|
|
291
|
+
previous_percentage: float | None = None
|
|
292
|
+
trend: str | None = None # "improving", "declining", "stable"
|
|
293
|
+
|
|
294
|
+
# Source
|
|
295
|
+
coverage_format: str = "xml" # "xml", "json", "lcov"
|
|
296
|
+
coverage_file: str = ""
|
|
297
|
+
|
|
298
|
+
# Metadata
|
|
299
|
+
workflow_id: str | None = None
|
|
300
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
301
|
+
|
|
302
|
+
def to_dict(self) -> dict[str, Any]:
|
|
303
|
+
"""Convert to dictionary for JSON serialization."""
|
|
304
|
+
return asdict(self)
|
|
305
|
+
|
|
306
|
+
@classmethod
|
|
307
|
+
def from_dict(cls, data: dict[str, Any]) -> "CoverageRecord":
|
|
308
|
+
"""Create from dictionary."""
|
|
309
|
+
return cls(**data)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
@dataclass
|
|
313
|
+
class AgentAssignmentRecord:
|
|
314
|
+
"""Record of agent assignment for simple tasks (Tier 1).
|
|
315
|
+
|
|
316
|
+
Tracks task assignments to agents/workflows with clear specs
|
|
317
|
+
and no complex dependencies for automation monitoring.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
# Identification (required)
|
|
321
|
+
assignment_id: str
|
|
322
|
+
timestamp: str # ISO format
|
|
323
|
+
|
|
324
|
+
# Task details (required)
|
|
325
|
+
task_id: str
|
|
326
|
+
task_title: str
|
|
327
|
+
task_description: str
|
|
328
|
+
|
|
329
|
+
# Assignment (required)
|
|
330
|
+
assigned_agent: str # Agent/workflow name
|
|
331
|
+
|
|
332
|
+
# Optional fields with defaults
|
|
333
|
+
task_spec_clarity: float = 0.0 # 0.0-1.0, higher = clearer spec
|
|
334
|
+
assignment_reason: str = "" # Why this agent was chosen
|
|
335
|
+
estimated_duration_hours: float = 0.0
|
|
336
|
+
|
|
337
|
+
# Criteria checks
|
|
338
|
+
has_clear_spec: bool = False
|
|
339
|
+
has_dependencies: bool = False
|
|
340
|
+
requires_human_review: bool = False
|
|
341
|
+
automated_eligible: bool = False # True for Tier 1
|
|
342
|
+
|
|
343
|
+
# Execution tracking
|
|
344
|
+
status: str = "assigned" # "assigned", "in_progress", "completed", "blocked"
|
|
345
|
+
started_at: str | None = None
|
|
346
|
+
completed_at: str | None = None
|
|
347
|
+
actual_duration_hours: float | None = None
|
|
348
|
+
|
|
349
|
+
# Outcome
|
|
350
|
+
success: bool = False
|
|
351
|
+
quality_check_passed: bool = False
|
|
352
|
+
human_review_required: bool = False
|
|
353
|
+
|
|
354
|
+
# Metadata
|
|
355
|
+
workflow_id: str | None = None
|
|
356
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
357
|
+
|
|
358
|
+
def to_dict(self) -> dict[str, Any]:
|
|
359
|
+
"""Convert to dictionary for JSON serialization."""
|
|
360
|
+
return asdict(self)
|
|
361
|
+
|
|
362
|
+
@classmethod
|
|
363
|
+
def from_dict(cls, data: dict[str, Any]) -> "AgentAssignmentRecord":
|
|
364
|
+
"""Create from dictionary."""
|
|
365
|
+
return cls(**data)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@dataclass
|
|
369
|
+
class FileTestRecord:
|
|
370
|
+
"""Record of test execution for a specific source file.
|
|
371
|
+
|
|
372
|
+
Tracks when tests for an individual file were last run, results,
|
|
373
|
+
and coverage - enabling per-file test status tracking.
|
|
374
|
+
|
|
375
|
+
This complements TestExecutionRecord (suite-level) by providing
|
|
376
|
+
granular file-level test tracking for better test maintenance.
|
|
377
|
+
"""
|
|
378
|
+
|
|
379
|
+
# Identification (required)
|
|
380
|
+
file_path: str # Source file path (relative to project root)
|
|
381
|
+
timestamp: str # ISO format - when tests were run
|
|
382
|
+
|
|
383
|
+
# Test results (required)
|
|
384
|
+
last_test_result: str # "passed", "failed", "error", "skipped", "no_tests"
|
|
385
|
+
test_count: int # Number of tests for this file
|
|
386
|
+
|
|
387
|
+
# Detailed results with defaults
|
|
388
|
+
passed: int = 0
|
|
389
|
+
failed: int = 0
|
|
390
|
+
skipped: int = 0
|
|
391
|
+
errors: int = 0
|
|
392
|
+
|
|
393
|
+
# Timing
|
|
394
|
+
duration_seconds: float = 0.0
|
|
395
|
+
|
|
396
|
+
# Coverage for this file (if available)
|
|
397
|
+
coverage_percent: float | None = None
|
|
398
|
+
lines_total: int = 0
|
|
399
|
+
lines_covered: int = 0
|
|
400
|
+
|
|
401
|
+
# Test file info
|
|
402
|
+
test_file_path: str | None = None # Associated test file
|
|
403
|
+
|
|
404
|
+
# Failure details (if any)
|
|
405
|
+
failed_tests: list[dict[str, Any]] = field(default_factory=list)
|
|
406
|
+
|
|
407
|
+
# Staleness tracking
|
|
408
|
+
source_modified_at: str | None = None # When source file was last modified
|
|
409
|
+
tests_modified_at: str | None = None # When test file was last modified
|
|
410
|
+
is_stale: bool = False # Tests haven't been run since source changed
|
|
411
|
+
|
|
412
|
+
# Link to execution
|
|
413
|
+
execution_id: str | None = None # Link to TestExecutionRecord
|
|
414
|
+
workflow_id: str | None = None
|
|
415
|
+
|
|
416
|
+
# Metadata
|
|
417
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
418
|
+
|
|
419
|
+
def to_dict(self) -> dict[str, Any]:
|
|
420
|
+
"""Convert to dictionary for JSON serialization."""
|
|
421
|
+
return asdict(self)
|
|
422
|
+
|
|
423
|
+
@classmethod
|
|
424
|
+
def from_dict(cls, data: dict[str, Any]) -> "FileTestRecord":
|
|
425
|
+
"""Create from dictionary."""
|
|
426
|
+
return cls(**data)
|
|
427
|
+
|
|
428
|
+
@property
|
|
429
|
+
def success(self) -> bool:
|
|
430
|
+
"""Check if all tests passed."""
|
|
431
|
+
return self.last_test_result == "passed" and self.failed == 0 and self.errors == 0
|