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,302 @@
|
|
|
1
|
+
"""Simplified long-term storage without security pipeline
|
|
2
|
+
|
|
3
|
+
Provides basic CRUD operations for long-term persistent storage without
|
|
4
|
+
the full security pipeline of SecureMemDocsIntegration. Suitable for
|
|
5
|
+
simple storage needs where PII scrubbing and encryption are not required.
|
|
6
|
+
|
|
7
|
+
Extracted from long_term.py for better modularity and testability.
|
|
8
|
+
|
|
9
|
+
Features:
|
|
10
|
+
- JSON file-based storage
|
|
11
|
+
- Classification support (PUBLIC/INTERNAL/SENSITIVE)
|
|
12
|
+
- Simple key-value interface
|
|
13
|
+
- List keys by classification
|
|
14
|
+
- Path validation for security
|
|
15
|
+
|
|
16
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
17
|
+
Licensed under Fair Source 0.9
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
from datetime import datetime
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import Any
|
|
24
|
+
|
|
25
|
+
import structlog
|
|
26
|
+
|
|
27
|
+
from attune.config import _validate_file_path
|
|
28
|
+
|
|
29
|
+
from .long_term_types import Classification
|
|
30
|
+
|
|
31
|
+
logger = structlog.get_logger(__name__)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LongTermMemory:
|
|
35
|
+
"""Simplified long-term persistent storage interface.
|
|
36
|
+
|
|
37
|
+
Provides basic CRUD operations for long-term memory storage without
|
|
38
|
+
the full security pipeline of SecureMemDocsIntegration. Suitable for
|
|
39
|
+
simple persistent storage needs where PII scrubbing and encryption
|
|
40
|
+
are not required.
|
|
41
|
+
|
|
42
|
+
Features:
|
|
43
|
+
- JSON file-based storage
|
|
44
|
+
- Classification support (PUBLIC/INTERNAL/SENSITIVE)
|
|
45
|
+
- Simple key-value interface
|
|
46
|
+
- List keys by classification
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
>>> memory = LongTermMemory(storage_path="./data")
|
|
50
|
+
>>> memory.store("config", {"setting": "value"}, classification="INTERNAL")
|
|
51
|
+
>>> data = memory.retrieve("config")
|
|
52
|
+
>>> keys = memory.list_keys(classification="INTERNAL")
|
|
53
|
+
|
|
54
|
+
Note:
|
|
55
|
+
For enterprise features (PII scrubbing, encryption, audit logging),
|
|
56
|
+
use SecureMemDocsIntegration instead.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, storage_path: str = "./long_term_storage"):
|
|
60
|
+
"""Initialize long-term memory storage.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
storage_path: Directory path for JSON storage
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
self.storage_path = Path(storage_path)
|
|
67
|
+
self.storage_path.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
logger.info("long_term_memory_initialized", storage_path=str(self.storage_path))
|
|
69
|
+
|
|
70
|
+
def store(
|
|
71
|
+
self,
|
|
72
|
+
key: str,
|
|
73
|
+
data: Any,
|
|
74
|
+
classification: str | Classification | None = None,
|
|
75
|
+
) -> bool:
|
|
76
|
+
"""Store data in long-term memory.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
key: Storage key
|
|
80
|
+
data: Data to store (must be JSON-serializable)
|
|
81
|
+
classification: Data classification (PUBLIC/INTERNAL/SENSITIVE)
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
True if stored successfully, False otherwise
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
ValueError: If key is empty or data is not JSON-serializable
|
|
88
|
+
TypeError: If data cannot be serialized to JSON
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
>>> memory = LongTermMemory()
|
|
92
|
+
>>> memory.store("user_prefs", {"theme": "dark"}, "INTERNAL")
|
|
93
|
+
True
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
if not key or not key.strip():
|
|
97
|
+
raise ValueError(f"key cannot be empty. Got: {key!r}")
|
|
98
|
+
|
|
99
|
+
# Validate key for path traversal attacks
|
|
100
|
+
if ".." in key or key.startswith("/") or "\x00" in key:
|
|
101
|
+
logger.error("path_traversal_attempt", key=key)
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
# Convert classification to string
|
|
106
|
+
classification_str = "INTERNAL" # Default
|
|
107
|
+
if classification is not None:
|
|
108
|
+
if isinstance(classification, Classification):
|
|
109
|
+
classification_str = classification.value
|
|
110
|
+
elif isinstance(classification, str):
|
|
111
|
+
# Validate classification string
|
|
112
|
+
try:
|
|
113
|
+
Classification[classification.upper()]
|
|
114
|
+
classification_str = classification.upper()
|
|
115
|
+
except KeyError:
|
|
116
|
+
logger.warning(
|
|
117
|
+
"invalid_classification",
|
|
118
|
+
classification=classification,
|
|
119
|
+
using_default="INTERNAL",
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Create storage record
|
|
123
|
+
record = {
|
|
124
|
+
"key": key,
|
|
125
|
+
"data": data,
|
|
126
|
+
"classification": classification_str,
|
|
127
|
+
"created_at": datetime.utcnow().isoformat() + "Z",
|
|
128
|
+
"updated_at": datetime.utcnow().isoformat() + "Z",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Store to JSON file
|
|
132
|
+
file_path = self.storage_path / f"{key}.json"
|
|
133
|
+
validated_file_path = _validate_file_path(str(file_path))
|
|
134
|
+
with validated_file_path.open("w", encoding="utf-8") as f:
|
|
135
|
+
json.dump(record, f, indent=2)
|
|
136
|
+
|
|
137
|
+
logger.debug("data_stored", key=key, classification=classification_str)
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
except (TypeError, ValueError) as e:
|
|
141
|
+
logger.error("store_failed", key=key, error=str(e))
|
|
142
|
+
raise
|
|
143
|
+
except (OSError, PermissionError) as e:
|
|
144
|
+
logger.error("storage_io_error", key=key, error=str(e))
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
def retrieve(self, key: str) -> Any | None:
|
|
148
|
+
"""Retrieve data from long-term memory.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
key: Storage key
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Stored data or None if not found
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
ValueError: If key is empty
|
|
158
|
+
|
|
159
|
+
Example:
|
|
160
|
+
>>> memory = LongTermMemory()
|
|
161
|
+
>>> memory.store("config", {"value": 42})
|
|
162
|
+
>>> data = memory.retrieve("config")
|
|
163
|
+
>>> print(data["value"])
|
|
164
|
+
42
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
if not key or not key.strip():
|
|
168
|
+
raise ValueError(f"key cannot be empty. Got: {key!r}")
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
file_path = self.storage_path / f"{key}.json"
|
|
172
|
+
|
|
173
|
+
if not file_path.exists():
|
|
174
|
+
logger.debug("key_not_found", key=key)
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
with file_path.open(encoding="utf-8") as f:
|
|
178
|
+
record = json.load(f)
|
|
179
|
+
|
|
180
|
+
logger.debug("data_retrieved", key=key)
|
|
181
|
+
return record.get("data")
|
|
182
|
+
|
|
183
|
+
except (OSError, PermissionError, json.JSONDecodeError) as e:
|
|
184
|
+
logger.error("retrieve_failed", key=key, error=str(e))
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
def delete(self, key: str) -> bool:
|
|
188
|
+
"""Delete data from long-term memory.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
key: Storage key
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
True if deleted, False if not found or error
|
|
195
|
+
|
|
196
|
+
Raises:
|
|
197
|
+
ValueError: If key is empty
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
>>> memory = LongTermMemory()
|
|
201
|
+
>>> memory.store("temp", {"data": "value"})
|
|
202
|
+
>>> memory.delete("temp")
|
|
203
|
+
True
|
|
204
|
+
|
|
205
|
+
"""
|
|
206
|
+
if not key or not key.strip():
|
|
207
|
+
raise ValueError(f"key cannot be empty. Got: {key!r}")
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
file_path = self.storage_path / f"{key}.json"
|
|
211
|
+
|
|
212
|
+
if not file_path.exists():
|
|
213
|
+
logger.debug("key_not_found_for_deletion", key=key)
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
file_path.unlink()
|
|
217
|
+
logger.info("data_deleted", key=key)
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
except (OSError, PermissionError) as e:
|
|
221
|
+
logger.error("delete_failed", key=key, error=str(e))
|
|
222
|
+
return False
|
|
223
|
+
|
|
224
|
+
def list_keys(self, classification: str | Classification | None = None) -> list[str]:
|
|
225
|
+
"""List all keys in long-term memory, optionally filtered by classification.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
classification: Filter by classification (PUBLIC/INTERNAL/SENSITIVE)
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
List of storage keys
|
|
232
|
+
|
|
233
|
+
Example:
|
|
234
|
+
>>> memory = LongTermMemory()
|
|
235
|
+
>>> memory.store("public_data", {"x": 1}, "PUBLIC")
|
|
236
|
+
>>> memory.store("internal_data", {"y": 2}, "INTERNAL")
|
|
237
|
+
>>> keys = memory.list_keys(classification="PUBLIC")
|
|
238
|
+
>>> print(keys)
|
|
239
|
+
['public_data']
|
|
240
|
+
|
|
241
|
+
"""
|
|
242
|
+
keys = []
|
|
243
|
+
|
|
244
|
+
# Convert classification to string if needed
|
|
245
|
+
filter_classification = None
|
|
246
|
+
if classification is not None:
|
|
247
|
+
if isinstance(classification, Classification):
|
|
248
|
+
filter_classification = classification.value
|
|
249
|
+
elif isinstance(classification, str):
|
|
250
|
+
try:
|
|
251
|
+
Classification[classification.upper()]
|
|
252
|
+
filter_classification = classification.upper()
|
|
253
|
+
except KeyError:
|
|
254
|
+
logger.warning("invalid_classification_filter", classification=classification)
|
|
255
|
+
return []
|
|
256
|
+
|
|
257
|
+
try:
|
|
258
|
+
for file_path in self.storage_path.glob("*.json"):
|
|
259
|
+
try:
|
|
260
|
+
with file_path.open(encoding="utf-8") as f:
|
|
261
|
+
record = json.load(f)
|
|
262
|
+
|
|
263
|
+
# Apply classification filter if specified
|
|
264
|
+
if filter_classification is not None:
|
|
265
|
+
if record.get("classification") != filter_classification:
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
keys.append(record.get("key", file_path.stem))
|
|
269
|
+
|
|
270
|
+
except (OSError, json.JSONDecodeError):
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
except (OSError, PermissionError) as e:
|
|
274
|
+
logger.error("list_keys_failed", error=str(e))
|
|
275
|
+
|
|
276
|
+
return keys
|
|
277
|
+
|
|
278
|
+
def clear(self) -> int:
|
|
279
|
+
"""Clear all data from long-term memory.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
Number of keys deleted
|
|
283
|
+
|
|
284
|
+
Warning:
|
|
285
|
+
This operation cannot be undone!
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
count = 0
|
|
289
|
+
try:
|
|
290
|
+
for file_path in self.storage_path.glob("*.json"):
|
|
291
|
+
try:
|
|
292
|
+
file_path.unlink()
|
|
293
|
+
count += 1
|
|
294
|
+
except (OSError, PermissionError):
|
|
295
|
+
continue
|
|
296
|
+
|
|
297
|
+
logger.warning("long_term_memory_cleared", count=count)
|
|
298
|
+
return count
|
|
299
|
+
|
|
300
|
+
except (OSError, PermissionError) as e:
|
|
301
|
+
logger.error("clear_failed", error=str(e))
|
|
302
|
+
return count
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Storage Backends for Empathy Framework Memory
|
|
2
|
+
|
|
3
|
+
Provides pluggable storage backends for pattern persistence:
|
|
4
|
+
- File-based (default): Simple JSON file storage
|
|
5
|
+
- SQLite (planned): Local database for better querying
|
|
6
|
+
- S3/Cloud (planned): Distributed storage for teams
|
|
7
|
+
|
|
8
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
9
|
+
Licensed under Fair Source 0.9
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Storage backends will be added here as they're implemented
|
|
13
|
+
# Current implementation uses file-based storage in long_term.py
|
|
14
|
+
|
|
15
|
+
__all__ = []
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""File-based storage backend for long-term memory patterns
|
|
2
|
+
|
|
3
|
+
Provides simple file-based storage for MemDocs patterns.
|
|
4
|
+
Extracted from long_term.py for better modularity and testability.
|
|
5
|
+
|
|
6
|
+
In production, this can be replaced with actual MemDocs library integration
|
|
7
|
+
or other storage backends (Redis, PostgreSQL, etc.).
|
|
8
|
+
|
|
9
|
+
Key Features:
|
|
10
|
+
- JSON-based file storage
|
|
11
|
+
- Pattern storage with metadata
|
|
12
|
+
- Query support (by classification, creator)
|
|
13
|
+
- Path validation for security
|
|
14
|
+
|
|
15
|
+
Copyright 2025 Smart AI Memory, LLC
|
|
16
|
+
Licensed under Fair Source 0.9
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
import structlog
|
|
24
|
+
|
|
25
|
+
from attune.config import _validate_file_path
|
|
26
|
+
|
|
27
|
+
logger = structlog.get_logger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class MemDocsStorage:
|
|
31
|
+
"""Mock/Simple MemDocs storage backend.
|
|
32
|
+
|
|
33
|
+
In production, this would integrate with the actual MemDocs library.
|
|
34
|
+
For now, provides a simple file-based storage for testing.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, storage_dir: str = "./memdocs_storage"):
|
|
38
|
+
"""Initialize storage backend.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
storage_dir: Directory for pattern storage
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
self.storage_dir = Path(storage_dir)
|
|
45
|
+
self.storage_dir.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
logger.info("memdocs_storage_initialized", storage_dir=str(self.storage_dir))
|
|
47
|
+
|
|
48
|
+
def store(self, pattern_id: str, content: str, metadata: dict[str, Any]) -> bool:
|
|
49
|
+
"""Store a pattern.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
pattern_id: Unique pattern identifier
|
|
53
|
+
content: Pattern content (may be encrypted)
|
|
54
|
+
metadata: Pattern metadata
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
True if successful
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
IOError: If storage fails
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
pattern_file = self.storage_dir / f"{pattern_id}.json"
|
|
65
|
+
|
|
66
|
+
# Ensure parent directory exists
|
|
67
|
+
pattern_file.parent.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
|
|
69
|
+
pattern_data = {"pattern_id": pattern_id, "content": content, "metadata": metadata}
|
|
70
|
+
|
|
71
|
+
validated_pattern_file = _validate_file_path(str(pattern_file))
|
|
72
|
+
with open(validated_pattern_file, "w", encoding="utf-8") as f:
|
|
73
|
+
json.dump(pattern_data, f, indent=2)
|
|
74
|
+
|
|
75
|
+
logger.debug("pattern_stored", pattern_id=pattern_id)
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
except (OSError, PermissionError, json.JSONDecodeError) as e:
|
|
79
|
+
logger.error("pattern_storage_failed", pattern_id=pattern_id, error=str(e))
|
|
80
|
+
raise
|
|
81
|
+
|
|
82
|
+
def retrieve(self, pattern_id: str) -> dict[str, Any] | None:
|
|
83
|
+
"""Retrieve a pattern.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
pattern_id: Unique pattern identifier
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Pattern data dictionary or None if not found
|
|
90
|
+
|
|
91
|
+
"""
|
|
92
|
+
try:
|
|
93
|
+
pattern_file = self.storage_dir / f"{pattern_id}.json"
|
|
94
|
+
|
|
95
|
+
if not pattern_file.exists():
|
|
96
|
+
logger.warning("pattern_not_found", pattern_id=pattern_id)
|
|
97
|
+
return None
|
|
98
|
+
|
|
99
|
+
with open(pattern_file, encoding="utf-8") as f:
|
|
100
|
+
pattern_data: dict[str, Any] = json.load(f)
|
|
101
|
+
|
|
102
|
+
logger.debug("pattern_retrieved", pattern_id=pattern_id)
|
|
103
|
+
return pattern_data
|
|
104
|
+
|
|
105
|
+
except (OSError, PermissionError, json.JSONDecodeError) as e:
|
|
106
|
+
logger.error("pattern_retrieval_failed", pattern_id=pattern_id, error=str(e))
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def delete(self, pattern_id: str) -> bool:
|
|
110
|
+
"""Delete a pattern.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
pattern_id: Unique pattern identifier
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
True if deleted, False if not found
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
pattern_file = self.storage_dir / f"{pattern_id}.json"
|
|
121
|
+
|
|
122
|
+
if not pattern_file.exists():
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
pattern_file.unlink()
|
|
126
|
+
logger.info("pattern_deleted", pattern_id=pattern_id)
|
|
127
|
+
return True
|
|
128
|
+
|
|
129
|
+
except (OSError, PermissionError) as e:
|
|
130
|
+
logger.error("pattern_deletion_failed", pattern_id=pattern_id, error=str(e))
|
|
131
|
+
return False
|
|
132
|
+
|
|
133
|
+
def list_patterns(
|
|
134
|
+
self,
|
|
135
|
+
classification: str | None = None,
|
|
136
|
+
created_by: str | None = None,
|
|
137
|
+
) -> list[str]:
|
|
138
|
+
"""List pattern IDs matching criteria.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
classification: Filter by classification
|
|
142
|
+
created_by: Filter by creator
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
List of pattern IDs
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
pattern_ids = []
|
|
149
|
+
|
|
150
|
+
for pattern_file in self.storage_dir.glob("*.json"):
|
|
151
|
+
try:
|
|
152
|
+
with open(pattern_file, encoding="utf-8") as f:
|
|
153
|
+
data = json.load(f)
|
|
154
|
+
metadata = data.get("metadata", {})
|
|
155
|
+
|
|
156
|
+
# Apply filters
|
|
157
|
+
if classification and metadata.get("classification") != classification:
|
|
158
|
+
continue
|
|
159
|
+
if created_by and metadata.get("created_by") != created_by:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
pattern_ids.append(data.get("pattern_id"))
|
|
163
|
+
|
|
164
|
+
except Exception:
|
|
165
|
+
continue
|
|
166
|
+
|
|
167
|
+
return pattern_ids
|