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,499 @@
|
|
|
1
|
+
"""Agent Configuration Store - Learning and Memory System for Agent Compositions
|
|
2
|
+
|
|
3
|
+
Saves and retrieves successful agent team configurations, enabling the meta-orchestrator
|
|
4
|
+
to learn from outcomes and reuse proven compositions.
|
|
5
|
+
|
|
6
|
+
This is the "memory" component of the meta-orchestration system - it learns which
|
|
7
|
+
agent teams work best for specific task types and progressively improves.
|
|
8
|
+
|
|
9
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
10
|
+
Licensed under Fair Source 0.9
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
from dataclasses import asdict, dataclass, field
|
|
16
|
+
from datetime import datetime
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any
|
|
19
|
+
|
|
20
|
+
from attune.pattern_library import Pattern, PatternLibrary
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _validate_file_path(path: str, allowed_dir: str | None = None) -> Path:
|
|
26
|
+
"""Validate file path to prevent path traversal and arbitrary writes.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
path: File path to validate
|
|
30
|
+
allowed_dir: Optional directory to restrict writes to
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Validated Path object
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
ValueError: If path is invalid or unsafe
|
|
37
|
+
"""
|
|
38
|
+
if not path or not isinstance(path, str):
|
|
39
|
+
raise ValueError("path must be a non-empty string")
|
|
40
|
+
|
|
41
|
+
# Check for null bytes
|
|
42
|
+
if "\x00" in path:
|
|
43
|
+
raise ValueError("path contains null bytes")
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
resolved = Path(path).resolve()
|
|
47
|
+
except (OSError, RuntimeError) as e:
|
|
48
|
+
raise ValueError(f"Invalid path: {e}")
|
|
49
|
+
|
|
50
|
+
# Check if within allowed directory
|
|
51
|
+
if allowed_dir:
|
|
52
|
+
try:
|
|
53
|
+
allowed = Path(allowed_dir).resolve()
|
|
54
|
+
resolved.relative_to(allowed)
|
|
55
|
+
except ValueError:
|
|
56
|
+
raise ValueError(f"path must be within {allowed_dir}")
|
|
57
|
+
|
|
58
|
+
# Check for dangerous system paths
|
|
59
|
+
dangerous_paths = ["/etc", "/sys", "/proc", "/dev"]
|
|
60
|
+
for dangerous in dangerous_paths:
|
|
61
|
+
if str(resolved).startswith(dangerous):
|
|
62
|
+
raise ValueError(f"Cannot write to system directory: {dangerous}")
|
|
63
|
+
|
|
64
|
+
return resolved
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass
|
|
68
|
+
class AgentConfiguration:
|
|
69
|
+
"""Saved configuration for a successful agent team composition.
|
|
70
|
+
|
|
71
|
+
Represents a proven solution: the team composition, execution strategy,
|
|
72
|
+
and performance metrics for a specific task pattern.
|
|
73
|
+
|
|
74
|
+
This is what gets saved when an orchestration succeeds, and what gets
|
|
75
|
+
retrieved when the system recognizes a similar task in the future.
|
|
76
|
+
|
|
77
|
+
Example:
|
|
78
|
+
>>> config = AgentConfiguration(
|
|
79
|
+
... id="comp_release_prep_001",
|
|
80
|
+
... task_pattern="release_preparation",
|
|
81
|
+
... agents=[
|
|
82
|
+
... {"role": "security_auditor", "tier": "PREMIUM"},
|
|
83
|
+
... {"role": "test_analyzer", "tier": "CAPABLE"},
|
|
84
|
+
... ],
|
|
85
|
+
... strategy="parallel",
|
|
86
|
+
... quality_gates={"min_coverage": 80},
|
|
87
|
+
... success_rate=0.95,
|
|
88
|
+
... avg_quality_score=87.5,
|
|
89
|
+
... )
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
# Identity
|
|
93
|
+
id: str
|
|
94
|
+
task_pattern: str # "release_prep", "test_coverage_boost", etc.
|
|
95
|
+
|
|
96
|
+
# Team Composition
|
|
97
|
+
agents: list[dict[str, Any]] # List of agent configs (role, tier, tools, etc.)
|
|
98
|
+
strategy: str # "sequential", "parallel", "debate", etc.
|
|
99
|
+
|
|
100
|
+
# Quality Criteria
|
|
101
|
+
quality_gates: dict[str, Any] # Min thresholds (coverage, security score, etc.)
|
|
102
|
+
|
|
103
|
+
# Performance Metrics
|
|
104
|
+
success_rate: float = 0.0 # 0.0-1.0
|
|
105
|
+
avg_quality_score: float = 0.0 # 0-100
|
|
106
|
+
usage_count: int = 0
|
|
107
|
+
success_count: int = 0
|
|
108
|
+
failure_count: int = 0
|
|
109
|
+
|
|
110
|
+
# Metadata
|
|
111
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
112
|
+
last_used: datetime | None = None
|
|
113
|
+
tags: list[str] = field(default_factory=list)
|
|
114
|
+
|
|
115
|
+
def record_outcome(self, success: bool, quality_score: float) -> None:
|
|
116
|
+
"""Record an execution outcome and update metrics.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
success: Whether the orchestration succeeded (met quality gates)
|
|
120
|
+
quality_score: Quality score from 0-100
|
|
121
|
+
|
|
122
|
+
Raises:
|
|
123
|
+
ValueError: If quality_score is out of range
|
|
124
|
+
"""
|
|
125
|
+
if not 0.0 <= quality_score <= 100.0:
|
|
126
|
+
raise ValueError(f"quality_score must be 0-100, got {quality_score}")
|
|
127
|
+
|
|
128
|
+
self.usage_count += 1
|
|
129
|
+
self.last_used = datetime.now()
|
|
130
|
+
|
|
131
|
+
if success:
|
|
132
|
+
self.success_count += 1
|
|
133
|
+
else:
|
|
134
|
+
self.failure_count += 1
|
|
135
|
+
|
|
136
|
+
# Update success rate
|
|
137
|
+
if self.usage_count > 0:
|
|
138
|
+
self.success_rate = self.success_count / self.usage_count
|
|
139
|
+
|
|
140
|
+
# Update rolling average quality score
|
|
141
|
+
if self.usage_count == 1:
|
|
142
|
+
self.avg_quality_score = quality_score
|
|
143
|
+
else:
|
|
144
|
+
# Weighted average: recent scores matter more
|
|
145
|
+
weight = 0.7 # 70% weight on new score
|
|
146
|
+
self.avg_quality_score = weight * quality_score + (1 - weight) * self.avg_quality_score
|
|
147
|
+
|
|
148
|
+
def to_dict(self) -> dict[str, Any]:
|
|
149
|
+
"""Serialize to dictionary for JSON storage."""
|
|
150
|
+
data = asdict(self)
|
|
151
|
+
# Convert datetime objects to ISO format strings
|
|
152
|
+
data["created_at"] = self.created_at.isoformat()
|
|
153
|
+
data["last_used"] = self.last_used.isoformat() if self.last_used else None
|
|
154
|
+
return data
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def from_dict(cls, data: dict[str, Any]) -> "AgentConfiguration":
|
|
158
|
+
"""Deserialize from dictionary.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
data: Dictionary from JSON
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
AgentConfiguration instance
|
|
165
|
+
"""
|
|
166
|
+
# Convert ISO format strings back to datetime objects
|
|
167
|
+
if "created_at" in data and isinstance(data["created_at"], str):
|
|
168
|
+
data["created_at"] = datetime.fromisoformat(data["created_at"])
|
|
169
|
+
if "last_used" in data and data["last_used"]:
|
|
170
|
+
data["last_used"] = datetime.fromisoformat(data["last_used"])
|
|
171
|
+
|
|
172
|
+
return cls(**data)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
class ConfigurationStore:
|
|
176
|
+
"""Persistent storage for successful agent team compositions.
|
|
177
|
+
|
|
178
|
+
This is the learning/memory system for the meta-orchestrator. It:
|
|
179
|
+
1. Saves successful compositions to disk
|
|
180
|
+
2. Retrieves proven solutions for similar tasks
|
|
181
|
+
3. Tracks performance metrics over time
|
|
182
|
+
4. Integrates with pattern library for cross-task learning
|
|
183
|
+
|
|
184
|
+
File structure:
|
|
185
|
+
.attune/orchestration/compositions/
|
|
186
|
+
├── release_prep_001.json
|
|
187
|
+
├── test_coverage_boost_001.json
|
|
188
|
+
└── security_deep_dive_001.json
|
|
189
|
+
|
|
190
|
+
Example:
|
|
191
|
+
>>> store = ConfigurationStore()
|
|
192
|
+
>>>
|
|
193
|
+
>>> # Save successful composition
|
|
194
|
+
>>> config = AgentConfiguration(
|
|
195
|
+
... id="comp_001",
|
|
196
|
+
... task_pattern="release_prep",
|
|
197
|
+
... agents=[...],
|
|
198
|
+
... strategy="parallel",
|
|
199
|
+
... )
|
|
200
|
+
>>> store.save(config)
|
|
201
|
+
>>>
|
|
202
|
+
>>> # Load for reuse
|
|
203
|
+
>>> loaded = store.load("comp_001")
|
|
204
|
+
>>>
|
|
205
|
+
>>> # Search for similar tasks
|
|
206
|
+
>>> matches = store.search(task_pattern="release_prep", min_success_rate=0.8)
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def __init__(
|
|
210
|
+
self,
|
|
211
|
+
storage_dir: str | None = None,
|
|
212
|
+
pattern_library: PatternLibrary | None = None,
|
|
213
|
+
):
|
|
214
|
+
"""Initialize configuration store.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
storage_dir: Directory for storing configurations
|
|
218
|
+
(default: .attune/orchestration/compositions/)
|
|
219
|
+
pattern_library: Optional pattern library for integration
|
|
220
|
+
"""
|
|
221
|
+
# Set default storage directory
|
|
222
|
+
if storage_dir is None:
|
|
223
|
+
storage_dir = ".attune/orchestration/compositions"
|
|
224
|
+
|
|
225
|
+
self.storage_dir = Path(storage_dir)
|
|
226
|
+
self.pattern_library = pattern_library
|
|
227
|
+
|
|
228
|
+
# Create storage directory if it doesn't exist
|
|
229
|
+
try:
|
|
230
|
+
self.storage_dir.mkdir(parents=True, exist_ok=True)
|
|
231
|
+
except OSError as e:
|
|
232
|
+
logger.error(f"Failed to create storage directory {self.storage_dir}: {e}")
|
|
233
|
+
raise ValueError(f"Cannot create storage directory: {e}") from e
|
|
234
|
+
|
|
235
|
+
# In-memory cache for fast lookups
|
|
236
|
+
self._cache: dict[str, AgentConfiguration] = {}
|
|
237
|
+
self._loaded = False
|
|
238
|
+
|
|
239
|
+
def _load_all_from_disk(self) -> None:
|
|
240
|
+
"""Load all configurations from disk into cache.
|
|
241
|
+
|
|
242
|
+
This is called lazily on first access to avoid startup overhead.
|
|
243
|
+
"""
|
|
244
|
+
if self._loaded:
|
|
245
|
+
return
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
for config_file in self.storage_dir.glob("*.json"):
|
|
249
|
+
try:
|
|
250
|
+
with config_file.open("r") as f:
|
|
251
|
+
data = json.load(f)
|
|
252
|
+
config = AgentConfiguration.from_dict(data)
|
|
253
|
+
self._cache[config.id] = config
|
|
254
|
+
except (json.JSONDecodeError, KeyError, TypeError) as e:
|
|
255
|
+
logger.warning(f"Failed to load {config_file}: {e}")
|
|
256
|
+
continue
|
|
257
|
+
|
|
258
|
+
logger.info(f"Loaded {len(self._cache)} configurations from disk")
|
|
259
|
+
self._loaded = True
|
|
260
|
+
|
|
261
|
+
except OSError as e:
|
|
262
|
+
logger.error(f"Error reading from {self.storage_dir}: {e}")
|
|
263
|
+
# Don't raise - start with empty cache
|
|
264
|
+
|
|
265
|
+
def save(self, config: AgentConfiguration) -> Path:
|
|
266
|
+
"""Save agent configuration to disk and update pattern library.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
config: Configuration to save
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Path to saved file
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
ValueError: If config.id is invalid or file path is unsafe
|
|
276
|
+
OSError: If file write fails
|
|
277
|
+
"""
|
|
278
|
+
if not config.id or not isinstance(config.id, str):
|
|
279
|
+
raise ValueError("config.id must be a non-empty string")
|
|
280
|
+
|
|
281
|
+
# Validate filename (prevent path traversal)
|
|
282
|
+
filename = f"{config.id}.json"
|
|
283
|
+
file_path = self.storage_dir / filename
|
|
284
|
+
|
|
285
|
+
# Security: validate path is within storage_dir
|
|
286
|
+
validated_path = _validate_file_path(str(file_path), allowed_dir=str(self.storage_dir))
|
|
287
|
+
|
|
288
|
+
# Save to disk
|
|
289
|
+
try:
|
|
290
|
+
with validated_path.open("w") as f:
|
|
291
|
+
json.dump(config.to_dict(), f, indent=2)
|
|
292
|
+
|
|
293
|
+
logger.info(f"Saved configuration {config.id} to {validated_path}")
|
|
294
|
+
|
|
295
|
+
except OSError as e:
|
|
296
|
+
logger.error(f"Failed to save configuration {config.id}: {e}")
|
|
297
|
+
raise
|
|
298
|
+
|
|
299
|
+
# Update in-memory cache
|
|
300
|
+
self._cache[config.id] = config
|
|
301
|
+
|
|
302
|
+
# Integrate with pattern library
|
|
303
|
+
if self.pattern_library:
|
|
304
|
+
self._contribute_to_pattern_library(config)
|
|
305
|
+
|
|
306
|
+
return validated_path
|
|
307
|
+
|
|
308
|
+
def load(self, config_id: str) -> AgentConfiguration | None:
|
|
309
|
+
"""Load configuration by ID.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
config_id: Configuration ID to load
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Configuration if found, None otherwise
|
|
316
|
+
|
|
317
|
+
Raises:
|
|
318
|
+
ValueError: If config_id is invalid
|
|
319
|
+
"""
|
|
320
|
+
if not config_id or not isinstance(config_id, str):
|
|
321
|
+
raise ValueError("config_id must be a non-empty string")
|
|
322
|
+
|
|
323
|
+
# Load all configs if not already loaded
|
|
324
|
+
self._load_all_from_disk()
|
|
325
|
+
|
|
326
|
+
# Return from cache
|
|
327
|
+
return self._cache.get(config_id)
|
|
328
|
+
|
|
329
|
+
def search(
|
|
330
|
+
self,
|
|
331
|
+
task_pattern: str | None = None,
|
|
332
|
+
min_success_rate: float = 0.0,
|
|
333
|
+
min_quality_score: float = 0.0,
|
|
334
|
+
limit: int = 10,
|
|
335
|
+
) -> list[AgentConfiguration]:
|
|
336
|
+
"""Search for configurations matching criteria.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
task_pattern: Filter by task pattern (e.g., "release_prep")
|
|
340
|
+
min_success_rate: Minimum success rate (0.0-1.0)
|
|
341
|
+
min_quality_score: Minimum average quality score (0-100)
|
|
342
|
+
limit: Maximum results to return
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
List of matching configurations, sorted by success rate descending
|
|
346
|
+
|
|
347
|
+
Raises:
|
|
348
|
+
ValueError: If parameters are out of range
|
|
349
|
+
"""
|
|
350
|
+
if not 0.0 <= min_success_rate <= 1.0:
|
|
351
|
+
raise ValueError(f"min_success_rate must be 0-1, got {min_success_rate}")
|
|
352
|
+
|
|
353
|
+
if not 0.0 <= min_quality_score <= 100.0:
|
|
354
|
+
raise ValueError(f"min_quality_score must be 0-100, got {min_quality_score}")
|
|
355
|
+
|
|
356
|
+
if limit < 1:
|
|
357
|
+
raise ValueError(f"limit must be positive, got {limit}")
|
|
358
|
+
|
|
359
|
+
# Load all configs if not already loaded
|
|
360
|
+
self._load_all_from_disk()
|
|
361
|
+
|
|
362
|
+
# Filter by criteria
|
|
363
|
+
matches: list[AgentConfiguration] = []
|
|
364
|
+
|
|
365
|
+
for config in self._cache.values():
|
|
366
|
+
# Filter by task pattern
|
|
367
|
+
if task_pattern and config.task_pattern != task_pattern:
|
|
368
|
+
continue
|
|
369
|
+
|
|
370
|
+
# Filter by success rate
|
|
371
|
+
if config.success_rate < min_success_rate:
|
|
372
|
+
continue
|
|
373
|
+
|
|
374
|
+
# Filter by quality score
|
|
375
|
+
if config.avg_quality_score < min_quality_score:
|
|
376
|
+
continue
|
|
377
|
+
|
|
378
|
+
matches.append(config)
|
|
379
|
+
|
|
380
|
+
# Sort by success rate (descending), then quality score (descending)
|
|
381
|
+
matches.sort(key=lambda c: (c.success_rate, c.avg_quality_score), reverse=True)
|
|
382
|
+
|
|
383
|
+
# Apply limit
|
|
384
|
+
return matches[:limit]
|
|
385
|
+
|
|
386
|
+
def get_best_for_task(self, task_pattern: str) -> AgentConfiguration | None:
|
|
387
|
+
"""Get the best-performing configuration for a specific task pattern.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
task_pattern: Task pattern to find configuration for
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
Best configuration if found, None otherwise
|
|
394
|
+
"""
|
|
395
|
+
results = self.search(task_pattern=task_pattern, limit=1)
|
|
396
|
+
return results[0] if results else None
|
|
397
|
+
|
|
398
|
+
def delete(self, config_id: str) -> bool:
|
|
399
|
+
"""Delete a configuration.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
config_id: Configuration ID to delete
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
True if deleted, False if not found
|
|
406
|
+
|
|
407
|
+
Raises:
|
|
408
|
+
ValueError: If config_id is invalid
|
|
409
|
+
OSError: If file deletion fails
|
|
410
|
+
"""
|
|
411
|
+
if not config_id or not isinstance(config_id, str):
|
|
412
|
+
raise ValueError("config_id must be a non-empty string")
|
|
413
|
+
|
|
414
|
+
# Load all configs if not already loaded
|
|
415
|
+
self._load_all_from_disk()
|
|
416
|
+
|
|
417
|
+
# Check if exists in cache
|
|
418
|
+
if config_id not in self._cache:
|
|
419
|
+
return False
|
|
420
|
+
|
|
421
|
+
# Delete from disk
|
|
422
|
+
file_path = self.storage_dir / f"{config_id}.json"
|
|
423
|
+
if file_path.exists():
|
|
424
|
+
try:
|
|
425
|
+
file_path.unlink()
|
|
426
|
+
logger.info(f"Deleted configuration {config_id}")
|
|
427
|
+
except OSError as e:
|
|
428
|
+
logger.error(f"Failed to delete {file_path}: {e}")
|
|
429
|
+
raise
|
|
430
|
+
|
|
431
|
+
# Delete from cache
|
|
432
|
+
del self._cache[config_id]
|
|
433
|
+
|
|
434
|
+
return True
|
|
435
|
+
|
|
436
|
+
def list_all(self) -> list[AgentConfiguration]:
|
|
437
|
+
"""List all configurations.
|
|
438
|
+
|
|
439
|
+
Returns:
|
|
440
|
+
List of all configurations, sorted by last_used descending
|
|
441
|
+
"""
|
|
442
|
+
self._load_all_from_disk()
|
|
443
|
+
|
|
444
|
+
configs = list(self._cache.values())
|
|
445
|
+
|
|
446
|
+
# Sort by last_used (most recent first), with never-used at end
|
|
447
|
+
configs.sort(
|
|
448
|
+
key=lambda c: c.last_used or datetime.min,
|
|
449
|
+
reverse=True,
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
return configs
|
|
453
|
+
|
|
454
|
+
def _contribute_to_pattern_library(self, config: AgentConfiguration) -> None:
|
|
455
|
+
"""Contribute successful configuration as a pattern to pattern library.
|
|
456
|
+
|
|
457
|
+
This enables cross-task learning - patterns learned from one type of
|
|
458
|
+
orchestration can inform decisions on other tasks.
|
|
459
|
+
|
|
460
|
+
Args:
|
|
461
|
+
config: Configuration to contribute as pattern
|
|
462
|
+
"""
|
|
463
|
+
if not self.pattern_library:
|
|
464
|
+
return
|
|
465
|
+
|
|
466
|
+
# Only contribute configurations with proven success
|
|
467
|
+
if config.usage_count < 3 or config.success_rate < 0.7:
|
|
468
|
+
logger.debug(
|
|
469
|
+
f"Configuration {config.id} not yet proven "
|
|
470
|
+
f"(uses={config.usage_count}, rate={config.success_rate:.2f})"
|
|
471
|
+
)
|
|
472
|
+
return
|
|
473
|
+
|
|
474
|
+
pattern = Pattern(
|
|
475
|
+
id=f"orchestration_{config.id}",
|
|
476
|
+
agent_id="meta_orchestrator",
|
|
477
|
+
pattern_type="agent_composition",
|
|
478
|
+
name=config.task_pattern,
|
|
479
|
+
description=f"Successful agent composition for {config.task_pattern}",
|
|
480
|
+
context={
|
|
481
|
+
"agents": config.agents,
|
|
482
|
+
"strategy": config.strategy,
|
|
483
|
+
"quality_gates": config.quality_gates,
|
|
484
|
+
"success_rate": config.success_rate,
|
|
485
|
+
"avg_quality_score": config.avg_quality_score,
|
|
486
|
+
},
|
|
487
|
+
confidence=config.success_rate,
|
|
488
|
+
usage_count=config.usage_count,
|
|
489
|
+
success_count=config.success_count,
|
|
490
|
+
failure_count=config.failure_count,
|
|
491
|
+
tags=config.tags,
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
try:
|
|
495
|
+
self.pattern_library.contribute_pattern("meta_orchestrator", pattern)
|
|
496
|
+
logger.info(f"Contributed pattern for {config.task_pattern} to pattern library")
|
|
497
|
+
except ValueError as e:
|
|
498
|
+
# Pattern might already exist - that's okay
|
|
499
|
+
logger.debug(f"Pattern already exists in library: {e}")
|