superlocalmemory 2.8.6 → 3.0.1
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.
- package/LICENSE +9 -1
- package/NOTICE +63 -0
- package/README.md +165 -480
- package/bin/slm +17 -449
- package/bin/slm-npm +62 -48
- package/conftest.py +5 -0
- package/docs/api-reference.md +284 -0
- package/docs/architecture.md +149 -0
- package/docs/auto-memory.md +150 -0
- package/docs/cli-reference.md +276 -0
- package/docs/compliance.md +191 -0
- package/docs/configuration.md +182 -0
- package/docs/getting-started.md +102 -0
- package/docs/ide-setup.md +261 -0
- package/docs/mcp-tools.md +220 -0
- package/docs/migration-from-v2.md +170 -0
- package/docs/profiles.md +173 -0
- package/docs/troubleshooting.md +310 -0
- package/{configs → ide/configs}/antigravity-mcp.json +3 -3
- package/ide/configs/chatgpt-desktop-mcp.json +16 -0
- package/{configs → ide/configs}/claude-desktop-mcp.json +3 -3
- package/{configs → ide/configs}/codex-mcp.toml +4 -4
- package/{configs → ide/configs}/continue-mcp.yaml +4 -3
- package/{configs → ide/configs}/continue-skills.yaml +6 -6
- package/ide/configs/cursor-mcp.json +15 -0
- package/{configs → ide/configs}/gemini-cli-mcp.json +2 -2
- package/{configs → ide/configs}/jetbrains-mcp.json +2 -2
- package/{configs → ide/configs}/opencode-mcp.json +2 -2
- package/{configs → ide/configs}/perplexity-mcp.json +2 -2
- package/{configs → ide/configs}/vscode-copilot-mcp.json +2 -2
- package/{configs → ide/configs}/windsurf-mcp.json +3 -3
- package/{configs → ide/configs}/zed-mcp.json +2 -2
- package/{hooks → ide/hooks}/context-hook.js +9 -20
- package/ide/hooks/memory-list-skill.js +70 -0
- package/ide/hooks/memory-profile-skill.js +101 -0
- package/ide/hooks/memory-recall-skill.js +62 -0
- package/ide/hooks/memory-remember-skill.js +68 -0
- package/ide/hooks/memory-reset-skill.js +160 -0
- package/{hooks → ide/hooks}/post-recall-hook.js +2 -2
- package/ide/integrations/langchain/README.md +106 -0
- package/ide/integrations/langchain/langchain_superlocalmemory/__init__.py +9 -0
- package/ide/integrations/langchain/langchain_superlocalmemory/chat_message_history.py +201 -0
- package/ide/integrations/langchain/pyproject.toml +38 -0
- package/{src/learning → ide/integrations/langchain}/tests/__init__.py +1 -0
- package/ide/integrations/langchain/tests/test_chat_message_history.py +215 -0
- package/ide/integrations/langchain/tests/test_security.py +117 -0
- package/ide/integrations/llamaindex/README.md +81 -0
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/__init__.py +9 -0
- package/ide/integrations/llamaindex/llama_index/storage/chat_store/superlocalmemory/base.py +316 -0
- package/ide/integrations/llamaindex/pyproject.toml +43 -0
- package/{src/lifecycle → ide/integrations/llamaindex}/tests/__init__.py +1 -2
- package/ide/integrations/llamaindex/tests/test_chat_store.py +294 -0
- package/ide/integrations/llamaindex/tests/test_security.py +241 -0
- package/{skills → ide/skills}/slm-build-graph/SKILL.md +6 -6
- package/{skills → ide/skills}/slm-list-recent/SKILL.md +5 -5
- package/{skills → ide/skills}/slm-recall/SKILL.md +5 -5
- package/{skills → ide/skills}/slm-remember/SKILL.md +6 -6
- package/{skills → ide/skills}/slm-show-patterns/SKILL.md +7 -7
- package/{skills → ide/skills}/slm-status/SKILL.md +9 -9
- package/{skills → ide/skills}/slm-switch-profile/SKILL.md +9 -9
- package/package.json +13 -22
- package/pyproject.toml +85 -0
- package/scripts/build-dmg.sh +417 -0
- package/scripts/install-skills.ps1 +334 -0
- package/scripts/postinstall.js +2 -2
- package/scripts/start-dashboard.ps1 +52 -0
- package/scripts/start-dashboard.sh +41 -0
- package/scripts/sync-wiki.ps1 +127 -0
- package/scripts/sync-wiki.sh +82 -0
- package/scripts/test-dmg.sh +161 -0
- package/scripts/test-npm-package.ps1 +252 -0
- package/scripts/test-npm-package.sh +207 -0
- package/scripts/verify-install.ps1 +294 -0
- package/scripts/verify-install.sh +266 -0
- package/src/superlocalmemory/__init__.py +0 -0
- package/src/superlocalmemory/attribution/__init__.py +9 -0
- package/src/superlocalmemory/attribution/mathematical_dna.py +235 -0
- package/src/superlocalmemory/attribution/signer.py +153 -0
- package/src/superlocalmemory/attribution/watermark.py +189 -0
- package/src/superlocalmemory/cli/__init__.py +5 -0
- package/src/superlocalmemory/cli/commands.py +245 -0
- package/src/superlocalmemory/cli/main.py +89 -0
- package/src/superlocalmemory/cli/migrate_cmd.py +55 -0
- package/src/superlocalmemory/cli/post_install.py +99 -0
- package/src/superlocalmemory/cli/setup_wizard.py +129 -0
- package/src/superlocalmemory/compliance/__init__.py +0 -0
- package/src/superlocalmemory/compliance/abac.py +204 -0
- package/src/superlocalmemory/compliance/audit.py +314 -0
- package/src/superlocalmemory/compliance/eu_ai_act.py +131 -0
- package/src/superlocalmemory/compliance/gdpr.py +294 -0
- package/src/superlocalmemory/compliance/lifecycle.py +158 -0
- package/src/superlocalmemory/compliance/retention.py +232 -0
- package/src/superlocalmemory/compliance/scheduler.py +148 -0
- package/src/superlocalmemory/core/__init__.py +0 -0
- package/src/superlocalmemory/core/config.py +391 -0
- package/src/superlocalmemory/core/embeddings.py +293 -0
- package/src/superlocalmemory/core/engine.py +701 -0
- package/src/superlocalmemory/core/hooks.py +65 -0
- package/src/superlocalmemory/core/maintenance.py +172 -0
- package/src/superlocalmemory/core/modes.py +140 -0
- package/src/superlocalmemory/core/profiles.py +234 -0
- package/src/superlocalmemory/core/registry.py +117 -0
- package/src/superlocalmemory/dynamics/__init__.py +0 -0
- package/src/superlocalmemory/dynamics/fisher_langevin_coupling.py +223 -0
- package/src/superlocalmemory/encoding/__init__.py +0 -0
- package/src/superlocalmemory/encoding/consolidator.py +485 -0
- package/src/superlocalmemory/encoding/emotional.py +125 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +525 -0
- package/src/superlocalmemory/encoding/entropy_gate.py +104 -0
- package/src/superlocalmemory/encoding/fact_extractor.py +775 -0
- package/src/superlocalmemory/encoding/foresight.py +91 -0
- package/src/superlocalmemory/encoding/graph_builder.py +302 -0
- package/src/superlocalmemory/encoding/observation_builder.py +160 -0
- package/src/superlocalmemory/encoding/scene_builder.py +183 -0
- package/src/superlocalmemory/encoding/signal_inference.py +90 -0
- package/src/superlocalmemory/encoding/temporal_parser.py +426 -0
- package/src/superlocalmemory/encoding/type_router.py +235 -0
- package/src/superlocalmemory/hooks/__init__.py +3 -0
- package/src/superlocalmemory/hooks/auto_capture.py +111 -0
- package/src/superlocalmemory/hooks/auto_recall.py +93 -0
- package/src/superlocalmemory/hooks/ide_connector.py +204 -0
- package/src/superlocalmemory/hooks/rules_engine.py +99 -0
- package/src/superlocalmemory/infra/__init__.py +3 -0
- package/src/superlocalmemory/infra/auth_middleware.py +82 -0
- package/src/superlocalmemory/infra/backup.py +317 -0
- package/src/superlocalmemory/infra/cache_manager.py +267 -0
- package/src/superlocalmemory/infra/event_bus.py +381 -0
- package/src/superlocalmemory/infra/rate_limiter.py +135 -0
- package/src/{webhook_dispatcher.py → superlocalmemory/infra/webhook_dispatcher.py} +104 -101
- package/src/superlocalmemory/learning/__init__.py +0 -0
- package/src/superlocalmemory/learning/adaptive.py +172 -0
- package/src/superlocalmemory/learning/behavioral.py +490 -0
- package/src/superlocalmemory/learning/behavioral_listener.py +94 -0
- package/src/superlocalmemory/learning/bootstrap.py +298 -0
- package/src/superlocalmemory/learning/cross_project.py +399 -0
- package/src/superlocalmemory/learning/database.py +376 -0
- package/src/superlocalmemory/learning/engagement.py +323 -0
- package/src/superlocalmemory/learning/features.py +138 -0
- package/src/superlocalmemory/learning/feedback.py +316 -0
- package/src/superlocalmemory/learning/outcomes.py +255 -0
- package/src/superlocalmemory/learning/project_context.py +366 -0
- package/src/superlocalmemory/learning/ranker.py +155 -0
- package/src/superlocalmemory/learning/source_quality.py +303 -0
- package/src/superlocalmemory/learning/workflows.py +309 -0
- package/src/superlocalmemory/llm/__init__.py +0 -0
- package/src/superlocalmemory/llm/backbone.py +316 -0
- package/src/superlocalmemory/math/__init__.py +0 -0
- package/src/superlocalmemory/math/fisher.py +356 -0
- package/src/superlocalmemory/math/langevin.py +398 -0
- package/src/superlocalmemory/math/sheaf.py +257 -0
- package/src/superlocalmemory/mcp/__init__.py +0 -0
- package/src/superlocalmemory/mcp/resources.py +245 -0
- package/src/superlocalmemory/mcp/server.py +61 -0
- package/src/superlocalmemory/mcp/tools.py +18 -0
- package/src/superlocalmemory/mcp/tools_core.py +305 -0
- package/src/superlocalmemory/mcp/tools_v28.py +223 -0
- package/src/superlocalmemory/mcp/tools_v3.py +286 -0
- package/src/superlocalmemory/retrieval/__init__.py +0 -0
- package/src/superlocalmemory/retrieval/agentic.py +295 -0
- package/src/superlocalmemory/retrieval/ann_index.py +223 -0
- package/src/superlocalmemory/retrieval/bm25_channel.py +185 -0
- package/src/superlocalmemory/retrieval/bridge_discovery.py +170 -0
- package/src/superlocalmemory/retrieval/engine.py +390 -0
- package/src/superlocalmemory/retrieval/entity_channel.py +179 -0
- package/src/superlocalmemory/retrieval/fusion.py +78 -0
- package/src/superlocalmemory/retrieval/profile_channel.py +105 -0
- package/src/superlocalmemory/retrieval/reranker.py +154 -0
- package/src/superlocalmemory/retrieval/semantic_channel.py +232 -0
- package/src/superlocalmemory/retrieval/strategy.py +96 -0
- package/src/superlocalmemory/retrieval/temporal_channel.py +175 -0
- package/src/superlocalmemory/server/__init__.py +1 -0
- package/src/superlocalmemory/server/api.py +248 -0
- package/src/superlocalmemory/server/routes/__init__.py +4 -0
- package/src/superlocalmemory/server/routes/agents.py +107 -0
- package/src/superlocalmemory/server/routes/backup.py +91 -0
- package/src/superlocalmemory/server/routes/behavioral.py +127 -0
- package/src/superlocalmemory/server/routes/compliance.py +160 -0
- package/src/superlocalmemory/server/routes/data_io.py +188 -0
- package/src/superlocalmemory/server/routes/events.py +183 -0
- package/src/superlocalmemory/server/routes/helpers.py +85 -0
- package/src/superlocalmemory/server/routes/learning.py +273 -0
- package/src/superlocalmemory/server/routes/lifecycle.py +116 -0
- package/src/superlocalmemory/server/routes/memories.py +399 -0
- package/src/superlocalmemory/server/routes/profiles.py +219 -0
- package/src/superlocalmemory/server/routes/stats.py +346 -0
- package/src/superlocalmemory/server/routes/v3_api.py +365 -0
- package/src/superlocalmemory/server/routes/ws.py +82 -0
- package/src/superlocalmemory/server/security_middleware.py +57 -0
- package/src/superlocalmemory/server/ui.py +245 -0
- package/src/superlocalmemory/storage/__init__.py +0 -0
- package/src/superlocalmemory/storage/access_control.py +182 -0
- package/src/superlocalmemory/storage/database.py +594 -0
- package/src/superlocalmemory/storage/migrations.py +303 -0
- package/src/superlocalmemory/storage/models.py +406 -0
- package/src/superlocalmemory/storage/schema.py +726 -0
- package/src/superlocalmemory/storage/v2_migrator.py +317 -0
- package/src/superlocalmemory/trust/__init__.py +0 -0
- package/src/superlocalmemory/trust/gate.py +130 -0
- package/src/superlocalmemory/trust/provenance.py +124 -0
- package/src/superlocalmemory/trust/scorer.py +347 -0
- package/src/superlocalmemory/trust/signals.py +153 -0
- package/ui/index.html +278 -5
- package/ui/js/auto-settings.js +70 -0
- package/ui/js/dashboard.js +90 -0
- package/ui/js/fact-detail.js +92 -0
- package/ui/js/feedback.js +2 -2
- package/ui/js/ide-status.js +102 -0
- package/ui/js/math-health.js +98 -0
- package/ui/js/recall-lab.js +127 -0
- package/ui/js/settings.js +2 -2
- package/ui/js/trust-dashboard.js +73 -0
- package/api_server.py +0 -724
- package/bin/aider-smart +0 -72
- package/bin/superlocalmemoryv2-learning +0 -4
- package/bin/superlocalmemoryv2-list +0 -3
- package/bin/superlocalmemoryv2-patterns +0 -4
- package/bin/superlocalmemoryv2-profile +0 -3
- package/bin/superlocalmemoryv2-recall +0 -3
- package/bin/superlocalmemoryv2-remember +0 -3
- package/bin/superlocalmemoryv2-reset +0 -3
- package/bin/superlocalmemoryv2-status +0 -3
- package/configs/chatgpt-desktop-mcp.json +0 -16
- package/configs/cursor-mcp.json +0 -15
- package/hooks/memory-list-skill.js +0 -139
- package/hooks/memory-profile-skill.js +0 -273
- package/hooks/memory-recall-skill.js +0 -114
- package/hooks/memory-remember-skill.js +0 -127
- package/hooks/memory-reset-skill.js +0 -274
- package/mcp_server.py +0 -1808
- package/requirements-core.txt +0 -22
- package/requirements-learning.txt +0 -12
- package/requirements.txt +0 -12
- package/src/agent_registry.py +0 -411
- package/src/auth_middleware.py +0 -61
- package/src/auto_backup.py +0 -459
- package/src/behavioral/__init__.py +0 -49
- package/src/behavioral/behavioral_listener.py +0 -203
- package/src/behavioral/behavioral_patterns.py +0 -275
- package/src/behavioral/cross_project_transfer.py +0 -206
- package/src/behavioral/outcome_inference.py +0 -194
- package/src/behavioral/outcome_tracker.py +0 -193
- package/src/behavioral/tests/__init__.py +0 -4
- package/src/behavioral/tests/test_behavioral_integration.py +0 -108
- package/src/behavioral/tests/test_behavioral_patterns.py +0 -150
- package/src/behavioral/tests/test_cross_project_transfer.py +0 -142
- package/src/behavioral/tests/test_mcp_behavioral.py +0 -139
- package/src/behavioral/tests/test_mcp_report_outcome.py +0 -117
- package/src/behavioral/tests/test_outcome_inference.py +0 -107
- package/src/behavioral/tests/test_outcome_tracker.py +0 -96
- package/src/cache_manager.py +0 -518
- package/src/compliance/__init__.py +0 -48
- package/src/compliance/abac_engine.py +0 -149
- package/src/compliance/abac_middleware.py +0 -116
- package/src/compliance/audit_db.py +0 -215
- package/src/compliance/audit_logger.py +0 -148
- package/src/compliance/retention_manager.py +0 -289
- package/src/compliance/retention_scheduler.py +0 -186
- package/src/compliance/tests/__init__.py +0 -4
- package/src/compliance/tests/test_abac_enforcement.py +0 -95
- package/src/compliance/tests/test_abac_engine.py +0 -124
- package/src/compliance/tests/test_abac_mcp_integration.py +0 -118
- package/src/compliance/tests/test_audit_db.py +0 -123
- package/src/compliance/tests/test_audit_logger.py +0 -98
- package/src/compliance/tests/test_mcp_audit.py +0 -128
- package/src/compliance/tests/test_mcp_retention_policy.py +0 -125
- package/src/compliance/tests/test_retention_manager.py +0 -131
- package/src/compliance/tests/test_retention_scheduler.py +0 -99
- package/src/compression/__init__.py +0 -25
- package/src/compression/cli.py +0 -150
- package/src/compression/cold_storage.py +0 -217
- package/src/compression/config.py +0 -72
- package/src/compression/orchestrator.py +0 -133
- package/src/compression/tier2_compressor.py +0 -228
- package/src/compression/tier3_compressor.py +0 -153
- package/src/compression/tier_classifier.py +0 -148
- package/src/db_connection_manager.py +0 -536
- package/src/embedding_engine.py +0 -63
- package/src/embeddings/__init__.py +0 -47
- package/src/embeddings/cache.py +0 -70
- package/src/embeddings/cli.py +0 -113
- package/src/embeddings/constants.py +0 -47
- package/src/embeddings/database.py +0 -91
- package/src/embeddings/engine.py +0 -247
- package/src/embeddings/model_loader.py +0 -145
- package/src/event_bus.py +0 -562
- package/src/graph/__init__.py +0 -36
- package/src/graph/build_helpers.py +0 -74
- package/src/graph/cli.py +0 -87
- package/src/graph/cluster_builder.py +0 -188
- package/src/graph/cluster_summary.py +0 -148
- package/src/graph/constants.py +0 -47
- package/src/graph/edge_builder.py +0 -162
- package/src/graph/entity_extractor.py +0 -95
- package/src/graph/graph_core.py +0 -226
- package/src/graph/graph_search.py +0 -231
- package/src/graph/hierarchical.py +0 -207
- package/src/graph/schema.py +0 -99
- package/src/graph_engine.py +0 -52
- package/src/hnsw_index.py +0 -628
- package/src/hybrid_search.py +0 -46
- package/src/learning/__init__.py +0 -217
- package/src/learning/adaptive_ranker.py +0 -682
- package/src/learning/bootstrap/__init__.py +0 -69
- package/src/learning/bootstrap/constants.py +0 -93
- package/src/learning/bootstrap/db_queries.py +0 -316
- package/src/learning/bootstrap/sampling.py +0 -82
- package/src/learning/bootstrap/text_utils.py +0 -71
- package/src/learning/cross_project_aggregator.py +0 -857
- package/src/learning/db/__init__.py +0 -40
- package/src/learning/db/constants.py +0 -44
- package/src/learning/db/schema.py +0 -279
- package/src/learning/engagement_tracker.py +0 -628
- package/src/learning/feature_extractor.py +0 -708
- package/src/learning/feedback_collector.py +0 -806
- package/src/learning/learning_db.py +0 -915
- package/src/learning/project_context_manager.py +0 -572
- package/src/learning/ranking/__init__.py +0 -33
- package/src/learning/ranking/constants.py +0 -84
- package/src/learning/ranking/helpers.py +0 -278
- package/src/learning/source_quality_scorer.py +0 -676
- package/src/learning/synthetic_bootstrap.py +0 -755
- package/src/learning/tests/test_adaptive_ranker.py +0 -325
- package/src/learning/tests/test_adaptive_ranker_v28.py +0 -60
- package/src/learning/tests/test_aggregator.py +0 -306
- package/src/learning/tests/test_auto_retrain_v28.py +0 -35
- package/src/learning/tests/test_e2e_ranking_v28.py +0 -82
- package/src/learning/tests/test_feature_extractor_v28.py +0 -93
- package/src/learning/tests/test_feedback_collector.py +0 -294
- package/src/learning/tests/test_learning_db.py +0 -602
- package/src/learning/tests/test_learning_db_v28.py +0 -110
- package/src/learning/tests/test_learning_init_v28.py +0 -48
- package/src/learning/tests/test_outcome_signals.py +0 -48
- package/src/learning/tests/test_project_context.py +0 -292
- package/src/learning/tests/test_schema_migration.py +0 -319
- package/src/learning/tests/test_signal_inference.py +0 -397
- package/src/learning/tests/test_source_quality.py +0 -351
- package/src/learning/tests/test_synthetic_bootstrap.py +0 -429
- package/src/learning/tests/test_workflow_miner.py +0 -318
- package/src/learning/workflow_pattern_miner.py +0 -655
- package/src/lifecycle/__init__.py +0 -54
- package/src/lifecycle/bounded_growth.py +0 -239
- package/src/lifecycle/compaction_engine.py +0 -226
- package/src/lifecycle/lifecycle_engine.py +0 -355
- package/src/lifecycle/lifecycle_evaluator.py +0 -257
- package/src/lifecycle/lifecycle_scheduler.py +0 -130
- package/src/lifecycle/retention_policy.py +0 -285
- package/src/lifecycle/tests/test_bounded_growth.py +0 -193
- package/src/lifecycle/tests/test_compaction.py +0 -179
- package/src/lifecycle/tests/test_lifecycle_engine.py +0 -137
- package/src/lifecycle/tests/test_lifecycle_evaluation.py +0 -177
- package/src/lifecycle/tests/test_lifecycle_scheduler.py +0 -127
- package/src/lifecycle/tests/test_lifecycle_search.py +0 -109
- package/src/lifecycle/tests/test_mcp_compact.py +0 -149
- package/src/lifecycle/tests/test_mcp_lifecycle_status.py +0 -114
- package/src/lifecycle/tests/test_retention_policy.py +0 -162
- package/src/mcp_tools_v28.py +0 -281
- package/src/memory/__init__.py +0 -36
- package/src/memory/cli.py +0 -205
- package/src/memory/constants.py +0 -39
- package/src/memory/helpers.py +0 -28
- package/src/memory/schema.py +0 -166
- package/src/memory-profiles.py +0 -595
- package/src/memory-reset.py +0 -491
- package/src/memory_compression.py +0 -989
- package/src/memory_store_v2.py +0 -1155
- package/src/migrate_v1_to_v2.py +0 -629
- package/src/pattern_learner.py +0 -34
- package/src/patterns/__init__.py +0 -24
- package/src/patterns/analyzers.py +0 -251
- package/src/patterns/learner.py +0 -271
- package/src/patterns/scoring.py +0 -171
- package/src/patterns/store.py +0 -225
- package/src/patterns/terminology.py +0 -140
- package/src/provenance_tracker.py +0 -312
- package/src/qualixar_attribution.py +0 -139
- package/src/qualixar_watermark.py +0 -78
- package/src/query_optimizer.py +0 -511
- package/src/rate_limiter.py +0 -83
- package/src/search/__init__.py +0 -20
- package/src/search/cli.py +0 -77
- package/src/search/constants.py +0 -26
- package/src/search/engine.py +0 -241
- package/src/search/fusion.py +0 -122
- package/src/search/index_loader.py +0 -114
- package/src/search/methods.py +0 -162
- package/src/search_engine_v2.py +0 -401
- package/src/setup_validator.py +0 -482
- package/src/subscription_manager.py +0 -391
- package/src/tree/__init__.py +0 -59
- package/src/tree/builder.py +0 -185
- package/src/tree/nodes.py +0 -202
- package/src/tree/queries.py +0 -257
- package/src/tree/schema.py +0 -80
- package/src/tree_manager.py +0 -19
- package/src/trust/__init__.py +0 -45
- package/src/trust/constants.py +0 -66
- package/src/trust/queries.py +0 -157
- package/src/trust/schema.py +0 -95
- package/src/trust/scorer.py +0 -299
- package/src/trust/signals.py +0 -95
- package/src/trust_scorer.py +0 -44
- package/ui/app.js +0 -1588
- package/ui/js/graph-cytoscape-monolithic-backup.js +0 -1168
- package/ui/js/graph-cytoscape.js +0 -1168
- package/ui/js/graph-d3-backup.js +0 -32
- package/ui/js/graph.js +0 -32
- package/ui_server.py +0 -286
- /package/docs/{ACCESSIBILITY.md → v2-archive/ACCESSIBILITY.md} +0 -0
- /package/docs/{ARCHITECTURE.md → v2-archive/ARCHITECTURE.md} +0 -0
- /package/docs/{CLI-COMMANDS-REFERENCE.md → v2-archive/CLI-COMMANDS-REFERENCE.md} +0 -0
- /package/docs/{COMPRESSION-README.md → v2-archive/COMPRESSION-README.md} +0 -0
- /package/docs/{FRAMEWORK-INTEGRATIONS.md → v2-archive/FRAMEWORK-INTEGRATIONS.md} +0 -0
- /package/docs/{MCP-MANUAL-SETUP.md → v2-archive/MCP-MANUAL-SETUP.md} +0 -0
- /package/docs/{MCP-TROUBLESHOOTING.md → v2-archive/MCP-TROUBLESHOOTING.md} +0 -0
- /package/docs/{PATTERN-LEARNING.md → v2-archive/PATTERN-LEARNING.md} +0 -0
- /package/docs/{PROFILES-GUIDE.md → v2-archive/PROFILES-GUIDE.md} +0 -0
- /package/docs/{RESET-GUIDE.md → v2-archive/RESET-GUIDE.md} +0 -0
- /package/docs/{SEARCH-ENGINE-V2.2.0.md → v2-archive/SEARCH-ENGINE-V2.2.0.md} +0 -0
- /package/docs/{SEARCH-INTEGRATION-GUIDE.md → v2-archive/SEARCH-INTEGRATION-GUIDE.md} +0 -0
- /package/docs/{UI-SERVER.md → v2-archive/UI-SERVER.md} +0 -0
- /package/docs/{UNIVERSAL-INTEGRATION.md → v2-archive/UNIVERSAL-INTEGRATION.md} +0 -0
- /package/docs/{V2.2.0-OPTIONAL-SEARCH.md → v2-archive/V2.2.0-OPTIONAL-SEARCH.md} +0 -0
- /package/docs/{WINDOWS-INSTALL-README.txt → v2-archive/WINDOWS-INSTALL-README.txt} +0 -0
- /package/docs/{WINDOWS-POST-INSTALL.txt → v2-archive/WINDOWS-POST-INSTALL.txt} +0 -0
- /package/docs/{example_graph_usage.py → v2-archive/example_graph_usage.py} +0 -0
- /package/{completions → ide/completions}/slm.bash +0 -0
- /package/{completions → ide/completions}/slm.zsh +0 -0
- /package/{configs → ide/configs}/cody-commands.json +0 -0
- /package/{install-skills.sh → scripts/install-skills.sh} +0 -0
- /package/{install.ps1 → scripts/install.ps1} +0 -0
- /package/{install.sh → scripts/install.sh} +0 -0
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""Graph traversal and query operations.
|
|
5
|
-
|
|
6
|
-
Provides graph traversal (get_related), cluster membership queries,
|
|
7
|
-
and graph statistics collection for the active profile.
|
|
8
|
-
"""
|
|
9
|
-
import sqlite3
|
|
10
|
-
import json
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
from typing import List, Dict
|
|
13
|
-
|
|
14
|
-
from graph.constants import logger, MEMORY_DIR
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _get_active_profile() -> str:
|
|
18
|
-
"""Get the currently active profile name from config."""
|
|
19
|
-
config_file = MEMORY_DIR / "profiles.json"
|
|
20
|
-
if config_file.exists():
|
|
21
|
-
try:
|
|
22
|
-
with open(config_file, 'r') as f:
|
|
23
|
-
config = json.load(f)
|
|
24
|
-
return config.get('active_profile', 'default')
|
|
25
|
-
except (json.JSONDecodeError, IOError):
|
|
26
|
-
pass
|
|
27
|
-
return 'default'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def get_related(db_path: Path, memory_id: int, max_hops: int = 2) -> List[Dict]:
|
|
31
|
-
"""
|
|
32
|
-
Get memories connected to this memory via graph edges (active profile only).
|
|
33
|
-
|
|
34
|
-
Args:
|
|
35
|
-
db_path: Path to SQLite database
|
|
36
|
-
memory_id: Source memory ID
|
|
37
|
-
max_hops: Maximum traversal depth (1 or 2)
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
List of related memory dictionaries
|
|
41
|
-
"""
|
|
42
|
-
conn = sqlite3.connect(db_path)
|
|
43
|
-
cursor = conn.cursor()
|
|
44
|
-
active_profile = _get_active_profile()
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
# Get 1-hop neighbors (filtered to active profile)
|
|
48
|
-
edges = cursor.execute('''
|
|
49
|
-
SELECT ge.target_memory_id, ge.relationship_type, ge.weight, ge.shared_entities
|
|
50
|
-
FROM graph_edges ge
|
|
51
|
-
JOIN memories m ON ge.target_memory_id = m.id
|
|
52
|
-
WHERE ge.source_memory_id = ? AND m.profile = ?
|
|
53
|
-
UNION
|
|
54
|
-
SELECT ge.source_memory_id, ge.relationship_type, ge.weight, ge.shared_entities
|
|
55
|
-
FROM graph_edges ge
|
|
56
|
-
JOIN memories m ON ge.source_memory_id = m.id
|
|
57
|
-
WHERE ge.target_memory_id = ? AND m.profile = ?
|
|
58
|
-
''', (memory_id, active_profile, memory_id, active_profile)).fetchall()
|
|
59
|
-
|
|
60
|
-
results = []
|
|
61
|
-
seen_ids = {memory_id}
|
|
62
|
-
|
|
63
|
-
for target_id, rel_type, weight, shared_entities in edges:
|
|
64
|
-
if target_id in seen_ids:
|
|
65
|
-
continue
|
|
66
|
-
|
|
67
|
-
seen_ids.add(target_id)
|
|
68
|
-
|
|
69
|
-
# Get memory details
|
|
70
|
-
memory = cursor.execute('''
|
|
71
|
-
SELECT id, summary, importance, tags
|
|
72
|
-
FROM memories WHERE id = ?
|
|
73
|
-
''', (target_id,)).fetchone()
|
|
74
|
-
|
|
75
|
-
if memory:
|
|
76
|
-
results.append({
|
|
77
|
-
'id': memory[0],
|
|
78
|
-
'summary': memory[1],
|
|
79
|
-
'importance': memory[2],
|
|
80
|
-
'tags': json.loads(memory[3]) if memory[3] else [],
|
|
81
|
-
'relationship': rel_type,
|
|
82
|
-
'weight': weight,
|
|
83
|
-
'shared_entities': json.loads(shared_entities) if shared_entities else [],
|
|
84
|
-
'hops': 1
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
# If max_hops == 2, get 2-hop neighbors
|
|
88
|
-
if max_hops >= 2:
|
|
89
|
-
for result in results[:]: # Copy to avoid modification during iteration
|
|
90
|
-
second_hop = cursor.execute('''
|
|
91
|
-
SELECT target_memory_id, relationship_type, weight
|
|
92
|
-
FROM graph_edges
|
|
93
|
-
WHERE source_memory_id = ?
|
|
94
|
-
UNION
|
|
95
|
-
SELECT source_memory_id, relationship_type, weight
|
|
96
|
-
FROM graph_edges
|
|
97
|
-
WHERE target_memory_id = ?
|
|
98
|
-
''', (result['id'], result['id'])).fetchall()
|
|
99
|
-
|
|
100
|
-
for target_id, rel_type, weight in second_hop:
|
|
101
|
-
if target_id in seen_ids:
|
|
102
|
-
continue
|
|
103
|
-
|
|
104
|
-
seen_ids.add(target_id)
|
|
105
|
-
|
|
106
|
-
memory = cursor.execute('''
|
|
107
|
-
SELECT id, summary, importance, tags
|
|
108
|
-
FROM memories WHERE id = ?
|
|
109
|
-
''', (target_id,)).fetchone()
|
|
110
|
-
|
|
111
|
-
if memory:
|
|
112
|
-
results.append({
|
|
113
|
-
'id': memory[0],
|
|
114
|
-
'summary': memory[1],
|
|
115
|
-
'importance': memory[2],
|
|
116
|
-
'tags': json.loads(memory[3]) if memory[3] else [],
|
|
117
|
-
'relationship': rel_type,
|
|
118
|
-
'weight': weight,
|
|
119
|
-
'shared_entities': [],
|
|
120
|
-
'hops': 2
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
# Sort by weight (strongest connections first)
|
|
124
|
-
results.sort(key=lambda x: (-x['hops'], -x['weight']))
|
|
125
|
-
|
|
126
|
-
return results
|
|
127
|
-
|
|
128
|
-
finally:
|
|
129
|
-
conn.close()
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def get_cluster_members(db_path: Path, cluster_id: int) -> List[Dict]:
|
|
133
|
-
"""
|
|
134
|
-
Get all memories in a cluster (filtered by active profile).
|
|
135
|
-
|
|
136
|
-
Args:
|
|
137
|
-
db_path: Path to SQLite database
|
|
138
|
-
cluster_id: Cluster ID
|
|
139
|
-
|
|
140
|
-
Returns:
|
|
141
|
-
List of memory dictionaries
|
|
142
|
-
"""
|
|
143
|
-
conn = sqlite3.connect(db_path)
|
|
144
|
-
cursor = conn.cursor()
|
|
145
|
-
active_profile = _get_active_profile()
|
|
146
|
-
|
|
147
|
-
try:
|
|
148
|
-
memories = cursor.execute('''
|
|
149
|
-
SELECT id, summary, importance, tags, created_at
|
|
150
|
-
FROM memories
|
|
151
|
-
WHERE cluster_id = ? AND profile = ?
|
|
152
|
-
ORDER BY importance DESC
|
|
153
|
-
''', (cluster_id, active_profile)).fetchall()
|
|
154
|
-
|
|
155
|
-
return [
|
|
156
|
-
{
|
|
157
|
-
'id': m[0],
|
|
158
|
-
'summary': m[1],
|
|
159
|
-
'importance': m[2],
|
|
160
|
-
'tags': json.loads(m[3]) if m[3] else [],
|
|
161
|
-
'created_at': m[4]
|
|
162
|
-
}
|
|
163
|
-
for m in memories
|
|
164
|
-
]
|
|
165
|
-
|
|
166
|
-
finally:
|
|
167
|
-
conn.close()
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def get_stats(db_path: Path) -> Dict[str, any]:
|
|
171
|
-
"""Get graph statistics for the active profile."""
|
|
172
|
-
conn = sqlite3.connect(db_path)
|
|
173
|
-
cursor = conn.cursor()
|
|
174
|
-
active_profile = _get_active_profile()
|
|
175
|
-
|
|
176
|
-
try:
|
|
177
|
-
# Count nodes for active profile's memories
|
|
178
|
-
nodes = cursor.execute('''
|
|
179
|
-
SELECT COUNT(*) FROM graph_nodes
|
|
180
|
-
WHERE memory_id IN (SELECT id FROM memories WHERE profile = ?)
|
|
181
|
-
''', (active_profile,)).fetchone()[0]
|
|
182
|
-
|
|
183
|
-
# Count edges where at least one end is in active profile
|
|
184
|
-
edges = cursor.execute('''
|
|
185
|
-
SELECT COUNT(*) FROM graph_edges
|
|
186
|
-
WHERE source_memory_id IN (SELECT id FROM memories WHERE profile = ?)
|
|
187
|
-
''', (active_profile,)).fetchone()[0]
|
|
188
|
-
|
|
189
|
-
# Clusters that have members in active profile
|
|
190
|
-
clusters = cursor.execute('''
|
|
191
|
-
SELECT COUNT(DISTINCT cluster_id) FROM memories
|
|
192
|
-
WHERE cluster_id IS NOT NULL AND profile = ?
|
|
193
|
-
''', (active_profile,)).fetchone()[0]
|
|
194
|
-
|
|
195
|
-
# Cluster breakdown for active profile (including hierarchy)
|
|
196
|
-
cluster_info = cursor.execute('''
|
|
197
|
-
SELECT gc.name, gc.member_count, gc.avg_importance,
|
|
198
|
-
gc.summary, gc.parent_cluster_id, gc.depth
|
|
199
|
-
FROM graph_clusters gc
|
|
200
|
-
WHERE gc.id IN (
|
|
201
|
-
SELECT DISTINCT cluster_id FROM memories
|
|
202
|
-
WHERE cluster_id IS NOT NULL AND profile = ?
|
|
203
|
-
)
|
|
204
|
-
ORDER BY gc.depth ASC, gc.member_count DESC
|
|
205
|
-
LIMIT 20
|
|
206
|
-
''', (active_profile,)).fetchall()
|
|
207
|
-
|
|
208
|
-
# Count hierarchical depth
|
|
209
|
-
max_depth = max((c[5] or 0 for c in cluster_info), default=0) if cluster_info else 0
|
|
210
|
-
|
|
211
|
-
return {
|
|
212
|
-
'profile': active_profile,
|
|
213
|
-
'nodes': nodes,
|
|
214
|
-
'edges': edges,
|
|
215
|
-
'clusters': clusters,
|
|
216
|
-
'max_depth': max_depth,
|
|
217
|
-
'top_clusters': [
|
|
218
|
-
{
|
|
219
|
-
'name': c[0],
|
|
220
|
-
'members': c[1],
|
|
221
|
-
'avg_importance': round(c[2], 1) if c[2] else 5.0,
|
|
222
|
-
'summary': c[3],
|
|
223
|
-
'parent_cluster_id': c[4],
|
|
224
|
-
'depth': c[5] or 0
|
|
225
|
-
}
|
|
226
|
-
for c in cluster_info
|
|
227
|
-
]
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
finally:
|
|
231
|
-
conn.close()
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""Hierarchical sub-clustering for the graph engine.
|
|
5
|
-
|
|
6
|
-
Implements recursive Leiden-based hierarchical clustering that decomposes
|
|
7
|
-
large communities into finer-grained thematic sub-clusters.
|
|
8
|
-
"""
|
|
9
|
-
import sqlite3
|
|
10
|
-
from typing import List, Dict, Tuple
|
|
11
|
-
|
|
12
|
-
from graph.constants import logger, IGRAPH_AVAILABLE, MEMORY_DIR
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def _get_active_profile() -> str:
|
|
16
|
-
"""Get the currently active profile name from config."""
|
|
17
|
-
import json
|
|
18
|
-
config_file = MEMORY_DIR / "profiles.json"
|
|
19
|
-
if config_file.exists():
|
|
20
|
-
try:
|
|
21
|
-
with open(config_file, 'r') as f:
|
|
22
|
-
config = json.load(f)
|
|
23
|
-
return config.get('active_profile', 'default')
|
|
24
|
-
except (json.JSONDecodeError, IOError):
|
|
25
|
-
pass
|
|
26
|
-
return 'default'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
def hierarchical_cluster(db_path, get_avg_importance_fn, generate_cluster_name_fn,
|
|
30
|
-
min_subcluster_size: int = 5, max_depth: int = 3) -> Dict[str, any]:
|
|
31
|
-
"""
|
|
32
|
-
Run recursive Leiden clustering -- cluster the clusters.
|
|
33
|
-
|
|
34
|
-
Large communities (>= min_subcluster_size * 2) are recursively sub-clustered
|
|
35
|
-
to reveal finer-grained thematic structure.
|
|
36
|
-
|
|
37
|
-
Args:
|
|
38
|
-
db_path: Path to SQLite database
|
|
39
|
-
get_avg_importance_fn: Callback to compute avg importance for memory IDs
|
|
40
|
-
generate_cluster_name_fn: Callback to generate cluster name from memory IDs
|
|
41
|
-
min_subcluster_size: Minimum members to attempt sub-clustering (default 5)
|
|
42
|
-
max_depth: Maximum recursion depth (default 3)
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
Dictionary with hierarchical clustering statistics
|
|
46
|
-
"""
|
|
47
|
-
if not IGRAPH_AVAILABLE:
|
|
48
|
-
logger.warning("igraph/leidenalg not installed. Hierarchical clustering disabled. Install with: pip3 install python-igraph leidenalg")
|
|
49
|
-
return {'subclusters_created': 0, 'depth_reached': 0}
|
|
50
|
-
import igraph as ig
|
|
51
|
-
import leidenalg
|
|
52
|
-
|
|
53
|
-
conn = sqlite3.connect(db_path)
|
|
54
|
-
cursor = conn.cursor()
|
|
55
|
-
active_profile = _get_active_profile()
|
|
56
|
-
|
|
57
|
-
try:
|
|
58
|
-
# Get top-level clusters for this profile that are large enough to sub-cluster
|
|
59
|
-
cursor.execute('''
|
|
60
|
-
SELECT cluster_id, COUNT(*) as cnt
|
|
61
|
-
FROM memories
|
|
62
|
-
WHERE cluster_id IS NOT NULL AND profile = ?
|
|
63
|
-
GROUP BY cluster_id
|
|
64
|
-
HAVING cnt >= ?
|
|
65
|
-
''', (active_profile, min_subcluster_size * 2))
|
|
66
|
-
large_clusters = cursor.fetchall()
|
|
67
|
-
|
|
68
|
-
if not large_clusters:
|
|
69
|
-
logger.info("No clusters large enough for hierarchical decomposition")
|
|
70
|
-
return {'subclusters_created': 0, 'depth_reached': 0}
|
|
71
|
-
|
|
72
|
-
total_subclusters = 0
|
|
73
|
-
max_depth_reached = 0
|
|
74
|
-
|
|
75
|
-
for parent_cid, member_count in large_clusters:
|
|
76
|
-
subs, depth = _recursive_subcluster(
|
|
77
|
-
conn, cursor, parent_cid, active_profile,
|
|
78
|
-
min_subcluster_size, max_depth, current_depth=1,
|
|
79
|
-
get_avg_importance_fn=get_avg_importance_fn,
|
|
80
|
-
generate_cluster_name_fn=generate_cluster_name_fn,
|
|
81
|
-
)
|
|
82
|
-
total_subclusters += subs
|
|
83
|
-
max_depth_reached = max(max_depth_reached, depth)
|
|
84
|
-
|
|
85
|
-
conn.commit()
|
|
86
|
-
logger.info(f"Hierarchical clustering: {total_subclusters} sub-clusters, depth {max_depth_reached}")
|
|
87
|
-
return {
|
|
88
|
-
'subclusters_created': total_subclusters,
|
|
89
|
-
'depth_reached': max_depth_reached,
|
|
90
|
-
'parent_clusters_processed': len(large_clusters)
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
except Exception as e:
|
|
94
|
-
logger.error(f"Hierarchical clustering failed: {e}")
|
|
95
|
-
conn.rollback()
|
|
96
|
-
return {'subclusters_created': 0, 'error': str(e)}
|
|
97
|
-
finally:
|
|
98
|
-
conn.close()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def _recursive_subcluster(conn, cursor, parent_cluster_id: int,
|
|
102
|
-
profile: str, min_size: int, max_depth: int,
|
|
103
|
-
current_depth: int,
|
|
104
|
-
get_avg_importance_fn, generate_cluster_name_fn) -> Tuple[int, int]:
|
|
105
|
-
"""Recursively sub-cluster a community using Leiden."""
|
|
106
|
-
if not IGRAPH_AVAILABLE:
|
|
107
|
-
return 0, current_depth - 1
|
|
108
|
-
import igraph as ig
|
|
109
|
-
import leidenalg
|
|
110
|
-
|
|
111
|
-
if current_depth > max_depth:
|
|
112
|
-
return 0, current_depth - 1
|
|
113
|
-
|
|
114
|
-
# Get memory IDs in this cluster
|
|
115
|
-
cursor.execute('''
|
|
116
|
-
SELECT id FROM memories
|
|
117
|
-
WHERE cluster_id = ? AND profile = ?
|
|
118
|
-
''', (parent_cluster_id, profile))
|
|
119
|
-
member_ids = [row[0] for row in cursor.fetchall()]
|
|
120
|
-
|
|
121
|
-
if len(member_ids) < min_size * 2:
|
|
122
|
-
return 0, current_depth - 1
|
|
123
|
-
|
|
124
|
-
# Get edges between members of this cluster
|
|
125
|
-
placeholders = ','.join('?' * len(member_ids))
|
|
126
|
-
edges = cursor.execute(f'''
|
|
127
|
-
SELECT source_memory_id, target_memory_id, weight
|
|
128
|
-
FROM graph_edges
|
|
129
|
-
WHERE source_memory_id IN ({placeholders})
|
|
130
|
-
AND target_memory_id IN ({placeholders})
|
|
131
|
-
''', member_ids + member_ids).fetchall()
|
|
132
|
-
|
|
133
|
-
if len(edges) < 2:
|
|
134
|
-
return 0, current_depth - 1
|
|
135
|
-
|
|
136
|
-
# Build sub-graph
|
|
137
|
-
id_to_vertex = {mid: idx for idx, mid in enumerate(member_ids)}
|
|
138
|
-
vertex_to_id = {idx: mid for mid, idx in id_to_vertex.items()}
|
|
139
|
-
|
|
140
|
-
g = ig.Graph()
|
|
141
|
-
g.add_vertices(len(member_ids))
|
|
142
|
-
edge_list, edge_weights = [], []
|
|
143
|
-
for src, tgt, w in edges:
|
|
144
|
-
if src in id_to_vertex and tgt in id_to_vertex:
|
|
145
|
-
edge_list.append((id_to_vertex[src], id_to_vertex[tgt]))
|
|
146
|
-
edge_weights.append(w)
|
|
147
|
-
|
|
148
|
-
if not edge_list:
|
|
149
|
-
return 0, current_depth - 1
|
|
150
|
-
|
|
151
|
-
g.add_edges(edge_list)
|
|
152
|
-
|
|
153
|
-
# Run Leiden with higher resolution for finer communities
|
|
154
|
-
partition = leidenalg.find_partition(
|
|
155
|
-
g, leidenalg.ModularityVertexPartition,
|
|
156
|
-
weights=edge_weights, n_iterations=100, seed=42
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
# Only proceed if Leiden found > 1 community (actual split)
|
|
160
|
-
non_singleton = [c for c in partition if len(c) >= 2]
|
|
161
|
-
if len(non_singleton) <= 1:
|
|
162
|
-
return 0, current_depth - 1
|
|
163
|
-
|
|
164
|
-
subclusters_created = 0
|
|
165
|
-
deepest = current_depth
|
|
166
|
-
|
|
167
|
-
# Get parent depth
|
|
168
|
-
cursor.execute('SELECT depth FROM graph_clusters WHERE id = ?', (parent_cluster_id,))
|
|
169
|
-
parent_row = cursor.fetchone()
|
|
170
|
-
parent_depth = parent_row[0] if parent_row else 0
|
|
171
|
-
|
|
172
|
-
for community in non_singleton:
|
|
173
|
-
sub_member_ids = [vertex_to_id[v] for v in community]
|
|
174
|
-
|
|
175
|
-
if len(sub_member_ids) < 2:
|
|
176
|
-
continue
|
|
177
|
-
|
|
178
|
-
avg_imp = get_avg_importance_fn(cursor, sub_member_ids)
|
|
179
|
-
cluster_name = generate_cluster_name_fn(cursor, sub_member_ids)
|
|
180
|
-
|
|
181
|
-
result = cursor.execute('''
|
|
182
|
-
INSERT INTO graph_clusters (name, member_count, avg_importance, parent_cluster_id, depth)
|
|
183
|
-
VALUES (?, ?, ?, ?, ?)
|
|
184
|
-
''', (cluster_name, len(sub_member_ids), avg_imp, parent_cluster_id, parent_depth + 1))
|
|
185
|
-
|
|
186
|
-
sub_cluster_id = result.lastrowid
|
|
187
|
-
|
|
188
|
-
# Update memories to point to sub-cluster
|
|
189
|
-
cursor.executemany('''
|
|
190
|
-
UPDATE memories SET cluster_id = ? WHERE id = ?
|
|
191
|
-
''', [(sub_cluster_id, mid) for mid in sub_member_ids])
|
|
192
|
-
|
|
193
|
-
subclusters_created += 1
|
|
194
|
-
logger.info(f"Sub-cluster {sub_cluster_id} under {parent_cluster_id}: "
|
|
195
|
-
f"'{cluster_name}' ({len(sub_member_ids)} members, depth {parent_depth + 1})")
|
|
196
|
-
|
|
197
|
-
# Recurse into this sub-cluster if large enough
|
|
198
|
-
child_subs, child_depth = _recursive_subcluster(
|
|
199
|
-
conn, cursor, sub_cluster_id, profile,
|
|
200
|
-
min_size, max_depth, current_depth + 1,
|
|
201
|
-
get_avg_importance_fn=get_avg_importance_fn,
|
|
202
|
-
generate_cluster_name_fn=generate_cluster_name_fn,
|
|
203
|
-
)
|
|
204
|
-
subclusters_created += child_subs
|
|
205
|
-
deepest = max(deepest, child_depth)
|
|
206
|
-
|
|
207
|
-
return subclusters_created, deepest
|
package/src/graph/schema.py
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""Database schema management for the graph engine.
|
|
5
|
-
|
|
6
|
-
Creates and maintains the graph_nodes, graph_edges, and graph_clusters
|
|
7
|
-
tables, including safe schema migrations for existing databases.
|
|
8
|
-
"""
|
|
9
|
-
import sqlite3
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
|
|
12
|
-
from graph.constants import logger
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def ensure_graph_tables(db_path: Path):
|
|
16
|
-
"""Create graph tables if they don't exist, or recreate if schema is incomplete."""
|
|
17
|
-
conn = sqlite3.connect(db_path)
|
|
18
|
-
cursor = conn.cursor()
|
|
19
|
-
|
|
20
|
-
# Check if existing tables have correct schema (not just id column)
|
|
21
|
-
for table_name, required_cols in [
|
|
22
|
-
('graph_nodes', {'memory_id', 'entities'}),
|
|
23
|
-
('graph_edges', {'source_memory_id', 'target_memory_id', 'weight'}),
|
|
24
|
-
('graph_clusters', {'name', 'member_count'}),
|
|
25
|
-
]:
|
|
26
|
-
cursor.execute(f"PRAGMA table_info({table_name})")
|
|
27
|
-
existing_cols = {row[1] for row in cursor.fetchall()}
|
|
28
|
-
if existing_cols and not required_cols.issubset(existing_cols):
|
|
29
|
-
# Table exists but has incomplete schema -- drop and recreate
|
|
30
|
-
logger.warning(f"Dropping incomplete {table_name} table (missing: {required_cols - existing_cols})")
|
|
31
|
-
cursor.execute(f'DROP TABLE IF EXISTS {table_name}')
|
|
32
|
-
|
|
33
|
-
# Graph nodes table
|
|
34
|
-
cursor.execute('''
|
|
35
|
-
CREATE TABLE IF NOT EXISTS graph_nodes (
|
|
36
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
37
|
-
memory_id INTEGER UNIQUE NOT NULL,
|
|
38
|
-
entities TEXT,
|
|
39
|
-
embedding_vector TEXT,
|
|
40
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
41
|
-
FOREIGN KEY (memory_id) REFERENCES memories(id) ON DELETE CASCADE
|
|
42
|
-
)
|
|
43
|
-
''')
|
|
44
|
-
|
|
45
|
-
# Graph edges table
|
|
46
|
-
cursor.execute('''
|
|
47
|
-
CREATE TABLE IF NOT EXISTS graph_edges (
|
|
48
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
-
source_memory_id INTEGER NOT NULL,
|
|
50
|
-
target_memory_id INTEGER NOT NULL,
|
|
51
|
-
relationship_type TEXT,
|
|
52
|
-
weight REAL DEFAULT 1.0,
|
|
53
|
-
shared_entities TEXT,
|
|
54
|
-
similarity_score REAL,
|
|
55
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
56
|
-
FOREIGN KEY (source_memory_id) REFERENCES memories(id) ON DELETE CASCADE,
|
|
57
|
-
FOREIGN KEY (target_memory_id) REFERENCES memories(id) ON DELETE CASCADE,
|
|
58
|
-
UNIQUE(source_memory_id, target_memory_id)
|
|
59
|
-
)
|
|
60
|
-
''')
|
|
61
|
-
|
|
62
|
-
# Graph clusters table
|
|
63
|
-
cursor.execute('''
|
|
64
|
-
CREATE TABLE IF NOT EXISTS graph_clusters (
|
|
65
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
66
|
-
name TEXT NOT NULL,
|
|
67
|
-
description TEXT,
|
|
68
|
-
summary TEXT,
|
|
69
|
-
member_count INTEGER DEFAULT 0,
|
|
70
|
-
avg_importance REAL,
|
|
71
|
-
parent_cluster_id INTEGER,
|
|
72
|
-
depth INTEGER DEFAULT 0,
|
|
73
|
-
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
74
|
-
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
75
|
-
FOREIGN KEY (parent_cluster_id) REFERENCES graph_clusters(id) ON DELETE SET NULL
|
|
76
|
-
)
|
|
77
|
-
''')
|
|
78
|
-
|
|
79
|
-
# Safe column additions for existing databases
|
|
80
|
-
for col, col_type in [('summary', 'TEXT'), ('parent_cluster_id', 'INTEGER'), ('depth', 'INTEGER DEFAULT 0')]:
|
|
81
|
-
try:
|
|
82
|
-
cursor.execute(f'ALTER TABLE graph_clusters ADD COLUMN {col} {col_type}')
|
|
83
|
-
except sqlite3.OperationalError:
|
|
84
|
-
pass
|
|
85
|
-
|
|
86
|
-
# Add cluster_id to memories if not exists
|
|
87
|
-
try:
|
|
88
|
-
cursor.execute('ALTER TABLE memories ADD COLUMN cluster_id INTEGER')
|
|
89
|
-
except sqlite3.OperationalError:
|
|
90
|
-
pass # Column already exists
|
|
91
|
-
|
|
92
|
-
# Create indexes
|
|
93
|
-
cursor.execute('CREATE INDEX IF NOT EXISTS idx_graph_source ON graph_edges(source_memory_id)')
|
|
94
|
-
cursor.execute('CREATE INDEX IF NOT EXISTS idx_graph_target ON graph_edges(target_memory_id)')
|
|
95
|
-
cursor.execute('CREATE INDEX IF NOT EXISTS idx_cluster_members ON memories(cluster_id)')
|
|
96
|
-
|
|
97
|
-
conn.commit()
|
|
98
|
-
conn.close()
|
|
99
|
-
logger.info("Graph tables initialized")
|
package/src/graph_engine.py
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# Copyright (c) 2026 SuperLocalMemory (superlocalmemory.com)
|
|
4
|
-
"""GraphEngine - Knowledge Graph Clustering for SuperLocalMemory V2
|
|
5
|
-
|
|
6
|
-
BACKWARD-COMPATIBILITY SHIM
|
|
7
|
-
----------------------------
|
|
8
|
-
This file re-exports every public symbol from the ``graph`` package so that
|
|
9
|
-
existing code using ``from graph_engine import GraphEngine`` (or any other
|
|
10
|
-
name) continues to work without modification.
|
|
11
|
-
|
|
12
|
-
The actual implementation now lives in:
|
|
13
|
-
src/graph/constants.py - Shared imports, constants, logger
|
|
14
|
-
src/graph/entity_extractor.py - EntityExtractor, ClusterNamer
|
|
15
|
-
src/graph/edge_builder.py - EdgeBuilder
|
|
16
|
-
src/graph/cluster_builder.py - ClusterBuilder
|
|
17
|
-
src/graph/graph_core.py - GraphEngine, main()
|
|
18
|
-
"""
|
|
19
|
-
# Re-export everything from the graph package
|
|
20
|
-
from graph import (
|
|
21
|
-
# Constants
|
|
22
|
-
MAX_MEMORIES_FOR_GRAPH,
|
|
23
|
-
SKLEARN_AVAILABLE,
|
|
24
|
-
IGRAPH_AVAILABLE,
|
|
25
|
-
MEMORY_DIR,
|
|
26
|
-
DB_PATH,
|
|
27
|
-
# Classes
|
|
28
|
-
EntityExtractor,
|
|
29
|
-
ClusterNamer,
|
|
30
|
-
EdgeBuilder,
|
|
31
|
-
ClusterBuilder,
|
|
32
|
-
GraphEngine,
|
|
33
|
-
# Functions
|
|
34
|
-
main,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
__all__ = [
|
|
38
|
-
"MAX_MEMORIES_FOR_GRAPH",
|
|
39
|
-
"SKLEARN_AVAILABLE",
|
|
40
|
-
"IGRAPH_AVAILABLE",
|
|
41
|
-
"MEMORY_DIR",
|
|
42
|
-
"DB_PATH",
|
|
43
|
-
"EntityExtractor",
|
|
44
|
-
"ClusterNamer",
|
|
45
|
-
"EdgeBuilder",
|
|
46
|
-
"ClusterBuilder",
|
|
47
|
-
"GraphEngine",
|
|
48
|
-
"main",
|
|
49
|
-
]
|
|
50
|
-
|
|
51
|
-
if __name__ == '__main__':
|
|
52
|
-
main()
|