langchain-sqlite-graph-memory 0.1.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.
@@ -0,0 +1,5 @@
1
+ """LangChain integration for sqlite-graph-memory (Graph RAG over markdown notes)."""
2
+ from langchain_sqlite_graph_memory.retrievers import SQLiteGraphMemoryRetriever
3
+
4
+ __all__ = ["SQLiteGraphMemoryRetriever"]
5
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,146 @@
1
+ """LangChain retriever over a sqlite-graph-memory index.
2
+
3
+ Паспорт (for the people who maintain this):
4
+ in: a query string; an index built by `sgm-index` (two files: _brain_e5.npy,
5
+ _brain_e5_meta.pkl) in `index_dir`.
6
+ out: list[Document], best first. page_content = the matched chunk; metadata =
7
+ path, title, date, score, rank, via_graph.
8
+ who calls: any LangChain / LangGraph app (`retriever.invoke(q)`, a tool, a chain).
9
+ what breaks it: sqlite-graph-memory changing the signatures of load_index,
10
+ query_sims, retrieve_candidates, expand_1hop, rerank_candidates. The test
11
+ suite pins that contract with fake models, so a drift fails CI, not a user.
12
+ рельса: none, no LLM call. Embedding + cross-encoder run locally.
13
+
14
+ It runs the SAME pipeline functions `sgm-ask` runs (imported from
15
+ sqlite_graph_memory.brain_ask), so recall through LangChain cannot drift away from
16
+ recall through the CLI or the MCP server.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import os
21
+ from pathlib import Path
22
+ from typing import Any, List, Literal, Optional, Union
23
+
24
+ from langchain_core.callbacks import CallbackManagerForRetrieverRun
25
+ from langchain_core.documents import Document
26
+ from langchain_core.retrievers import BaseRetriever
27
+ from pydantic import ConfigDict, Field, PrivateAttr
28
+
29
+ from sqlite_graph_memory import brain_ask as _sgm
30
+
31
+ GraphMode = Union[bool, Literal["auto"]]
32
+
33
+
34
+ class SQLiteGraphMemoryRetriever(BaseRetriever):
35
+ """Associative recall over a folder of markdown notes.
36
+
37
+ Vector search finds entry points, 1-hop ``[[wikilinks]]`` pull in linked notes,
38
+ a cross-encoder reranks the union. The index is two files on disk; no server.
39
+
40
+ Setup:
41
+ .. code-block:: bash
42
+
43
+ pip install "langchain-sqlite-graph-memory[lite]"
44
+ sgm-index /path/to/vault --out ./index
45
+
46
+ Instantiate:
47
+ .. code-block:: python
48
+
49
+ from langchain_sqlite_graph_memory import SQLiteGraphMemoryRetriever
50
+
51
+ retriever = SQLiteGraphMemoryRetriever(index_dir="./index", k=8)
52
+
53
+ Invoke:
54
+ .. code-block:: python
55
+
56
+ docs = retriever.invoke("why did we drop the graph database?")
57
+ for d in docs:
58
+ print(d.metadata["rank"], d.metadata["title"], d.metadata["via_graph"])
59
+ """
60
+
61
+ model_config = ConfigDict(arbitrary_types_allowed=True)
62
+
63
+ index_dir: Optional[str] = None
64
+ """Directory with the `sgm-index` output. Default: env BRAIN_INDEX_DIR, then ./index."""
65
+ k: int = Field(default=_sgm.TOPN, ge=1)
66
+ """How many documents to return."""
67
+ graph: GraphMode = "auto"
68
+ """Wikilink expansion. True = always, False = never (plain vector + rerank),
69
+ "auto" = on, except for name/tool lookups, where it measured as noise
70
+ (sqlite_graph_memory.brain_ask.looks_like_entity)."""
71
+ fetch_k: int = Field(default=_sgm.TOPK_RETRIEVE, ge=1)
72
+ """Unique notes taken from vector search before expansion and rerank."""
73
+ encoder: Any = None
74
+ """Optional preloaded encoder with `.encode(...)`. Default: the SGM e5 model."""
75
+ reranker: Any = None
76
+ """Optional preloaded cross-encoder with `.predict(pairs)`. Default: the SGM reranker."""
77
+
78
+ _emb: Any = PrivateAttr(default=None)
79
+ _meta: Any = PrivateAttr(default=None)
80
+ _by_base: Any = PrivateAttr(default=None)
81
+
82
+ # ------------------------------------------------------------------ loading
83
+ def _index_paths(self) -> tuple[Path, Path]:
84
+ base = Path(self.index_dir or os.getenv("BRAIN_INDEX_DIR", "index"))
85
+ return base / "_brain_e5.npy", base / "_brain_e5_meta.pkl"
86
+
87
+ def _ensure_loaded(self) -> None:
88
+ if self._meta is not None:
89
+ return
90
+ emb_path, meta_path = self._index_paths()
91
+ if not emb_path.exists() or not meta_path.exists():
92
+ raise FileNotFoundError(
93
+ "No sqlite-graph-memory index at %s. Build one first: "
94
+ "sgm-index <notes folder> (see https://github.com/tonydzi/sqlite-graph-memory)"
95
+ % emb_path.parent
96
+ )
97
+ self._emb, self._meta = _sgm.load_index(emb_path, meta_path)
98
+ self._by_base = _sgm.index_by_basename(self._meta)
99
+ if self.encoder is None:
100
+ self.encoder = _sgm.load_encoder()
101
+ if self.reranker is None:
102
+ self.reranker = _sgm.load_reranker()
103
+
104
+ def _use_graph(self, query: str) -> bool:
105
+ if self.graph == "auto":
106
+ return not _sgm.looks_like_entity(query)
107
+ return bool(self.graph)
108
+
109
+ # ------------------------------------------------------------------ retrieval
110
+ def _get_relevant_documents(
111
+ self,
112
+ query: str,
113
+ *,
114
+ run_manager: CallbackManagerForRetrieverRun,
115
+ **kwargs: Any,
116
+ ) -> List[Document]:
117
+ k = int(kwargs.get("k", self.k))
118
+ self._ensure_loaded()
119
+ meta = self._meta
120
+ sims = _sgm.query_sims(self.encoder, self._emb, query)
121
+ base_order = _sgm.retrieve_candidates(sims, meta, topk=self.fetch_k)
122
+ added: List[int] = []
123
+ if base_order and self._use_graph(query):
124
+ added = _sgm.expand_1hop(sims, meta, base_order, by_base=self._by_base)
125
+ via_graph = set(added)
126
+ ranked = _sgm.rerank_candidates(
127
+ self.reranker, query, base_order + added, meta, topn=k
128
+ )
129
+ docs = []
130
+ for rank, (i, score) in enumerate(ranked, 1):
131
+ m = meta[i]
132
+ docs.append(
133
+ Document(
134
+ page_content=m.get("snippet", ""),
135
+ metadata={
136
+ "path": m.get("path"),
137
+ "title": m.get("title"),
138
+ "date": m.get("date"),
139
+ "score": float(score),
140
+ "rank": rank,
141
+ "via_graph": i in via_graph,
142
+ "source": m.get("path"),
143
+ },
144
+ )
145
+ )
146
+ return docs
@@ -0,0 +1,129 @@
1
+ Metadata-Version: 2.5
2
+ Name: langchain-sqlite-graph-memory
3
+ Version: 0.1.0
4
+ Summary: LangChain retriever for sqlite-graph-memory: vector search + [[wikilink]] graph hop + cross-encoder rerank over your markdown notes. No server.
5
+ Project-URL: Homepage, https://github.com/tonydzi/langchain-sqlite-graph-memory
6
+ Project-URL: Repository, https://github.com/tonydzi/langchain-sqlite-graph-memory
7
+ Project-URL: Issues, https://github.com/tonydzi/langchain-sqlite-graph-memory/issues
8
+ Project-URL: Core library, https://github.com/tonydzi/sqlite-graph-memory
9
+ Author: Anton Dziatkovskii
10
+ License: MIT License
11
+
12
+ Copyright (c) 2026 Palo Alto AI Research Lab
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
31
+ License-File: LICENSE
32
+ Keywords: agent-memory,graph-rag,langchain,obsidian,rag,retriever,wikilinks
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Intended Audience :: Developers
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3.10
37
+ Classifier: Programming Language :: Python :: 3.11
38
+ Classifier: Programming Language :: Python :: 3.12
39
+ Classifier: Programming Language :: Python :: 3.13
40
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
41
+ Requires-Python: >=3.10
42
+ Requires-Dist: langchain-core<2,>=1.0
43
+ Requires-Dist: sqlite-graph-memory>=0.3.6
44
+ Provides-Extra: embeddings
45
+ Requires-Dist: sqlite-graph-memory[embeddings]>=0.3.6; extra == 'embeddings'
46
+ Provides-Extra: lite
47
+ Requires-Dist: sqlite-graph-memory[lite]>=0.3.6; extra == 'lite'
48
+ Provides-Extra: test
49
+ Requires-Dist: langchain-tests<2,>=1.0; extra == 'test'
50
+ Requires-Dist: pytest>=8; extra == 'test'
51
+ Description-Content-Type: text/markdown
52
+
53
+ # langchain-sqlite-graph-memory
54
+
55
+ A LangChain retriever over your own markdown notes, with the links between them. It runs
56
+ [sqlite-graph-memory](https://github.com/tonydzi/sqlite-graph-memory): vector search finds
57
+ the entry points, the `[[wikilinks]]` you already wrote are the graph, and a cross-encoder
58
+ reranks what the 1-hop expansion pulled in. Everything runs on your machine: no server, no
59
+ graph database, no LLM call at retrieval time.
60
+
61
+ ```bash
62
+ pip install "langchain-sqlite-graph-memory[lite]" # ONNX models, starts in seconds
63
+ sgm-index /path/to/your/notes # builds ./index once
64
+ ```
65
+
66
+ ```python
67
+ from langchain_sqlite_graph_memory import SQLiteGraphMemoryRetriever
68
+
69
+ retriever = SQLiteGraphMemoryRetriever(index_dir="./index", k=8)
70
+
71
+ for doc in retriever.invoke("why did we drop the graph database?"):
72
+ print(doc.metadata["rank"], doc.metadata["title"], doc.metadata["via_graph"])
73
+ ```
74
+
75
+ Each result is a `Document`. `page_content` holds the matched chunk of the note. `metadata`
76
+ holds `path`, `title`, `date`, `score` (cross-encoder), `rank`, and `via_graph`. `via_graph`
77
+ is `True` when the note came in only through a wikilink from a top vector hit, so you can
78
+ tell associative recall from direct recall.
79
+
80
+ ## Options
81
+
82
+ | Argument | Default | What it does |
83
+ |---|---|---|
84
+ | `index_dir` | `$BRAIN_INDEX_DIR`, else `./index` | Folder with the `sgm-index` output |
85
+ | `k` | 12 | Documents returned. `retriever.invoke(q, k=3)` overrides it per call |
86
+ | `graph` | `"auto"` | `True` always follows wikilinks, `False` never does. `"auto"` follows them except for name or tool lookups, where the hop measured as noise |
87
+ | `fetch_k` | 60 | Unique notes taken from vector search before expansion and rerank |
88
+ | `encoder`, `reranker` | SGM models | Pass your own preloaded models, or fakes in tests |
89
+
90
+ ## In an agent
91
+
92
+ ```python
93
+ from langchain_core.tools import create_retriever_tool
94
+
95
+ recall = create_retriever_tool(
96
+ retriever, "recall_notes",
97
+ "Search my notes and the notes they link to. Use for anything I wrote down before.",
98
+ )
99
+ ```
100
+
101
+ ## Scope, stated plainly
102
+
103
+ - **This adapter calls the core library and adds nothing to retrieval.** It calls the same
104
+ functions `sgm-ask` calls (`query_sims`, `retrieve_candidates`, `expand_1hop`,
105
+ `rerank_candidates`). A test pins this: the adapter's order must equal a direct call of
106
+ those functions. On a real vault of 29,474 notes (105,383 chunks), the adapter returned the same 12 notes in the
107
+ same order as `sgm-ask --graph`.
108
+ - **The index is two files on disk**, an embedding matrix (`.npy`) and note metadata
109
+ (`.pkl`), written by `sgm-index`. Wikilinks are read from the notes at query time.
110
+ SQLite stores the core library's session memory and A/B logs. The retriever needs only
111
+ the two index files.
112
+ - Embeddings: `intfloat/multilingual-e5-base`. Reranker:
113
+ `cross-encoder/mmarco-mMiniLMv2-L12-H384-v1`. Both work for many languages.
114
+ - Not implemented yet: a `VectorStore` (adding documents through LangChain) and a LangGraph
115
+ long-term memory store. Indexing goes through `sgm-index`.
116
+
117
+ ## Tests
118
+
119
+ ```bash
120
+ pip install -e ".[test]"
121
+ pytest
122
+ ```
123
+
124
+ The suite includes LangChain's standard retriever tests (`langchain-tests`). It runs with
125
+ fake models on a four-note vault: no download, no GPU, a few seconds.
126
+
127
+ ## License
128
+
129
+ MIT
@@ -0,0 +1,7 @@
1
+ langchain_sqlite_graph_memory/__init__.py,sha256=JW0teu2TVrBaK63pXEOh1AeDukNdvnVOwkgwuop5mFY,229
2
+ langchain_sqlite_graph_memory/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ langchain_sqlite_graph_memory/retrievers.py,sha256=rb7tAbxoJ4xwzPmlMZFctkD82v5kxrLRh6kUNNwohw8,5932
4
+ langchain_sqlite_graph_memory-0.1.0.dist-info/METADATA,sha256=f2W8XyaJZryVUUkHwgeQdgiB1hiB7GEQSPGFoDrOx-c,6089
5
+ langchain_sqlite_graph_memory-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
6
+ langchain_sqlite_graph_memory-0.1.0.dist-info/licenses/LICENSE,sha256=p88nZdrfMUiES3_MpUEwhGmqz2hQFpyMh0k0cqdpfU8,1082
7
+ langchain_sqlite_graph_memory-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Palo Alto AI Research Lab
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.