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,282 @@
|
|
|
1
|
+
"""Provider Configuration System
|
|
2
|
+
|
|
3
|
+
Handles user provider selection during install/update and runtime configuration.
|
|
4
|
+
Supports single-provider mode (default) and hybrid mode (multi-provider).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
from attune.config import _validate_file_path
|
|
17
|
+
|
|
18
|
+
from .registry import MODEL_REGISTRY, ModelInfo, ModelTier
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProviderMode(str, Enum):
|
|
22
|
+
"""Provider selection mode (Anthropic-only as of v5.0.0)."""
|
|
23
|
+
|
|
24
|
+
SINGLE = "single" # Anthropic for all tiers
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class ProviderConfig:
|
|
29
|
+
"""User's provider configuration."""
|
|
30
|
+
|
|
31
|
+
# Primary mode
|
|
32
|
+
mode: ProviderMode = ProviderMode.SINGLE
|
|
33
|
+
|
|
34
|
+
# For SINGLE mode: which provider to use
|
|
35
|
+
primary_provider: str = "anthropic"
|
|
36
|
+
|
|
37
|
+
# For CUSTOM mode: per-tier provider overrides
|
|
38
|
+
tier_providers: dict[str, str] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
# API key availability (detected at runtime)
|
|
41
|
+
available_providers: list[str] = field(default_factory=list)
|
|
42
|
+
|
|
43
|
+
# User preferences
|
|
44
|
+
prefer_local: bool = False # Deprecated (v5.0.0)
|
|
45
|
+
cost_optimization: bool = True # Use cheaper tiers when appropriate
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def detect_available_providers(cls) -> list[str]:
|
|
49
|
+
"""Detect if Anthropic API key is configured (Anthropic-only as of v5.0.0)."""
|
|
50
|
+
available = []
|
|
51
|
+
|
|
52
|
+
# Load .env files if they exist (project root and home)
|
|
53
|
+
env_keys = cls._load_env_files()
|
|
54
|
+
|
|
55
|
+
# Check for ANTHROPIC_API_KEY
|
|
56
|
+
if os.getenv("ANTHROPIC_API_KEY") or env_keys.get("ANTHROPIC_API_KEY"):
|
|
57
|
+
available.append("anthropic")
|
|
58
|
+
|
|
59
|
+
return available
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def _load_env_files() -> dict[str, str]:
|
|
63
|
+
"""Load API keys from .env files without modifying os.environ."""
|
|
64
|
+
env_keys: dict[str, str] = {}
|
|
65
|
+
|
|
66
|
+
# Possible .env file locations
|
|
67
|
+
env_paths = [
|
|
68
|
+
Path.cwd() / ".env",
|
|
69
|
+
Path.home() / ".env",
|
|
70
|
+
Path.home() / ".empathy" / ".env",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
for env_path in env_paths:
|
|
74
|
+
if env_path.exists():
|
|
75
|
+
try:
|
|
76
|
+
with open(env_path) as f:
|
|
77
|
+
for line in f:
|
|
78
|
+
line = line.strip()
|
|
79
|
+
if line and not line.startswith("#") and "=" in line:
|
|
80
|
+
key, _, value = line.partition("=")
|
|
81
|
+
key = key.strip()
|
|
82
|
+
value = value.strip().strip("'\"")
|
|
83
|
+
if key and value:
|
|
84
|
+
env_keys[key] = value
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
return env_keys
|
|
89
|
+
|
|
90
|
+
@classmethod
|
|
91
|
+
def auto_detect(cls) -> ProviderConfig:
|
|
92
|
+
"""Auto-detect configuration (Anthropic-only as of v5.0.0).
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
ProviderConfig with Anthropic as primary provider
|
|
96
|
+
"""
|
|
97
|
+
available = cls.detect_available_providers()
|
|
98
|
+
|
|
99
|
+
return cls(
|
|
100
|
+
mode=ProviderMode.SINGLE,
|
|
101
|
+
primary_provider="anthropic",
|
|
102
|
+
available_providers=available,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def get_model_for_tier(self, tier: str | ModelTier) -> ModelInfo | None:
|
|
106
|
+
"""Get the model to use for a given tier (Anthropic-only as of v5.0.0).
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
tier: Tier level (cheap, capable, premium)
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
ModelInfo for the Anthropic model at the specified tier
|
|
113
|
+
"""
|
|
114
|
+
tier_str = tier.value if isinstance(tier, ModelTier) else tier
|
|
115
|
+
return MODEL_REGISTRY.get("anthropic", {}).get(tier_str)
|
|
116
|
+
|
|
117
|
+
def get_effective_registry(self) -> dict[str, ModelInfo]:
|
|
118
|
+
"""Get the effective model registry based on current config."""
|
|
119
|
+
result = {}
|
|
120
|
+
for tier in ["cheap", "capable", "premium"]:
|
|
121
|
+
model = self.get_model_for_tier(tier)
|
|
122
|
+
if model:
|
|
123
|
+
result[tier] = model
|
|
124
|
+
return result
|
|
125
|
+
|
|
126
|
+
def to_dict(self) -> dict[str, Any]:
|
|
127
|
+
"""Serialize to dictionary."""
|
|
128
|
+
return {
|
|
129
|
+
"mode": self.mode.value,
|
|
130
|
+
"primary_provider": self.primary_provider,
|
|
131
|
+
"tier_providers": self.tier_providers,
|
|
132
|
+
"prefer_local": self.prefer_local,
|
|
133
|
+
"cost_optimization": self.cost_optimization,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def from_dict(cls, data: dict[str, Any]) -> ProviderConfig:
|
|
138
|
+
"""Deserialize from dictionary."""
|
|
139
|
+
return cls(
|
|
140
|
+
mode=ProviderMode(data.get("mode", "single")),
|
|
141
|
+
primary_provider=data.get("primary_provider", "anthropic"),
|
|
142
|
+
tier_providers=data.get("tier_providers", {}),
|
|
143
|
+
prefer_local=data.get("prefer_local", False),
|
|
144
|
+
cost_optimization=data.get("cost_optimization", True),
|
|
145
|
+
available_providers=cls.detect_available_providers(),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def save(self, path: Path | None = None) -> None:
|
|
149
|
+
"""Save configuration to file."""
|
|
150
|
+
if path is None:
|
|
151
|
+
path = Path.home() / ".empathy" / "provider_config.json"
|
|
152
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
153
|
+
validated_path = _validate_file_path(str(path))
|
|
154
|
+
with open(validated_path, "w") as f:
|
|
155
|
+
json.dump(self.to_dict(), f, indent=2)
|
|
156
|
+
|
|
157
|
+
@classmethod
|
|
158
|
+
def load(cls, path: Path | None = None) -> ProviderConfig:
|
|
159
|
+
"""Load configuration from file, or auto-detect if not found."""
|
|
160
|
+
if path is None:
|
|
161
|
+
path = Path.home() / ".empathy" / "provider_config.json"
|
|
162
|
+
|
|
163
|
+
if path.exists():
|
|
164
|
+
try:
|
|
165
|
+
with open(path) as f:
|
|
166
|
+
data = json.load(f)
|
|
167
|
+
return cls.from_dict(data)
|
|
168
|
+
except Exception:
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
# Auto-detect if no config exists
|
|
172
|
+
return cls.auto_detect()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# Interactive configuration for install/update
|
|
176
|
+
def configure_provider_interactive() -> ProviderConfig:
|
|
177
|
+
"""Interactive provider configuration for install/update (Anthropic-only as of v5.0.0).
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
ProviderConfig configured for Anthropic
|
|
181
|
+
"""
|
|
182
|
+
print("\n" + "=" * 60)
|
|
183
|
+
print("Empathy Framework - Provider Configuration (Claude-Native v5.0.0)")
|
|
184
|
+
print("=" * 60)
|
|
185
|
+
|
|
186
|
+
# Check for Anthropic API key
|
|
187
|
+
config = ProviderConfig.auto_detect()
|
|
188
|
+
available = config.available_providers
|
|
189
|
+
|
|
190
|
+
if not available:
|
|
191
|
+
print("\n⚠️ ANTHROPIC_API_KEY not detected.")
|
|
192
|
+
print("\nPlease set your Anthropic API key:")
|
|
193
|
+
print(" export ANTHROPIC_API_KEY='your-key-here'")
|
|
194
|
+
print("\nGet your API key at: https://console.anthropic.com/settings/keys")
|
|
195
|
+
print("\nDefaulting to Anthropic configuration.")
|
|
196
|
+
print("You'll need to set ANTHROPIC_API_KEY before running workflows.")
|
|
197
|
+
return ProviderConfig(
|
|
198
|
+
mode=ProviderMode.SINGLE,
|
|
199
|
+
primary_provider="anthropic",
|
|
200
|
+
available_providers=[],
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
# Anthropic API key detected
|
|
204
|
+
print("\n✓ ANTHROPIC_API_KEY detected")
|
|
205
|
+
print("\nConfiguring Anthropic as provider...")
|
|
206
|
+
|
|
207
|
+
config = ProviderConfig(
|
|
208
|
+
mode=ProviderMode.SINGLE,
|
|
209
|
+
primary_provider="anthropic",
|
|
210
|
+
available_providers=available,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Show effective models
|
|
214
|
+
print("\nEffective model mapping:")
|
|
215
|
+
effective = config.get_effective_registry()
|
|
216
|
+
for tier, model in effective.items():
|
|
217
|
+
if model:
|
|
218
|
+
print(f" {tier:8} → {model.id}")
|
|
219
|
+
|
|
220
|
+
# Save configuration
|
|
221
|
+
config.save()
|
|
222
|
+
print("\n✓ Configuration saved to ~/.attune/provider_config.json")
|
|
223
|
+
|
|
224
|
+
return config
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def configure_provider_cli(
|
|
228
|
+
provider: str | None = None,
|
|
229
|
+
mode: str | None = None,
|
|
230
|
+
) -> ProviderConfig:
|
|
231
|
+
"""CLI-based provider configuration (Anthropic-only as of v5.0.0).
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
provider: Provider name (must be 'anthropic' or None)
|
|
235
|
+
mode: Mode (must be 'single' or None)
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
Configured ProviderConfig for Anthropic
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
ValueError: If provider is not 'anthropic'
|
|
242
|
+
"""
|
|
243
|
+
if provider and provider.lower() != "anthropic":
|
|
244
|
+
raise ValueError(
|
|
245
|
+
f"Provider '{provider}' is not supported. "
|
|
246
|
+
f"Empathy Framework is now Claude-native (v5.0.0). "
|
|
247
|
+
f"Only 'anthropic' provider is available. "
|
|
248
|
+
f"See docs/CLAUDE_NATIVE.md for migration guide."
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
if mode and mode.lower() != "single":
|
|
252
|
+
raise ValueError(
|
|
253
|
+
f"Mode '{mode}' is not supported. "
|
|
254
|
+
f"Only 'single' mode is available in v5.0.0 (Anthropic-only)."
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Always return Anthropic configuration
|
|
258
|
+
return ProviderConfig.auto_detect()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# Global config instance (lazy-loaded)
|
|
262
|
+
_global_config: ProviderConfig | None = None
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def get_provider_config() -> ProviderConfig:
|
|
266
|
+
"""Get the global provider configuration."""
|
|
267
|
+
global _global_config
|
|
268
|
+
if _global_config is None:
|
|
269
|
+
_global_config = ProviderConfig.load()
|
|
270
|
+
return _global_config
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def set_provider_config(config: ProviderConfig) -> None:
|
|
274
|
+
"""Set the global provider configuration."""
|
|
275
|
+
global _global_config
|
|
276
|
+
_global_config = config
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def reset_provider_config() -> None:
|
|
280
|
+
"""Reset the global provider configuration (forces reload)."""
|
|
281
|
+
global _global_config
|
|
282
|
+
_global_config = None
|