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,645 @@
|
|
|
1
|
+
"""Workflow Configuration
|
|
2
|
+
|
|
3
|
+
Provides flexible configuration for workflow model selection:
|
|
4
|
+
- YAML/JSON config file support
|
|
5
|
+
- Environment variable overrides
|
|
6
|
+
- Per-workflow provider and model customization
|
|
7
|
+
- Easy extension for new models/providers
|
|
8
|
+
|
|
9
|
+
Configuration priority (highest to lowest):
|
|
10
|
+
1. Constructor arguments
|
|
11
|
+
2. Environment variables (EMPATHY_WORKFLOW_PROVIDER, etc.)
|
|
12
|
+
3. Config file (.attune/workflows.yaml)
|
|
13
|
+
4. Built-in defaults
|
|
14
|
+
|
|
15
|
+
Model configurations are sourced from the unified registry at
|
|
16
|
+
attune.models.MODEL_REGISTRY.
|
|
17
|
+
|
|
18
|
+
Copyright 2025 Smart-AI-Memory
|
|
19
|
+
Licensed under Fair Source License 0.9
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
# Import from unified registry
|
|
29
|
+
from attune.models import MODEL_REGISTRY, ModelInfo
|
|
30
|
+
from attune.models.registry import ModelProvider, ModelTier
|
|
31
|
+
|
|
32
|
+
# Try to import yaml, fall back gracefully
|
|
33
|
+
try:
|
|
34
|
+
import yaml
|
|
35
|
+
|
|
36
|
+
YAML_AVAILABLE = True
|
|
37
|
+
except ImportError:
|
|
38
|
+
YAML_AVAILABLE = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _validate_file_path(path: str, allowed_dir: str | None = None) -> Path:
|
|
42
|
+
"""Validate file path to prevent path traversal and arbitrary writes.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
path: File path to validate
|
|
46
|
+
allowed_dir: Optional directory to restrict writes to
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Validated Path object
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
ValueError: If path is invalid or unsafe
|
|
53
|
+
"""
|
|
54
|
+
if not path or not isinstance(path, str):
|
|
55
|
+
raise ValueError("path must be a non-empty string")
|
|
56
|
+
|
|
57
|
+
# Check for null bytes
|
|
58
|
+
if "\x00" in path:
|
|
59
|
+
raise ValueError("path contains null bytes")
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
resolved = Path(path).resolve()
|
|
63
|
+
except (OSError, RuntimeError) as e:
|
|
64
|
+
raise ValueError(f"Invalid path: {e}")
|
|
65
|
+
|
|
66
|
+
# Check if within allowed directory
|
|
67
|
+
if allowed_dir:
|
|
68
|
+
try:
|
|
69
|
+
allowed = Path(allowed_dir).resolve()
|
|
70
|
+
resolved.relative_to(allowed)
|
|
71
|
+
except ValueError:
|
|
72
|
+
raise ValueError(f"path must be within {allowed_dir}")
|
|
73
|
+
|
|
74
|
+
# Check for dangerous system paths
|
|
75
|
+
dangerous_paths = ["/etc", "/sys", "/proc", "/dev"]
|
|
76
|
+
for dangerous in dangerous_paths:
|
|
77
|
+
if str(resolved).startswith(dangerous):
|
|
78
|
+
raise ValueError(f"Cannot write to system directory: {dangerous}")
|
|
79
|
+
|
|
80
|
+
return resolved
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# Re-export for backward compatibility
|
|
84
|
+
__all__ = [
|
|
85
|
+
"DEFAULT_MODELS",
|
|
86
|
+
"ModelConfig",
|
|
87
|
+
"ModelProvider",
|
|
88
|
+
"ModelTier",
|
|
89
|
+
"WorkflowConfig",
|
|
90
|
+
"get_model",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@dataclass
|
|
95
|
+
class ModelConfig:
|
|
96
|
+
"""Configuration for a specific model.
|
|
97
|
+
|
|
98
|
+
Note: This class is kept for backward compatibility. New code should
|
|
99
|
+
use attune.models.ModelInfo from the unified registry.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
name: str
|
|
103
|
+
provider: str
|
|
104
|
+
tier: str
|
|
105
|
+
input_cost_per_million: float = 0.0
|
|
106
|
+
output_cost_per_million: float = 0.0
|
|
107
|
+
max_tokens: int = 4096
|
|
108
|
+
supports_vision: bool = False
|
|
109
|
+
supports_tools: bool = True
|
|
110
|
+
|
|
111
|
+
@classmethod
|
|
112
|
+
def from_model_info(cls, info: ModelInfo) -> "ModelConfig":
|
|
113
|
+
"""Create ModelConfig from unified ModelInfo."""
|
|
114
|
+
return cls(
|
|
115
|
+
name=info.id,
|
|
116
|
+
provider=info.provider,
|
|
117
|
+
tier=info.tier,
|
|
118
|
+
input_cost_per_million=info.input_cost_per_million,
|
|
119
|
+
output_cost_per_million=info.output_cost_per_million,
|
|
120
|
+
max_tokens=info.max_tokens,
|
|
121
|
+
supports_vision=info.supports_vision,
|
|
122
|
+
supports_tools=info.supports_tools,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@dataclass
|
|
127
|
+
class WorkflowConfig:
|
|
128
|
+
"""Configuration for workflow model selection and XML prompts."""
|
|
129
|
+
|
|
130
|
+
# Default provider for all workflows
|
|
131
|
+
default_provider: str = "anthropic"
|
|
132
|
+
|
|
133
|
+
# Per-workflow provider overrides
|
|
134
|
+
workflow_providers: dict[str, str] = field(default_factory=dict)
|
|
135
|
+
|
|
136
|
+
# Custom model mappings (provider -> tier -> model)
|
|
137
|
+
custom_models: dict[str, dict[str, str]] = field(default_factory=dict)
|
|
138
|
+
|
|
139
|
+
# Model pricing overrides
|
|
140
|
+
pricing_overrides: dict[str, dict[str, float]] = field(default_factory=dict)
|
|
141
|
+
|
|
142
|
+
# XML prompt configuration - global defaults
|
|
143
|
+
xml_prompt_defaults: dict[str, Any] = field(default_factory=dict)
|
|
144
|
+
|
|
145
|
+
# Per-workflow XML prompt configuration overrides
|
|
146
|
+
workflow_xml_configs: dict[str, dict[str, Any]] = field(default_factory=dict)
|
|
147
|
+
|
|
148
|
+
# ==========================================================================
|
|
149
|
+
# Compliance and Feature Flags
|
|
150
|
+
# ==========================================================================
|
|
151
|
+
|
|
152
|
+
# Compliance mode: "standard" (default) or "hipaa" (healthcare)
|
|
153
|
+
# - standard: PII scrubbing disabled, test-gen disabled
|
|
154
|
+
# - hipaa: PII scrubbing enabled, test-gen enabled, stricter auditing
|
|
155
|
+
compliance_mode: str = "standard"
|
|
156
|
+
|
|
157
|
+
# Explicitly enabled workflows (added to defaults)
|
|
158
|
+
# Use this to opt-in to workflows like "test-gen"
|
|
159
|
+
enabled_workflows: list[str] = field(default_factory=list)
|
|
160
|
+
|
|
161
|
+
# Explicitly disabled workflows (removed from defaults)
|
|
162
|
+
disabled_workflows: list[str] = field(default_factory=list)
|
|
163
|
+
|
|
164
|
+
# PII scrubbing - auto-enabled in hipaa mode, opt-in otherwise
|
|
165
|
+
pii_scrubbing_enabled: bool | None = None # None = use compliance_mode default
|
|
166
|
+
|
|
167
|
+
# Audit logging level - "standard", "enhanced", or "hipaa"
|
|
168
|
+
audit_level: str = "standard"
|
|
169
|
+
|
|
170
|
+
@classmethod
|
|
171
|
+
def load(cls, config_path: str | Path | None = None) -> "WorkflowConfig":
|
|
172
|
+
"""Load workflow configuration from file and environment.
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
config_path: Optional path to config file. If None, searches:
|
|
176
|
+
1. .attune/workflows.yaml
|
|
177
|
+
2. .attune/workflows.json
|
|
178
|
+
3. attune.config.yaml (workflows section)
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
WorkflowConfig instance
|
|
182
|
+
|
|
183
|
+
"""
|
|
184
|
+
config_data: dict[str, Any] = {}
|
|
185
|
+
|
|
186
|
+
# Search for config file
|
|
187
|
+
if config_path is None:
|
|
188
|
+
search_paths = [
|
|
189
|
+
Path(".attune/workflows.yaml"),
|
|
190
|
+
Path(".attune/workflows.yml"),
|
|
191
|
+
Path(".attune/workflows.json"),
|
|
192
|
+
Path("attune.config.yml"), # Main config file
|
|
193
|
+
Path("attune.config.yaml"),
|
|
194
|
+
]
|
|
195
|
+
for path in search_paths:
|
|
196
|
+
if path.exists():
|
|
197
|
+
config_path = path
|
|
198
|
+
break
|
|
199
|
+
|
|
200
|
+
# Load from file if found
|
|
201
|
+
if config_path is not None:
|
|
202
|
+
config_path = Path(config_path)
|
|
203
|
+
if config_path.exists():
|
|
204
|
+
config_data = cls._load_file(config_path)
|
|
205
|
+
|
|
206
|
+
# Apply environment variable overrides
|
|
207
|
+
config_data = cls._apply_env_overrides(config_data)
|
|
208
|
+
|
|
209
|
+
return cls(
|
|
210
|
+
default_provider=config_data.get("default_provider", "anthropic"),
|
|
211
|
+
workflow_providers=config_data.get("workflow_providers", {}),
|
|
212
|
+
custom_models=config_data.get("custom_models", {}),
|
|
213
|
+
pricing_overrides=config_data.get("pricing_overrides", {}),
|
|
214
|
+
xml_prompt_defaults=config_data.get("xml_prompt_defaults", {}),
|
|
215
|
+
workflow_xml_configs=config_data.get("workflow_xml_configs", {}),
|
|
216
|
+
# Compliance and feature flags
|
|
217
|
+
compliance_mode=config_data.get("compliance_mode", "standard"),
|
|
218
|
+
enabled_workflows=config_data.get("enabled_workflows", []),
|
|
219
|
+
disabled_workflows=config_data.get("disabled_workflows", []),
|
|
220
|
+
pii_scrubbing_enabled=config_data.get("pii_scrubbing_enabled"),
|
|
221
|
+
audit_level=config_data.get("audit_level", "standard"),
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
@staticmethod
|
|
225
|
+
def _load_file(path: Path) -> dict[str, Any]:
|
|
226
|
+
"""Load config from YAML or JSON file."""
|
|
227
|
+
content = path.read_text()
|
|
228
|
+
|
|
229
|
+
if path.suffix in (".yaml", ".yml"):
|
|
230
|
+
if not YAML_AVAILABLE:
|
|
231
|
+
raise ImportError("PyYAML required for YAML config. Install: pip install pyyaml")
|
|
232
|
+
data = yaml.safe_load(content)
|
|
233
|
+
else:
|
|
234
|
+
data = json.loads(content)
|
|
235
|
+
|
|
236
|
+
result: dict[str, Any] = {}
|
|
237
|
+
|
|
238
|
+
# Handle root-level provider from attune.config.yml
|
|
239
|
+
if "provider" in data:
|
|
240
|
+
result["default_provider"] = data["provider"]
|
|
241
|
+
|
|
242
|
+
# Handle model_preferences as custom_models
|
|
243
|
+
if "model_preferences" in data:
|
|
244
|
+
provider = data.get("provider", "anthropic")
|
|
245
|
+
result["custom_models"] = {provider: data["model_preferences"]}
|
|
246
|
+
|
|
247
|
+
# Handle nested 'workflows' key from attune.config.yaml
|
|
248
|
+
if "workflows" in data and isinstance(data["workflows"], dict):
|
|
249
|
+
result.update(data["workflows"])
|
|
250
|
+
elif "workflows" not in data and "provider" not in data:
|
|
251
|
+
# Legacy format: entire file is workflow config
|
|
252
|
+
result = dict(data)
|
|
253
|
+
|
|
254
|
+
return result
|
|
255
|
+
|
|
256
|
+
@staticmethod
|
|
257
|
+
def _apply_env_overrides(config: dict[str, Any]) -> dict[str, Any]:
|
|
258
|
+
"""Apply environment variable overrides."""
|
|
259
|
+
# Ensure nested dicts exist (YAML may load them as None)
|
|
260
|
+
if config.get("workflow_providers") is None:
|
|
261
|
+
config["workflow_providers"] = {}
|
|
262
|
+
if config.get("custom_models") is None:
|
|
263
|
+
config["custom_models"] = {}
|
|
264
|
+
if config.get("pricing_overrides") is None:
|
|
265
|
+
config["pricing_overrides"] = {}
|
|
266
|
+
|
|
267
|
+
# EMPATHY_WORKFLOW_PROVIDER - default provider
|
|
268
|
+
env_provider = os.environ.get("EMPATHY_WORKFLOW_PROVIDER")
|
|
269
|
+
if env_provider:
|
|
270
|
+
config["default_provider"] = env_provider.lower()
|
|
271
|
+
|
|
272
|
+
# EMPATHY_WORKFLOW_<NAME>_PROVIDER - per-workflow provider
|
|
273
|
+
for key, value in os.environ.items():
|
|
274
|
+
if key.startswith("EMPATHY_WORKFLOW_") and key.endswith("_PROVIDER"):
|
|
275
|
+
workflow_name = key[17:-9].lower().replace("_", "-")
|
|
276
|
+
config["workflow_providers"][workflow_name] = value.lower()
|
|
277
|
+
|
|
278
|
+
# EMPATHY_MODEL_<TIER> - tier model overrides
|
|
279
|
+
for tier in ["CHEAP", "CAPABLE", "PREMIUM"]:
|
|
280
|
+
env_model = os.environ.get(f"EMPATHY_MODEL_{tier}")
|
|
281
|
+
if env_model:
|
|
282
|
+
if "env" not in config["custom_models"]:
|
|
283
|
+
config["custom_models"]["env"] = {}
|
|
284
|
+
config["custom_models"]["env"][tier.lower()] = env_model
|
|
285
|
+
|
|
286
|
+
return config
|
|
287
|
+
|
|
288
|
+
def get_provider_for_workflow(self, workflow_name: str) -> str:
|
|
289
|
+
"""Get the provider for a specific workflow."""
|
|
290
|
+
return self.workflow_providers.get(workflow_name, self.default_provider)
|
|
291
|
+
|
|
292
|
+
def get_model_for_tier(self, provider: str, tier: str) -> str | None:
|
|
293
|
+
"""Get custom model for a provider/tier, or None for default."""
|
|
294
|
+
# Check for env overrides first
|
|
295
|
+
if "env" in self.custom_models:
|
|
296
|
+
if tier in self.custom_models["env"]:
|
|
297
|
+
return self.custom_models["env"][tier]
|
|
298
|
+
|
|
299
|
+
# Check provider-specific overrides
|
|
300
|
+
if provider in self.custom_models:
|
|
301
|
+
if tier in self.custom_models[provider]:
|
|
302
|
+
return self.custom_models[provider][tier]
|
|
303
|
+
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
def get_pricing(self, model: str) -> dict[str, float] | None:
|
|
307
|
+
"""Get custom pricing for a model, or None for default."""
|
|
308
|
+
return self.pricing_overrides.get(model)
|
|
309
|
+
|
|
310
|
+
def get_xml_config_for_workflow(self, workflow_name: str) -> dict[str, Any]:
|
|
311
|
+
"""Get XML prompt configuration for a specific workflow.
|
|
312
|
+
|
|
313
|
+
Merges global defaults with workflow-specific overrides.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
workflow_name: The workflow name (e.g., "security-audit").
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
Dictionary with XML prompt configuration.
|
|
320
|
+
|
|
321
|
+
"""
|
|
322
|
+
# Start with defaults
|
|
323
|
+
config = dict(self.xml_prompt_defaults)
|
|
324
|
+
|
|
325
|
+
# Apply workflow-specific overrides
|
|
326
|
+
if workflow_name in self.workflow_xml_configs:
|
|
327
|
+
config.update(self.workflow_xml_configs[workflow_name])
|
|
328
|
+
|
|
329
|
+
return config
|
|
330
|
+
|
|
331
|
+
def is_xml_enabled_for_workflow(self, workflow_name: str) -> bool:
|
|
332
|
+
"""Check if XML prompts are enabled for a workflow.
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
workflow_name: The workflow name.
|
|
336
|
+
|
|
337
|
+
Returns:
|
|
338
|
+
True if XML prompts are enabled.
|
|
339
|
+
|
|
340
|
+
"""
|
|
341
|
+
config = self.get_xml_config_for_workflow(workflow_name)
|
|
342
|
+
return bool(config.get("enabled", False))
|
|
343
|
+
|
|
344
|
+
# ==========================================================================
|
|
345
|
+
# Compliance and Feature Flag Methods
|
|
346
|
+
# ==========================================================================
|
|
347
|
+
|
|
348
|
+
def is_hipaa_mode(self) -> bool:
|
|
349
|
+
"""Check if HIPAA compliance mode is enabled."""
|
|
350
|
+
return self.compliance_mode.lower() == "hipaa"
|
|
351
|
+
|
|
352
|
+
def is_pii_scrubbing_enabled(self) -> bool:
|
|
353
|
+
"""Check if PII scrubbing is enabled.
|
|
354
|
+
|
|
355
|
+
Returns True if:
|
|
356
|
+
- Explicitly enabled via pii_scrubbing_enabled=True
|
|
357
|
+
- OR compliance_mode is "hipaa" (and not explicitly disabled)
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
True if PII scrubbing should be active
|
|
361
|
+
|
|
362
|
+
"""
|
|
363
|
+
# Explicit setting takes precedence
|
|
364
|
+
if self.pii_scrubbing_enabled is not None:
|
|
365
|
+
return self.pii_scrubbing_enabled
|
|
366
|
+
|
|
367
|
+
# Default based on compliance mode
|
|
368
|
+
return self.is_hipaa_mode()
|
|
369
|
+
|
|
370
|
+
def is_workflow_enabled(self, workflow_name: str) -> bool | None:
|
|
371
|
+
"""Check if a specific workflow is enabled.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
workflow_name: Name of the workflow (e.g., "test-gen")
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
True if workflow is enabled, False if disabled, None for default behavior
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
# Explicitly disabled takes precedence
|
|
381
|
+
if workflow_name in self.disabled_workflows:
|
|
382
|
+
return False
|
|
383
|
+
|
|
384
|
+
# Explicitly enabled
|
|
385
|
+
if workflow_name in self.enabled_workflows:
|
|
386
|
+
return True
|
|
387
|
+
|
|
388
|
+
# HIPAA mode enables healthcare-specific workflows
|
|
389
|
+
if self.is_hipaa_mode():
|
|
390
|
+
hipaa_workflows = {"test-gen"} # Workflows auto-enabled in HIPAA mode
|
|
391
|
+
if workflow_name in hipaa_workflows:
|
|
392
|
+
return True
|
|
393
|
+
|
|
394
|
+
# Default: workflow must be in standard registry (handled by __init__.py)
|
|
395
|
+
return None # None means "use default registry behavior"
|
|
396
|
+
|
|
397
|
+
def get_effective_audit_level(self) -> str:
|
|
398
|
+
"""Get the effective audit level based on compliance mode.
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
Audit level string: "standard", "enhanced", or "hipaa"
|
|
402
|
+
|
|
403
|
+
"""
|
|
404
|
+
# Explicit setting takes precedence
|
|
405
|
+
if self.audit_level != "standard":
|
|
406
|
+
return self.audit_level
|
|
407
|
+
|
|
408
|
+
# HIPAA mode defaults to hipaa audit level
|
|
409
|
+
if self.is_hipaa_mode():
|
|
410
|
+
return "hipaa"
|
|
411
|
+
|
|
412
|
+
return "standard"
|
|
413
|
+
|
|
414
|
+
def save(self, path: str | Path) -> None:
|
|
415
|
+
"""Save configuration to file."""
|
|
416
|
+
# Validate path first (convert Path to string for validation)
|
|
417
|
+
path_str = str(path)
|
|
418
|
+
validated_path = _validate_file_path(path_str)
|
|
419
|
+
|
|
420
|
+
data = {
|
|
421
|
+
"default_provider": self.default_provider,
|
|
422
|
+
"workflow_providers": self.workflow_providers,
|
|
423
|
+
"custom_models": self.custom_models,
|
|
424
|
+
"pricing_overrides": self.pricing_overrides,
|
|
425
|
+
"xml_prompt_defaults": self.xml_prompt_defaults,
|
|
426
|
+
"workflow_xml_configs": self.workflow_xml_configs,
|
|
427
|
+
# Compliance and feature flags
|
|
428
|
+
"compliance_mode": self.compliance_mode,
|
|
429
|
+
"enabled_workflows": self.enabled_workflows,
|
|
430
|
+
"disabled_workflows": self.disabled_workflows,
|
|
431
|
+
"pii_scrubbing_enabled": self.pii_scrubbing_enabled,
|
|
432
|
+
"audit_level": self.audit_level,
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
validated_path.parent.mkdir(parents=True, exist_ok=True)
|
|
436
|
+
|
|
437
|
+
if validated_path.suffix in (".yaml", ".yml"):
|
|
438
|
+
if not YAML_AVAILABLE:
|
|
439
|
+
raise ImportError("PyYAML required for YAML config")
|
|
440
|
+
with open(validated_path, "w") as f:
|
|
441
|
+
yaml.dump(data, f, default_flow_style=False)
|
|
442
|
+
else:
|
|
443
|
+
with open(validated_path, "w") as f:
|
|
444
|
+
json.dump(data, f, indent=2)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
# =============================================================================
|
|
448
|
+
# DEFAULT_MODELS - Built from unified registry
|
|
449
|
+
# =============================================================================
|
|
450
|
+
# This is now populated from attune.models.MODEL_REGISTRY for consistency
|
|
451
|
+
# across the framework.
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def _build_default_models() -> dict[str, dict[str, ModelConfig]]:
|
|
455
|
+
"""Build DEFAULT_MODELS from the unified registry."""
|
|
456
|
+
result: dict[str, dict[str, ModelConfig]] = {}
|
|
457
|
+
for provider, tiers in MODEL_REGISTRY.items():
|
|
458
|
+
result[provider] = {}
|
|
459
|
+
for tier, info in tiers.items():
|
|
460
|
+
result[provider][tier] = ModelConfig.from_model_info(info)
|
|
461
|
+
return result
|
|
462
|
+
|
|
463
|
+
|
|
464
|
+
# Lazy initialization - built on first access
|
|
465
|
+
_default_models_cache: dict[str, dict[str, ModelConfig]] | None = None
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _get_default_models() -> dict[str, dict[str, ModelConfig]]:
|
|
469
|
+
"""Get DEFAULT_MODELS, building from registry if needed."""
|
|
470
|
+
global _default_models_cache
|
|
471
|
+
if _default_models_cache is None:
|
|
472
|
+
_default_models_cache = _build_default_models()
|
|
473
|
+
return _default_models_cache
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
# For backward compatibility, DEFAULT_MODELS is now a property-like access
|
|
477
|
+
# Users should access via get_default_models() or directly use MODEL_REGISTRY
|
|
478
|
+
DEFAULT_MODELS: dict[str, dict[str, ModelConfig]] = {} # Populated below
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _ensure_default_models() -> None:
|
|
482
|
+
"""Ensure DEFAULT_MODELS is populated."""
|
|
483
|
+
global DEFAULT_MODELS
|
|
484
|
+
if not DEFAULT_MODELS:
|
|
485
|
+
DEFAULT_MODELS.update(_get_default_models())
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
def get_model(provider: str, tier: str, config: WorkflowConfig | None = None) -> str:
|
|
489
|
+
"""Get the model name for a provider/tier combination.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
provider: Model provider (anthropic, openai, ollama, hybrid)
|
|
493
|
+
tier: Model tier (cheap, capable, premium)
|
|
494
|
+
config: Optional WorkflowConfig for custom overrides
|
|
495
|
+
|
|
496
|
+
Returns:
|
|
497
|
+
Model name string
|
|
498
|
+
|
|
499
|
+
"""
|
|
500
|
+
# Ensure DEFAULT_MODELS is populated from registry
|
|
501
|
+
_ensure_default_models()
|
|
502
|
+
|
|
503
|
+
# Check config overrides first
|
|
504
|
+
if config:
|
|
505
|
+
custom = config.get_model_for_tier(provider, tier)
|
|
506
|
+
if custom:
|
|
507
|
+
return custom
|
|
508
|
+
|
|
509
|
+
# Fall back to defaults
|
|
510
|
+
if provider in DEFAULT_MODELS and tier in DEFAULT_MODELS[provider]:
|
|
511
|
+
return DEFAULT_MODELS[provider][tier].name
|
|
512
|
+
|
|
513
|
+
# Ultimate fallback
|
|
514
|
+
return DEFAULT_MODELS["anthropic"]["capable"].name
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def create_example_config() -> str:
|
|
518
|
+
"""Generate an example configuration file content."""
|
|
519
|
+
return """# Empathy Framework - Workflow Configuration
|
|
520
|
+
# Place this file at: .attune/workflows.yaml
|
|
521
|
+
|
|
522
|
+
# =============================================================================
|
|
523
|
+
# PROVIDER SELECTION
|
|
524
|
+
# =============================================================================
|
|
525
|
+
# Choose from: anthropic, openai, ollama, hybrid
|
|
526
|
+
#
|
|
527
|
+
# - anthropic: All Claude models (Haiku → Sonnet → Opus 4.5)
|
|
528
|
+
# - openai: All OpenAI models (GPT-4o-mini → GPT-4o → GPT-5.2)
|
|
529
|
+
# - ollama: Local models (llama3.2:3b → llama3.1:8b → llama3.1:70b)
|
|
530
|
+
# - hybrid: Mix of best models from different providers:
|
|
531
|
+
# cheap: gpt-4o-mini (cheapest)
|
|
532
|
+
# capable: claude-sonnet-4 (best reasoning)
|
|
533
|
+
# premium: claude-opus-4.5 (best overall)
|
|
534
|
+
|
|
535
|
+
default_provider: anthropic
|
|
536
|
+
|
|
537
|
+
# =============================================================================
|
|
538
|
+
# PER-WORKFLOW PROVIDER OVERRIDES
|
|
539
|
+
# =============================================================================
|
|
540
|
+
# Use different providers for specific workflows
|
|
541
|
+
workflow_providers:
|
|
542
|
+
# research: hybrid # Use hybrid for research
|
|
543
|
+
# code-review: anthropic
|
|
544
|
+
# doc-gen: openai
|
|
545
|
+
|
|
546
|
+
# =============================================================================
|
|
547
|
+
# CUSTOM MODEL MAPPINGS
|
|
548
|
+
# =============================================================================
|
|
549
|
+
# Override default models for specific provider/tier combinations
|
|
550
|
+
custom_models:
|
|
551
|
+
anthropic:
|
|
552
|
+
cheap: claude-3-5-haiku-20241022
|
|
553
|
+
capable: claude-sonnet-4-20250514
|
|
554
|
+
premium: claude-opus-4-5-20251101
|
|
555
|
+
openai:
|
|
556
|
+
cheap: gpt-4o-mini
|
|
557
|
+
capable: gpt-4o
|
|
558
|
+
premium: gpt-5.2
|
|
559
|
+
ollama:
|
|
560
|
+
cheap: llama3.2:3b
|
|
561
|
+
capable: llama3.1:8b
|
|
562
|
+
premium: llama3.1:70b
|
|
563
|
+
# Create your own hybrid mix:
|
|
564
|
+
hybrid:
|
|
565
|
+
cheap: gpt-4o-mini # OpenAI - cheapest per token
|
|
566
|
+
capable: claude-sonnet-4-20250514 # Anthropic - best code/reasoning
|
|
567
|
+
premium: claude-opus-4-5-20251101 # Anthropic - best overall
|
|
568
|
+
|
|
569
|
+
# =============================================================================
|
|
570
|
+
# CUSTOM PRICING (per million tokens)
|
|
571
|
+
# =============================================================================
|
|
572
|
+
# Add pricing for models not in the default list
|
|
573
|
+
pricing_overrides:
|
|
574
|
+
mixtral:latest:
|
|
575
|
+
input: 0.0
|
|
576
|
+
output: 0.0
|
|
577
|
+
my-custom-model:
|
|
578
|
+
input: 1.00
|
|
579
|
+
output: 5.00
|
|
580
|
+
|
|
581
|
+
# =============================================================================
|
|
582
|
+
# XML PROMPT CONFIGURATION
|
|
583
|
+
# =============================================================================
|
|
584
|
+
# Enable structured XML prompts for consistent LLM interactions.
|
|
585
|
+
# XML prompts improve parsing reliability for dashboards and automation.
|
|
586
|
+
|
|
587
|
+
# Global defaults for all workflows
|
|
588
|
+
xml_prompt_defaults:
|
|
589
|
+
enabled: false # Set to true to enable XML prompts globally
|
|
590
|
+
schema_version: "1.0" # XML schema version
|
|
591
|
+
enforce_response_xml: false # Require XML in responses
|
|
592
|
+
fallback_on_parse_error: true # Fall back to raw text if XML fails
|
|
593
|
+
|
|
594
|
+
# Per-workflow XML configuration (overrides defaults)
|
|
595
|
+
workflow_xml_configs:
|
|
596
|
+
security-audit:
|
|
597
|
+
enabled: true
|
|
598
|
+
enforce_response_xml: true
|
|
599
|
+
template_name: "security-audit"
|
|
600
|
+
code-review:
|
|
601
|
+
enabled: true
|
|
602
|
+
enforce_response_xml: true
|
|
603
|
+
template_name: "code-review"
|
|
604
|
+
research:
|
|
605
|
+
enabled: true
|
|
606
|
+
enforce_response_xml: false # More flexible for research
|
|
607
|
+
template_name: "research"
|
|
608
|
+
bug-predict:
|
|
609
|
+
enabled: true
|
|
610
|
+
enforce_response_xml: true
|
|
611
|
+
template_name: "bug-analysis"
|
|
612
|
+
perf-audit:
|
|
613
|
+
enabled: true
|
|
614
|
+
enforce_response_xml: true
|
|
615
|
+
template_name: "perf-audit"
|
|
616
|
+
test-gen:
|
|
617
|
+
enabled: true
|
|
618
|
+
enforce_response_xml: true
|
|
619
|
+
template_name: "test-gen"
|
|
620
|
+
doc-gen:
|
|
621
|
+
enabled: true
|
|
622
|
+
enforce_response_xml: true
|
|
623
|
+
template_name: "doc-gen"
|
|
624
|
+
release-prep:
|
|
625
|
+
enabled: true
|
|
626
|
+
enforce_response_xml: true
|
|
627
|
+
template_name: "release-prep"
|
|
628
|
+
dependency-check:
|
|
629
|
+
enabled: true
|
|
630
|
+
enforce_response_xml: true
|
|
631
|
+
template_name: "dependency-check"
|
|
632
|
+
refactor-plan:
|
|
633
|
+
enabled: true
|
|
634
|
+
enforce_response_xml: true
|
|
635
|
+
template_name: "refactor-plan"
|
|
636
|
+
|
|
637
|
+
# =============================================================================
|
|
638
|
+
# ENVIRONMENT VARIABLE OVERRIDES
|
|
639
|
+
# =============================================================================
|
|
640
|
+
# EMPATHY_WORKFLOW_PROVIDER=hybrid # Default provider
|
|
641
|
+
# EMPATHY_WORKFLOW_RESEARCH_PROVIDER=anthropic # Per-workflow
|
|
642
|
+
# EMPATHY_MODEL_CHEAP=gpt-4o-mini # Tier model override
|
|
643
|
+
# EMPATHY_MODEL_CAPABLE=claude-sonnet-4-20250514
|
|
644
|
+
# EMPATHY_MODEL_PREMIUM=claude-opus-4-5-20251101
|
|
645
|
+
"""
|