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,738 @@
|
|
|
1
|
+
"""Pattern learning from historical meta-workflow executions.
|
|
2
|
+
|
|
3
|
+
Analyzes saved execution results to generate insights and recommendations
|
|
4
|
+
for optimizing future workflows.
|
|
5
|
+
|
|
6
|
+
Hybrid Storage:
|
|
7
|
+
- File-based storage: Persistent, human-readable execution results
|
|
8
|
+
- Memory-based storage: Rich semantic queries, relationship modeling
|
|
9
|
+
|
|
10
|
+
Created: 2026-01-17
|
|
11
|
+
Purpose: Self-optimizing meta-workflows through pattern analysis
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import logging
|
|
15
|
+
from collections import defaultdict
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
from attune.meta_workflows.models import PatternInsight
|
|
20
|
+
from attune.meta_workflows.workflow import list_execution_results, load_execution_result
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from attune.memory.unified import UnifiedMemory
|
|
24
|
+
from attune.meta_workflows.models import FormResponse
|
|
25
|
+
from attune.meta_workflows.workflow import MetaWorkflowResult
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class PatternLearner:
|
|
31
|
+
"""Analyzes historical workflow executions to generate insights.
|
|
32
|
+
|
|
33
|
+
Learns patterns from past executions to recommend optimizations
|
|
34
|
+
for future workflows.
|
|
35
|
+
|
|
36
|
+
Hybrid Architecture:
|
|
37
|
+
- Files: Persistent storage of execution results
|
|
38
|
+
- Memory: Rich semantic queries and relationship modeling
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
executions_dir: Directory where execution results are stored
|
|
42
|
+
memory: Optional UnifiedMemory instance for enhanced querying
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
executions_dir: str | None = None,
|
|
48
|
+
memory: "UnifiedMemory | None" = None,
|
|
49
|
+
):
|
|
50
|
+
"""Initialize pattern learner with hybrid storage.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
executions_dir: Directory for execution results
|
|
54
|
+
(default: .attune/meta_workflows/executions/)
|
|
55
|
+
memory: Optional UnifiedMemory instance for enhanced querying
|
|
56
|
+
If provided, insights will be stored in both files and memory
|
|
57
|
+
"""
|
|
58
|
+
if executions_dir is None:
|
|
59
|
+
executions_dir = str(Path.home() / ".empathy" / "meta_workflows" / "executions")
|
|
60
|
+
self.executions_dir = Path(executions_dir)
|
|
61
|
+
self.memory = memory
|
|
62
|
+
|
|
63
|
+
logger.info(
|
|
64
|
+
f"Pattern learner initialized: {self.executions_dir}",
|
|
65
|
+
extra={"memory_enabled": memory is not None},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def analyze_patterns(
|
|
69
|
+
self, template_id: str | None = None, min_confidence: float = 0.5
|
|
70
|
+
) -> list[PatternInsight]:
|
|
71
|
+
"""Analyze patterns from historical executions.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
template_id: Optional template ID to filter by
|
|
75
|
+
min_confidence: Minimum confidence threshold (0.0-1.0)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
List of pattern insights
|
|
79
|
+
"""
|
|
80
|
+
# Load all execution results
|
|
81
|
+
run_ids = list_execution_results(storage_dir=str(self.executions_dir))
|
|
82
|
+
|
|
83
|
+
if not run_ids:
|
|
84
|
+
logger.warning("No execution results found")
|
|
85
|
+
return []
|
|
86
|
+
|
|
87
|
+
# Filter by template if specified
|
|
88
|
+
results = []
|
|
89
|
+
for run_id in run_ids:
|
|
90
|
+
try:
|
|
91
|
+
result = load_execution_result(run_id, storage_dir=str(self.executions_dir))
|
|
92
|
+
if template_id is None or result.template_id == template_id:
|
|
93
|
+
results.append(result)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.warning(f"Failed to load result {run_id}: {e}")
|
|
96
|
+
|
|
97
|
+
if not results:
|
|
98
|
+
logger.warning(f"No results found for template: {template_id}")
|
|
99
|
+
return []
|
|
100
|
+
|
|
101
|
+
logger.info(f"Analyzing {len(results)} execution(s)")
|
|
102
|
+
|
|
103
|
+
# Generate insights
|
|
104
|
+
insights = []
|
|
105
|
+
|
|
106
|
+
# 1. Agent count patterns
|
|
107
|
+
insights.extend(self._analyze_agent_counts(results))
|
|
108
|
+
|
|
109
|
+
# 2. Tier performance patterns
|
|
110
|
+
insights.extend(self._analyze_tier_performance(results))
|
|
111
|
+
|
|
112
|
+
# 3. Cost patterns
|
|
113
|
+
insights.extend(self._analyze_costs(results))
|
|
114
|
+
|
|
115
|
+
# 4. Common failures
|
|
116
|
+
insights.extend(self._analyze_failures(results))
|
|
117
|
+
|
|
118
|
+
# Filter by confidence
|
|
119
|
+
insights = [i for i in insights if i.confidence >= min_confidence]
|
|
120
|
+
|
|
121
|
+
logger.info(f"Generated {len(insights)} insights")
|
|
122
|
+
|
|
123
|
+
return insights
|
|
124
|
+
|
|
125
|
+
def _analyze_agent_counts(self, results: list) -> list[PatternInsight]:
|
|
126
|
+
"""Analyze patterns in agent counts.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
results: List of workflow results
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
List of insights about agent counts
|
|
133
|
+
"""
|
|
134
|
+
insights = []
|
|
135
|
+
|
|
136
|
+
agent_counts = [len(r.agents_created) for r in results]
|
|
137
|
+
|
|
138
|
+
if not agent_counts:
|
|
139
|
+
return insights
|
|
140
|
+
|
|
141
|
+
avg_count = sum(agent_counts) / len(agent_counts)
|
|
142
|
+
min_count = min(agent_counts)
|
|
143
|
+
max_count = max(agent_counts)
|
|
144
|
+
|
|
145
|
+
# Calculate confidence based on sample size
|
|
146
|
+
confidence = min(len(results) / 10.0, 1.0)
|
|
147
|
+
|
|
148
|
+
insights.append(
|
|
149
|
+
PatternInsight(
|
|
150
|
+
insight_type="agent_count",
|
|
151
|
+
description=f"Average {avg_count:.1f} agents per workflow (range: {min_count}-{max_count})",
|
|
152
|
+
confidence=confidence,
|
|
153
|
+
data={
|
|
154
|
+
"average": avg_count,
|
|
155
|
+
"min": min_count,
|
|
156
|
+
"max": max_count,
|
|
157
|
+
"counts": agent_counts,
|
|
158
|
+
},
|
|
159
|
+
sample_size=len(results),
|
|
160
|
+
)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
return insights
|
|
164
|
+
|
|
165
|
+
def _analyze_tier_performance(self, results: list) -> list[PatternInsight]:
|
|
166
|
+
"""Analyze tier performance patterns.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
results: List of workflow results
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
List of insights about tier performance
|
|
173
|
+
"""
|
|
174
|
+
insights = []
|
|
175
|
+
|
|
176
|
+
# Track success rate by agent role and tier
|
|
177
|
+
tier_stats = defaultdict(lambda: {"success": 0, "total": 0, "costs": []})
|
|
178
|
+
|
|
179
|
+
for result in results:
|
|
180
|
+
for agent_result in result.agent_results:
|
|
181
|
+
key = f"{agent_result.role}:{agent_result.tier_used}"
|
|
182
|
+
tier_stats[key]["total"] += 1
|
|
183
|
+
if agent_result.success:
|
|
184
|
+
tier_stats[key]["success"] += 1
|
|
185
|
+
tier_stats[key]["costs"].append(agent_result.cost)
|
|
186
|
+
|
|
187
|
+
# Generate insights for agents with enough data
|
|
188
|
+
for key, stats in tier_stats.items():
|
|
189
|
+
if stats["total"] >= 3: # Minimum 3 samples
|
|
190
|
+
role, tier = key.split(":")
|
|
191
|
+
success_rate = stats["success"] / stats["total"]
|
|
192
|
+
avg_cost = sum(stats["costs"]) / len(stats["costs"])
|
|
193
|
+
|
|
194
|
+
confidence = min(stats["total"] / 10.0, 1.0)
|
|
195
|
+
|
|
196
|
+
insights.append(
|
|
197
|
+
PatternInsight(
|
|
198
|
+
insight_type="tier_performance",
|
|
199
|
+
description=f"{role} succeeds {success_rate:.0%} at {tier} tier (avg cost: ${avg_cost:.2f})",
|
|
200
|
+
confidence=confidence,
|
|
201
|
+
data={
|
|
202
|
+
"role": role,
|
|
203
|
+
"tier": tier,
|
|
204
|
+
"success_rate": success_rate,
|
|
205
|
+
"avg_cost": avg_cost,
|
|
206
|
+
"total_runs": stats["total"],
|
|
207
|
+
},
|
|
208
|
+
sample_size=stats["total"],
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
return insights
|
|
213
|
+
|
|
214
|
+
def _analyze_costs(self, results: list) -> list[PatternInsight]:
|
|
215
|
+
"""Analyze cost patterns.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
results: List of workflow results
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
List of insights about costs
|
|
222
|
+
"""
|
|
223
|
+
insights = []
|
|
224
|
+
|
|
225
|
+
if not results:
|
|
226
|
+
return insights
|
|
227
|
+
|
|
228
|
+
total_costs = [r.total_cost for r in results]
|
|
229
|
+
avg_cost = sum(total_costs) / len(total_costs)
|
|
230
|
+
min_cost = min(total_costs)
|
|
231
|
+
max_cost = max(total_costs)
|
|
232
|
+
|
|
233
|
+
# Calculate cost by tier
|
|
234
|
+
tier_costs = defaultdict(list)
|
|
235
|
+
for result in results:
|
|
236
|
+
for agent_result in result.agent_results:
|
|
237
|
+
tier_costs[agent_result.tier_used].append(agent_result.cost)
|
|
238
|
+
|
|
239
|
+
tier_breakdown = {}
|
|
240
|
+
for tier, costs in tier_costs.items():
|
|
241
|
+
tier_breakdown[tier] = {
|
|
242
|
+
"avg": sum(costs) / len(costs),
|
|
243
|
+
"total": sum(costs),
|
|
244
|
+
"count": len(costs),
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
confidence = min(len(results) / 10.0, 1.0)
|
|
248
|
+
|
|
249
|
+
insights.append(
|
|
250
|
+
PatternInsight(
|
|
251
|
+
insight_type="cost_analysis",
|
|
252
|
+
description=f"Average workflow cost ${avg_cost:.2f} (range: ${min_cost:.2f}-${max_cost:.2f})",
|
|
253
|
+
confidence=confidence,
|
|
254
|
+
data={
|
|
255
|
+
"average": avg_cost,
|
|
256
|
+
"min": min_cost,
|
|
257
|
+
"max": max_cost,
|
|
258
|
+
"tier_breakdown": tier_breakdown,
|
|
259
|
+
},
|
|
260
|
+
sample_size=len(results),
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
return insights
|
|
265
|
+
|
|
266
|
+
def _analyze_failures(self, results: list) -> list[PatternInsight]:
|
|
267
|
+
"""Analyze failure patterns.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
results: List of workflow results
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
List of insights about failures
|
|
274
|
+
"""
|
|
275
|
+
insights = []
|
|
276
|
+
|
|
277
|
+
failed_agents = defaultdict(int)
|
|
278
|
+
total_agents = defaultdict(int)
|
|
279
|
+
|
|
280
|
+
for result in results:
|
|
281
|
+
for agent_result in result.agent_results:
|
|
282
|
+
total_agents[agent_result.role] += 1
|
|
283
|
+
if not agent_result.success:
|
|
284
|
+
failed_agents[agent_result.role] += 1
|
|
285
|
+
|
|
286
|
+
# Find agents with failures
|
|
287
|
+
for role, failure_count in failed_agents.items():
|
|
288
|
+
if failure_count > 0:
|
|
289
|
+
total = total_agents[role]
|
|
290
|
+
failure_rate = failure_count / total
|
|
291
|
+
|
|
292
|
+
confidence = min(total / 10.0, 1.0)
|
|
293
|
+
|
|
294
|
+
insights.append(
|
|
295
|
+
PatternInsight(
|
|
296
|
+
insight_type="failure_analysis",
|
|
297
|
+
description=f"{role} fails {failure_rate:.0%} of the time ({failure_count}/{total})",
|
|
298
|
+
confidence=confidence,
|
|
299
|
+
data={
|
|
300
|
+
"role": role,
|
|
301
|
+
"failure_count": failure_count,
|
|
302
|
+
"total_runs": total,
|
|
303
|
+
"failure_rate": failure_rate,
|
|
304
|
+
},
|
|
305
|
+
sample_size=total,
|
|
306
|
+
)
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
return insights
|
|
310
|
+
|
|
311
|
+
def get_recommendations(self, template_id: str, min_confidence: float = 0.7) -> list[str]:
|
|
312
|
+
"""Get actionable recommendations for a template.
|
|
313
|
+
|
|
314
|
+
Args:
|
|
315
|
+
template_id: Template ID to get recommendations for
|
|
316
|
+
min_confidence: Minimum confidence for recommendations
|
|
317
|
+
|
|
318
|
+
Returns:
|
|
319
|
+
List of recommendation strings
|
|
320
|
+
"""
|
|
321
|
+
insights = self.analyze_patterns(template_id=template_id, min_confidence=min_confidence)
|
|
322
|
+
|
|
323
|
+
recommendations = []
|
|
324
|
+
|
|
325
|
+
for insight in insights:
|
|
326
|
+
if insight.insight_type == "tier_performance":
|
|
327
|
+
role = insight.data["role"]
|
|
328
|
+
tier = insight.data["tier"]
|
|
329
|
+
success_rate = insight.data["success_rate"]
|
|
330
|
+
|
|
331
|
+
if success_rate >= 0.9:
|
|
332
|
+
recommendations.append(
|
|
333
|
+
f"✓ {role} works well at {tier} tier ({success_rate:.0%} success)"
|
|
334
|
+
)
|
|
335
|
+
elif success_rate < 0.6:
|
|
336
|
+
recommendations.append(
|
|
337
|
+
f"⚠ {role} struggles at {tier} tier ({success_rate:.0%} success) - consider upgrading tier"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
elif insight.insight_type == "cost_analysis":
|
|
341
|
+
avg_cost = insight.data["average"]
|
|
342
|
+
recommendations.append(f"💰 Expected workflow cost: ${avg_cost:.2f}")
|
|
343
|
+
|
|
344
|
+
elif insight.insight_type == "failure_analysis":
|
|
345
|
+
role = insight.data["role"]
|
|
346
|
+
failure_rate = insight.data["failure_rate"]
|
|
347
|
+
if failure_rate > 0.3:
|
|
348
|
+
recommendations.append(
|
|
349
|
+
f"🔧 {role} needs attention ({failure_rate:.0%} failure rate)"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
return recommendations
|
|
353
|
+
|
|
354
|
+
def generate_analytics_report(self, template_id: str | None = None) -> dict[str, Any]:
|
|
355
|
+
"""Generate comprehensive analytics report.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
template_id: Optional template ID to filter by
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Dictionary with analytics data
|
|
362
|
+
"""
|
|
363
|
+
insights = self.analyze_patterns(template_id=template_id, min_confidence=0.0)
|
|
364
|
+
|
|
365
|
+
# Group insights by type
|
|
366
|
+
insights_by_type = defaultdict(list)
|
|
367
|
+
for insight in insights:
|
|
368
|
+
insights_by_type[insight.insight_type].append(insight)
|
|
369
|
+
|
|
370
|
+
# Load all results for summary stats
|
|
371
|
+
run_ids = list_execution_results(storage_dir=str(self.executions_dir))
|
|
372
|
+
results = []
|
|
373
|
+
for run_id in run_ids:
|
|
374
|
+
try:
|
|
375
|
+
result = load_execution_result(run_id, storage_dir=str(self.executions_dir))
|
|
376
|
+
if template_id is None or result.template_id == template_id:
|
|
377
|
+
results.append(result)
|
|
378
|
+
except Exception:
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
# Calculate summary statistics
|
|
382
|
+
total_runs = len(results)
|
|
383
|
+
successful_runs = sum(1 for r in results if r.success)
|
|
384
|
+
total_cost = sum(r.total_cost for r in results)
|
|
385
|
+
total_agents = sum(len(r.agents_created) for r in results)
|
|
386
|
+
|
|
387
|
+
report = {
|
|
388
|
+
"summary": {
|
|
389
|
+
"total_runs": total_runs,
|
|
390
|
+
"successful_runs": successful_runs,
|
|
391
|
+
"success_rate": successful_runs / total_runs if total_runs > 0 else 0,
|
|
392
|
+
"total_cost": total_cost,
|
|
393
|
+
"avg_cost_per_run": total_cost / total_runs if total_runs > 0 else 0,
|
|
394
|
+
"total_agents_created": total_agents,
|
|
395
|
+
"avg_agents_per_run": total_agents / total_runs if total_runs > 0 else 0,
|
|
396
|
+
},
|
|
397
|
+
"insights": {
|
|
398
|
+
insight_type: [i.to_dict() for i in insights_list]
|
|
399
|
+
for insight_type, insights_list in insights_by_type.items()
|
|
400
|
+
},
|
|
401
|
+
"recommendations": self.get_recommendations(template_id) if template_id else [],
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return report
|
|
405
|
+
|
|
406
|
+
# =========================================================================
|
|
407
|
+
# MEMORY INTEGRATION
|
|
408
|
+
# =========================================================================
|
|
409
|
+
|
|
410
|
+
def store_execution_in_memory(self, result: "MetaWorkflowResult") -> str | None:
|
|
411
|
+
"""Store execution result in memory for semantic querying.
|
|
412
|
+
|
|
413
|
+
This stores execution insights in long-term memory IN ADDITION to
|
|
414
|
+
file-based storage. Memory enables rich semantic queries like:
|
|
415
|
+
- "Find workflows that succeeded with test coverage >80%"
|
|
416
|
+
- "Show me all workflows that used progressive tier escalation"
|
|
417
|
+
|
|
418
|
+
Args:
|
|
419
|
+
result: MetaWorkflowResult to store
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
Pattern ID if stored successfully, None otherwise
|
|
423
|
+
"""
|
|
424
|
+
if not self.memory:
|
|
425
|
+
logger.debug("Memory not available, skipping memory storage")
|
|
426
|
+
return None
|
|
427
|
+
|
|
428
|
+
try:
|
|
429
|
+
# Calculate tier distribution
|
|
430
|
+
tier_counts = defaultdict(int)
|
|
431
|
+
for agent_result in result.agent_results:
|
|
432
|
+
tier_counts[agent_result.tier_used] += 1
|
|
433
|
+
|
|
434
|
+
# Create rich metadata for semantic querying
|
|
435
|
+
metadata = {
|
|
436
|
+
"run_id": result.run_id,
|
|
437
|
+
"template_id": result.template_id,
|
|
438
|
+
"success": result.success,
|
|
439
|
+
"total_cost": result.total_cost,
|
|
440
|
+
"total_duration": result.total_duration,
|
|
441
|
+
"agents_created": len(result.agents_created),
|
|
442
|
+
"agents_succeeded": sum(1 for a in result.agent_results if a.success),
|
|
443
|
+
"tier_distribution": dict(tier_counts),
|
|
444
|
+
"form_responses": result.form_responses.responses,
|
|
445
|
+
"timestamp": result.timestamp,
|
|
446
|
+
"error": result.error,
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
# Create searchable content
|
|
450
|
+
content = f"""Meta-workflow execution: {result.template_id}
|
|
451
|
+
Run ID: {result.run_id}
|
|
452
|
+
Status: {"SUCCESS" if result.success else "FAILED"}
|
|
453
|
+
Agents created: {len(result.agents_created)}
|
|
454
|
+
Total cost: ${result.total_cost:.2f}
|
|
455
|
+
Duration: {result.total_duration:.1f}s
|
|
456
|
+
|
|
457
|
+
Agents:
|
|
458
|
+
{self._format_agents_for_content(result)}
|
|
459
|
+
|
|
460
|
+
Form Responses:
|
|
461
|
+
{self._format_responses_for_content(result.form_responses.responses)}
|
|
462
|
+
"""
|
|
463
|
+
|
|
464
|
+
# Store in long-term memory
|
|
465
|
+
storage_result = self.memory.persist_pattern(
|
|
466
|
+
content=content,
|
|
467
|
+
pattern_type="meta_workflow_execution",
|
|
468
|
+
classification="INTERNAL", # Workflow metadata is internal
|
|
469
|
+
auto_classify=False,
|
|
470
|
+
metadata=metadata,
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
if storage_result:
|
|
474
|
+
pattern_id = storage_result.get("pattern_id")
|
|
475
|
+
logger.info(
|
|
476
|
+
f"Execution stored in memory: {pattern_id}",
|
|
477
|
+
extra={
|
|
478
|
+
"run_id": result.run_id,
|
|
479
|
+
"template_id": result.template_id,
|
|
480
|
+
},
|
|
481
|
+
)
|
|
482
|
+
return pattern_id
|
|
483
|
+
|
|
484
|
+
return None
|
|
485
|
+
|
|
486
|
+
except Exception as e:
|
|
487
|
+
logger.error(f"Failed to store execution in memory: {e}")
|
|
488
|
+
return None
|
|
489
|
+
|
|
490
|
+
def _format_agents_for_content(self, result: "MetaWorkflowResult") -> str:
|
|
491
|
+
"""Format agents for searchable content."""
|
|
492
|
+
lines = []
|
|
493
|
+
for agent_result in result.agent_results:
|
|
494
|
+
status = "✅" if agent_result.success else "❌"
|
|
495
|
+
lines.append(
|
|
496
|
+
f"- {status} {agent_result.role} (tier: {agent_result.tier_used}, "
|
|
497
|
+
f"cost: ${agent_result.cost:.2f})"
|
|
498
|
+
)
|
|
499
|
+
return "\n".join(lines)
|
|
500
|
+
|
|
501
|
+
def _format_responses_for_content(self, responses: dict) -> str:
|
|
502
|
+
"""Format form responses for searchable content."""
|
|
503
|
+
lines = []
|
|
504
|
+
for key, value in responses.items():
|
|
505
|
+
lines.append(f"- {key}: {value}")
|
|
506
|
+
return "\n".join(lines)
|
|
507
|
+
|
|
508
|
+
def search_executions_by_context(
|
|
509
|
+
self,
|
|
510
|
+
query: str,
|
|
511
|
+
template_id: str | None = None,
|
|
512
|
+
limit: int = 10,
|
|
513
|
+
) -> list["MetaWorkflowResult"]:
|
|
514
|
+
"""Search executions using semantic memory queries.
|
|
515
|
+
|
|
516
|
+
This provides richer querying than file-based search:
|
|
517
|
+
- Natural language queries
|
|
518
|
+
- Semantic similarity matching
|
|
519
|
+
- Cross-template pattern recognition
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
query: Natural language search query
|
|
523
|
+
e.g., "workflows with high test coverage"
|
|
524
|
+
template_id: Optional filter by template
|
|
525
|
+
limit: Maximum results to return
|
|
526
|
+
|
|
527
|
+
Returns:
|
|
528
|
+
List of matching MetaWorkflowResult objects
|
|
529
|
+
|
|
530
|
+
Example:
|
|
531
|
+
>>> learner.search_executions_by_context(
|
|
532
|
+
... "successful workflows with test coverage > 80%",
|
|
533
|
+
... limit=5
|
|
534
|
+
... )
|
|
535
|
+
"""
|
|
536
|
+
if not self.memory:
|
|
537
|
+
logger.warning("Memory not available, falling back to file-based search")
|
|
538
|
+
return self._search_executions_files(query, template_id, limit)
|
|
539
|
+
|
|
540
|
+
try:
|
|
541
|
+
# Search memory patterns
|
|
542
|
+
patterns = self.memory.search_patterns(
|
|
543
|
+
query=query,
|
|
544
|
+
pattern_type="meta_workflow_execution",
|
|
545
|
+
limit=limit,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
# Convert to MetaWorkflowResult objects
|
|
549
|
+
results = []
|
|
550
|
+
for pattern in patterns:
|
|
551
|
+
metadata = pattern.get("metadata", {})
|
|
552
|
+
run_id = metadata.get("run_id")
|
|
553
|
+
|
|
554
|
+
if run_id:
|
|
555
|
+
# Filter by template if specified
|
|
556
|
+
if template_id and metadata.get("template_id") != template_id:
|
|
557
|
+
continue
|
|
558
|
+
|
|
559
|
+
# Load full result from files
|
|
560
|
+
try:
|
|
561
|
+
result = load_execution_result(run_id, storage_dir=str(self.executions_dir))
|
|
562
|
+
results.append(result)
|
|
563
|
+
except FileNotFoundError:
|
|
564
|
+
logger.warning(f"Result file not found for run_id: {run_id}")
|
|
565
|
+
continue
|
|
566
|
+
|
|
567
|
+
return results
|
|
568
|
+
|
|
569
|
+
except Exception as e:
|
|
570
|
+
logger.error(f"Memory search failed: {e}")
|
|
571
|
+
return self._search_executions_files(query, template_id, limit)
|
|
572
|
+
|
|
573
|
+
def _search_executions_files(
|
|
574
|
+
self,
|
|
575
|
+
query: str,
|
|
576
|
+
template_id: str | None,
|
|
577
|
+
limit: int,
|
|
578
|
+
) -> list["MetaWorkflowResult"]:
|
|
579
|
+
"""Fallback file-based search when memory is unavailable."""
|
|
580
|
+
# Simple keyword search in file-based storage
|
|
581
|
+
results = []
|
|
582
|
+
run_ids = list_execution_results(storage_dir=str(self.executions_dir))
|
|
583
|
+
|
|
584
|
+
for run_id in run_ids[:limit]:
|
|
585
|
+
try:
|
|
586
|
+
result = load_execution_result(run_id, storage_dir=str(self.executions_dir))
|
|
587
|
+
|
|
588
|
+
# Filter by template
|
|
589
|
+
if template_id and result.template_id != template_id:
|
|
590
|
+
continue
|
|
591
|
+
|
|
592
|
+
# Simple keyword matching
|
|
593
|
+
result_json = result.to_json().lower()
|
|
594
|
+
if query.lower() in result_json:
|
|
595
|
+
results.append(result)
|
|
596
|
+
|
|
597
|
+
except Exception as e:
|
|
598
|
+
logger.warning(f"Failed to load result {run_id}: {e}")
|
|
599
|
+
continue
|
|
600
|
+
|
|
601
|
+
return results[:limit]
|
|
602
|
+
|
|
603
|
+
def get_smart_recommendations(
|
|
604
|
+
self,
|
|
605
|
+
template_id: str,
|
|
606
|
+
form_response: "FormResponse | None" = None,
|
|
607
|
+
min_confidence: float = 0.7,
|
|
608
|
+
) -> list[str]:
|
|
609
|
+
"""Get context-aware recommendations enhanced by memory.
|
|
610
|
+
|
|
611
|
+
Combines statistical pattern analysis with semantic memory queries
|
|
612
|
+
to provide more intelligent recommendations.
|
|
613
|
+
|
|
614
|
+
Args:
|
|
615
|
+
template_id: Template ID to get recommendations for
|
|
616
|
+
form_response: Optional form responses for context-aware suggestions
|
|
617
|
+
min_confidence: Minimum confidence threshold
|
|
618
|
+
|
|
619
|
+
Returns:
|
|
620
|
+
List of recommendation strings
|
|
621
|
+
|
|
622
|
+
Example:
|
|
623
|
+
>>> recommendations = learner.get_smart_recommendations(
|
|
624
|
+
... "python_package_publish",
|
|
625
|
+
... form_response=response,
|
|
626
|
+
... min_confidence=0.7
|
|
627
|
+
... )
|
|
628
|
+
"""
|
|
629
|
+
# Get base recommendations from statistical analysis
|
|
630
|
+
base_recs = self.get_recommendations(template_id, min_confidence)
|
|
631
|
+
|
|
632
|
+
# If no memory, return base recommendations
|
|
633
|
+
if not self.memory or not form_response:
|
|
634
|
+
return base_recs
|
|
635
|
+
|
|
636
|
+
# Enhance with memory-based context
|
|
637
|
+
try:
|
|
638
|
+
# Find similar past executions
|
|
639
|
+
query = f"Successful workflows for {template_id}"
|
|
640
|
+
if form_response:
|
|
641
|
+
# Add context from form responses
|
|
642
|
+
key_responses = []
|
|
643
|
+
for key, value in form_response.responses.items():
|
|
644
|
+
key_responses.append(f"{key}={value}")
|
|
645
|
+
query += f" with {', '.join(key_responses[:3])}"
|
|
646
|
+
|
|
647
|
+
similar_executions = self.search_executions_by_context(
|
|
648
|
+
query=query,
|
|
649
|
+
template_id=template_id,
|
|
650
|
+
limit=5,
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
# Generate memory-enhanced recommendations
|
|
654
|
+
if similar_executions:
|
|
655
|
+
success_rate = sum(1 for e in similar_executions if e.success) / len(
|
|
656
|
+
similar_executions
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
if success_rate >= 0.8:
|
|
660
|
+
base_recs.insert(
|
|
661
|
+
0,
|
|
662
|
+
f"📊 {len(similar_executions)} similar workflows found "
|
|
663
|
+
f"with {success_rate:.0%} success rate",
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
# Add tier recommendations from similar executions
|
|
667
|
+
tier_usage = defaultdict(int)
|
|
668
|
+
for execution in similar_executions:
|
|
669
|
+
for agent_result in execution.agent_results:
|
|
670
|
+
tier_usage[agent_result.tier_used] += 1
|
|
671
|
+
|
|
672
|
+
if tier_usage:
|
|
673
|
+
most_common_tier = max(tier_usage.items(), key=lambda x: x[1])[0]
|
|
674
|
+
base_recs.append(
|
|
675
|
+
f"💡 Similar workflows typically use '{most_common_tier}' tier"
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
except Exception as e:
|
|
679
|
+
logger.error(f"Failed to enhance recommendations with memory: {e}")
|
|
680
|
+
|
|
681
|
+
return base_recs
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
# =============================================================================
|
|
685
|
+
# Helper functions
|
|
686
|
+
# =============================================================================
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def print_analytics_report(report: dict[str, Any]) -> None:
|
|
690
|
+
"""Print analytics report in human-readable format.
|
|
691
|
+
|
|
692
|
+
Args:
|
|
693
|
+
report: Analytics report dictionary
|
|
694
|
+
"""
|
|
695
|
+
print("\n" + "=" * 70)
|
|
696
|
+
print("META-WORKFLOW ANALYTICS REPORT")
|
|
697
|
+
print("=" * 70)
|
|
698
|
+
|
|
699
|
+
# Summary
|
|
700
|
+
summary = report["summary"]
|
|
701
|
+
print("\n## Summary")
|
|
702
|
+
print(f"\n Total Runs: {summary['total_runs']}")
|
|
703
|
+
print(f" Successful: {summary['successful_runs']} ({summary['success_rate']:.0%})")
|
|
704
|
+
print(f" Total Cost: ${summary['total_cost']:.2f}")
|
|
705
|
+
print(f" Avg Cost/Run: ${summary['avg_cost_per_run']:.2f}")
|
|
706
|
+
print(f" Total Agents: {summary['total_agents_created']}")
|
|
707
|
+
print(f" Avg Agents/Run: {summary['avg_agents_per_run']:.1f}")
|
|
708
|
+
|
|
709
|
+
# Recommendations
|
|
710
|
+
if report.get("recommendations"):
|
|
711
|
+
print("\n## Recommendations")
|
|
712
|
+
print()
|
|
713
|
+
for rec in report["recommendations"]:
|
|
714
|
+
print(f" {rec}")
|
|
715
|
+
|
|
716
|
+
# Insights by type
|
|
717
|
+
insights = report.get("insights", {})
|
|
718
|
+
|
|
719
|
+
if insights.get("tier_performance"):
|
|
720
|
+
print("\n## Tier Performance")
|
|
721
|
+
print()
|
|
722
|
+
for insight in insights["tier_performance"]:
|
|
723
|
+
print(f" • {insight['description']}")
|
|
724
|
+
print(f" Confidence: {insight['confidence']:.0%} (n={insight['sample_size']})")
|
|
725
|
+
|
|
726
|
+
if insights.get("cost_analysis"):
|
|
727
|
+
print("\n## Cost Analysis")
|
|
728
|
+
print()
|
|
729
|
+
for insight in insights["cost_analysis"]:
|
|
730
|
+
print(f" • {insight['description']}")
|
|
731
|
+
|
|
732
|
+
if insights.get("failure_analysis"):
|
|
733
|
+
print("\n## Failure Analysis")
|
|
734
|
+
print()
|
|
735
|
+
for insight in insights["failure_analysis"]:
|
|
736
|
+
print(f" • {insight['description']}")
|
|
737
|
+
|
|
738
|
+
print("\n" + "=" * 70 + "\n")
|