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,219 @@
|
|
|
1
|
+
"""CLI commands for progressive workflow management.
|
|
2
|
+
|
|
3
|
+
Provides commands for:
|
|
4
|
+
- Listing saved results
|
|
5
|
+
- Viewing detailed reports
|
|
6
|
+
- Generating analytics
|
|
7
|
+
- Cleaning up old results
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
from attune.workflows.progressive.reports import (
|
|
14
|
+
cleanup_old_results,
|
|
15
|
+
format_cost_analytics_report,
|
|
16
|
+
generate_cost_analytics,
|
|
17
|
+
list_saved_results,
|
|
18
|
+
load_result_from_disk,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def cmd_list_results(args: argparse.Namespace) -> int:
|
|
23
|
+
"""List all saved progressive workflow results.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
args: Parsed command line arguments
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Exit code (0 for success)
|
|
30
|
+
"""
|
|
31
|
+
storage_path = args.storage_path or ".attune/progressive_runs"
|
|
32
|
+
results = list_saved_results(storage_path)
|
|
33
|
+
|
|
34
|
+
if not results:
|
|
35
|
+
print(f"No results found in {storage_path}")
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
print(f"\n📋 Found {len(results)} progressive workflow results:\n")
|
|
39
|
+
print(f"{'Task ID':<40} {'Workflow':<15} {'Cost':<10} {'Savings':<12} {'Success'}")
|
|
40
|
+
print("─" * 90)
|
|
41
|
+
|
|
42
|
+
for result in results:
|
|
43
|
+
task_id = result.get("task_id", "unknown")
|
|
44
|
+
workflow = result.get("workflow", "unknown")[:14]
|
|
45
|
+
cost = result.get("total_cost", 0.0)
|
|
46
|
+
savings = result.get("cost_savings_percent", 0.0)
|
|
47
|
+
success = "✅" if result.get("success", False) else "❌"
|
|
48
|
+
|
|
49
|
+
print(f"{task_id:<40} {workflow:<15} ${cost:<9.2f} {savings:>6.1f}% {success}")
|
|
50
|
+
|
|
51
|
+
print()
|
|
52
|
+
return 0
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def cmd_show_report(args: argparse.Namespace) -> int:
|
|
56
|
+
"""Show detailed report for a specific task.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
args: Parsed command line arguments
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Exit code (0 for success, 1 for error)
|
|
63
|
+
"""
|
|
64
|
+
task_id = args.task_id
|
|
65
|
+
storage_path = args.storage_path or ".attune/progressive_runs"
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
result_data = load_result_from_disk(task_id, storage_path)
|
|
69
|
+
|
|
70
|
+
if args.json:
|
|
71
|
+
import json
|
|
72
|
+
|
|
73
|
+
print(json.dumps(result_data, indent=2))
|
|
74
|
+
else:
|
|
75
|
+
# Show human-readable report
|
|
76
|
+
report = result_data.get("report", "")
|
|
77
|
+
if report:
|
|
78
|
+
print(report)
|
|
79
|
+
else:
|
|
80
|
+
print("No report found for this task")
|
|
81
|
+
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
except FileNotFoundError as e:
|
|
85
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
86
|
+
return 1
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def cmd_analytics(args: argparse.Namespace) -> int:
|
|
90
|
+
"""Show cost optimization analytics.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
args: Parsed command line arguments
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Exit code (0 for success)
|
|
97
|
+
"""
|
|
98
|
+
storage_path = args.storage_path or ".attune/progressive_runs"
|
|
99
|
+
analytics = generate_cost_analytics(storage_path)
|
|
100
|
+
|
|
101
|
+
if analytics["total_runs"] == 0:
|
|
102
|
+
print(f"No results found in {storage_path}")
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
if args.json:
|
|
106
|
+
import json
|
|
107
|
+
|
|
108
|
+
print(json.dumps(analytics, indent=2))
|
|
109
|
+
else:
|
|
110
|
+
report = format_cost_analytics_report(analytics)
|
|
111
|
+
print(report)
|
|
112
|
+
|
|
113
|
+
return 0
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def cmd_cleanup(args: argparse.Namespace) -> int:
|
|
117
|
+
"""Clean up old progressive workflow results.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
args: Parsed command line arguments
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Exit code (0 for success)
|
|
124
|
+
"""
|
|
125
|
+
storage_path = args.storage_path or ".attune/progressive_runs"
|
|
126
|
+
retention_days = args.retention_days
|
|
127
|
+
dry_run = args.dry_run
|
|
128
|
+
|
|
129
|
+
deleted, retained = cleanup_old_results(
|
|
130
|
+
storage_path=storage_path, retention_days=retention_days, dry_run=dry_run
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if dry_run:
|
|
134
|
+
print("\n🔍 Dry run mode - no files deleted\n")
|
|
135
|
+
print(f"Would delete: {deleted} results older than {retention_days} days")
|
|
136
|
+
print(f"Would retain: {retained} recent results")
|
|
137
|
+
else:
|
|
138
|
+
print("\n🗑️ Cleanup complete\n")
|
|
139
|
+
print(f"Deleted: {deleted} results older than {retention_days} days")
|
|
140
|
+
print(f"Retained: {retained} recent results")
|
|
141
|
+
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
146
|
+
"""Create argument parser for progressive CLI.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Configured argument parser
|
|
150
|
+
"""
|
|
151
|
+
parser = argparse.ArgumentParser(
|
|
152
|
+
prog="empathy progressive", description="Manage progressive tier escalation workflows"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
parser.add_argument(
|
|
156
|
+
"--storage-path",
|
|
157
|
+
type=str,
|
|
158
|
+
default=None,
|
|
159
|
+
help="Custom storage path (default: .attune/progressive_runs)",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
163
|
+
|
|
164
|
+
# List command
|
|
165
|
+
list_parser = subparsers.add_parser("list", help="List all saved progressive workflow results")
|
|
166
|
+
list_parser.set_defaults(func=cmd_list_results)
|
|
167
|
+
|
|
168
|
+
# Show command
|
|
169
|
+
show_parser = subparsers.add_parser("show", help="Show detailed report for a specific task")
|
|
170
|
+
show_parser.add_argument("task_id", type=str, help="Task ID to display")
|
|
171
|
+
show_parser.add_argument("--json", action="store_true", help="Output in JSON format")
|
|
172
|
+
show_parser.set_defaults(func=cmd_show_report)
|
|
173
|
+
|
|
174
|
+
# Analytics command
|
|
175
|
+
analytics_parser = subparsers.add_parser("analytics", help="Show cost optimization analytics")
|
|
176
|
+
analytics_parser.add_argument("--json", action="store_true", help="Output in JSON format")
|
|
177
|
+
analytics_parser.set_defaults(func=cmd_analytics)
|
|
178
|
+
|
|
179
|
+
# Cleanup command
|
|
180
|
+
cleanup_parser = subparsers.add_parser(
|
|
181
|
+
"cleanup", help="Clean up old progressive workflow results"
|
|
182
|
+
)
|
|
183
|
+
cleanup_parser.add_argument(
|
|
184
|
+
"--retention-days",
|
|
185
|
+
type=int,
|
|
186
|
+
default=30,
|
|
187
|
+
help="Number of days to retain results (default: 30)",
|
|
188
|
+
)
|
|
189
|
+
cleanup_parser.add_argument(
|
|
190
|
+
"--dry-run",
|
|
191
|
+
action="store_true",
|
|
192
|
+
help="Show what would be deleted without actually deleting",
|
|
193
|
+
)
|
|
194
|
+
cleanup_parser.set_defaults(func=cmd_cleanup)
|
|
195
|
+
|
|
196
|
+
return parser
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def main(argv: list[str] | None = None) -> int:
|
|
200
|
+
"""Main entry point for progressive CLI.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
argv: Command line arguments (defaults to sys.argv[1:])
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Exit code
|
|
207
|
+
"""
|
|
208
|
+
parser = create_parser()
|
|
209
|
+
args = parser.parse_args(argv)
|
|
210
|
+
|
|
211
|
+
if not hasattr(args, "func"):
|
|
212
|
+
parser.print_help()
|
|
213
|
+
return 1
|
|
214
|
+
|
|
215
|
+
return args.func(args)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
if __name__ == "__main__":
|
|
219
|
+
sys.exit(main())
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
"""Core data structures for progressive tier escalation.
|
|
2
|
+
|
|
3
|
+
This module defines the fundamental data structures used throughout the
|
|
4
|
+
progressive escalation system, including failure analysis, quality metrics,
|
|
5
|
+
tier results, and configuration.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Tier(Enum):
|
|
15
|
+
"""Model tier levels for progressive escalation.
|
|
16
|
+
|
|
17
|
+
Attributes:
|
|
18
|
+
CHEAP: Low-cost models (e.g., gpt-4o-mini, claude-3-haiku)
|
|
19
|
+
CAPABLE: Mid-tier models (e.g., claude-3-5-sonnet, gpt-4o)
|
|
20
|
+
PREMIUM: High-end models (e.g., claude-opus-4, o1)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
CHEAP = "cheap"
|
|
24
|
+
CAPABLE = "capable"
|
|
25
|
+
PREMIUM = "premium"
|
|
26
|
+
|
|
27
|
+
def __lt__(self, other: "Tier") -> bool:
|
|
28
|
+
"""Compare tiers for ordering (CHEAP < CAPABLE < PREMIUM)."""
|
|
29
|
+
order = {Tier.CHEAP: 0, Tier.CAPABLE: 1, Tier.PREMIUM: 2}
|
|
30
|
+
return order[self] < order[other]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class FailureAnalysis:
|
|
35
|
+
"""Multi-signal failure detection and quality analysis.
|
|
36
|
+
|
|
37
|
+
Combines multiple signals to provide robust failure detection:
|
|
38
|
+
1. Syntax errors in generated code
|
|
39
|
+
2. Execution failures (test pass rate)
|
|
40
|
+
3. Quality metrics (coverage, assertion depth)
|
|
41
|
+
4. LLM confidence signals
|
|
42
|
+
|
|
43
|
+
The composite quality score (CQS) provides an objective measure
|
|
44
|
+
that combines all signals with appropriate weighting.
|
|
45
|
+
|
|
46
|
+
Attributes:
|
|
47
|
+
syntax_errors: List of syntax errors found in generated code
|
|
48
|
+
test_failures: List of test execution failures
|
|
49
|
+
test_pass_rate: Percentage of tests that passed (0.0-1.0)
|
|
50
|
+
coverage_percent: Code coverage percentage (0.0-100.0)
|
|
51
|
+
assertion_depth: Average number of assertions per test
|
|
52
|
+
confidence_score: LLM confidence level (0.0-1.0)
|
|
53
|
+
llm_uncertainty_signals: Uncertainty phrases detected in LLM response
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> analysis = FailureAnalysis(
|
|
57
|
+
... test_pass_rate=0.85,
|
|
58
|
+
... coverage_percent=78.0,
|
|
59
|
+
... assertion_depth=5.2,
|
|
60
|
+
... confidence_score=0.92
|
|
61
|
+
... )
|
|
62
|
+
>>> analysis.calculate_quality_score()
|
|
63
|
+
87.7
|
|
64
|
+
>>> analysis.should_escalate
|
|
65
|
+
False
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
syntax_errors: list[SyntaxError] = field(default_factory=list)
|
|
69
|
+
test_failures: list[dict[str, Any]] = field(default_factory=list)
|
|
70
|
+
test_pass_rate: float = 0.0
|
|
71
|
+
coverage_percent: float = 0.0
|
|
72
|
+
assertion_depth: float = 0.0
|
|
73
|
+
confidence_score: float = 0.0
|
|
74
|
+
llm_uncertainty_signals: list[str] = field(default_factory=list)
|
|
75
|
+
|
|
76
|
+
def calculate_quality_score(self) -> float:
|
|
77
|
+
"""Calculate composite quality score (CQS) from 0-100.
|
|
78
|
+
|
|
79
|
+
Formula:
|
|
80
|
+
CQS = (
|
|
81
|
+
0.40 × test_pass_rate +
|
|
82
|
+
0.25 × code_coverage +
|
|
83
|
+
0.20 × assertion_quality +
|
|
84
|
+
0.15 × llm_confidence
|
|
85
|
+
) × syntax_error_penalty
|
|
86
|
+
|
|
87
|
+
Weights:
|
|
88
|
+
- Test pass rate: 40% (most important - functionality must work)
|
|
89
|
+
- Code coverage: 25% (thoroughness matters)
|
|
90
|
+
- Assertion quality: 20% (test depth is important)
|
|
91
|
+
- LLM confidence: 15% (signals potential brittleness)
|
|
92
|
+
|
|
93
|
+
Penalties:
|
|
94
|
+
- Syntax errors: 50% penalty (halves the score)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
Quality score from 0.0 (worst) to 100.0 (perfect)
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
>>> analysis = FailureAnalysis(
|
|
101
|
+
... test_pass_rate=0.90,
|
|
102
|
+
... coverage_percent=85.0,
|
|
103
|
+
... assertion_depth=6.0,
|
|
104
|
+
... confidence_score=0.95
|
|
105
|
+
... )
|
|
106
|
+
>>> analysis.calculate_quality_score()
|
|
107
|
+
91.25
|
|
108
|
+
"""
|
|
109
|
+
# Component scores (convert to 0-100 scale)
|
|
110
|
+
pass_rate_score = self.test_pass_rate * 100
|
|
111
|
+
coverage_score = self.coverage_percent
|
|
112
|
+
|
|
113
|
+
# Assertion quality: cap at 100% (10 assertions = 100%)
|
|
114
|
+
assertion_quality_score = min(self.assertion_depth * 10, 100)
|
|
115
|
+
|
|
116
|
+
confidence_score_scaled = self.confidence_score * 100
|
|
117
|
+
|
|
118
|
+
# Weighted composite
|
|
119
|
+
cqs = (
|
|
120
|
+
0.40 * pass_rate_score
|
|
121
|
+
+ 0.25 * coverage_score
|
|
122
|
+
+ 0.20 * assertion_quality_score
|
|
123
|
+
+ 0.15 * confidence_score_scaled
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Apply syntax error penalty
|
|
127
|
+
if len(self.syntax_errors) > 0:
|
|
128
|
+
cqs *= 0.5 # Halve score for any syntax errors
|
|
129
|
+
|
|
130
|
+
return min(cqs, 100.0)
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def should_escalate(self) -> bool:
|
|
134
|
+
"""Determine if this result should trigger escalation.
|
|
135
|
+
|
|
136
|
+
Multi-criteria decision based on:
|
|
137
|
+
- Low CQS (<70)
|
|
138
|
+
- Multiple syntax errors (>3)
|
|
139
|
+
- Low test pass rate (<70%)
|
|
140
|
+
- Low coverage (<60%)
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
True if escalation is recommended, False otherwise
|
|
144
|
+
|
|
145
|
+
Example:
|
|
146
|
+
>>> analysis = FailureAnalysis(test_pass_rate=0.50)
|
|
147
|
+
>>> analysis.should_escalate
|
|
148
|
+
True
|
|
149
|
+
"""
|
|
150
|
+
cqs = self.calculate_quality_score()
|
|
151
|
+
return (
|
|
152
|
+
cqs < 70
|
|
153
|
+
or len(self.syntax_errors) > 3
|
|
154
|
+
or self.test_pass_rate < 0.7
|
|
155
|
+
or self.coverage_percent < 60
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def failure_severity(self) -> str:
|
|
160
|
+
"""Determine severity level of failures.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
"CRITICAL": Severe failures, consider skipping to Premium
|
|
164
|
+
"HIGH": Significant failures, escalate to next tier
|
|
165
|
+
"MODERATE": Minor failures, retry at current tier
|
|
166
|
+
"LOW": Acceptable quality, no escalation needed
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> analysis = FailureAnalysis(test_pass_rate=0.25)
|
|
170
|
+
>>> analysis.failure_severity
|
|
171
|
+
'CRITICAL'
|
|
172
|
+
"""
|
|
173
|
+
cqs = self.calculate_quality_score()
|
|
174
|
+
|
|
175
|
+
if len(self.syntax_errors) > 5 or self.test_pass_rate < 0.3:
|
|
176
|
+
return "CRITICAL"
|
|
177
|
+
elif cqs < 70 or self.test_pass_rate < 0.5:
|
|
178
|
+
return "HIGH"
|
|
179
|
+
elif cqs < 80 or self.test_pass_rate < 0.7:
|
|
180
|
+
return "MODERATE"
|
|
181
|
+
else:
|
|
182
|
+
return "LOW"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass
|
|
186
|
+
class TierResult:
|
|
187
|
+
"""Results from a single tier execution attempt.
|
|
188
|
+
|
|
189
|
+
Captures all information about a tier's execution including
|
|
190
|
+
generated artifacts, quality analysis, cost, and escalation decision.
|
|
191
|
+
|
|
192
|
+
Attributes:
|
|
193
|
+
tier: Which tier executed (CHEAP, CAPABLE, or PREMIUM)
|
|
194
|
+
model: Specific model used (e.g., "gpt-4o-mini")
|
|
195
|
+
attempt: Attempt number at this tier (1-based)
|
|
196
|
+
timestamp: When this execution occurred
|
|
197
|
+
generated_items: Generated artifacts (tests, code, etc.)
|
|
198
|
+
failure_analysis: Quality and failure analysis
|
|
199
|
+
cost: Cost in USD for this execution
|
|
200
|
+
duration: Execution time in seconds
|
|
201
|
+
escalated: Whether this result triggered escalation
|
|
202
|
+
escalation_reason: Human-readable reason for escalation
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> result = TierResult(
|
|
206
|
+
... tier=Tier.CHEAP,
|
|
207
|
+
... model="gpt-4o-mini",
|
|
208
|
+
... attempt=1,
|
|
209
|
+
... timestamp=datetime.now(),
|
|
210
|
+
... generated_items=[{"code": "test_foo()"}],
|
|
211
|
+
... failure_analysis=FailureAnalysis(test_pass_rate=0.65),
|
|
212
|
+
... cost=0.15,
|
|
213
|
+
... duration=12.5
|
|
214
|
+
... )
|
|
215
|
+
>>> result.quality_score
|
|
216
|
+
65.0
|
|
217
|
+
"""
|
|
218
|
+
|
|
219
|
+
tier: Tier
|
|
220
|
+
model: str
|
|
221
|
+
attempt: int
|
|
222
|
+
timestamp: datetime
|
|
223
|
+
|
|
224
|
+
# Generated artifacts
|
|
225
|
+
generated_items: list[dict[str, Any]] = field(default_factory=list)
|
|
226
|
+
|
|
227
|
+
# Analysis
|
|
228
|
+
failure_analysis: FailureAnalysis = field(default_factory=FailureAnalysis)
|
|
229
|
+
cost: float = 0.0
|
|
230
|
+
duration: float = 0.0
|
|
231
|
+
tokens_used: dict[str, int] = field(default_factory=dict)
|
|
232
|
+
|
|
233
|
+
# Decision
|
|
234
|
+
escalated: bool = False
|
|
235
|
+
escalation_reason: str = ""
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def quality_score(self) -> float:
|
|
239
|
+
"""Get composite quality score for this tier result.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
CQS from 0.0 to 100.0
|
|
243
|
+
"""
|
|
244
|
+
return self.failure_analysis.calculate_quality_score()
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def success_count(self) -> int:
|
|
248
|
+
"""Count of successfully generated items (CQS >= 80).
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Number of items meeting quality threshold
|
|
252
|
+
"""
|
|
253
|
+
return sum(1 for item in self.generated_items if item.get("quality_score", 0) >= 80)
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def success_rate(self) -> float:
|
|
257
|
+
"""Percentage of items successfully generated.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Success rate from 0.0 to 1.0
|
|
261
|
+
"""
|
|
262
|
+
if not self.generated_items:
|
|
263
|
+
return 0.0
|
|
264
|
+
return self.success_count / len(self.generated_items)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@dataclass
|
|
268
|
+
class ProgressiveWorkflowResult:
|
|
269
|
+
"""Complete results from a progressive workflow execution.
|
|
270
|
+
|
|
271
|
+
Captures the full progression history across all tiers, including
|
|
272
|
+
costs, quality metrics, and escalation decisions.
|
|
273
|
+
|
|
274
|
+
Attributes:
|
|
275
|
+
workflow_name: Name of the workflow (e.g., "test-gen")
|
|
276
|
+
task_id: Unique identifier for this execution
|
|
277
|
+
tier_results: Chronological list of tier execution results
|
|
278
|
+
final_result: The last tier result (may be successful or failed)
|
|
279
|
+
total_cost: Total cost in USD across all tiers
|
|
280
|
+
total_duration: Total execution time in seconds
|
|
281
|
+
success: Whether the workflow completed successfully
|
|
282
|
+
|
|
283
|
+
Example:
|
|
284
|
+
>>> result = ProgressiveWorkflowResult(
|
|
285
|
+
... workflow_name="test-gen",
|
|
286
|
+
... task_id="test-gen-20260117-143022",
|
|
287
|
+
... tier_results=[cheap_result, capable_result],
|
|
288
|
+
... final_result=capable_result,
|
|
289
|
+
... total_cost=0.75,
|
|
290
|
+
... total_duration=45.2,
|
|
291
|
+
... success=True
|
|
292
|
+
... )
|
|
293
|
+
>>> print(result.generate_report())
|
|
294
|
+
🎯 PROGRESSIVE ESCALATION REPORT
|
|
295
|
+
...
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
workflow_name: str
|
|
299
|
+
task_id: str
|
|
300
|
+
tier_results: list[TierResult]
|
|
301
|
+
|
|
302
|
+
final_result: TierResult
|
|
303
|
+
total_cost: float
|
|
304
|
+
total_duration: float
|
|
305
|
+
success: bool
|
|
306
|
+
|
|
307
|
+
def generate_report(self) -> str:
|
|
308
|
+
"""Generate human-readable progression report.
|
|
309
|
+
|
|
310
|
+
Creates a detailed report showing:
|
|
311
|
+
- Tier-by-tier breakdown
|
|
312
|
+
- Quality scores and success rates
|
|
313
|
+
- Cost analysis and savings
|
|
314
|
+
- Escalation decisions
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Formatted report string
|
|
318
|
+
"""
|
|
319
|
+
# Implementation will be in reports.py module
|
|
320
|
+
from attune.workflows.progressive.reports import generate_progression_report
|
|
321
|
+
|
|
322
|
+
return generate_progression_report(self)
|
|
323
|
+
|
|
324
|
+
def save_to_disk(self, storage_path: str) -> None:
|
|
325
|
+
"""Save detailed results to disk.
|
|
326
|
+
|
|
327
|
+
Creates a directory with:
|
|
328
|
+
- summary.json: High-level metrics
|
|
329
|
+
- tier_N_<tier_name>.json: Detailed tier results
|
|
330
|
+
- report.txt: Human-readable report
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
storage_path: Base path for saving results
|
|
334
|
+
"""
|
|
335
|
+
from attune.workflows.progressive.reports import save_results_to_disk
|
|
336
|
+
|
|
337
|
+
save_results_to_disk(self, storage_path)
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def cost_savings(self) -> float:
|
|
341
|
+
"""Calculate cost savings vs running all items at Premium tier.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Dollar amount saved by using progressive escalation
|
|
345
|
+
"""
|
|
346
|
+
# Estimate what it would cost if all items were Premium
|
|
347
|
+
total_items = sum(len(r.generated_items) for r in self.tier_results)
|
|
348
|
+
|
|
349
|
+
# Assume Premium costs ~$0.05 per item (conservative estimate)
|
|
350
|
+
all_premium_cost = total_items * 0.05
|
|
351
|
+
|
|
352
|
+
savings = all_premium_cost - self.total_cost
|
|
353
|
+
return max(savings, 0.0)
|
|
354
|
+
|
|
355
|
+
@property
|
|
356
|
+
def cost_savings_percent(self) -> float:
|
|
357
|
+
"""Calculate percentage of cost saved.
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
Savings percentage (0-100)
|
|
361
|
+
"""
|
|
362
|
+
total_items = sum(len(r.generated_items) for r in self.tier_results)
|
|
363
|
+
all_premium_cost = total_items * 0.05
|
|
364
|
+
|
|
365
|
+
if all_premium_cost == 0:
|
|
366
|
+
return 0.0
|
|
367
|
+
|
|
368
|
+
return (self.cost_savings / all_premium_cost) * 100
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@dataclass
|
|
372
|
+
class EscalationConfig:
|
|
373
|
+
"""Configuration for progressive tier escalation.
|
|
374
|
+
|
|
375
|
+
Controls all aspects of the escalation system including retry logic,
|
|
376
|
+
thresholds, cost management, and storage.
|
|
377
|
+
|
|
378
|
+
Attributes:
|
|
379
|
+
enabled: Whether progressive escalation is active
|
|
380
|
+
tiers: Ordered list of tiers to use (default: all three)
|
|
381
|
+
|
|
382
|
+
Retry configuration:
|
|
383
|
+
cheap_min_attempts: Minimum attempts at cheap tier
|
|
384
|
+
cheap_max_attempts: Maximum attempts at cheap tier
|
|
385
|
+
capable_min_attempts: Minimum attempts at capable tier
|
|
386
|
+
capable_max_attempts: Maximum attempts at capable tier
|
|
387
|
+
premium_max_attempts: Maximum attempts at premium tier
|
|
388
|
+
|
|
389
|
+
Thresholds (Cheap → Capable):
|
|
390
|
+
cheap_to_capable_failure_rate: Max failure rate before escalation
|
|
391
|
+
cheap_to_capable_min_cqs: Min quality score to avoid escalation
|
|
392
|
+
cheap_to_capable_max_syntax_errors: Max syntax errors allowed
|
|
393
|
+
|
|
394
|
+
Thresholds (Capable → Premium):
|
|
395
|
+
capable_to_premium_failure_rate: Max failure rate before escalation
|
|
396
|
+
capable_to_premium_min_cqs: Min quality score to avoid escalation
|
|
397
|
+
capable_to_premium_max_syntax_errors: Max syntax errors allowed
|
|
398
|
+
|
|
399
|
+
Stagnation detection:
|
|
400
|
+
improvement_threshold: Min CQS improvement to avoid stagnation (%)
|
|
401
|
+
consecutive_stagnation_limit: Consecutive stagnations before escalation
|
|
402
|
+
|
|
403
|
+
Cost management:
|
|
404
|
+
max_cost: Maximum total cost in USD
|
|
405
|
+
auto_approve_under: Auto-approve escalations under this cost
|
|
406
|
+
warn_on_budget_exceeded: Print warning if budget exceeded
|
|
407
|
+
abort_on_budget_exceeded: Abort execution if budget exceeded
|
|
408
|
+
|
|
409
|
+
Storage:
|
|
410
|
+
save_tier_results: Whether to save tier results to disk
|
|
411
|
+
storage_path: Directory for saving results
|
|
412
|
+
|
|
413
|
+
Example:
|
|
414
|
+
>>> config = EscalationConfig(
|
|
415
|
+
... enabled=True,
|
|
416
|
+
... max_cost=10.00,
|
|
417
|
+
... auto_approve_under=5.00,
|
|
418
|
+
... cheap_min_attempts=2,
|
|
419
|
+
... capable_max_attempts=6
|
|
420
|
+
... )
|
|
421
|
+
"""
|
|
422
|
+
|
|
423
|
+
# Global settings
|
|
424
|
+
enabled: bool = False
|
|
425
|
+
tiers: list[Tier] = field(default_factory=lambda: [Tier.CHEAP, Tier.CAPABLE, Tier.PREMIUM])
|
|
426
|
+
|
|
427
|
+
# Retry configuration
|
|
428
|
+
cheap_min_attempts: int = 2
|
|
429
|
+
cheap_max_attempts: int = 3
|
|
430
|
+
capable_min_attempts: int = 2
|
|
431
|
+
capable_max_attempts: int = 6
|
|
432
|
+
premium_max_attempts: int = 1
|
|
433
|
+
|
|
434
|
+
# Thresholds: Cheap → Capable
|
|
435
|
+
cheap_to_capable_failure_rate: float = 0.30
|
|
436
|
+
cheap_to_capable_min_cqs: float = 70.0
|
|
437
|
+
cheap_to_capable_max_syntax_errors: int = 3
|
|
438
|
+
|
|
439
|
+
# Thresholds: Capable → Premium
|
|
440
|
+
capable_to_premium_failure_rate: float = 0.20
|
|
441
|
+
capable_to_premium_min_cqs: float = 80.0
|
|
442
|
+
capable_to_premium_max_syntax_errors: int = 1
|
|
443
|
+
|
|
444
|
+
# Stagnation detection
|
|
445
|
+
improvement_threshold: float = 5.0 # 5% CQS improvement required
|
|
446
|
+
consecutive_stagnation_limit: int = 2
|
|
447
|
+
|
|
448
|
+
# Cost management
|
|
449
|
+
max_cost: float = 5.00
|
|
450
|
+
auto_approve_under: float | None = None
|
|
451
|
+
warn_on_budget_exceeded: bool = True
|
|
452
|
+
abort_on_budget_exceeded: bool = False
|
|
453
|
+
|
|
454
|
+
# Storage
|
|
455
|
+
save_tier_results: bool = True
|
|
456
|
+
storage_path: str = ".attune/progressive_runs"
|
|
457
|
+
|
|
458
|
+
def get_max_attempts(self, tier: Tier) -> int:
|
|
459
|
+
"""Get maximum attempts for a specific tier.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
tier: The tier to query
|
|
463
|
+
|
|
464
|
+
Returns:
|
|
465
|
+
Maximum number of attempts allowed
|
|
466
|
+
"""
|
|
467
|
+
if tier == Tier.CHEAP:
|
|
468
|
+
return self.cheap_max_attempts
|
|
469
|
+
elif tier == Tier.CAPABLE:
|
|
470
|
+
return self.capable_max_attempts
|
|
471
|
+
else: # PREMIUM
|
|
472
|
+
return self.premium_max_attempts
|
|
473
|
+
|
|
474
|
+
def get_min_attempts(self, tier: Tier) -> int:
|
|
475
|
+
"""Get minimum attempts for a specific tier.
|
|
476
|
+
|
|
477
|
+
Args:
|
|
478
|
+
tier: The tier to query
|
|
479
|
+
|
|
480
|
+
Returns:
|
|
481
|
+
Minimum number of attempts required
|
|
482
|
+
"""
|
|
483
|
+
if tier == Tier.CHEAP:
|
|
484
|
+
return self.cheap_min_attempts
|
|
485
|
+
elif tier == Tier.CAPABLE:
|
|
486
|
+
return self.capable_min_attempts
|
|
487
|
+
else: # PREMIUM
|
|
488
|
+
return 1 # Premium always gets exactly 1 attempt
|