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,271 @@
|
|
|
1
|
+
"""Markdown Agent Parser
|
|
2
|
+
|
|
3
|
+
Parses Markdown files with YAML frontmatter into UnifiedAgentConfig.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from attune_llm.config.unified import ModelTier, Provider, UnifiedAgentConfig
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# YAML frontmatter regex pattern
|
|
19
|
+
FRONTMATTER_PATTERN = re.compile(
|
|
20
|
+
r"^---\s*\n(.*?)\n---\s*\n",
|
|
21
|
+
re.DOTALL,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MarkdownAgentParser:
|
|
26
|
+
"""Parser for Markdown agent definition files.
|
|
27
|
+
|
|
28
|
+
Parses files with YAML frontmatter containing agent configuration,
|
|
29
|
+
followed by Markdown content that becomes the system prompt.
|
|
30
|
+
|
|
31
|
+
Example file format:
|
|
32
|
+
---
|
|
33
|
+
name: architect
|
|
34
|
+
description: Software architecture specialist
|
|
35
|
+
model: capable
|
|
36
|
+
tools: Read, Grep, Glob
|
|
37
|
+
empathy_level: 4
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
You are an expert software architect...
|
|
41
|
+
|
|
42
|
+
Example usage:
|
|
43
|
+
parser = MarkdownAgentParser()
|
|
44
|
+
config = parser.parse_file("agents/architect.md")
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
# Mapping from string model names to ModelTier
|
|
48
|
+
MODEL_TIER_MAP = {
|
|
49
|
+
"cheap": ModelTier.CHEAP,
|
|
50
|
+
"haiku": ModelTier.CHEAP,
|
|
51
|
+
"capable": ModelTier.CAPABLE,
|
|
52
|
+
"sonnet": ModelTier.CAPABLE,
|
|
53
|
+
"premium": ModelTier.PREMIUM,
|
|
54
|
+
"opus": ModelTier.PREMIUM,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Mapping from string provider names to Provider
|
|
58
|
+
PROVIDER_MAP = {
|
|
59
|
+
"anthropic": Provider.ANTHROPIC,
|
|
60
|
+
"openai": Provider.OPENAI,
|
|
61
|
+
"local": Provider.LOCAL,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def __init__(self):
|
|
65
|
+
"""Initialize the parser."""
|
|
66
|
+
pass
|
|
67
|
+
|
|
68
|
+
def parse_file(self, file_path: str | Path) -> UnifiedAgentConfig:
|
|
69
|
+
"""Parse a Markdown agent file into UnifiedAgentConfig.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
file_path: Path to the Markdown agent file
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
UnifiedAgentConfig instance
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
FileNotFoundError: If file doesn't exist
|
|
79
|
+
ValueError: If file format is invalid
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
file_path = Path(file_path)
|
|
83
|
+
|
|
84
|
+
if not file_path.exists():
|
|
85
|
+
raise FileNotFoundError(f"Agent file not found: {file_path}")
|
|
86
|
+
|
|
87
|
+
with open(file_path, encoding="utf-8") as f:
|
|
88
|
+
content = f.read()
|
|
89
|
+
|
|
90
|
+
return self.parse_content(content, source=str(file_path))
|
|
91
|
+
|
|
92
|
+
def parse_content(
|
|
93
|
+
self,
|
|
94
|
+
content: str,
|
|
95
|
+
source: str = "unknown",
|
|
96
|
+
) -> UnifiedAgentConfig:
|
|
97
|
+
"""Parse Markdown content into UnifiedAgentConfig.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
content: Markdown content with YAML frontmatter
|
|
101
|
+
source: Source identifier for error messages
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
UnifiedAgentConfig instance
|
|
105
|
+
|
|
106
|
+
Raises:
|
|
107
|
+
ValueError: If content format is invalid
|
|
108
|
+
|
|
109
|
+
"""
|
|
110
|
+
# Extract frontmatter
|
|
111
|
+
match = FRONTMATTER_PATTERN.match(content)
|
|
112
|
+
|
|
113
|
+
if not match:
|
|
114
|
+
raise ValueError(f"Invalid agent file format - missing YAML frontmatter: {source}")
|
|
115
|
+
|
|
116
|
+
frontmatter_yaml = match.group(1)
|
|
117
|
+
body = content[match.end() :].strip()
|
|
118
|
+
|
|
119
|
+
# Parse YAML
|
|
120
|
+
try:
|
|
121
|
+
import yaml
|
|
122
|
+
|
|
123
|
+
frontmatter = yaml.safe_load(frontmatter_yaml) or {}
|
|
124
|
+
except yaml.YAMLError as e:
|
|
125
|
+
raise ValueError(f"Invalid YAML frontmatter in {source}: {e}")
|
|
126
|
+
|
|
127
|
+
return self._create_config(frontmatter, body, source)
|
|
128
|
+
|
|
129
|
+
def _create_config(
|
|
130
|
+
self,
|
|
131
|
+
frontmatter: dict[str, Any],
|
|
132
|
+
body: str,
|
|
133
|
+
source: str,
|
|
134
|
+
) -> UnifiedAgentConfig:
|
|
135
|
+
"""Create UnifiedAgentConfig from parsed data.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
frontmatter: Parsed YAML frontmatter
|
|
139
|
+
body: Markdown body content
|
|
140
|
+
source: Source identifier
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
UnifiedAgentConfig instance
|
|
144
|
+
|
|
145
|
+
"""
|
|
146
|
+
# Required field
|
|
147
|
+
name = frontmatter.get("name")
|
|
148
|
+
if not name:
|
|
149
|
+
raise ValueError(f"Agent file missing required 'name' field: {source}")
|
|
150
|
+
|
|
151
|
+
# Parse model tier
|
|
152
|
+
model_str = frontmatter.get("model", "capable").lower()
|
|
153
|
+
model_tier = self.MODEL_TIER_MAP.get(model_str, ModelTier.CAPABLE)
|
|
154
|
+
|
|
155
|
+
# Parse provider
|
|
156
|
+
provider_str = frontmatter.get("provider", "anthropic").lower()
|
|
157
|
+
provider = self.PROVIDER_MAP.get(provider_str, Provider.ANTHROPIC)
|
|
158
|
+
|
|
159
|
+
# Parse tools list
|
|
160
|
+
tools_raw = frontmatter.get("tools", [])
|
|
161
|
+
if isinstance(tools_raw, str):
|
|
162
|
+
# Handle comma-separated string
|
|
163
|
+
tools = [t.strip() for t in tools_raw.split(",")]
|
|
164
|
+
else:
|
|
165
|
+
tools = list(tools_raw)
|
|
166
|
+
|
|
167
|
+
# Parse capabilities
|
|
168
|
+
capabilities = frontmatter.get("capabilities", [])
|
|
169
|
+
if isinstance(capabilities, str):
|
|
170
|
+
capabilities = [c.strip() for c in capabilities.split(",")]
|
|
171
|
+
|
|
172
|
+
# Build config
|
|
173
|
+
config = UnifiedAgentConfig(
|
|
174
|
+
name=name,
|
|
175
|
+
role=frontmatter.get("role", name),
|
|
176
|
+
description=frontmatter.get("description", ""),
|
|
177
|
+
model_tier=model_tier,
|
|
178
|
+
model_override=frontmatter.get("model_override"),
|
|
179
|
+
provider=provider,
|
|
180
|
+
empathy_level=int(frontmatter.get("empathy_level", 4)),
|
|
181
|
+
memory_enabled=frontmatter.get("memory_enabled", True),
|
|
182
|
+
pattern_learning=frontmatter.get("pattern_learning", True),
|
|
183
|
+
cost_tracking=frontmatter.get("cost_tracking", True),
|
|
184
|
+
use_patterns=frontmatter.get("use_patterns", True),
|
|
185
|
+
temperature=float(frontmatter.get("temperature", 0.7)),
|
|
186
|
+
max_tokens=int(frontmatter.get("max_tokens", 4096)),
|
|
187
|
+
timeout=int(frontmatter.get("timeout", 120)),
|
|
188
|
+
retry_attempts=int(frontmatter.get("retry_attempts", 3)),
|
|
189
|
+
retry_delay=float(frontmatter.get("retry_delay", 1.0)),
|
|
190
|
+
system_prompt=body if body else None,
|
|
191
|
+
tools=tools,
|
|
192
|
+
capabilities=capabilities,
|
|
193
|
+
framework_options=frontmatter.get("framework_options", {}),
|
|
194
|
+
extra={
|
|
195
|
+
"source_file": source,
|
|
196
|
+
"raw_frontmatter": frontmatter,
|
|
197
|
+
"interaction_mode": frontmatter.get("interaction_mode", "standard"),
|
|
198
|
+
"socratic_config": frontmatter.get("socratic_config", {}),
|
|
199
|
+
},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
logger.debug("Parsed agent config: %s from %s", name, source)
|
|
203
|
+
return config
|
|
204
|
+
|
|
205
|
+
def validate_file(self, file_path: str | Path) -> list[str]:
|
|
206
|
+
"""Validate a Markdown agent file without fully parsing.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
file_path: Path to validate
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
List of validation error messages (empty if valid)
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
errors = []
|
|
216
|
+
file_path = Path(file_path)
|
|
217
|
+
|
|
218
|
+
if not file_path.exists():
|
|
219
|
+
return [f"File not found: {file_path}"]
|
|
220
|
+
|
|
221
|
+
try:
|
|
222
|
+
with open(file_path, encoding="utf-8") as f:
|
|
223
|
+
content = f.read()
|
|
224
|
+
except OSError as e:
|
|
225
|
+
return [f"Cannot read file: {e}"]
|
|
226
|
+
|
|
227
|
+
# Check frontmatter
|
|
228
|
+
match = FRONTMATTER_PATTERN.match(content)
|
|
229
|
+
if not match:
|
|
230
|
+
errors.append("Missing YAML frontmatter (must start with ---)")
|
|
231
|
+
return errors
|
|
232
|
+
|
|
233
|
+
# Parse YAML
|
|
234
|
+
try:
|
|
235
|
+
import yaml
|
|
236
|
+
|
|
237
|
+
frontmatter = yaml.safe_load(match.group(1)) or {}
|
|
238
|
+
except yaml.YAMLError as e:
|
|
239
|
+
errors.append(f"Invalid YAML: {e}")
|
|
240
|
+
return errors
|
|
241
|
+
|
|
242
|
+
# Check required fields
|
|
243
|
+
if not frontmatter.get("name"):
|
|
244
|
+
errors.append("Missing required field: name")
|
|
245
|
+
|
|
246
|
+
# Validate model tier
|
|
247
|
+
model = frontmatter.get("model", "").lower()
|
|
248
|
+
if model and model not in self.MODEL_TIER_MAP:
|
|
249
|
+
errors.append(
|
|
250
|
+
f"Invalid model '{model}'. Valid options: {', '.join(self.MODEL_TIER_MAP.keys())}"
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
# Validate provider
|
|
254
|
+
provider = frontmatter.get("provider", "").lower()
|
|
255
|
+
if provider and provider not in self.PROVIDER_MAP:
|
|
256
|
+
errors.append(
|
|
257
|
+
f"Invalid provider '{provider}'. "
|
|
258
|
+
f"Valid options: {', '.join(self.PROVIDER_MAP.keys())}"
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
# Validate empathy level
|
|
262
|
+
empathy_level = frontmatter.get("empathy_level")
|
|
263
|
+
if empathy_level is not None:
|
|
264
|
+
try:
|
|
265
|
+
level = int(empathy_level)
|
|
266
|
+
if not 1 <= level <= 5:
|
|
267
|
+
errors.append(f"empathy_level must be 1-5, got {level}")
|
|
268
|
+
except (TypeError, ValueError):
|
|
269
|
+
errors.append(f"empathy_level must be an integer, got {empathy_level}")
|
|
270
|
+
|
|
271
|
+
return errors
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"""Agent Registry
|
|
2
|
+
|
|
3
|
+
Central registry for managing available agents.
|
|
4
|
+
|
|
5
|
+
Copyright 2025 Smart-AI-Memory
|
|
6
|
+
Licensed under Fair Source License 0.9
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import logging
|
|
10
|
+
from collections.abc import Iterator
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from attune_llm.agents_md.loader import AgentLoader
|
|
15
|
+
from attune_llm.config.unified import UnifiedAgentConfig
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class AgentRegistry:
|
|
21
|
+
"""Central registry for agent configurations.
|
|
22
|
+
|
|
23
|
+
Provides a single point of access for all available agents,
|
|
24
|
+
supporting both programmatic registration and directory loading.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
# Create registry and load agents
|
|
28
|
+
registry = AgentRegistry()
|
|
29
|
+
registry.load_from_directory("agents/")
|
|
30
|
+
|
|
31
|
+
# Get an agent by name
|
|
32
|
+
architect = registry.get("architect")
|
|
33
|
+
|
|
34
|
+
# Register a custom agent
|
|
35
|
+
registry.register(my_custom_config)
|
|
36
|
+
|
|
37
|
+
# List all available agents
|
|
38
|
+
for name in registry.list_agents():
|
|
39
|
+
print(name)
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
_instance: "AgentRegistry | None" = None
|
|
43
|
+
|
|
44
|
+
def __init__(self):
|
|
45
|
+
"""Initialize the registry."""
|
|
46
|
+
self._agents: dict[str, UnifiedAgentConfig] = {}
|
|
47
|
+
self._loader = AgentLoader()
|
|
48
|
+
self._load_paths: list[Path] = []
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def get_instance(cls) -> "AgentRegistry":
|
|
52
|
+
"""Get the singleton registry instance.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
The global AgentRegistry instance
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
if cls._instance is None:
|
|
59
|
+
cls._instance = cls()
|
|
60
|
+
return cls._instance
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def reset_instance(cls) -> None:
|
|
64
|
+
"""Reset the singleton instance (mainly for testing)."""
|
|
65
|
+
cls._instance = None
|
|
66
|
+
|
|
67
|
+
def register(
|
|
68
|
+
self,
|
|
69
|
+
config: UnifiedAgentConfig,
|
|
70
|
+
overwrite: bool = False,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Register an agent configuration.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
config: Agent configuration to register
|
|
76
|
+
overwrite: If True, overwrite existing agent with same name
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ValueError: If agent name already exists and overwrite=False
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
if config.name in self._agents and not overwrite:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Agent '{config.name}' already registered. Use overwrite=True to replace."
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
self._agents[config.name] = config
|
|
88
|
+
logger.debug("Registered agent: %s", config.name)
|
|
89
|
+
|
|
90
|
+
def unregister(self, name: str) -> bool:
|
|
91
|
+
"""Unregister an agent by name.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
name: Agent name to remove
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
True if agent was found and removed
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
if name in self._agents:
|
|
101
|
+
del self._agents[name]
|
|
102
|
+
logger.debug("Unregistered agent: %s", name)
|
|
103
|
+
return True
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
def get(self, name: str) -> UnifiedAgentConfig | None:
|
|
107
|
+
"""Get an agent configuration by name.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
name: Agent name
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Agent config or None if not found
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
return self._agents.get(name)
|
|
117
|
+
|
|
118
|
+
def get_required(self, name: str) -> UnifiedAgentConfig:
|
|
119
|
+
"""Get an agent configuration, raising if not found.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
name: Agent name
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Agent configuration
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
KeyError: If agent not found
|
|
129
|
+
|
|
130
|
+
"""
|
|
131
|
+
config = self.get(name)
|
|
132
|
+
if config is None:
|
|
133
|
+
available = ", ".join(sorted(self._agents.keys()))
|
|
134
|
+
raise KeyError(f"Agent '{name}' not found. Available agents: {available}")
|
|
135
|
+
return config
|
|
136
|
+
|
|
137
|
+
def has(self, name: str) -> bool:
|
|
138
|
+
"""Check if an agent is registered.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
name: Agent name
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
True if agent exists
|
|
145
|
+
|
|
146
|
+
"""
|
|
147
|
+
return name in self._agents
|
|
148
|
+
|
|
149
|
+
def list_agents(self) -> list[str]:
|
|
150
|
+
"""Get list of all registered agent names.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Sorted list of agent names
|
|
154
|
+
|
|
155
|
+
"""
|
|
156
|
+
return sorted(self._agents.keys())
|
|
157
|
+
|
|
158
|
+
def iter_agents(self) -> Iterator[UnifiedAgentConfig]:
|
|
159
|
+
"""Iterate over all registered agents.
|
|
160
|
+
|
|
161
|
+
Yields:
|
|
162
|
+
Agent configurations
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
for name in sorted(self._agents.keys()):
|
|
166
|
+
yield self._agents[name]
|
|
167
|
+
|
|
168
|
+
def load_from_directory(
|
|
169
|
+
self,
|
|
170
|
+
directory: str | Path,
|
|
171
|
+
recursive: bool = False,
|
|
172
|
+
overwrite: bool = False,
|
|
173
|
+
) -> int:
|
|
174
|
+
"""Load agents from a directory.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
directory: Directory containing .md agent files
|
|
178
|
+
recursive: If True, scan subdirectories
|
|
179
|
+
overwrite: If True, overwrite existing agents
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Number of agents loaded
|
|
183
|
+
|
|
184
|
+
"""
|
|
185
|
+
directory = Path(directory)
|
|
186
|
+
self._load_paths.append(directory)
|
|
187
|
+
|
|
188
|
+
agents = self._loader.load_directory(directory, recursive=recursive)
|
|
189
|
+
|
|
190
|
+
loaded = 0
|
|
191
|
+
for _name, config in agents.items():
|
|
192
|
+
try:
|
|
193
|
+
self.register(config, overwrite=overwrite)
|
|
194
|
+
loaded += 1
|
|
195
|
+
except ValueError as e:
|
|
196
|
+
logger.warning("Skipping agent: %s", e)
|
|
197
|
+
|
|
198
|
+
logger.info("Loaded %d agent(s) from %s", loaded, directory)
|
|
199
|
+
return loaded
|
|
200
|
+
|
|
201
|
+
def load_from_file(
|
|
202
|
+
self,
|
|
203
|
+
file_path: str | Path,
|
|
204
|
+
overwrite: bool = False,
|
|
205
|
+
) -> UnifiedAgentConfig:
|
|
206
|
+
"""Load a single agent from a file.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
file_path: Path to agent markdown file
|
|
210
|
+
overwrite: If True, overwrite existing agent
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Loaded agent configuration
|
|
214
|
+
|
|
215
|
+
"""
|
|
216
|
+
config = self._loader.load(file_path)
|
|
217
|
+
self.register(config, overwrite=overwrite)
|
|
218
|
+
return config
|
|
219
|
+
|
|
220
|
+
def reload(self) -> int:
|
|
221
|
+
"""Reload all agents from previously loaded directories.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Total number of agents after reload
|
|
225
|
+
|
|
226
|
+
"""
|
|
227
|
+
self._agents.clear()
|
|
228
|
+
|
|
229
|
+
for directory in self._load_paths:
|
|
230
|
+
if directory.exists():
|
|
231
|
+
self.load_from_directory(directory, overwrite=True)
|
|
232
|
+
|
|
233
|
+
return len(self._agents)
|
|
234
|
+
|
|
235
|
+
def get_by_role(self, role: str) -> list[UnifiedAgentConfig]:
|
|
236
|
+
"""Get all agents with a specific role.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
role: Role to filter by
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
List of matching agent configs
|
|
243
|
+
|
|
244
|
+
"""
|
|
245
|
+
role = role.lower()
|
|
246
|
+
return [config for config in self._agents.values() if config.role.lower() == role]
|
|
247
|
+
|
|
248
|
+
def get_by_empathy_level(
|
|
249
|
+
self,
|
|
250
|
+
min_level: int = 1,
|
|
251
|
+
max_level: int = 5,
|
|
252
|
+
) -> list[UnifiedAgentConfig]:
|
|
253
|
+
"""Get agents within an empathy level range.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
min_level: Minimum empathy level (inclusive)
|
|
257
|
+
max_level: Maximum empathy level (inclusive)
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
List of matching agent configs
|
|
261
|
+
|
|
262
|
+
"""
|
|
263
|
+
return [
|
|
264
|
+
config
|
|
265
|
+
for config in self._agents.values()
|
|
266
|
+
if min_level <= config.empathy_level <= max_level
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
def get_summary(self) -> dict[str, Any]:
|
|
270
|
+
"""Get a summary of registered agents.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
Summary dictionary
|
|
274
|
+
|
|
275
|
+
"""
|
|
276
|
+
by_role = {}
|
|
277
|
+
by_level = {}
|
|
278
|
+
by_tier = {}
|
|
279
|
+
|
|
280
|
+
for config in self._agents.values():
|
|
281
|
+
role = config.role
|
|
282
|
+
by_role[role] = by_role.get(role, 0) + 1
|
|
283
|
+
|
|
284
|
+
level = config.empathy_level
|
|
285
|
+
by_level[level] = by_level.get(level, 0) + 1
|
|
286
|
+
|
|
287
|
+
tier = (
|
|
288
|
+
config.model_tier.value
|
|
289
|
+
if hasattr(config.model_tier, "value")
|
|
290
|
+
else str(config.model_tier)
|
|
291
|
+
)
|
|
292
|
+
by_tier[tier] = by_tier.get(tier, 0) + 1
|
|
293
|
+
|
|
294
|
+
return {
|
|
295
|
+
"total_agents": len(self._agents),
|
|
296
|
+
"agent_names": self.list_agents(),
|
|
297
|
+
"by_role": by_role,
|
|
298
|
+
"by_empathy_level": by_level,
|
|
299
|
+
"by_model_tier": by_tier,
|
|
300
|
+
"load_paths": [str(p) for p in self._load_paths],
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
def clear(self) -> None:
|
|
304
|
+
"""Clear all registered agents."""
|
|
305
|
+
self._agents.clear()
|
|
306
|
+
self._load_paths.clear()
|
|
307
|
+
logger.debug("Cleared agent registry")
|