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
attune/memory/types.py
ADDED
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
"""Memory types and data classes for Empathy Framework.
|
|
2
|
+
|
|
3
|
+
This module contains shared data structures used by the memory subsystem:
|
|
4
|
+
- Access control (AccessTier, AgentCredentials)
|
|
5
|
+
- TTL strategies (TTLStrategy)
|
|
6
|
+
- Configuration (RedisConfig)
|
|
7
|
+
- Metrics (RedisMetrics)
|
|
8
|
+
- Query types (PaginatedResult, TimeWindowQuery)
|
|
9
|
+
- Domain objects (StagedPattern, ConflictContext)
|
|
10
|
+
- Exceptions (SecurityError)
|
|
11
|
+
|
|
12
|
+
These types are independent of Redis and can be imported without the redis package.
|
|
13
|
+
|
|
14
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
15
|
+
Licensed under Fair Source 0.9
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from enum import Enum
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AccessTier(Enum):
|
|
25
|
+
"""Role-based access tiers per EMPATHY_PHILOSOPHY.md
|
|
26
|
+
|
|
27
|
+
Tier 1 - Observer: Read-only access to validated patterns
|
|
28
|
+
Tier 2 - Contributor: Can stage patterns for validation
|
|
29
|
+
Tier 3 - Validator: Can promote staged patterns to active
|
|
30
|
+
Tier 4 - Steward: Full access including deprecation and audit
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
OBSERVER = 1
|
|
34
|
+
CONTRIBUTOR = 2
|
|
35
|
+
VALIDATOR = 3
|
|
36
|
+
STEWARD = 4
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TTLStrategy(Enum):
|
|
40
|
+
"""TTL strategies for different memory types
|
|
41
|
+
|
|
42
|
+
Per EMPATHY_PHILOSOPHY.md Section 9.3:
|
|
43
|
+
- Working results: 1 hour
|
|
44
|
+
- Staged patterns: 24 hours
|
|
45
|
+
- Coordination signals: 5 minutes (REMOVED in v5.0 - see CoordinationSignals)
|
|
46
|
+
- Conflict context: Until resolution
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
WORKING_RESULTS = 3600 # 1 hour
|
|
50
|
+
STAGED_PATTERNS = 86400 # 24 hours
|
|
51
|
+
# COORDINATION removed in v5.0 - use CoordinationSignals with custom TTLs
|
|
52
|
+
CONFLICT_CONTEXT = 604800 # 7 days (fallback for unresolved)
|
|
53
|
+
SESSION = 1800 # 30 minutes
|
|
54
|
+
STREAM_ENTRY = 86400 * 7 # 7 days for audit stream entries
|
|
55
|
+
TASK_QUEUE = 3600 * 4 # 4 hours for task queue items
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class RedisConfig:
|
|
60
|
+
"""Enhanced Redis configuration with SSL and retry support.
|
|
61
|
+
|
|
62
|
+
Supports:
|
|
63
|
+
- Standard connections (host:port)
|
|
64
|
+
- URL-based connections (redis://...)
|
|
65
|
+
- SSL/TLS for managed services (rediss://...)
|
|
66
|
+
- Sentinel for high availability
|
|
67
|
+
- Connection pooling
|
|
68
|
+
- Retry with exponential backoff
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
host: str = "localhost"
|
|
72
|
+
port: int = 6379
|
|
73
|
+
db: int = 0
|
|
74
|
+
password: str | None = None
|
|
75
|
+
use_mock: bool = False
|
|
76
|
+
|
|
77
|
+
# Security settings
|
|
78
|
+
pii_scrub_enabled: bool = True # Scrub PII before storing (HIPAA/GDPR compliance)
|
|
79
|
+
secrets_detection_enabled: bool = True # Block storage of detected secrets
|
|
80
|
+
|
|
81
|
+
# SSL/TLS settings
|
|
82
|
+
ssl: bool = False
|
|
83
|
+
ssl_cert_reqs: str | None = None # "required", "optional", "none"
|
|
84
|
+
ssl_ca_certs: str | None = None
|
|
85
|
+
ssl_certfile: str | None = None
|
|
86
|
+
ssl_keyfile: str | None = None
|
|
87
|
+
|
|
88
|
+
# Connection pool settings
|
|
89
|
+
max_connections: int = 10
|
|
90
|
+
socket_timeout: float = 5.0
|
|
91
|
+
socket_connect_timeout: float = 5.0
|
|
92
|
+
|
|
93
|
+
# Retry settings
|
|
94
|
+
retry_on_timeout: bool = True
|
|
95
|
+
retry_max_attempts: int = 3
|
|
96
|
+
retry_base_delay: float = 0.1 # seconds
|
|
97
|
+
retry_max_delay: float = 2.0 # seconds
|
|
98
|
+
|
|
99
|
+
# Local LRU cache settings (two-tier caching)
|
|
100
|
+
local_cache_enabled: bool = True # Enable local memory cache (reduces Redis network I/O)
|
|
101
|
+
local_cache_size: int = 500 # Maximum number of cached keys (~50KB memory)
|
|
102
|
+
|
|
103
|
+
# Sentinel settings (for HA)
|
|
104
|
+
sentinel_hosts: list[tuple[str, int]] | None = None
|
|
105
|
+
sentinel_master_name: str | None = None
|
|
106
|
+
|
|
107
|
+
def to_redis_kwargs(self) -> dict:
|
|
108
|
+
"""Convert to redis.Redis constructor kwargs."""
|
|
109
|
+
kwargs: dict[str, Any] = {
|
|
110
|
+
"host": self.host,
|
|
111
|
+
"port": self.port,
|
|
112
|
+
"db": self.db,
|
|
113
|
+
"password": self.password,
|
|
114
|
+
"decode_responses": True,
|
|
115
|
+
"socket_timeout": self.socket_timeout,
|
|
116
|
+
"socket_connect_timeout": self.socket_connect_timeout,
|
|
117
|
+
"retry_on_timeout": self.retry_on_timeout,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if self.ssl:
|
|
121
|
+
kwargs["ssl"] = True
|
|
122
|
+
if self.ssl_cert_reqs:
|
|
123
|
+
kwargs["ssl_cert_reqs"] = self.ssl_cert_reqs
|
|
124
|
+
if self.ssl_ca_certs:
|
|
125
|
+
kwargs["ssl_ca_certs"] = self.ssl_ca_certs
|
|
126
|
+
if self.ssl_certfile:
|
|
127
|
+
kwargs["ssl_certfile"] = self.ssl_certfile
|
|
128
|
+
if self.ssl_keyfile:
|
|
129
|
+
kwargs["ssl_keyfile"] = self.ssl_keyfile
|
|
130
|
+
|
|
131
|
+
return kwargs
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@dataclass
|
|
135
|
+
class RedisMetrics:
|
|
136
|
+
"""Metrics for Redis operations."""
|
|
137
|
+
|
|
138
|
+
operations_total: int = 0
|
|
139
|
+
operations_success: int = 0
|
|
140
|
+
operations_failed: int = 0
|
|
141
|
+
retries_total: int = 0
|
|
142
|
+
latency_sum_ms: float = 0.0
|
|
143
|
+
latency_max_ms: float = 0.0
|
|
144
|
+
|
|
145
|
+
# Per-operation metrics
|
|
146
|
+
stash_count: int = 0
|
|
147
|
+
retrieve_count: int = 0
|
|
148
|
+
publish_count: int = 0
|
|
149
|
+
stream_append_count: int = 0
|
|
150
|
+
|
|
151
|
+
# Security metrics
|
|
152
|
+
pii_scrubbed_total: int = 0 # Total PII instances scrubbed
|
|
153
|
+
pii_scrub_operations: int = 0 # Operations that had PII scrubbed
|
|
154
|
+
secrets_blocked_total: int = 0 # Total secrets blocked from storage
|
|
155
|
+
|
|
156
|
+
def record_operation(self, operation: str, latency_ms: float, success: bool = True) -> None:
|
|
157
|
+
"""Record an operation metric."""
|
|
158
|
+
self.operations_total += 1
|
|
159
|
+
self.latency_sum_ms += latency_ms
|
|
160
|
+
self.latency_max_ms = max(self.latency_max_ms, latency_ms)
|
|
161
|
+
|
|
162
|
+
if success:
|
|
163
|
+
self.operations_success += 1
|
|
164
|
+
else:
|
|
165
|
+
self.operations_failed += 1
|
|
166
|
+
|
|
167
|
+
# Track by operation type
|
|
168
|
+
if operation == "stash":
|
|
169
|
+
self.stash_count += 1
|
|
170
|
+
elif operation == "retrieve":
|
|
171
|
+
self.retrieve_count += 1
|
|
172
|
+
elif operation == "publish":
|
|
173
|
+
self.publish_count += 1
|
|
174
|
+
elif operation == "stream_append":
|
|
175
|
+
self.stream_append_count += 1
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def latency_avg_ms(self) -> float:
|
|
179
|
+
"""Average latency in milliseconds."""
|
|
180
|
+
if self.operations_total == 0:
|
|
181
|
+
return 0.0
|
|
182
|
+
return self.latency_sum_ms / self.operations_total
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def success_rate(self) -> float:
|
|
186
|
+
"""Success rate as percentage."""
|
|
187
|
+
if self.operations_total == 0:
|
|
188
|
+
return 100.0
|
|
189
|
+
return (self.operations_success / self.operations_total) * 100
|
|
190
|
+
|
|
191
|
+
@property
|
|
192
|
+
def total_requests(self) -> int:
|
|
193
|
+
"""Total requests (alias for operations_total for backward compatibility)."""
|
|
194
|
+
return self.operations_total
|
|
195
|
+
|
|
196
|
+
def to_dict(self) -> dict:
|
|
197
|
+
"""Convert metrics to dictionary for reporting and serialization.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Dictionary with keys: operations_total, operations_success,
|
|
201
|
+
operations_failed, retries_total, latency_avg_ms, latency_max_ms,
|
|
202
|
+
success_rate, by_operation, security.
|
|
203
|
+
"""
|
|
204
|
+
return {
|
|
205
|
+
"operations_total": self.operations_total,
|
|
206
|
+
"operations_success": self.operations_success,
|
|
207
|
+
"operations_failed": self.operations_failed,
|
|
208
|
+
"retries_total": self.retries_total,
|
|
209
|
+
"latency_avg_ms": round(self.latency_avg_ms, 2),
|
|
210
|
+
"latency_max_ms": round(self.latency_max_ms, 2),
|
|
211
|
+
"success_rate": round(self.success_rate, 2),
|
|
212
|
+
"by_operation": {
|
|
213
|
+
"stash": self.stash_count,
|
|
214
|
+
"retrieve": self.retrieve_count,
|
|
215
|
+
"publish": self.publish_count,
|
|
216
|
+
"stream_append": self.stream_append_count,
|
|
217
|
+
},
|
|
218
|
+
"security": {
|
|
219
|
+
"pii_scrubbed_total": self.pii_scrubbed_total,
|
|
220
|
+
"pii_scrub_operations": self.pii_scrub_operations,
|
|
221
|
+
"secrets_blocked_total": self.secrets_blocked_total,
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
@dataclass
|
|
227
|
+
class PaginatedResult:
|
|
228
|
+
"""Result of a paginated query."""
|
|
229
|
+
|
|
230
|
+
items: list[Any]
|
|
231
|
+
cursor: str
|
|
232
|
+
has_more: bool
|
|
233
|
+
total_scanned: int = 0
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@dataclass
|
|
237
|
+
class TimeWindowQuery:
|
|
238
|
+
"""Query parameters for time-window operations."""
|
|
239
|
+
|
|
240
|
+
start_time: datetime | None = None
|
|
241
|
+
end_time: datetime | None = None
|
|
242
|
+
limit: int = 100
|
|
243
|
+
offset: int = 0
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def start_score(self) -> float:
|
|
247
|
+
"""Start timestamp as Redis score."""
|
|
248
|
+
if self.start_time is None:
|
|
249
|
+
return float("-inf")
|
|
250
|
+
return self.start_time.timestamp()
|
|
251
|
+
|
|
252
|
+
@property
|
|
253
|
+
def end_score(self) -> float:
|
|
254
|
+
"""End timestamp as Redis score."""
|
|
255
|
+
if self.end_time is None:
|
|
256
|
+
return float("+inf")
|
|
257
|
+
return self.end_time.timestamp()
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
@dataclass
|
|
261
|
+
class AgentCredentials:
|
|
262
|
+
"""Agent identity and access permissions"""
|
|
263
|
+
|
|
264
|
+
agent_id: str
|
|
265
|
+
tier: AccessTier
|
|
266
|
+
roles: list[str] = field(default_factory=list)
|
|
267
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
268
|
+
|
|
269
|
+
def can_read(self) -> bool:
|
|
270
|
+
"""All tiers can read"""
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
def can_stage(self) -> bool:
|
|
274
|
+
"""Contributor+ can stage patterns"""
|
|
275
|
+
return self.tier.value >= AccessTier.CONTRIBUTOR.value
|
|
276
|
+
|
|
277
|
+
def can_validate(self) -> bool:
|
|
278
|
+
"""Validator+ can promote patterns"""
|
|
279
|
+
return self.tier.value >= AccessTier.VALIDATOR.value
|
|
280
|
+
|
|
281
|
+
def can_administer(self) -> bool:
|
|
282
|
+
"""Only Stewards have full admin access"""
|
|
283
|
+
return self.tier.value >= AccessTier.STEWARD.value
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@dataclass
|
|
287
|
+
class StagedPattern:
|
|
288
|
+
"""Pattern awaiting validation"""
|
|
289
|
+
|
|
290
|
+
pattern_id: str
|
|
291
|
+
agent_id: str
|
|
292
|
+
pattern_type: str
|
|
293
|
+
name: str
|
|
294
|
+
description: str
|
|
295
|
+
code: str | None = None
|
|
296
|
+
context: dict = field(default_factory=dict)
|
|
297
|
+
confidence: float = 0.5
|
|
298
|
+
staged_at: datetime = field(default_factory=datetime.now)
|
|
299
|
+
interests: list[str] = field(default_factory=list) # For negotiation
|
|
300
|
+
|
|
301
|
+
def __post_init__(self):
|
|
302
|
+
"""Validate fields after initialization"""
|
|
303
|
+
# Pattern 1: String ID validation
|
|
304
|
+
if not self.pattern_id or not self.pattern_id.strip():
|
|
305
|
+
raise ValueError(f"pattern_id cannot be empty. Got: {self.pattern_id!r}")
|
|
306
|
+
if not self.agent_id or not self.agent_id.strip():
|
|
307
|
+
raise ValueError(f"agent_id cannot be empty. Got: {self.agent_id!r}")
|
|
308
|
+
if not self.pattern_type or not self.pattern_type.strip():
|
|
309
|
+
raise ValueError(f"pattern_type cannot be empty. Got: {self.pattern_type!r}")
|
|
310
|
+
|
|
311
|
+
# Pattern 4: Range validation for confidence
|
|
312
|
+
if not 0.0 <= self.confidence <= 1.0:
|
|
313
|
+
raise ValueError(f"confidence must be between 0.0 and 1.0, got {self.confidence}")
|
|
314
|
+
|
|
315
|
+
# Pattern 5: Type validation
|
|
316
|
+
if not isinstance(self.context, dict):
|
|
317
|
+
raise TypeError(f"context must be dict, got {type(self.context).__name__}")
|
|
318
|
+
if not isinstance(self.interests, list):
|
|
319
|
+
raise TypeError(f"interests must be list, got {type(self.interests).__name__}")
|
|
320
|
+
|
|
321
|
+
def to_dict(self) -> dict:
|
|
322
|
+
"""Convert staged pattern to dictionary for serialization.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
Dictionary with keys: pattern_id, agent_id, pattern_type, name,
|
|
326
|
+
description, code, context, confidence, staged_at, interests.
|
|
327
|
+
"""
|
|
328
|
+
return {
|
|
329
|
+
"pattern_id": self.pattern_id,
|
|
330
|
+
"agent_id": self.agent_id,
|
|
331
|
+
"pattern_type": self.pattern_type,
|
|
332
|
+
"name": self.name,
|
|
333
|
+
"description": self.description,
|
|
334
|
+
"code": self.code,
|
|
335
|
+
"context": self.context,
|
|
336
|
+
"confidence": self.confidence,
|
|
337
|
+
"staged_at": self.staged_at.isoformat(),
|
|
338
|
+
"interests": self.interests,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
@classmethod
|
|
342
|
+
def from_dict(cls, data: dict) -> "StagedPattern":
|
|
343
|
+
"""Reconstruct StagedPattern from dictionary.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
data: Dictionary with required keys: pattern_id, agent_id,
|
|
347
|
+
pattern_type, name, description, staged_at.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Reconstructed StagedPattern instance.
|
|
351
|
+
|
|
352
|
+
Raises:
|
|
353
|
+
KeyError: If required keys are missing.
|
|
354
|
+
ValueError: If data format is invalid.
|
|
355
|
+
"""
|
|
356
|
+
return cls(
|
|
357
|
+
pattern_id=data["pattern_id"],
|
|
358
|
+
agent_id=data["agent_id"],
|
|
359
|
+
pattern_type=data["pattern_type"],
|
|
360
|
+
name=data["name"],
|
|
361
|
+
description=data["description"],
|
|
362
|
+
code=data.get("code"),
|
|
363
|
+
context=data.get("context", {}),
|
|
364
|
+
confidence=data.get("confidence", 0.5),
|
|
365
|
+
staged_at=datetime.fromisoformat(data["staged_at"]),
|
|
366
|
+
interests=data.get("interests", []),
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
@dataclass
|
|
371
|
+
class ConflictContext:
|
|
372
|
+
"""Context for principled negotiation
|
|
373
|
+
|
|
374
|
+
Per Getting to Yes framework:
|
|
375
|
+
- Positions: What each party says they want
|
|
376
|
+
- Interests: Why they want it (underlying needs)
|
|
377
|
+
- BATNA: Best Alternative to Negotiated Agreement
|
|
378
|
+
"""
|
|
379
|
+
|
|
380
|
+
conflict_id: str
|
|
381
|
+
positions: dict[str, Any] # agent_id -> stated position
|
|
382
|
+
interests: dict[str, list[str]] # agent_id -> underlying interests
|
|
383
|
+
batna: str | None = None # Fallback strategy
|
|
384
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
385
|
+
resolved: bool = False
|
|
386
|
+
resolution: str | None = None
|
|
387
|
+
|
|
388
|
+
def to_dict(self) -> dict:
|
|
389
|
+
"""Convert conflict context to dictionary for serialization.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
Dictionary with keys: conflict_id, positions, interests,
|
|
393
|
+
batna, created_at, resolved, resolution.
|
|
394
|
+
"""
|
|
395
|
+
return {
|
|
396
|
+
"conflict_id": self.conflict_id,
|
|
397
|
+
"positions": self.positions,
|
|
398
|
+
"interests": self.interests,
|
|
399
|
+
"batna": self.batna,
|
|
400
|
+
"created_at": self.created_at.isoformat(),
|
|
401
|
+
"resolved": self.resolved,
|
|
402
|
+
"resolution": self.resolution,
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@classmethod
|
|
406
|
+
def from_dict(cls, data: dict) -> "ConflictContext":
|
|
407
|
+
"""Reconstruct ConflictContext from dictionary.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
data: Dictionary with required keys: conflict_id, positions,
|
|
411
|
+
interests, created_at.
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
Reconstructed ConflictContext instance.
|
|
415
|
+
|
|
416
|
+
Raises:
|
|
417
|
+
KeyError: If required keys are missing.
|
|
418
|
+
ValueError: If data format is invalid.
|
|
419
|
+
"""
|
|
420
|
+
return cls(
|
|
421
|
+
conflict_id=data["conflict_id"],
|
|
422
|
+
positions=data["positions"],
|
|
423
|
+
interests=data["interests"],
|
|
424
|
+
batna=data.get("batna"),
|
|
425
|
+
created_at=datetime.fromisoformat(data["created_at"]),
|
|
426
|
+
resolved=data.get("resolved", False),
|
|
427
|
+
resolution=data.get("resolution"),
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class SecurityError(Exception):
|
|
432
|
+
"""Raised when a security policy is violated (e.g., secrets detected in data)."""
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
__all__ = [
|
|
436
|
+
"AccessTier",
|
|
437
|
+
"AgentCredentials",
|
|
438
|
+
"ConflictContext",
|
|
439
|
+
"PaginatedResult",
|
|
440
|
+
"RedisConfig",
|
|
441
|
+
"RedisMetrics",
|
|
442
|
+
"SecurityError",
|
|
443
|
+
"StagedPattern",
|
|
444
|
+
"TTLStrategy",
|
|
445
|
+
"TimeWindowQuery",
|
|
446
|
+
]
|
attune/memory/unified.py
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Unified Memory Interface for Empathy Framework
|
|
2
|
+
|
|
3
|
+
Provides a single API for both short-term (Redis) and long-term (persistent) memory,
|
|
4
|
+
with automatic pattern promotion and environment-aware storage backend selection.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from attune.memory import UnifiedMemory
|
|
8
|
+
|
|
9
|
+
memory = UnifiedMemory(
|
|
10
|
+
user_id="agent@company.com",
|
|
11
|
+
environment="production", # or "staging", "development"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
# Short-term operations
|
|
15
|
+
memory.stash("working_data", {"key": "value"})
|
|
16
|
+
data = memory.retrieve("working_data")
|
|
17
|
+
|
|
18
|
+
# Long-term operations
|
|
19
|
+
result = memory.persist_pattern(content, pattern_type="algorithm")
|
|
20
|
+
pattern = memory.recall_pattern(pattern_id)
|
|
21
|
+
|
|
22
|
+
# Pattern promotion
|
|
23
|
+
memory.promote_pattern(staged_pattern_id)
|
|
24
|
+
|
|
25
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
26
|
+
Licensed under Fair Source 0.9
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import os
|
|
30
|
+
from dataclasses import dataclass, field
|
|
31
|
+
from enum import Enum
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
import structlog
|
|
35
|
+
|
|
36
|
+
from .file_session import FileSessionMemory
|
|
37
|
+
from .long_term import LongTermMemory, SecureMemDocsIntegration
|
|
38
|
+
from .mixins import (
|
|
39
|
+
BackendInitMixin,
|
|
40
|
+
CapabilitiesMixin,
|
|
41
|
+
HandoffAndExportMixin,
|
|
42
|
+
LifecycleMixin,
|
|
43
|
+
LongTermOperationsMixin,
|
|
44
|
+
PatternPromotionMixin,
|
|
45
|
+
ShortTermOperationsMixin,
|
|
46
|
+
)
|
|
47
|
+
from .redis_bootstrap import RedisStatus
|
|
48
|
+
from .short_term import (
|
|
49
|
+
AccessTier,
|
|
50
|
+
RedisShortTermMemory,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
logger = structlog.get_logger(__name__)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class Environment(Enum):
|
|
57
|
+
"""Deployment environment for storage configuration."""
|
|
58
|
+
|
|
59
|
+
DEVELOPMENT = "development"
|
|
60
|
+
STAGING = "staging"
|
|
61
|
+
PRODUCTION = "production"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass
|
|
65
|
+
class MemoryConfig:
|
|
66
|
+
"""Configuration for unified memory system."""
|
|
67
|
+
|
|
68
|
+
# Environment
|
|
69
|
+
environment: Environment = Environment.DEVELOPMENT
|
|
70
|
+
|
|
71
|
+
# File-first architecture settings (always available)
|
|
72
|
+
file_session_enabled: bool = True # Use file-based session as primary
|
|
73
|
+
file_session_dir: str = ".empathy" # Directory for file-based storage
|
|
74
|
+
|
|
75
|
+
# Short-term memory settings (Redis - optional enhancement)
|
|
76
|
+
redis_url: str | None = None
|
|
77
|
+
redis_host: str = "localhost"
|
|
78
|
+
redis_port: int = 6379
|
|
79
|
+
redis_mock: bool = False
|
|
80
|
+
redis_auto_start: bool = False # Changed to False - file-first by default
|
|
81
|
+
redis_required: bool = False # If True, fail without Redis
|
|
82
|
+
default_ttl_seconds: int = 3600 # 1 hour
|
|
83
|
+
|
|
84
|
+
# Long-term memory settings
|
|
85
|
+
storage_dir: str = "./memdocs_storage"
|
|
86
|
+
encryption_enabled: bool = True
|
|
87
|
+
|
|
88
|
+
# Claude memory settings
|
|
89
|
+
claude_memory_enabled: bool = True
|
|
90
|
+
load_enterprise_memory: bool = True
|
|
91
|
+
load_project_memory: bool = True
|
|
92
|
+
load_user_memory: bool = True
|
|
93
|
+
|
|
94
|
+
# Pattern promotion settings
|
|
95
|
+
auto_promote_threshold: float = 0.8 # Confidence threshold for auto-promotion
|
|
96
|
+
|
|
97
|
+
# Compact state auto-generation
|
|
98
|
+
auto_generate_compact_state: bool = True
|
|
99
|
+
compact_state_path: str = ".claude/compact-state.md"
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def from_environment(cls) -> "MemoryConfig":
|
|
103
|
+
"""Create configuration from environment variables.
|
|
104
|
+
|
|
105
|
+
Environment Variables:
|
|
106
|
+
EMPATHY_ENV: Environment (development/staging/production)
|
|
107
|
+
EMPATHY_FILE_SESSION: Enable file-based session (true/false, default: true)
|
|
108
|
+
EMPATHY_FILE_SESSION_DIR: Directory for file-based storage
|
|
109
|
+
REDIS_URL: Redis connection URL
|
|
110
|
+
EMPATHY_REDIS_MOCK: Use mock Redis (true/false)
|
|
111
|
+
EMPATHY_REDIS_AUTO_START: Auto-start Redis (true/false, default: false)
|
|
112
|
+
EMPATHY_REDIS_REQUIRED: Fail without Redis (true/false, default: false)
|
|
113
|
+
EMPATHY_STORAGE_DIR: Long-term storage directory
|
|
114
|
+
EMPATHY_ENCRYPTION: Enable encryption (true/false)
|
|
115
|
+
"""
|
|
116
|
+
env_str = os.getenv("EMPATHY_ENV", "development").lower()
|
|
117
|
+
environment = (
|
|
118
|
+
Environment(env_str)
|
|
119
|
+
if env_str in [e.value for e in Environment]
|
|
120
|
+
else Environment.DEVELOPMENT
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return cls(
|
|
124
|
+
environment=environment,
|
|
125
|
+
# File-first settings (always available)
|
|
126
|
+
file_session_enabled=os.getenv("EMPATHY_FILE_SESSION", "true").lower() == "true",
|
|
127
|
+
file_session_dir=os.getenv("EMPATHY_FILE_SESSION_DIR", ".empathy"),
|
|
128
|
+
# Redis settings (optional)
|
|
129
|
+
redis_url=os.getenv("REDIS_URL"),
|
|
130
|
+
redis_host=os.getenv("EMPATHY_REDIS_HOST", "localhost"),
|
|
131
|
+
redis_port=int(os.getenv("EMPATHY_REDIS_PORT", "6379")),
|
|
132
|
+
redis_mock=os.getenv("EMPATHY_REDIS_MOCK", "").lower() == "true",
|
|
133
|
+
redis_auto_start=os.getenv("EMPATHY_REDIS_AUTO_START", "false").lower() == "true",
|
|
134
|
+
redis_required=os.getenv("EMPATHY_REDIS_REQUIRED", "false").lower() == "true",
|
|
135
|
+
# Long-term storage
|
|
136
|
+
storage_dir=os.getenv("EMPATHY_STORAGE_DIR", "./memdocs_storage"),
|
|
137
|
+
encryption_enabled=os.getenv("EMPATHY_ENCRYPTION", "true").lower() == "true",
|
|
138
|
+
claude_memory_enabled=os.getenv("EMPATHY_CLAUDE_MEMORY", "true").lower() == "true",
|
|
139
|
+
# Compact state
|
|
140
|
+
auto_generate_compact_state=os.getenv("EMPATHY_AUTO_COMPACT_STATE", "true").lower()
|
|
141
|
+
== "true",
|
|
142
|
+
compact_state_path=os.getenv("EMPATHY_COMPACT_STATE_PATH", ".claude/compact-state.md"),
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@dataclass
|
|
147
|
+
class UnifiedMemory(
|
|
148
|
+
BackendInitMixin,
|
|
149
|
+
ShortTermOperationsMixin,
|
|
150
|
+
LongTermOperationsMixin,
|
|
151
|
+
PatternPromotionMixin,
|
|
152
|
+
CapabilitiesMixin,
|
|
153
|
+
HandoffAndExportMixin,
|
|
154
|
+
LifecycleMixin,
|
|
155
|
+
):
|
|
156
|
+
"""Unified interface for short-term and long-term memory.
|
|
157
|
+
|
|
158
|
+
Provides:
|
|
159
|
+
- Short-term memory (Redis): Fast, TTL-based working memory
|
|
160
|
+
- Long-term memory (Persistent): Cross-session pattern storage
|
|
161
|
+
- Pattern promotion: Move validated patterns from short to long-term
|
|
162
|
+
- Environment-aware configuration: Auto-detect storage backends
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
user_id: str
|
|
166
|
+
config: MemoryConfig = field(default_factory=MemoryConfig.from_environment)
|
|
167
|
+
access_tier: AccessTier = AccessTier.CONTRIBUTOR
|
|
168
|
+
|
|
169
|
+
# Internal state
|
|
170
|
+
_file_session: FileSessionMemory | None = field(default=None, init=False) # Primary storage
|
|
171
|
+
_short_term: RedisShortTermMemory | None = field(default=None, init=False) # Optional Redis
|
|
172
|
+
_long_term: SecureMemDocsIntegration | None = field(default=None, init=False)
|
|
173
|
+
_simple_long_term: LongTermMemory | None = field(default=None, init=False)
|
|
174
|
+
_redis_status: RedisStatus | None = field(default=None, init=False)
|
|
175
|
+
_initialized: bool = field(default=False, init=False)
|
|
176
|
+
# LRU cache for pattern lookups (pattern_id -> pattern_data)
|
|
177
|
+
_pattern_cache: dict[str, dict[str, Any]] = field(default_factory=dict, init=False)
|
|
178
|
+
_pattern_cache_max_size: int = field(default=100, init=False)
|
|
179
|
+
|
|
180
|
+
def __post_init__(self):
|
|
181
|
+
"""Initialize memory backends based on configuration."""
|
|
182
|
+
self._initialize_backends()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Meta-workflow system for dynamic agent team generation.
|
|
2
|
+
|
|
3
|
+
This package provides:
|
|
4
|
+
- Socratic form engine for requirements gathering
|
|
5
|
+
- Dynamic agent generation from templates
|
|
6
|
+
- Pattern learning from historical executions
|
|
7
|
+
- Template registry for reusable workflows
|
|
8
|
+
|
|
9
|
+
Created: 2026-01-17
|
|
10
|
+
Version: 1.0.0 (experimental)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from attune.meta_workflows.agent_creator import DynamicAgentCreator
|
|
14
|
+
from attune.meta_workflows.form_engine import SocraticFormEngine
|
|
15
|
+
from attune.meta_workflows.intent_detector import (
|
|
16
|
+
IntentDetector,
|
|
17
|
+
IntentMatch,
|
|
18
|
+
auto_detect_template,
|
|
19
|
+
detect_and_suggest,
|
|
20
|
+
)
|
|
21
|
+
from attune.meta_workflows.models import (
|
|
22
|
+
AgentCompositionRule,
|
|
23
|
+
AgentExecutionResult,
|
|
24
|
+
AgentSpec,
|
|
25
|
+
FormQuestion,
|
|
26
|
+
FormResponse,
|
|
27
|
+
FormSchema,
|
|
28
|
+
MetaWorkflowResult,
|
|
29
|
+
MetaWorkflowTemplate,
|
|
30
|
+
PatternInsight,
|
|
31
|
+
QuestionType,
|
|
32
|
+
TierStrategy,
|
|
33
|
+
)
|
|
34
|
+
from attune.meta_workflows.pattern_learner import PatternLearner
|
|
35
|
+
from attune.meta_workflows.template_registry import TemplateRegistry
|
|
36
|
+
from attune.meta_workflows.workflow import (
|
|
37
|
+
MetaWorkflow,
|
|
38
|
+
list_execution_results,
|
|
39
|
+
load_execution_result,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
__version__ = "1.0.0"
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
# Enums
|
|
46
|
+
"QuestionType",
|
|
47
|
+
"TierStrategy",
|
|
48
|
+
# Form components
|
|
49
|
+
"FormQuestion",
|
|
50
|
+
"FormSchema",
|
|
51
|
+
"FormResponse",
|
|
52
|
+
"SocraticFormEngine",
|
|
53
|
+
# Agent components
|
|
54
|
+
"AgentCompositionRule",
|
|
55
|
+
"AgentSpec",
|
|
56
|
+
"AgentExecutionResult",
|
|
57
|
+
"DynamicAgentCreator",
|
|
58
|
+
# Meta-workflow components
|
|
59
|
+
"MetaWorkflowTemplate",
|
|
60
|
+
"MetaWorkflowResult",
|
|
61
|
+
"TemplateRegistry",
|
|
62
|
+
"MetaWorkflow",
|
|
63
|
+
# Helpers
|
|
64
|
+
"list_execution_results",
|
|
65
|
+
"load_execution_result",
|
|
66
|
+
# Analytics
|
|
67
|
+
"PatternInsight",
|
|
68
|
+
"PatternLearner",
|
|
69
|
+
# Intent detection
|
|
70
|
+
"IntentDetector",
|
|
71
|
+
"IntentMatch",
|
|
72
|
+
"auto_detect_template",
|
|
73
|
+
"detect_and_suggest",
|
|
74
|
+
]
|