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,1168 @@
|
|
|
1
|
+
"""Meta-orchestrator for intelligent agent composition.
|
|
2
|
+
|
|
3
|
+
This module implements the core orchestration logic that analyzes tasks,
|
|
4
|
+
selects appropriate agents, and chooses composition patterns.
|
|
5
|
+
|
|
6
|
+
Security:
|
|
7
|
+
- All inputs validated before processing
|
|
8
|
+
- No eval() or exec() usage
|
|
9
|
+
- Agent selection based on whitelisted templates
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
>>> orchestrator = MetaOrchestrator()
|
|
13
|
+
>>> plan = orchestrator.analyze_and_compose(
|
|
14
|
+
... task="Boost test coverage to 90%",
|
|
15
|
+
... context={"current_coverage": 75}
|
|
16
|
+
... )
|
|
17
|
+
>>> print(plan.strategy)
|
|
18
|
+
sequential
|
|
19
|
+
>>> print([a.role for a in plan.agents])
|
|
20
|
+
['Test Coverage Expert', 'Test Generation Specialist', 'Quality Assurance Validator']
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
from dataclasses import dataclass, field
|
|
25
|
+
from enum import Enum
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
from .agent_templates import AgentTemplate, get_template, get_templates_by_capability
|
|
29
|
+
|
|
30
|
+
logger = logging.getLogger(__name__)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TaskComplexity(Enum):
|
|
34
|
+
"""Task complexity classification."""
|
|
35
|
+
|
|
36
|
+
SIMPLE = "simple" # Single agent, straightforward
|
|
37
|
+
MODERATE = "moderate" # 2-3 agents, some coordination
|
|
38
|
+
COMPLEX = "complex" # 4+ agents, multi-phase execution
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class TaskDomain(Enum):
|
|
42
|
+
"""Task domain classification."""
|
|
43
|
+
|
|
44
|
+
TESTING = "testing"
|
|
45
|
+
SECURITY = "security"
|
|
46
|
+
CODE_QUALITY = "code_quality"
|
|
47
|
+
DOCUMENTATION = "documentation"
|
|
48
|
+
PERFORMANCE = "performance"
|
|
49
|
+
ARCHITECTURE = "architecture"
|
|
50
|
+
REFACTORING = "refactoring"
|
|
51
|
+
GENERAL = "general"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class CompositionPattern(Enum):
|
|
55
|
+
"""Available composition patterns (grammar rules)."""
|
|
56
|
+
|
|
57
|
+
# Original 7 patterns
|
|
58
|
+
SEQUENTIAL = "sequential" # A → B → C
|
|
59
|
+
PARALLEL = "parallel" # A || B || C
|
|
60
|
+
DEBATE = "debate" # A ⇄ B ⇄ C → Synthesis
|
|
61
|
+
TEACHING = "teaching" # Junior → Expert validation
|
|
62
|
+
REFINEMENT = "refinement" # Draft → Review → Polish
|
|
63
|
+
ADAPTIVE = "adaptive" # Classifier → Specialist
|
|
64
|
+
CONDITIONAL = "conditional" # If-then-else routing
|
|
65
|
+
|
|
66
|
+
# Anthropic-inspired patterns (Patterns 8-10)
|
|
67
|
+
TOOL_ENHANCED = "tool_enhanced" # Single agent with tools
|
|
68
|
+
PROMPT_CACHED_SEQUENTIAL = "prompt_cached_sequential" # Shared cached context
|
|
69
|
+
DELEGATION_CHAIN = "delegation_chain" # Hierarchical delegation (≤3 levels)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass
|
|
73
|
+
class TaskRequirements:
|
|
74
|
+
"""Extracted requirements from task analysis.
|
|
75
|
+
|
|
76
|
+
Attributes:
|
|
77
|
+
complexity: Task complexity level
|
|
78
|
+
domain: Primary task domain
|
|
79
|
+
capabilities_needed: List of capabilities required
|
|
80
|
+
parallelizable: Whether task can be parallelized
|
|
81
|
+
quality_gates: Quality thresholds to enforce
|
|
82
|
+
context: Additional context for customization
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
complexity: TaskComplexity
|
|
86
|
+
domain: TaskDomain
|
|
87
|
+
capabilities_needed: list[str]
|
|
88
|
+
parallelizable: bool = False
|
|
89
|
+
quality_gates: dict[str, Any] = field(default_factory=dict)
|
|
90
|
+
context: dict[str, Any] = field(default_factory=dict)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class ExecutionPlan:
|
|
95
|
+
"""Plan for agent execution.
|
|
96
|
+
|
|
97
|
+
Attributes:
|
|
98
|
+
agents: List of agents to execute
|
|
99
|
+
strategy: Composition pattern to use
|
|
100
|
+
quality_gates: Quality thresholds to enforce
|
|
101
|
+
estimated_cost: Estimated execution cost
|
|
102
|
+
estimated_duration: Estimated time in seconds
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
agents: list[AgentTemplate]
|
|
106
|
+
strategy: CompositionPattern
|
|
107
|
+
quality_gates: dict[str, Any] = field(default_factory=dict)
|
|
108
|
+
estimated_cost: float = 0.0
|
|
109
|
+
estimated_duration: int = 0
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class MetaOrchestrator:
|
|
113
|
+
"""Intelligent task analyzer and agent composition engine.
|
|
114
|
+
|
|
115
|
+
The meta-orchestrator analyzes tasks to determine requirements,
|
|
116
|
+
selects appropriate agents, and chooses optimal composition patterns.
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
>>> orchestrator = MetaOrchestrator()
|
|
120
|
+
>>> plan = orchestrator.analyze_and_compose(
|
|
121
|
+
... task="Prepare for v3.12.0 release",
|
|
122
|
+
... context={"version": "3.12.0"}
|
|
123
|
+
... )
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
# Keyword patterns for task analysis
|
|
127
|
+
COMPLEXITY_KEYWORDS = {
|
|
128
|
+
TaskComplexity.SIMPLE: [
|
|
129
|
+
"format",
|
|
130
|
+
"lint",
|
|
131
|
+
"check",
|
|
132
|
+
"validate",
|
|
133
|
+
"document",
|
|
134
|
+
],
|
|
135
|
+
TaskComplexity.MODERATE: [
|
|
136
|
+
"improve",
|
|
137
|
+
"refactor",
|
|
138
|
+
"optimize",
|
|
139
|
+
"test",
|
|
140
|
+
"review",
|
|
141
|
+
],
|
|
142
|
+
TaskComplexity.COMPLEX: [
|
|
143
|
+
"release",
|
|
144
|
+
"migrate",
|
|
145
|
+
"redesign",
|
|
146
|
+
"architecture",
|
|
147
|
+
"prepare",
|
|
148
|
+
],
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
DOMAIN_KEYWORDS = {
|
|
152
|
+
TaskDomain.TESTING: [
|
|
153
|
+
"test",
|
|
154
|
+
"coverage",
|
|
155
|
+
"pytest",
|
|
156
|
+
"unit test",
|
|
157
|
+
"integration test",
|
|
158
|
+
],
|
|
159
|
+
TaskDomain.SECURITY: [
|
|
160
|
+
"security",
|
|
161
|
+
"vulnerability",
|
|
162
|
+
"audit",
|
|
163
|
+
"penetration",
|
|
164
|
+
"threat",
|
|
165
|
+
],
|
|
166
|
+
TaskDomain.CODE_QUALITY: [
|
|
167
|
+
"quality",
|
|
168
|
+
"code review",
|
|
169
|
+
"lint",
|
|
170
|
+
"best practices",
|
|
171
|
+
"maintainability",
|
|
172
|
+
],
|
|
173
|
+
TaskDomain.DOCUMENTATION: [
|
|
174
|
+
"docs",
|
|
175
|
+
"documentation",
|
|
176
|
+
"readme",
|
|
177
|
+
"guide",
|
|
178
|
+
"tutorial",
|
|
179
|
+
],
|
|
180
|
+
TaskDomain.PERFORMANCE: [
|
|
181
|
+
"performance",
|
|
182
|
+
"optimize",
|
|
183
|
+
"speed",
|
|
184
|
+
"benchmark",
|
|
185
|
+
"profile",
|
|
186
|
+
],
|
|
187
|
+
TaskDomain.ARCHITECTURE: [
|
|
188
|
+
"architecture",
|
|
189
|
+
"design",
|
|
190
|
+
"structure",
|
|
191
|
+
"pattern",
|
|
192
|
+
"dependency",
|
|
193
|
+
],
|
|
194
|
+
TaskDomain.REFACTORING: [
|
|
195
|
+
"refactor",
|
|
196
|
+
"cleanup",
|
|
197
|
+
"simplify",
|
|
198
|
+
"restructure",
|
|
199
|
+
"debt",
|
|
200
|
+
],
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
# Capability mapping by domain
|
|
204
|
+
DOMAIN_CAPABILITIES = {
|
|
205
|
+
TaskDomain.TESTING: [
|
|
206
|
+
"analyze_gaps",
|
|
207
|
+
"suggest_tests",
|
|
208
|
+
"validate_coverage",
|
|
209
|
+
],
|
|
210
|
+
TaskDomain.SECURITY: [
|
|
211
|
+
"vulnerability_scan",
|
|
212
|
+
"threat_modeling",
|
|
213
|
+
"compliance_check",
|
|
214
|
+
],
|
|
215
|
+
TaskDomain.CODE_QUALITY: [
|
|
216
|
+
"code_review",
|
|
217
|
+
"quality_assessment",
|
|
218
|
+
"best_practices_check",
|
|
219
|
+
],
|
|
220
|
+
TaskDomain.DOCUMENTATION: [
|
|
221
|
+
"generate_docs",
|
|
222
|
+
"check_completeness",
|
|
223
|
+
"update_examples",
|
|
224
|
+
],
|
|
225
|
+
TaskDomain.PERFORMANCE: [
|
|
226
|
+
"profile_code",
|
|
227
|
+
"identify_bottlenecks",
|
|
228
|
+
"suggest_optimizations",
|
|
229
|
+
],
|
|
230
|
+
TaskDomain.ARCHITECTURE: [
|
|
231
|
+
"analyze_architecture",
|
|
232
|
+
"identify_patterns",
|
|
233
|
+
"suggest_improvements",
|
|
234
|
+
],
|
|
235
|
+
TaskDomain.REFACTORING: [
|
|
236
|
+
"identify_code_smells",
|
|
237
|
+
"suggest_refactorings",
|
|
238
|
+
"validate_changes",
|
|
239
|
+
],
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
def __init__(self):
|
|
243
|
+
"""Initialize meta-orchestrator."""
|
|
244
|
+
logger.info("MetaOrchestrator initialized")
|
|
245
|
+
|
|
246
|
+
def analyze_task(self, task: str, context: dict[str, Any] | None = None) -> TaskRequirements:
|
|
247
|
+
"""Analyze task to extract requirements (public wrapper for testing).
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
task: Task description (e.g., "Boost test coverage to 90%")
|
|
251
|
+
context: Optional context dictionary
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
TaskRequirements with extracted information
|
|
255
|
+
|
|
256
|
+
Raises:
|
|
257
|
+
ValueError: If task is invalid
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
>>> orchestrator = MetaOrchestrator()
|
|
261
|
+
>>> requirements = orchestrator.analyze_task(
|
|
262
|
+
... task="Improve test coverage",
|
|
263
|
+
... context={"current_coverage": 75}
|
|
264
|
+
... )
|
|
265
|
+
>>> print(requirements.domain)
|
|
266
|
+
TaskDomain.TESTING
|
|
267
|
+
"""
|
|
268
|
+
if not task or not isinstance(task, str):
|
|
269
|
+
raise ValueError("task must be a non-empty string")
|
|
270
|
+
|
|
271
|
+
context = context or {}
|
|
272
|
+
return self._analyze_task(task, context)
|
|
273
|
+
|
|
274
|
+
def create_execution_plan(
|
|
275
|
+
self,
|
|
276
|
+
requirements: TaskRequirements,
|
|
277
|
+
agents: list[AgentTemplate],
|
|
278
|
+
strategy: CompositionPattern,
|
|
279
|
+
) -> ExecutionPlan:
|
|
280
|
+
"""Create execution plan from components (extracted for testing).
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
requirements: Task requirements with quality gates
|
|
284
|
+
agents: Selected agents for execution
|
|
285
|
+
strategy: Composition pattern to use
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
ExecutionPlan with all components configured
|
|
289
|
+
|
|
290
|
+
Example:
|
|
291
|
+
>>> orchestrator = MetaOrchestrator()
|
|
292
|
+
>>> requirements = TaskRequirements(
|
|
293
|
+
... complexity=TaskComplexity.MODERATE,
|
|
294
|
+
... domain=TaskDomain.TESTING,
|
|
295
|
+
... capabilities_needed=["analyze_gaps"],
|
|
296
|
+
... quality_gates={"min_coverage": 80}
|
|
297
|
+
... )
|
|
298
|
+
>>> agents = [get_template("test_coverage_analyzer")]
|
|
299
|
+
>>> strategy = CompositionPattern.SEQUENTIAL
|
|
300
|
+
>>> plan = orchestrator.create_execution_plan(requirements, agents, strategy)
|
|
301
|
+
>>> print(plan.strategy)
|
|
302
|
+
CompositionPattern.SEQUENTIAL
|
|
303
|
+
"""
|
|
304
|
+
return ExecutionPlan(
|
|
305
|
+
agents=agents,
|
|
306
|
+
strategy=strategy,
|
|
307
|
+
quality_gates=requirements.quality_gates,
|
|
308
|
+
estimated_cost=self._estimate_cost(agents),
|
|
309
|
+
estimated_duration=self._estimate_duration(agents, strategy),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
def analyze_and_compose(
|
|
313
|
+
self, task: str, context: dict[str, Any] | None = None, interactive: bool = False
|
|
314
|
+
) -> ExecutionPlan:
|
|
315
|
+
"""Analyze task and create execution plan.
|
|
316
|
+
|
|
317
|
+
This is the main entry point for the meta-orchestrator.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
task: Task description (e.g., "Boost test coverage to 90%")
|
|
321
|
+
context: Optional context dictionary
|
|
322
|
+
interactive: If True, prompts user for low-confidence cases (default: False)
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
ExecutionPlan with agents and strategy
|
|
326
|
+
|
|
327
|
+
Raises:
|
|
328
|
+
ValueError: If task is invalid
|
|
329
|
+
|
|
330
|
+
Example:
|
|
331
|
+
>>> orchestrator = MetaOrchestrator()
|
|
332
|
+
>>> plan = orchestrator.analyze_and_compose(
|
|
333
|
+
... task="Improve test coverage",
|
|
334
|
+
... context={"current_coverage": 75}
|
|
335
|
+
... )
|
|
336
|
+
"""
|
|
337
|
+
if not task or not isinstance(task, str):
|
|
338
|
+
raise ValueError("task must be a non-empty string")
|
|
339
|
+
|
|
340
|
+
context = context or {}
|
|
341
|
+
|
|
342
|
+
# Use interactive mode if requested
|
|
343
|
+
if interactive:
|
|
344
|
+
return self.analyze_and_compose_interactive(task, context)
|
|
345
|
+
|
|
346
|
+
logger.info(f"Analyzing task: {task}")
|
|
347
|
+
|
|
348
|
+
# Step 1: Analyze task requirements
|
|
349
|
+
requirements = self._analyze_task(task, context)
|
|
350
|
+
logger.info(
|
|
351
|
+
f"Task analysis: complexity={requirements.complexity.value}, "
|
|
352
|
+
f"domain={requirements.domain.value}, "
|
|
353
|
+
f"capabilities={requirements.capabilities_needed}"
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Step 2: Select appropriate agents
|
|
357
|
+
agents = self._select_agents(requirements)
|
|
358
|
+
logger.info(f"Selected {len(agents)} agents: {[a.id for a in agents]}")
|
|
359
|
+
|
|
360
|
+
# Step 3: Choose composition pattern
|
|
361
|
+
strategy = self._choose_composition_pattern(requirements, agents)
|
|
362
|
+
logger.info(f"Selected strategy: {strategy.value}")
|
|
363
|
+
|
|
364
|
+
# Step 4: Create execution plan (using extracted public method)
|
|
365
|
+
plan = self.create_execution_plan(requirements, agents, strategy)
|
|
366
|
+
|
|
367
|
+
return plan
|
|
368
|
+
|
|
369
|
+
def analyze_and_compose_interactive(
|
|
370
|
+
self, task: str, context: dict[str, Any] | None = None
|
|
371
|
+
) -> ExecutionPlan:
|
|
372
|
+
"""Analyze task with user confirmation for ambiguous cases.
|
|
373
|
+
|
|
374
|
+
This method uses confidence scoring to determine when to ask the user
|
|
375
|
+
for input. High-confidence selections proceed automatically, while
|
|
376
|
+
low-confidence cases prompt the user to choose.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
task: Task description
|
|
380
|
+
context: Optional context dictionary
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
ExecutionPlan with agents and strategy
|
|
384
|
+
|
|
385
|
+
Raises:
|
|
386
|
+
ValueError: If task is invalid
|
|
387
|
+
ImportError: If AskUserQuestion tool is not available
|
|
388
|
+
|
|
389
|
+
Example:
|
|
390
|
+
>>> orchestrator = MetaOrchestrator()
|
|
391
|
+
>>> plan = orchestrator.analyze_and_compose_interactive(
|
|
392
|
+
... task="Complex architectural redesign",
|
|
393
|
+
... context={}
|
|
394
|
+
... )
|
|
395
|
+
# User may be prompted to choose approach if confidence is low
|
|
396
|
+
"""
|
|
397
|
+
if not task or not isinstance(task, str):
|
|
398
|
+
raise ValueError("task must be a non-empty string")
|
|
399
|
+
|
|
400
|
+
context = context or {}
|
|
401
|
+
logger.info(f"Analyzing task interactively: {task}")
|
|
402
|
+
|
|
403
|
+
# Step 1: Analyze task requirements
|
|
404
|
+
requirements = self._analyze_task(task, context)
|
|
405
|
+
logger.info(
|
|
406
|
+
f"Task analysis: complexity={requirements.complexity.value}, "
|
|
407
|
+
f"domain={requirements.domain.value}"
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
# Step 2: Select agents
|
|
411
|
+
agents = self._select_agents(requirements)
|
|
412
|
+
logger.info(f"Selected {len(agents)} agents: {[a.id for a in agents]}")
|
|
413
|
+
|
|
414
|
+
# Step 3: Choose pattern
|
|
415
|
+
recommended_pattern = self._choose_composition_pattern(requirements, agents)
|
|
416
|
+
logger.info(f"Recommended strategy: {recommended_pattern.value}")
|
|
417
|
+
|
|
418
|
+
# Step 4: Calculate confidence in recommendation
|
|
419
|
+
confidence = self._calculate_confidence(requirements, agents, recommended_pattern)
|
|
420
|
+
logger.info(f"Confidence score: {confidence:.2f}")
|
|
421
|
+
|
|
422
|
+
# Step 5: Branch based on confidence
|
|
423
|
+
if confidence >= 0.8:
|
|
424
|
+
# High confidence → automatic execution
|
|
425
|
+
logger.info("High confidence - proceeding automatically")
|
|
426
|
+
return self.create_execution_plan(requirements, agents, recommended_pattern)
|
|
427
|
+
|
|
428
|
+
else:
|
|
429
|
+
# Low confidence → ask user
|
|
430
|
+
logger.info("Low confidence - prompting user for choice")
|
|
431
|
+
return self._prompt_user_for_approach(
|
|
432
|
+
requirements, agents, recommended_pattern, confidence
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
def _calculate_confidence(
|
|
436
|
+
self,
|
|
437
|
+
requirements: TaskRequirements,
|
|
438
|
+
agents: list[AgentTemplate],
|
|
439
|
+
pattern: CompositionPattern,
|
|
440
|
+
) -> float:
|
|
441
|
+
"""Calculate confidence in automatic pattern selection.
|
|
442
|
+
|
|
443
|
+
Confidence scoring considers:
|
|
444
|
+
- Domain clarity (GENERAL domain reduces confidence)
|
|
445
|
+
- Agent count (many agents = complex coordination)
|
|
446
|
+
- Task complexity (complex tasks have multiple valid approaches)
|
|
447
|
+
- Pattern specificity (Anthropic patterns have clear heuristics)
|
|
448
|
+
|
|
449
|
+
Args:
|
|
450
|
+
requirements: Task requirements
|
|
451
|
+
agents: Selected agents
|
|
452
|
+
pattern: Recommended composition pattern
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Confidence score between 0.0 and 1.0
|
|
456
|
+
|
|
457
|
+
Example:
|
|
458
|
+
>>> confidence = orchestrator._calculate_confidence(
|
|
459
|
+
... requirements=TaskRequirements(
|
|
460
|
+
... complexity=TaskComplexity.SIMPLE,
|
|
461
|
+
... domain=TaskDomain.TESTING,
|
|
462
|
+
... capabilities_needed=["analyze_gaps"]
|
|
463
|
+
... ),
|
|
464
|
+
... agents=[test_agent],
|
|
465
|
+
... pattern=CompositionPattern.SEQUENTIAL
|
|
466
|
+
... )
|
|
467
|
+
>>> confidence >= 0.8 # High confidence for simple, clear task
|
|
468
|
+
True
|
|
469
|
+
"""
|
|
470
|
+
confidence = 1.0
|
|
471
|
+
|
|
472
|
+
# Reduce confidence for ambiguous cases
|
|
473
|
+
if requirements.domain == TaskDomain.GENERAL:
|
|
474
|
+
confidence *= 0.7 # Generic tasks are less clear
|
|
475
|
+
|
|
476
|
+
if len(agents) > 5:
|
|
477
|
+
confidence *= 0.8 # Many agents → complex coordination
|
|
478
|
+
|
|
479
|
+
if requirements.complexity == TaskComplexity.COMPLEX:
|
|
480
|
+
confidence *= 0.85 # Complex → multiple valid approaches
|
|
481
|
+
|
|
482
|
+
# Increase confidence for clear patterns
|
|
483
|
+
if pattern in [
|
|
484
|
+
CompositionPattern.TOOL_ENHANCED,
|
|
485
|
+
CompositionPattern.DELEGATION_CHAIN,
|
|
486
|
+
CompositionPattern.PROMPT_CACHED_SEQUENTIAL,
|
|
487
|
+
]:
|
|
488
|
+
confidence *= 1.1 # New Anthropic patterns have clear heuristics
|
|
489
|
+
|
|
490
|
+
# Specific domain patterns also get confidence boost
|
|
491
|
+
if pattern in [
|
|
492
|
+
CompositionPattern.TEACHING,
|
|
493
|
+
CompositionPattern.REFINEMENT,
|
|
494
|
+
] and requirements.domain in [TaskDomain.DOCUMENTATION, TaskDomain.REFACTORING]:
|
|
495
|
+
confidence *= 1.05 # Domain-specific pattern match
|
|
496
|
+
|
|
497
|
+
return min(confidence, 1.0)
|
|
498
|
+
|
|
499
|
+
def _prompt_user_for_approach(
|
|
500
|
+
self,
|
|
501
|
+
requirements: TaskRequirements,
|
|
502
|
+
agents: list[AgentTemplate],
|
|
503
|
+
recommended_pattern: CompositionPattern,
|
|
504
|
+
confidence: float,
|
|
505
|
+
) -> ExecutionPlan:
|
|
506
|
+
"""Prompt user to choose approach when confidence is low.
|
|
507
|
+
|
|
508
|
+
Presents three options:
|
|
509
|
+
1. Use recommended pattern (with confidence score)
|
|
510
|
+
2. Customize team composition
|
|
511
|
+
3. Show all patterns and choose
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
requirements: Task requirements
|
|
515
|
+
agents: Selected agents
|
|
516
|
+
recommended_pattern: Recommended pattern
|
|
517
|
+
confidence: Confidence score (0.0-1.0)
|
|
518
|
+
|
|
519
|
+
Returns:
|
|
520
|
+
ExecutionPlan based on user choice
|
|
521
|
+
|
|
522
|
+
Raises:
|
|
523
|
+
ImportError: If AskUserQuestion tool not available
|
|
524
|
+
"""
|
|
525
|
+
try:
|
|
526
|
+
# Import here to avoid circular dependency and allow graceful degradation
|
|
527
|
+
from attune.tools import AskUserQuestion
|
|
528
|
+
except ImportError as e:
|
|
529
|
+
logger.warning(f"AskUserQuestion not available: {e}")
|
|
530
|
+
logger.info("Falling back to automatic selection")
|
|
531
|
+
return self.create_execution_plan(requirements, agents, recommended_pattern)
|
|
532
|
+
|
|
533
|
+
# Format agent list for display
|
|
534
|
+
agent_summary = ", ".join([a.role for a in agents])
|
|
535
|
+
|
|
536
|
+
# Ask user for approach
|
|
537
|
+
response = AskUserQuestion(
|
|
538
|
+
questions=[
|
|
539
|
+
{
|
|
540
|
+
"header": "Approach",
|
|
541
|
+
"question": "How would you like to create the agent team?",
|
|
542
|
+
"multiSelect": False,
|
|
543
|
+
"options": [
|
|
544
|
+
{
|
|
545
|
+
"label": f"Use recommended: {recommended_pattern.value} (Recommended)",
|
|
546
|
+
"description": f"Auto-selected based on task analysis. "
|
|
547
|
+
f"{len(agents)} agents: {agent_summary}. "
|
|
548
|
+
f"Confidence: {confidence:.0%}",
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
"label": "Customize team composition",
|
|
552
|
+
"description": "Choose specific agents and pattern manually",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
"label": "Show all 10 patterns",
|
|
556
|
+
"description": "Learn about patterns and select one",
|
|
557
|
+
},
|
|
558
|
+
],
|
|
559
|
+
}
|
|
560
|
+
]
|
|
561
|
+
)
|
|
562
|
+
|
|
563
|
+
# Handle user response
|
|
564
|
+
user_choice = response.get("Approach", "")
|
|
565
|
+
|
|
566
|
+
if "Use recommended" in user_choice:
|
|
567
|
+
logger.info("User accepted recommended approach")
|
|
568
|
+
return self.create_execution_plan(requirements, agents, recommended_pattern)
|
|
569
|
+
|
|
570
|
+
elif "Customize" in user_choice:
|
|
571
|
+
logger.info("User chose to customize team")
|
|
572
|
+
return self._interactive_team_builder(requirements, agents, recommended_pattern)
|
|
573
|
+
|
|
574
|
+
else: # Show patterns
|
|
575
|
+
logger.info("User chose to explore patterns")
|
|
576
|
+
return self._pattern_chooser_wizard(requirements, agents)
|
|
577
|
+
|
|
578
|
+
def _interactive_team_builder(
|
|
579
|
+
self,
|
|
580
|
+
requirements: TaskRequirements,
|
|
581
|
+
suggested_agents: list[AgentTemplate],
|
|
582
|
+
suggested_pattern: CompositionPattern,
|
|
583
|
+
) -> ExecutionPlan:
|
|
584
|
+
"""Interactive team builder for manual customization.
|
|
585
|
+
|
|
586
|
+
Allows user to:
|
|
587
|
+
1. Review suggested agents and modify selection
|
|
588
|
+
2. Choose composition pattern
|
|
589
|
+
3. Configure quality gates
|
|
590
|
+
|
|
591
|
+
Args:
|
|
592
|
+
requirements: Task requirements
|
|
593
|
+
suggested_agents: Auto-selected agents
|
|
594
|
+
suggested_pattern: Auto-selected pattern
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
ExecutionPlan with user-customized configuration
|
|
598
|
+
"""
|
|
599
|
+
try:
|
|
600
|
+
from attune.tools import AskUserQuestion
|
|
601
|
+
except ImportError:
|
|
602
|
+
logger.warning("AskUserQuestion not available, using defaults")
|
|
603
|
+
return self.create_execution_plan(requirements, suggested_agents, suggested_pattern)
|
|
604
|
+
|
|
605
|
+
# Step 1: Agent selection
|
|
606
|
+
agent_response = AskUserQuestion(
|
|
607
|
+
questions=[
|
|
608
|
+
{
|
|
609
|
+
"header": "Agents",
|
|
610
|
+
"question": "Which agents should be included in the team?",
|
|
611
|
+
"multiSelect": True,
|
|
612
|
+
"options": [
|
|
613
|
+
{
|
|
614
|
+
"label": agent.role,
|
|
615
|
+
"description": f"{agent.id} - {', '.join(agent.capabilities[:3])}",
|
|
616
|
+
}
|
|
617
|
+
for agent in suggested_agents
|
|
618
|
+
],
|
|
619
|
+
}
|
|
620
|
+
]
|
|
621
|
+
)
|
|
622
|
+
|
|
623
|
+
# Filter agents based on user selection
|
|
624
|
+
selected_agent_roles = agent_response.get("Agents", [])
|
|
625
|
+
if not isinstance(selected_agent_roles, list):
|
|
626
|
+
selected_agent_roles = [selected_agent_roles]
|
|
627
|
+
|
|
628
|
+
selected_agents = [a for a in suggested_agents if a.role in selected_agent_roles]
|
|
629
|
+
if not selected_agents:
|
|
630
|
+
# User deselected all - use defaults
|
|
631
|
+
selected_agents = suggested_agents
|
|
632
|
+
|
|
633
|
+
# Step 2: Pattern selection
|
|
634
|
+
pattern_response = AskUserQuestion(
|
|
635
|
+
questions=[
|
|
636
|
+
{
|
|
637
|
+
"header": "Pattern",
|
|
638
|
+
"question": "Which composition pattern should be used?",
|
|
639
|
+
"multiSelect": False,
|
|
640
|
+
"options": [
|
|
641
|
+
{
|
|
642
|
+
"label": f"{suggested_pattern.value} (Recommended)",
|
|
643
|
+
"description": self._get_pattern_description(suggested_pattern),
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
"label": "sequential",
|
|
647
|
+
"description": "Execute agents one after another (A → B → C)",
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
"label": "parallel",
|
|
651
|
+
"description": "Execute agents simultaneously (A || B || C)",
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
"label": "tool_enhanced",
|
|
655
|
+
"description": "Single agent with comprehensive tool access",
|
|
656
|
+
},
|
|
657
|
+
],
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
# Parse pattern choice
|
|
663
|
+
pattern_choice = pattern_response.get("Pattern", suggested_pattern.value)
|
|
664
|
+
if "(Recommended)" in pattern_choice:
|
|
665
|
+
selected_pattern = suggested_pattern
|
|
666
|
+
else:
|
|
667
|
+
# Extract pattern name
|
|
668
|
+
pattern_name = pattern_choice.split()[0]
|
|
669
|
+
try:
|
|
670
|
+
selected_pattern = CompositionPattern(pattern_name)
|
|
671
|
+
except ValueError:
|
|
672
|
+
logger.warning(f"Invalid pattern: {pattern_name}, using suggested")
|
|
673
|
+
selected_pattern = suggested_pattern
|
|
674
|
+
|
|
675
|
+
# Create execution plan with user selections
|
|
676
|
+
return self.create_execution_plan(requirements, selected_agents, selected_pattern)
|
|
677
|
+
|
|
678
|
+
def _pattern_chooser_wizard(
|
|
679
|
+
self,
|
|
680
|
+
requirements: TaskRequirements,
|
|
681
|
+
suggested_agents: list[AgentTemplate],
|
|
682
|
+
) -> ExecutionPlan:
|
|
683
|
+
"""Interactive pattern chooser with educational previews.
|
|
684
|
+
|
|
685
|
+
Shows all 10 composition patterns with:
|
|
686
|
+
- Description and when to use
|
|
687
|
+
- Visual preview of agent flow
|
|
688
|
+
- Estimated cost and duration
|
|
689
|
+
- Examples of similar tasks
|
|
690
|
+
|
|
691
|
+
Args:
|
|
692
|
+
requirements: Task requirements
|
|
693
|
+
suggested_agents: Auto-selected agents
|
|
694
|
+
|
|
695
|
+
Returns:
|
|
696
|
+
ExecutionPlan with user-selected pattern
|
|
697
|
+
"""
|
|
698
|
+
try:
|
|
699
|
+
from attune.tools import AskUserQuestion
|
|
700
|
+
except ImportError:
|
|
701
|
+
logger.warning("AskUserQuestion not available, using defaults")
|
|
702
|
+
suggested_pattern = self._choose_composition_pattern(requirements, suggested_agents)
|
|
703
|
+
return self.create_execution_plan(
|
|
704
|
+
requirements, suggested_agents, suggested_pattern
|
|
705
|
+
)
|
|
706
|
+
|
|
707
|
+
# Present all patterns with descriptions
|
|
708
|
+
pattern_response = AskUserQuestion(
|
|
709
|
+
questions=[
|
|
710
|
+
{
|
|
711
|
+
"header": "Pattern",
|
|
712
|
+
"question": "Choose a composition pattern (with preview):",
|
|
713
|
+
"multiSelect": False,
|
|
714
|
+
"options": [
|
|
715
|
+
{
|
|
716
|
+
"label": "sequential",
|
|
717
|
+
"description": "A → B → C | Step-by-step pipeline | "
|
|
718
|
+
"Example: Parse → Analyze → Report",
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
"label": "parallel",
|
|
722
|
+
"description": "A || B || C | Independent tasks | "
|
|
723
|
+
"Example: Security + Quality + Performance audits",
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
"label": "debate",
|
|
727
|
+
"description": "A ⇄ B ⇄ C → Synthesis | Multiple perspectives | "
|
|
728
|
+
"Example: 3 reviewers discuss approach",
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
"label": "teaching",
|
|
732
|
+
"description": "Junior → Expert validation | Draft + review | "
|
|
733
|
+
"Example: Cheap model drafts, expert validates",
|
|
734
|
+
},
|
|
735
|
+
{
|
|
736
|
+
"label": "refinement",
|
|
737
|
+
"description": "Draft → Review → Polish | Iterative improvement | "
|
|
738
|
+
"Example: Code → Review → Refine",
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
"label": "adaptive",
|
|
742
|
+
"description": "Classifier → Specialist | Dynamic routing | "
|
|
743
|
+
"Example: Analyze task type → Route to expert",
|
|
744
|
+
},
|
|
745
|
+
{
|
|
746
|
+
"label": "tool_enhanced (NEW)",
|
|
747
|
+
"description": "Single agent + tools | Most efficient | "
|
|
748
|
+
"Example: File reader with analysis tools",
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
"label": "prompt_cached_sequential (NEW)",
|
|
752
|
+
"description": "Shared large context | Cost-optimized | "
|
|
753
|
+
"Example: 3 agents using same codebase docs",
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
"label": "delegation_chain (NEW)",
|
|
757
|
+
"description": "Coordinator → Specialists | Hierarchical | "
|
|
758
|
+
"Example: Task planner delegates to architects",
|
|
759
|
+
},
|
|
760
|
+
],
|
|
761
|
+
}
|
|
762
|
+
]
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
# Parse pattern choice
|
|
766
|
+
pattern_choice = pattern_response.get("Pattern", "sequential")
|
|
767
|
+
pattern_name = pattern_choice.split()[0] # Extract name before any annotations
|
|
768
|
+
|
|
769
|
+
try:
|
|
770
|
+
selected_pattern = CompositionPattern(pattern_name)
|
|
771
|
+
except ValueError:
|
|
772
|
+
logger.warning(f"Invalid pattern: {pattern_name}, using sequential")
|
|
773
|
+
selected_pattern = CompositionPattern.SEQUENTIAL
|
|
774
|
+
|
|
775
|
+
logger.info(f"User selected pattern: {selected_pattern.value}")
|
|
776
|
+
|
|
777
|
+
# Create execution plan with user-selected pattern
|
|
778
|
+
return self.create_execution_plan(requirements, suggested_agents, selected_pattern)
|
|
779
|
+
|
|
780
|
+
def _get_pattern_description(self, pattern: CompositionPattern) -> str:
|
|
781
|
+
"""Get human-readable description of a pattern.
|
|
782
|
+
|
|
783
|
+
Args:
|
|
784
|
+
pattern: Composition pattern
|
|
785
|
+
|
|
786
|
+
Returns:
|
|
787
|
+
Description string
|
|
788
|
+
"""
|
|
789
|
+
descriptions = {
|
|
790
|
+
CompositionPattern.SEQUENTIAL: "Execute agents one after another (A → B → C)",
|
|
791
|
+
CompositionPattern.PARALLEL: "Execute agents simultaneously (A || B || C)",
|
|
792
|
+
CompositionPattern.DEBATE: "Multiple agents discuss and synthesize (A ⇄ B → Result)",
|
|
793
|
+
CompositionPattern.TEACHING: "Junior agent with expert validation (Draft → Review)",
|
|
794
|
+
CompositionPattern.REFINEMENT: "Iterative improvement (Draft → Review → Polish)",
|
|
795
|
+
CompositionPattern.ADAPTIVE: "Dynamic routing based on classification",
|
|
796
|
+
CompositionPattern.CONDITIONAL: "If-then-else branching logic",
|
|
797
|
+
CompositionPattern.TOOL_ENHANCED: "Single agent with comprehensive tool access",
|
|
798
|
+
CompositionPattern.PROMPT_CACHED_SEQUENTIAL: "Sequential with shared cached context",
|
|
799
|
+
CompositionPattern.DELEGATION_CHAIN: "Hierarchical coordinator → specialists",
|
|
800
|
+
}
|
|
801
|
+
return descriptions.get(pattern, "Custom composition pattern")
|
|
802
|
+
|
|
803
|
+
def _analyze_task(self, task: str, context: dict[str, Any]) -> TaskRequirements:
|
|
804
|
+
"""Analyze task to extract requirements.
|
|
805
|
+
|
|
806
|
+
Args:
|
|
807
|
+
task: Task description
|
|
808
|
+
context: Context dictionary
|
|
809
|
+
|
|
810
|
+
Returns:
|
|
811
|
+
TaskRequirements with extracted information
|
|
812
|
+
"""
|
|
813
|
+
task_lower = task.lower()
|
|
814
|
+
|
|
815
|
+
# Determine complexity
|
|
816
|
+
complexity = self._classify_complexity(task_lower)
|
|
817
|
+
|
|
818
|
+
# Determine domain
|
|
819
|
+
domain = self._classify_domain(task_lower)
|
|
820
|
+
|
|
821
|
+
# Extract needed capabilities
|
|
822
|
+
capabilities = self._extract_capabilities(domain, context)
|
|
823
|
+
|
|
824
|
+
# Determine if parallelizable
|
|
825
|
+
parallelizable = self._is_parallelizable(task_lower, complexity)
|
|
826
|
+
|
|
827
|
+
# Extract quality gates from context
|
|
828
|
+
quality_gates = context.get("quality_gates", {})
|
|
829
|
+
|
|
830
|
+
return TaskRequirements(
|
|
831
|
+
complexity=complexity,
|
|
832
|
+
domain=domain,
|
|
833
|
+
capabilities_needed=capabilities,
|
|
834
|
+
parallelizable=parallelizable,
|
|
835
|
+
quality_gates=quality_gates,
|
|
836
|
+
context=context,
|
|
837
|
+
)
|
|
838
|
+
|
|
839
|
+
def _classify_complexity(self, task_lower: str) -> TaskComplexity:
|
|
840
|
+
"""Classify task complexity based on keywords.
|
|
841
|
+
|
|
842
|
+
Args:
|
|
843
|
+
task_lower: Lowercase task description
|
|
844
|
+
|
|
845
|
+
Returns:
|
|
846
|
+
TaskComplexity classification
|
|
847
|
+
"""
|
|
848
|
+
# Check for complex keywords first (most specific)
|
|
849
|
+
for keyword in self.COMPLEXITY_KEYWORDS[TaskComplexity.COMPLEX]:
|
|
850
|
+
if keyword in task_lower:
|
|
851
|
+
return TaskComplexity.COMPLEX
|
|
852
|
+
|
|
853
|
+
# Check for moderate keywords
|
|
854
|
+
for keyword in self.COMPLEXITY_KEYWORDS[TaskComplexity.MODERATE]:
|
|
855
|
+
if keyword in task_lower:
|
|
856
|
+
return TaskComplexity.MODERATE
|
|
857
|
+
|
|
858
|
+
# Check for simple keywords
|
|
859
|
+
for keyword in self.COMPLEXITY_KEYWORDS[TaskComplexity.SIMPLE]:
|
|
860
|
+
if keyword in task_lower:
|
|
861
|
+
return TaskComplexity.SIMPLE
|
|
862
|
+
|
|
863
|
+
# Default to moderate if no keywords match
|
|
864
|
+
return TaskComplexity.MODERATE
|
|
865
|
+
|
|
866
|
+
def _classify_domain(self, task_lower: str) -> TaskDomain:
|
|
867
|
+
"""Classify task domain based on keywords.
|
|
868
|
+
|
|
869
|
+
Args:
|
|
870
|
+
task_lower: Lowercase task description
|
|
871
|
+
|
|
872
|
+
Returns:
|
|
873
|
+
TaskDomain classification
|
|
874
|
+
"""
|
|
875
|
+
# Score each domain based on keyword matches
|
|
876
|
+
domain_scores: dict[TaskDomain, int] = dict.fromkeys(TaskDomain, 0)
|
|
877
|
+
|
|
878
|
+
for domain, keywords in self.DOMAIN_KEYWORDS.items():
|
|
879
|
+
for keyword in keywords:
|
|
880
|
+
if keyword in task_lower:
|
|
881
|
+
domain_scores[domain] += 1
|
|
882
|
+
|
|
883
|
+
# Return domain with highest score
|
|
884
|
+
max_score = max(domain_scores.values())
|
|
885
|
+
if max_score > 0:
|
|
886
|
+
for domain, score in domain_scores.items():
|
|
887
|
+
if score == max_score:
|
|
888
|
+
return domain
|
|
889
|
+
|
|
890
|
+
# Default to general if no keywords match
|
|
891
|
+
return TaskDomain.GENERAL
|
|
892
|
+
|
|
893
|
+
def _extract_capabilities(self, domain: TaskDomain, context: dict[str, Any]) -> list[str]:
|
|
894
|
+
"""Extract needed capabilities based on domain.
|
|
895
|
+
|
|
896
|
+
Args:
|
|
897
|
+
domain: Task domain
|
|
898
|
+
context: Context dictionary
|
|
899
|
+
|
|
900
|
+
Returns:
|
|
901
|
+
List of capability names
|
|
902
|
+
"""
|
|
903
|
+
# Get default capabilities for domain
|
|
904
|
+
capabilities = self.DOMAIN_CAPABILITIES.get(domain, []).copy()
|
|
905
|
+
|
|
906
|
+
# Add capabilities from context if provided
|
|
907
|
+
if "capabilities" in context:
|
|
908
|
+
additional = context["capabilities"]
|
|
909
|
+
if isinstance(additional, list):
|
|
910
|
+
capabilities.extend(additional)
|
|
911
|
+
|
|
912
|
+
return capabilities
|
|
913
|
+
|
|
914
|
+
def _is_parallelizable(self, task_lower: str, complexity: TaskComplexity) -> bool:
|
|
915
|
+
"""Determine if task can be parallelized.
|
|
916
|
+
|
|
917
|
+
Args:
|
|
918
|
+
task_lower: Lowercase task description
|
|
919
|
+
complexity: Task complexity
|
|
920
|
+
|
|
921
|
+
Returns:
|
|
922
|
+
True if task can be parallelized
|
|
923
|
+
"""
|
|
924
|
+
# Keywords indicating parallel execution
|
|
925
|
+
parallel_keywords = [
|
|
926
|
+
"release",
|
|
927
|
+
"audit",
|
|
928
|
+
"check",
|
|
929
|
+
"validate",
|
|
930
|
+
"review",
|
|
931
|
+
]
|
|
932
|
+
|
|
933
|
+
# Keywords indicating sequential execution
|
|
934
|
+
sequential_keywords = [
|
|
935
|
+
"migrate",
|
|
936
|
+
"refactor",
|
|
937
|
+
"generate",
|
|
938
|
+
"create",
|
|
939
|
+
]
|
|
940
|
+
|
|
941
|
+
# Check for sequential keywords first (higher precedence)
|
|
942
|
+
for keyword in sequential_keywords:
|
|
943
|
+
if keyword in task_lower:
|
|
944
|
+
return False
|
|
945
|
+
|
|
946
|
+
# Check for parallel keywords
|
|
947
|
+
for keyword in parallel_keywords:
|
|
948
|
+
if keyword in task_lower:
|
|
949
|
+
return True
|
|
950
|
+
|
|
951
|
+
# Complex tasks often benefit from parallel execution
|
|
952
|
+
return complexity == TaskComplexity.COMPLEX
|
|
953
|
+
|
|
954
|
+
def _select_agents(self, requirements: TaskRequirements) -> list[AgentTemplate]:
|
|
955
|
+
"""Select appropriate agents based on requirements.
|
|
956
|
+
|
|
957
|
+
Args:
|
|
958
|
+
requirements: Task requirements
|
|
959
|
+
|
|
960
|
+
Returns:
|
|
961
|
+
List of agent templates
|
|
962
|
+
|
|
963
|
+
Raises:
|
|
964
|
+
ValueError: If no agents match requirements
|
|
965
|
+
"""
|
|
966
|
+
agents: list[AgentTemplate] = []
|
|
967
|
+
|
|
968
|
+
# Select agents based on needed capabilities
|
|
969
|
+
for capability in requirements.capabilities_needed:
|
|
970
|
+
templates = get_templates_by_capability(capability)
|
|
971
|
+
if templates:
|
|
972
|
+
# Pick the first template with this capability
|
|
973
|
+
# In future: could rank by success rate, cost, etc.
|
|
974
|
+
agent = templates[0]
|
|
975
|
+
if agent not in agents:
|
|
976
|
+
agents.append(agent)
|
|
977
|
+
|
|
978
|
+
# If no agents found, use domain-appropriate default
|
|
979
|
+
if not agents:
|
|
980
|
+
agents = self._get_default_agents(requirements.domain)
|
|
981
|
+
|
|
982
|
+
if not agents:
|
|
983
|
+
raise ValueError(f"No agents available for domain: {requirements.domain.value}")
|
|
984
|
+
|
|
985
|
+
return agents
|
|
986
|
+
|
|
987
|
+
def _get_default_agents(self, domain: TaskDomain) -> list[AgentTemplate]:
|
|
988
|
+
"""Get default agents for a domain.
|
|
989
|
+
|
|
990
|
+
Args:
|
|
991
|
+
domain: Task domain
|
|
992
|
+
|
|
993
|
+
Returns:
|
|
994
|
+
List of default agent templates
|
|
995
|
+
"""
|
|
996
|
+
defaults = {
|
|
997
|
+
TaskDomain.TESTING: ["test_coverage_analyzer"],
|
|
998
|
+
TaskDomain.SECURITY: ["security_auditor"],
|
|
999
|
+
TaskDomain.CODE_QUALITY: ["code_reviewer"],
|
|
1000
|
+
TaskDomain.DOCUMENTATION: ["documentation_writer"],
|
|
1001
|
+
TaskDomain.PERFORMANCE: ["performance_optimizer"],
|
|
1002
|
+
TaskDomain.ARCHITECTURE: ["architecture_analyst"],
|
|
1003
|
+
TaskDomain.REFACTORING: ["refactoring_specialist"],
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
template_ids = defaults.get(domain, ["code_reviewer"])
|
|
1007
|
+
agents = []
|
|
1008
|
+
for template_id in template_ids:
|
|
1009
|
+
template = get_template(template_id)
|
|
1010
|
+
if template:
|
|
1011
|
+
agents.append(template)
|
|
1012
|
+
|
|
1013
|
+
return agents
|
|
1014
|
+
|
|
1015
|
+
def _choose_composition_pattern(
|
|
1016
|
+
self, requirements: TaskRequirements, agents: list[AgentTemplate]
|
|
1017
|
+
) -> CompositionPattern:
|
|
1018
|
+
"""Choose optimal composition pattern.
|
|
1019
|
+
|
|
1020
|
+
Args:
|
|
1021
|
+
requirements: Task requirements
|
|
1022
|
+
agents: Selected agents
|
|
1023
|
+
|
|
1024
|
+
Returns:
|
|
1025
|
+
CompositionPattern to use
|
|
1026
|
+
"""
|
|
1027
|
+
num_agents = len(agents)
|
|
1028
|
+
context = requirements.context
|
|
1029
|
+
|
|
1030
|
+
# Anthropic Pattern 8: Tool-Enhanced (single agent + tools preferred)
|
|
1031
|
+
if num_agents == 1 and context.get("tools"):
|
|
1032
|
+
return CompositionPattern.TOOL_ENHANCED
|
|
1033
|
+
|
|
1034
|
+
# Anthropic Pattern 10: Delegation Chain (hierarchical coordination)
|
|
1035
|
+
# Use when: Complex task + coordinator pattern + 2+ specialists
|
|
1036
|
+
has_coordinator = any("coordinator" in agent.role.lower() for agent in agents)
|
|
1037
|
+
if (
|
|
1038
|
+
requirements.complexity == TaskComplexity.COMPLEX
|
|
1039
|
+
and has_coordinator
|
|
1040
|
+
and num_agents >= 2
|
|
1041
|
+
):
|
|
1042
|
+
return CompositionPattern.DELEGATION_CHAIN
|
|
1043
|
+
|
|
1044
|
+
# Anthropic Pattern 9: Prompt-Cached Sequential (large shared context)
|
|
1045
|
+
# Use when: 3+ agents need same large context (>2000 tokens)
|
|
1046
|
+
large_context = context.get("cached_context") or context.get("shared_knowledge")
|
|
1047
|
+
if num_agents >= 3 and large_context and len(str(large_context)) > 2000:
|
|
1048
|
+
return CompositionPattern.PROMPT_CACHED_SEQUENTIAL
|
|
1049
|
+
|
|
1050
|
+
# Parallelizable tasks: use parallel strategy (check before single agent)
|
|
1051
|
+
if requirements.parallelizable:
|
|
1052
|
+
return CompositionPattern.PARALLEL
|
|
1053
|
+
|
|
1054
|
+
# Security/architecture: benefit from multiple perspectives (even with 1 agent)
|
|
1055
|
+
if requirements.domain in [TaskDomain.SECURITY, TaskDomain.ARCHITECTURE]:
|
|
1056
|
+
return CompositionPattern.PARALLEL
|
|
1057
|
+
|
|
1058
|
+
# Documentation: teaching pattern (cheap → validate → expert if needed)
|
|
1059
|
+
if requirements.domain == TaskDomain.DOCUMENTATION:
|
|
1060
|
+
return CompositionPattern.TEACHING
|
|
1061
|
+
|
|
1062
|
+
# Refactoring: refinement pattern (identify → refactor → validate)
|
|
1063
|
+
if requirements.domain == TaskDomain.REFACTORING:
|
|
1064
|
+
return CompositionPattern.REFINEMENT
|
|
1065
|
+
|
|
1066
|
+
# Single agent: sequential (after domain-specific patterns)
|
|
1067
|
+
if num_agents == 1:
|
|
1068
|
+
return CompositionPattern.SEQUENTIAL
|
|
1069
|
+
|
|
1070
|
+
# Multiple agents with same capability: debate/consensus
|
|
1071
|
+
capabilities = [cap for agent in agents for cap in agent.capabilities]
|
|
1072
|
+
if len(capabilities) != len(set(capabilities)):
|
|
1073
|
+
# Duplicate capabilities detected → debate
|
|
1074
|
+
return CompositionPattern.DEBATE
|
|
1075
|
+
|
|
1076
|
+
# Testing domain: typically sequential (analyze → generate → validate)
|
|
1077
|
+
if requirements.domain == TaskDomain.TESTING:
|
|
1078
|
+
return CompositionPattern.SEQUENTIAL
|
|
1079
|
+
|
|
1080
|
+
# Complex tasks: adaptive routing
|
|
1081
|
+
if requirements.complexity == TaskComplexity.COMPLEX:
|
|
1082
|
+
return CompositionPattern.ADAPTIVE
|
|
1083
|
+
|
|
1084
|
+
# Default: sequential
|
|
1085
|
+
return CompositionPattern.SEQUENTIAL
|
|
1086
|
+
|
|
1087
|
+
def _estimate_cost(self, agents: list[AgentTemplate]) -> float:
|
|
1088
|
+
"""Estimate execution cost based on agent tiers.
|
|
1089
|
+
|
|
1090
|
+
Args:
|
|
1091
|
+
agents: List of agents
|
|
1092
|
+
|
|
1093
|
+
Returns:
|
|
1094
|
+
Estimated cost in arbitrary units
|
|
1095
|
+
"""
|
|
1096
|
+
tier_costs = {
|
|
1097
|
+
"CHEAP": 1.0,
|
|
1098
|
+
"CAPABLE": 3.0,
|
|
1099
|
+
"PREMIUM": 10.0,
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
total_cost = 0.0
|
|
1103
|
+
for agent in agents:
|
|
1104
|
+
total_cost += tier_costs.get(agent.tier_preference, 3.0)
|
|
1105
|
+
|
|
1106
|
+
return total_cost
|
|
1107
|
+
|
|
1108
|
+
def _estimate_duration(self, agents: list[AgentTemplate], strategy: CompositionPattern) -> int:
|
|
1109
|
+
"""Estimate execution duration in seconds.
|
|
1110
|
+
|
|
1111
|
+
Args:
|
|
1112
|
+
agents: List of agents
|
|
1113
|
+
strategy: Composition pattern
|
|
1114
|
+
|
|
1115
|
+
Returns:
|
|
1116
|
+
Estimated duration in seconds
|
|
1117
|
+
"""
|
|
1118
|
+
# Get max timeout from agents
|
|
1119
|
+
max_timeout = max(
|
|
1120
|
+
(agent.resource_requirements.timeout_seconds for agent in agents),
|
|
1121
|
+
default=300,
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
# Sequential: sum of timeouts
|
|
1125
|
+
if strategy == CompositionPattern.SEQUENTIAL:
|
|
1126
|
+
return sum(agent.resource_requirements.timeout_seconds for agent in agents)
|
|
1127
|
+
|
|
1128
|
+
# Parallel: max timeout
|
|
1129
|
+
if strategy == CompositionPattern.PARALLEL:
|
|
1130
|
+
return max_timeout
|
|
1131
|
+
|
|
1132
|
+
# Debate: multiple rounds, estimate 2x max timeout
|
|
1133
|
+
if strategy == CompositionPattern.DEBATE:
|
|
1134
|
+
return max_timeout * 2
|
|
1135
|
+
|
|
1136
|
+
# Teaching: initial attempt + possible expert review
|
|
1137
|
+
if strategy == CompositionPattern.TEACHING:
|
|
1138
|
+
return int(max_timeout * 1.5)
|
|
1139
|
+
|
|
1140
|
+
# Refinement: 3 passes (draft → review → polish)
|
|
1141
|
+
if strategy == CompositionPattern.REFINEMENT:
|
|
1142
|
+
return max_timeout * 3
|
|
1143
|
+
|
|
1144
|
+
# Adaptive: classification + specialist
|
|
1145
|
+
if strategy == CompositionPattern.ADAPTIVE:
|
|
1146
|
+
return int(max_timeout * 1.2)
|
|
1147
|
+
|
|
1148
|
+
# Anthropic Pattern 8: Tool-Enhanced (single agent with tools, efficient)
|
|
1149
|
+
if strategy == CompositionPattern.TOOL_ENHANCED:
|
|
1150
|
+
return max_timeout # Similar to sequential for single agent
|
|
1151
|
+
|
|
1152
|
+
# Anthropic Pattern 9: Prompt-Cached Sequential (faster with cache hits)
|
|
1153
|
+
if strategy == CompositionPattern.PROMPT_CACHED_SEQUENTIAL:
|
|
1154
|
+
# Sequential but 20% faster due to cached context reducing token processing
|
|
1155
|
+
total = sum(agent.resource_requirements.timeout_seconds for agent in agents)
|
|
1156
|
+
return int(total * 0.8)
|
|
1157
|
+
|
|
1158
|
+
# Anthropic Pattern 10: Delegation Chain (coordinator + specialists in sequence)
|
|
1159
|
+
if strategy == CompositionPattern.DELEGATION_CHAIN:
|
|
1160
|
+
# Coordinator analyzes, then specialists execute (sequential-like)
|
|
1161
|
+
return sum(agent.resource_requirements.timeout_seconds for agent in agents)
|
|
1162
|
+
|
|
1163
|
+
# Conditional: branch evaluation + selected path
|
|
1164
|
+
if strategy == CompositionPattern.CONDITIONAL:
|
|
1165
|
+
return int(max_timeout * 1.1)
|
|
1166
|
+
|
|
1167
|
+
# Default: max timeout
|
|
1168
|
+
return max_timeout
|