mem-os-python 1.6.27__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.
- mem_os_python-1.6.27.dist-info/METADATA +1032 -0
- mem_os_python-1.6.27.dist-info/RECORD +253 -0
- mem_os_python-1.6.27.dist-info/WHEEL +4 -0
- mem_os_python-1.6.27.dist-info/entry_points.txt +2 -0
- mem_os_python-1.6.27.dist-info/licenses/LICENSE +21 -0
- memos/__init__.py +16 -0
- memos/_js/adapters/anthropic.d.ts +279 -0
- memos/_js/adapters/anthropic.d.ts.map +1 -0
- memos/_js/adapters/anthropic.js +348 -0
- memos/_js/adapters/anthropic.js.map +1 -0
- memos/_js/adapters/base.d.ts +10 -0
- memos/_js/adapters/base.d.ts.map +1 -0
- memos/_js/adapters/base.js +2 -0
- memos/_js/adapters/base.js.map +1 -0
- memos/_js/adapters/openai.d.ts +264 -0
- memos/_js/adapters/openai.d.ts.map +1 -0
- memos/_js/adapters/openai.js +342 -0
- memos/_js/adapters/openai.js.map +1 -0
- memos/_js/apps/explorer.d.ts +244 -0
- memos/_js/apps/explorer.d.ts.map +1 -0
- memos/_js/apps/explorer.js +738 -0
- memos/_js/apps/explorer.js.map +1 -0
- memos/_js/citations.d.ts +65 -0
- memos/_js/citations.d.ts.map +1 -0
- memos/_js/citations.js +143 -0
- memos/_js/citations.js.map +1 -0
- memos/_js/cli-browse.d.ts +3 -0
- memos/_js/cli-browse.d.ts.map +1 -0
- memos/_js/cli-browse.js +129 -0
- memos/_js/cli-browse.js.map +1 -0
- memos/_js/cli-completion.d.ts +31 -0
- memos/_js/cli-completion.d.ts.map +1 -0
- memos/_js/cli-completion.js +340 -0
- memos/_js/cli-completion.js.map +1 -0
- memos/_js/cli-help.d.ts +18 -0
- memos/_js/cli-help.d.ts.map +1 -0
- memos/_js/cli-help.js +357 -0
- memos/_js/cli-help.js.map +1 -0
- memos/_js/cli.d.ts +28 -0
- memos/_js/cli.d.ts.map +1 -0
- memos/_js/cli.js +2880 -0
- memos/_js/cli.js.map +1 -0
- memos/_js/compression.d.ts +25 -0
- memos/_js/compression.d.ts.map +1 -0
- memos/_js/compression.js +319 -0
- memos/_js/compression.js.map +1 -0
- memos/_js/confidence-machine.d.ts +99 -0
- memos/_js/confidence-machine.d.ts.map +1 -0
- memos/_js/confidence-machine.js +222 -0
- memos/_js/confidence-machine.js.map +1 -0
- memos/_js/context-pack.d.ts +506 -0
- memos/_js/context-pack.d.ts.map +1 -0
- memos/_js/context-pack.js +1435 -0
- memos/_js/context-pack.js.map +1 -0
- memos/_js/contradictions.d.ts +160 -0
- memos/_js/contradictions.d.ts.map +1 -0
- memos/_js/contradictions.js +255 -0
- memos/_js/contradictions.js.map +1 -0
- memos/_js/dashboard.d.ts +30 -0
- memos/_js/dashboard.d.ts.map +1 -0
- memos/_js/dashboard.js +302 -0
- memos/_js/dashboard.js.map +1 -0
- memos/_js/ebbinghaus.d.ts +29 -0
- memos/_js/ebbinghaus.d.ts.map +1 -0
- memos/_js/ebbinghaus.js +32 -0
- memos/_js/ebbinghaus.js.map +1 -0
- memos/_js/embedding-queue.d.ts +190 -0
- memos/_js/embedding-queue.d.ts.map +1 -0
- memos/_js/embedding-queue.js +316 -0
- memos/_js/embedding-queue.js.map +1 -0
- memos/_js/embeddings.d.ts +187 -0
- memos/_js/embeddings.d.ts.map +1 -0
- memos/_js/embeddings.js +633 -0
- memos/_js/embeddings.js.map +1 -0
- memos/_js/entity-extraction.d.ts +54 -0
- memos/_js/entity-extraction.d.ts.map +1 -0
- memos/_js/entity-extraction.js +325 -0
- memos/_js/entity-extraction.js.map +1 -0
- memos/_js/entity-index.d.ts +93 -0
- memos/_js/entity-index.d.ts.map +1 -0
- memos/_js/entity-index.js +165 -0
- memos/_js/entity-index.js.map +1 -0
- memos/_js/event-memory.d.ts +147 -0
- memos/_js/event-memory.d.ts.map +1 -0
- memos/_js/event-memory.js +290 -0
- memos/_js/event-memory.js.map +1 -0
- memos/_js/external-import.d.ts +98 -0
- memos/_js/external-import.d.ts.map +1 -0
- memos/_js/external-import.js +669 -0
- memos/_js/external-import.js.map +1 -0
- memos/_js/fidelity.d.ts +367 -0
- memos/_js/fidelity.d.ts.map +1 -0
- memos/_js/fidelity.js +911 -0
- memos/_js/fidelity.js.map +1 -0
- memos/_js/graph-expansion.d.ts +119 -0
- memos/_js/graph-expansion.d.ts.map +1 -0
- memos/_js/graph-expansion.js +213 -0
- memos/_js/graph-expansion.js.map +1 -0
- memos/_js/graph-mermaid.d.ts +13 -0
- memos/_js/graph-mermaid.d.ts.map +1 -0
- memos/_js/graph-mermaid.js +32 -0
- memos/_js/graph-mermaid.js.map +1 -0
- memos/_js/graph.d.ts +170 -0
- memos/_js/graph.d.ts.map +1 -0
- memos/_js/graph.js +482 -0
- memos/_js/graph.js.map +1 -0
- memos/_js/harness.d.ts +63 -0
- memos/_js/harness.d.ts.map +1 -0
- memos/_js/harness.js +93 -0
- memos/_js/harness.js.map +1 -0
- memos/_js/importance.d.ts +118 -0
- memos/_js/importance.d.ts.map +1 -0
- memos/_js/importance.js +144 -0
- memos/_js/importance.js.map +1 -0
- memos/_js/index.d.ts +51 -0
- memos/_js/index.d.ts.map +1 -0
- memos/_js/index.js +53 -0
- memos/_js/index.js.map +1 -0
- memos/_js/mcp.d.ts +58 -0
- memos/_js/mcp.d.ts.map +1 -0
- memos/_js/mcp.js +1853 -0
- memos/_js/mcp.js.map +1 -0
- memos/_js/memory.d.ts +1762 -0
- memos/_js/memory.d.ts.map +1 -0
- memos/_js/memory.js +6283 -0
- memos/_js/memory.js.map +1 -0
- memos/_js/procedural.d.ts +74 -0
- memos/_js/procedural.d.ts.map +1 -0
- memos/_js/procedural.js +125 -0
- memos/_js/procedural.js.map +1 -0
- memos/_js/project-config.d.ts +68 -0
- memos/_js/project-config.d.ts.map +1 -0
- memos/_js/project-config.js +59 -0
- memos/_js/project-config.js.map +1 -0
- memos/_js/proof-of-forgetting.d.ts +57 -0
- memos/_js/proof-of-forgetting.d.ts.map +1 -0
- memos/_js/proof-of-forgetting.js +80 -0
- memos/_js/proof-of-forgetting.js.map +1 -0
- memos/_js/provenance.d.ts +122 -0
- memos/_js/provenance.d.ts.map +1 -0
- memos/_js/provenance.js +182 -0
- memos/_js/provenance.js.map +1 -0
- memos/_js/quarantine-commands.d.ts +23 -0
- memos/_js/quarantine-commands.d.ts.map +1 -0
- memos/_js/quarantine-commands.js +42 -0
- memos/_js/quarantine-commands.js.map +1 -0
- memos/_js/quarantine.d.ts +120 -0
- memos/_js/quarantine.d.ts.map +1 -0
- memos/_js/quarantine.js +873 -0
- memos/_js/quarantine.js.map +1 -0
- memos/_js/redact.d.ts +12 -0
- memos/_js/redact.d.ts.map +1 -0
- memos/_js/redact.js +26 -0
- memos/_js/redact.js.map +1 -0
- memos/_js/repl.d.ts +57 -0
- memos/_js/repl.d.ts.map +1 -0
- memos/_js/repl.js +295 -0
- memos/_js/repl.js.map +1 -0
- memos/_js/rerank-local.d.ts +61 -0
- memos/_js/rerank-local.d.ts.map +1 -0
- memos/_js/rerank-local.js +210 -0
- memos/_js/rerank-local.js.map +1 -0
- memos/_js/retain-filter.d.ts +98 -0
- memos/_js/retain-filter.d.ts.map +1 -0
- memos/_js/retain-filter.js +232 -0
- memos/_js/retain-filter.js.map +1 -0
- memos/_js/retrieval.d.ts +265 -0
- memos/_js/retrieval.d.ts.map +1 -0
- memos/_js/retrieval.js +1018 -0
- memos/_js/retrieval.js.map +1 -0
- memos/_js/revert.d.ts +77 -0
- memos/_js/revert.d.ts.map +1 -0
- memos/_js/revert.js +252 -0
- memos/_js/revert.js.map +1 -0
- memos/_js/scope.d.ts +30 -0
- memos/_js/scope.d.ts.map +1 -0
- memos/_js/scope.js +71 -0
- memos/_js/scope.js.map +1 -0
- memos/_js/semantic-screen.d.ts +152 -0
- memos/_js/semantic-screen.d.ts.map +1 -0
- memos/_js/semantic-screen.js +290 -0
- memos/_js/semantic-screen.js.map +1 -0
- memos/_js/session-hooks.d.ts +127 -0
- memos/_js/session-hooks.d.ts.map +1 -0
- memos/_js/session-hooks.js +482 -0
- memos/_js/session-hooks.js.map +1 -0
- memos/_js/storage/sqlite.d.ts +427 -0
- memos/_js/storage/sqlite.d.ts.map +1 -0
- memos/_js/storage/sqlite.js +2428 -0
- memos/_js/storage/sqlite.js.map +1 -0
- memos/_js/sync.d.ts +213 -0
- memos/_js/sync.d.ts.map +1 -0
- memos/_js/sync.js +389 -0
- memos/_js/sync.js.map +1 -0
- memos/_js/tamper-log.d.ts +98 -0
- memos/_js/tamper-log.d.ts.map +1 -0
- memos/_js/tamper-log.js +327 -0
- memos/_js/tamper-log.js.map +1 -0
- memos/_js/temporal.d.ts +92 -0
- memos/_js/temporal.d.ts.map +1 -0
- memos/_js/temporal.js +536 -0
- memos/_js/temporal.js.map +1 -0
- memos/_js/time-travel.d.ts +38 -0
- memos/_js/time-travel.d.ts.map +1 -0
- memos/_js/time-travel.js +46 -0
- memos/_js/time-travel.js.map +1 -0
- memos/_js/timestamp-format.d.ts +18 -0
- memos/_js/timestamp-format.d.ts.map +1 -0
- memos/_js/timestamp-format.js +52 -0
- memos/_js/timestamp-format.js.map +1 -0
- memos/_js/triple-extraction.d.ts +31 -0
- memos/_js/triple-extraction.d.ts.map +1 -0
- memos/_js/triple-extraction.js +116 -0
- memos/_js/triple-extraction.js.map +1 -0
- memos/_js/types.d.ts +2474 -0
- memos/_js/types.d.ts.map +1 -0
- memos/_js/types.js +20 -0
- memos/_js/types.js.map +1 -0
- memos/_js/updater.d.ts +95 -0
- memos/_js/updater.d.ts.map +1 -0
- memos/_js/updater.js +268 -0
- memos/_js/updater.js.map +1 -0
- memos/_js/version.d.ts +17 -0
- memos/_js/version.d.ts.map +1 -0
- memos/_js/version.js +34 -0
- memos/_js/version.js.map +1 -0
- memos/_js/vision/clip-query-cache.d.ts +58 -0
- memos/_js/vision/clip-query-cache.d.ts.map +1 -0
- memos/_js/vision/clip-query-cache.js +79 -0
- memos/_js/vision/clip-query-cache.js.map +1 -0
- memos/_js/vision/clip.d.ts +113 -0
- memos/_js/vision/clip.d.ts.map +1 -0
- memos/_js/vision/clip.js +224 -0
- memos/_js/vision/clip.js.map +1 -0
- memos/_js/vision/fake-clip.d.ts +55 -0
- memos/_js/vision/fake-clip.d.ts.map +1 -0
- memos/_js/vision/fake-clip.js +122 -0
- memos/_js/vision/fake-clip.js.map +1 -0
- memos/_js/vision/index.d.ts +15 -0
- memos/_js/vision/index.d.ts.map +1 -0
- memos/_js/vision/index.js +13 -0
- memos/_js/vision/index.js.map +1 -0
- memos/_js/vision/store.d.ts +137 -0
- memos/_js/vision/store.d.ts.map +1 -0
- memos/_js/vision/store.js +383 -0
- memos/_js/vision/store.js.map +1 -0
- memos/adapters/__init__.py +0 -0
- memos/adapters/crewai.py +385 -0
- memos/adapters/langchain.py +301 -0
- memos/adapters/ollama.py +396 -0
- memos/server/__init__.py +0 -0
- memos/server/main.py +547 -0
- memos/server/routes.py +360 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity extraction for fused retrieval scoring.
|
|
3
|
+
*
|
|
4
|
+
* The third retrieval signal alongside keyword (FTS5) and semantic
|
|
5
|
+
* (embedding) legs: memories whose stored entities overlap the query's
|
|
6
|
+
* entities get a post-fusion boost. This mirrors the 2026 fused
|
|
7
|
+
* multi-signal retrieval consensus (semantic + BM25 + entity match).
|
|
8
|
+
*
|
|
9
|
+
* The extractor is deliberately lightweight and dependency-free —
|
|
10
|
+
* capitalized multi-word sequences, acronyms, quoted strings, and
|
|
11
|
+
* code-identifier shapes. It runs on every hybrid search, so it must
|
|
12
|
+
* stay in the sub-millisecond class. Entities for a memory are captured
|
|
13
|
+
* at write time into `metadata.entities` (see `MemOS.store`), so the
|
|
14
|
+
* per-candidate overlap check is a set intersection, not a rescan.
|
|
15
|
+
*
|
|
16
|
+
* @module @mem-os/entity-extraction
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Extract candidate entities from free text.
|
|
20
|
+
*
|
|
21
|
+
* Returns lowercase-normalized entities so overlap checks are
|
|
22
|
+
* case-insensitive. Order is stable (first appearance), duplicates are
|
|
23
|
+
* removed. Sentence-initial capitalization is ignored (it's grammatical,
|
|
24
|
+
* not a name), which keeps "Fix the parser" from yielding "fix".
|
|
25
|
+
*/
|
|
26
|
+
export declare function extractQueryEntities(text: string): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Overlap between query entities and a memory's stored entities
|
|
29
|
+
* (`metadata.entities`) and tags, in [0, 1]. Jaccard-style: shared
|
|
30
|
+
* count over the smaller set, so a single match on a 1-entity query
|
|
31
|
+
* counts fully.
|
|
32
|
+
*/
|
|
33
|
+
export declare function entityOverlap(queryEntities: readonly string[], node: {
|
|
34
|
+
tags?: readonly string[];
|
|
35
|
+
metadata?: {
|
|
36
|
+
entities?: unknown;
|
|
37
|
+
};
|
|
38
|
+
}): number;
|
|
39
|
+
/**
|
|
40
|
+
* Built-in alias table mapping surface forms to a canonical entity.
|
|
41
|
+
* Lexical extraction alone yields "Postgres", "PostgreSQL", and "pg" as
|
|
42
|
+
* three different entities; canonicalizing through this map (plus any
|
|
43
|
+
* deployment-specific `MemOSConfig.entityAliases`) collapses them so the
|
|
44
|
+
* overlap signal fires across phrasings.
|
|
45
|
+
*/
|
|
46
|
+
export declare const BUILTIN_ENTITY_ALIASES: Readonly<Record<string, string>>;
|
|
47
|
+
/**
|
|
48
|
+
* Canonicalize extracted entities through the built-in alias table plus
|
|
49
|
+
* any deployment-specific aliases (from `MemOSConfig.entityAliases`,
|
|
50
|
+
* which wins over built-ins). Deduplicates after mapping, preserving
|
|
51
|
+
* first-appearance order.
|
|
52
|
+
*/
|
|
53
|
+
export declare function canonicalizeEntities(entities: readonly string[], extraAliases?: Readonly<Record<string, string>>): string[];
|
|
54
|
+
//# sourceMappingURL=entity-extraction.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-extraction.d.ts","sourceRoot":"","sources":["../src/entity-extraction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAwHH;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA0E3D;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,SAAS,MAAM,EAAE,EAChC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACpE,MAAM,CA+BR;AAMD;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuCnE,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9C,MAAM,EAAE,CAeV"}
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity extraction for fused retrieval scoring.
|
|
3
|
+
*
|
|
4
|
+
* The third retrieval signal alongside keyword (FTS5) and semantic
|
|
5
|
+
* (embedding) legs: memories whose stored entities overlap the query's
|
|
6
|
+
* entities get a post-fusion boost. This mirrors the 2026 fused
|
|
7
|
+
* multi-signal retrieval consensus (semantic + BM25 + entity match).
|
|
8
|
+
*
|
|
9
|
+
* The extractor is deliberately lightweight and dependency-free —
|
|
10
|
+
* capitalized multi-word sequences, acronyms, quoted strings, and
|
|
11
|
+
* code-identifier shapes. It runs on every hybrid search, so it must
|
|
12
|
+
* stay in the sub-millisecond class. Entities for a memory are captured
|
|
13
|
+
* at write time into `metadata.entities` (see `MemOS.store`), so the
|
|
14
|
+
* per-candidate overlap check is a set intersection, not a rescan.
|
|
15
|
+
*
|
|
16
|
+
* @module @mem-os/entity-extraction
|
|
17
|
+
*/
|
|
18
|
+
/** Minimum length for a single-word entity (filters noise like "I", "A"). */
|
|
19
|
+
const MIN_WORD_LENGTH = 3;
|
|
20
|
+
/** Words that are capitalized mid-sentence but are not entities. */
|
|
21
|
+
const STOPWORDS = new Set([
|
|
22
|
+
"the",
|
|
23
|
+
"this",
|
|
24
|
+
"that",
|
|
25
|
+
"these",
|
|
26
|
+
"those",
|
|
27
|
+
"there",
|
|
28
|
+
"then",
|
|
29
|
+
"than",
|
|
30
|
+
"when",
|
|
31
|
+
"while",
|
|
32
|
+
"where",
|
|
33
|
+
"which",
|
|
34
|
+
"who",
|
|
35
|
+
"how",
|
|
36
|
+
"why",
|
|
37
|
+
"what",
|
|
38
|
+
"and",
|
|
39
|
+
"but",
|
|
40
|
+
"for",
|
|
41
|
+
"not",
|
|
42
|
+
"with",
|
|
43
|
+
"from",
|
|
44
|
+
"into",
|
|
45
|
+
"onto",
|
|
46
|
+
"over",
|
|
47
|
+
"after",
|
|
48
|
+
"before",
|
|
49
|
+
"because",
|
|
50
|
+
"if",
|
|
51
|
+
"it",
|
|
52
|
+
"its",
|
|
53
|
+
"we",
|
|
54
|
+
"our",
|
|
55
|
+
"you",
|
|
56
|
+
"your",
|
|
57
|
+
"they",
|
|
58
|
+
"their",
|
|
59
|
+
"she",
|
|
60
|
+
"his",
|
|
61
|
+
"her",
|
|
62
|
+
"him",
|
|
63
|
+
"us",
|
|
64
|
+
"them",
|
|
65
|
+
"please",
|
|
66
|
+
"also",
|
|
67
|
+
"just",
|
|
68
|
+
"very",
|
|
69
|
+
"can",
|
|
70
|
+
"could",
|
|
71
|
+
"should",
|
|
72
|
+
"would",
|
|
73
|
+
"will",
|
|
74
|
+
"shall",
|
|
75
|
+
"may",
|
|
76
|
+
"might",
|
|
77
|
+
"must",
|
|
78
|
+
"do",
|
|
79
|
+
"does",
|
|
80
|
+
"did",
|
|
81
|
+
"done",
|
|
82
|
+
"have",
|
|
83
|
+
"has",
|
|
84
|
+
"had",
|
|
85
|
+
"was",
|
|
86
|
+
"were",
|
|
87
|
+
"are",
|
|
88
|
+
"is",
|
|
89
|
+
"am",
|
|
90
|
+
"be",
|
|
91
|
+
"been",
|
|
92
|
+
"being",
|
|
93
|
+
"add",
|
|
94
|
+
"use",
|
|
95
|
+
"using",
|
|
96
|
+
"used",
|
|
97
|
+
"make",
|
|
98
|
+
"made",
|
|
99
|
+
"run",
|
|
100
|
+
"runs",
|
|
101
|
+
"new",
|
|
102
|
+
"old",
|
|
103
|
+
"get",
|
|
104
|
+
"got",
|
|
105
|
+
"set",
|
|
106
|
+
"let",
|
|
107
|
+
"put",
|
|
108
|
+
"try",
|
|
109
|
+
"tried",
|
|
110
|
+
"i'd",
|
|
111
|
+
"i'm",
|
|
112
|
+
"i've",
|
|
113
|
+
"don't",
|
|
114
|
+
"doesn't",
|
|
115
|
+
"didn't",
|
|
116
|
+
"won't",
|
|
117
|
+
"can't",
|
|
118
|
+
]);
|
|
119
|
+
/** Terms that never count as entities regardless of casing. */
|
|
120
|
+
const NON_ENTITY_TOKEN = /^[0-9_]+$/;
|
|
121
|
+
function isCodeIdentifier(token) {
|
|
122
|
+
// snake_case, kebab-case, dotted paths, or CamelCase with an internal
|
|
123
|
+
// uppercase — the shapes that show up when agents store technical memory.
|
|
124
|
+
return /[_\-.]/.test(token) || /^[a-z]+[A-Z]/.test(token);
|
|
125
|
+
}
|
|
126
|
+
function isAcronym(token) {
|
|
127
|
+
return (token.length >= 2 && token === token.toUpperCase() && /[A-Z]/.test(token));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Extract candidate entities from free text.
|
|
131
|
+
*
|
|
132
|
+
* Returns lowercase-normalized entities so overlap checks are
|
|
133
|
+
* case-insensitive. Order is stable (first appearance), duplicates are
|
|
134
|
+
* removed. Sentence-initial capitalization is ignored (it's grammatical,
|
|
135
|
+
* not a name), which keeps "Fix the parser" from yielding "fix".
|
|
136
|
+
*/
|
|
137
|
+
export function extractQueryEntities(text) {
|
|
138
|
+
if (!text)
|
|
139
|
+
return [];
|
|
140
|
+
const entities = [];
|
|
141
|
+
const seen = new Set();
|
|
142
|
+
const push = (raw, minLen = MIN_WORD_LENGTH) => {
|
|
143
|
+
const normalized = raw.toLowerCase().trim();
|
|
144
|
+
if (normalized.length < minLen)
|
|
145
|
+
return;
|
|
146
|
+
if (NON_ENTITY_TOKEN.test(normalized))
|
|
147
|
+
return;
|
|
148
|
+
if (STOPWORDS.has(normalized))
|
|
149
|
+
return;
|
|
150
|
+
if (seen.has(normalized))
|
|
151
|
+
return;
|
|
152
|
+
seen.add(normalized);
|
|
153
|
+
entities.push(normalized);
|
|
154
|
+
};
|
|
155
|
+
// 1. Quoted strings and backticked code spans are always entities.
|
|
156
|
+
// Single-character delimiters with lazy content (no `+` quantifiers
|
|
157
|
+
// adjacent to the negated class): nothing can be matched two ways, so
|
|
158
|
+
// the scan stays linear on adversarial, quote-heavy input — a
|
|
159
|
+
// polynomial-backtracking hazard with the previous `["'`]+` form.
|
|
160
|
+
for (const match of text.matchAll(/["'`]([^"'`\n]{3,60}?)["'`]/g)) {
|
|
161
|
+
push(match[1]);
|
|
162
|
+
}
|
|
163
|
+
// 2. Code identifiers (snake_case / kebab-case / dotted / camelCase).
|
|
164
|
+
for (const token of text.split(/[^A-Za-z0-9_\-.]+/)) {
|
|
165
|
+
if (token.length >= MIN_WORD_LENGTH &&
|
|
166
|
+
isCodeIdentifier(token) &&
|
|
167
|
+
!STOPWORDS.has(token.toLowerCase())) {
|
|
168
|
+
push(token);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// 3. Capitalized sequences (proper nouns), skipping sentence starts.
|
|
172
|
+
// A token is "sentence-initial" when the previous non-space char is
|
|
173
|
+
// nothing, `.`, `!`, `?`, or a newline.
|
|
174
|
+
const tokens = [...text.matchAll(/[A-Za-z][A-Za-z0-9'_-]*/g)];
|
|
175
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
176
|
+
const match = tokens[i];
|
|
177
|
+
const token = match[0];
|
|
178
|
+
const prevChar = match.index && match.index > 0 ? text[match.index - 1] : "";
|
|
179
|
+
const sentenceInitial = match.index === 0 || /[.!?\n]/.test(prevChar) || prevChar === "\n";
|
|
180
|
+
if (isAcronym(token)) {
|
|
181
|
+
// Acronyms are high-signal at any length (CI, KV, DB) — the
|
|
182
|
+
// general minimum length only guards prose words.
|
|
183
|
+
push(token, 2);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (sentenceInitial)
|
|
187
|
+
continue;
|
|
188
|
+
if (!/^[A-Z]/.test(token))
|
|
189
|
+
continue;
|
|
190
|
+
if (STOPWORDS.has(token.toLowerCase()))
|
|
191
|
+
continue;
|
|
192
|
+
// Extend across adjacent capitalized words ("Postgres Connection
|
|
193
|
+
// Pool") into one entity.
|
|
194
|
+
let phrase = token;
|
|
195
|
+
let lastIndex = match.index + token.length;
|
|
196
|
+
for (let j = i + 1; j < tokens.length; j += 1) {
|
|
197
|
+
const next = tokens[j];
|
|
198
|
+
const between = text.slice(lastIndex, next.index);
|
|
199
|
+
if (between !== " ")
|
|
200
|
+
break;
|
|
201
|
+
if (!/^[A-Z]/.test(next[0]) || STOPWORDS.has(next[0].toLowerCase()))
|
|
202
|
+
break;
|
|
203
|
+
phrase += ` ${next[0]}`;
|
|
204
|
+
lastIndex = next.index + next[0].length;
|
|
205
|
+
}
|
|
206
|
+
push(phrase);
|
|
207
|
+
}
|
|
208
|
+
return entities;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Overlap between query entities and a memory's stored entities
|
|
212
|
+
* (`metadata.entities`) and tags, in [0, 1]. Jaccard-style: shared
|
|
213
|
+
* count over the smaller set, so a single match on a 1-entity query
|
|
214
|
+
* counts fully.
|
|
215
|
+
*/
|
|
216
|
+
export function entityOverlap(queryEntities, node) {
|
|
217
|
+
if (queryEntities.length === 0)
|
|
218
|
+
return 0;
|
|
219
|
+
const stored = new Set();
|
|
220
|
+
for (const tag of node.tags ?? []) {
|
|
221
|
+
stored.add(String(tag).toLowerCase());
|
|
222
|
+
}
|
|
223
|
+
const metaEntities = node.metadata?.entities;
|
|
224
|
+
if (Array.isArray(metaEntities)) {
|
|
225
|
+
for (const entity of metaEntities) {
|
|
226
|
+
stored.add(String(entity).toLowerCase());
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (stored.size === 0)
|
|
230
|
+
return 0;
|
|
231
|
+
let shared = 0;
|
|
232
|
+
for (const entity of queryEntities) {
|
|
233
|
+
// Substring match both ways so "postgres connection pool" matches a
|
|
234
|
+
// memory tagged "postgres" and vice versa.
|
|
235
|
+
for (const candidate of stored) {
|
|
236
|
+
if (candidate === entity ||
|
|
237
|
+
candidate.includes(entity) ||
|
|
238
|
+
entity.includes(candidate)) {
|
|
239
|
+
shared += 1;
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return shared / Math.min(queryEntities.length, stored.size);
|
|
245
|
+
}
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
// Entity resolution (aliasing)
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
/**
|
|
250
|
+
* Built-in alias table mapping surface forms to a canonical entity.
|
|
251
|
+
* Lexical extraction alone yields "Postgres", "PostgreSQL", and "pg" as
|
|
252
|
+
* three different entities; canonicalizing through this map (plus any
|
|
253
|
+
* deployment-specific `MemOSConfig.entityAliases`) collapses them so the
|
|
254
|
+
* overlap signal fires across phrasings.
|
|
255
|
+
*/
|
|
256
|
+
export const BUILTIN_ENTITY_ALIASES = {
|
|
257
|
+
pg: "postgres",
|
|
258
|
+
postgres: "postgres",
|
|
259
|
+
postgresql: "postgres",
|
|
260
|
+
pgsql: "postgres",
|
|
261
|
+
js: "javascript",
|
|
262
|
+
nodejs: "node",
|
|
263
|
+
"node.js": "node",
|
|
264
|
+
ts: "typescript",
|
|
265
|
+
k8s: "kubernetes",
|
|
266
|
+
ks: "kubernetes",
|
|
267
|
+
py: "python",
|
|
268
|
+
pip: "python",
|
|
269
|
+
yarn: "npm",
|
|
270
|
+
npm: "npm",
|
|
271
|
+
docker: "docker",
|
|
272
|
+
containerd: "docker",
|
|
273
|
+
rust: "rust",
|
|
274
|
+
go: "golang",
|
|
275
|
+
golang: "golang",
|
|
276
|
+
rails: "rails",
|
|
277
|
+
react: "react",
|
|
278
|
+
nextjs: "next",
|
|
279
|
+
"next.js": "next",
|
|
280
|
+
vue: "vue",
|
|
281
|
+
s3: "aws",
|
|
282
|
+
aws: "aws",
|
|
283
|
+
gcp: "gcp",
|
|
284
|
+
gh: "github",
|
|
285
|
+
github: "github",
|
|
286
|
+
gha: "github-actions",
|
|
287
|
+
llm: "llm",
|
|
288
|
+
agent: "agent",
|
|
289
|
+
agents: "agent",
|
|
290
|
+
db: "database",
|
|
291
|
+
database: "database",
|
|
292
|
+
redis: "redis",
|
|
293
|
+
sqlite: "sqlite",
|
|
294
|
+
graphql: "graphql",
|
|
295
|
+
};
|
|
296
|
+
/**
|
|
297
|
+
* Canonicalize extracted entities through the built-in alias table plus
|
|
298
|
+
* any deployment-specific aliases (from `MemOSConfig.entityAliases`,
|
|
299
|
+
* which wins over built-ins). Deduplicates after mapping, preserving
|
|
300
|
+
* first-appearance order.
|
|
301
|
+
*/
|
|
302
|
+
export function canonicalizeEntities(entities, extraAliases) {
|
|
303
|
+
const aliases = extraAliases
|
|
304
|
+
? { ...BUILTIN_ENTITY_ALIASES, ...lowerKeys(extraAliases) }
|
|
305
|
+
: BUILTIN_ENTITY_ALIASES;
|
|
306
|
+
const out = [];
|
|
307
|
+
const seen = new Set();
|
|
308
|
+
for (const entity of entities) {
|
|
309
|
+
const key = entity.toLowerCase();
|
|
310
|
+
const canonical = (aliases[key] ?? key).toLowerCase();
|
|
311
|
+
if (seen.has(canonical))
|
|
312
|
+
continue;
|
|
313
|
+
seen.add(canonical);
|
|
314
|
+
out.push(canonical);
|
|
315
|
+
}
|
|
316
|
+
return out;
|
|
317
|
+
}
|
|
318
|
+
function lowerKeys(record) {
|
|
319
|
+
const out = {};
|
|
320
|
+
for (const [key, value] of Object.entries(record)) {
|
|
321
|
+
out[key.toLowerCase()] = value.toLowerCase();
|
|
322
|
+
}
|
|
323
|
+
return out;
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=entity-extraction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-extraction.js","sourceRoot":"","sources":["../src/entity-extraction.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-class entity index: an entity → memories inverted table in SQLite.
|
|
3
|
+
*
|
|
4
|
+
* MemOS already had an entity *signal* — the post-fusion entity-overlap
|
|
5
|
+
* boost in `fuseResults` (`src/retrieval.ts`) — but it could only re-score
|
|
6
|
+
* candidates the keyword (FTS5) and semantic legs had already retrieved.
|
|
7
|
+
* Memories that share entities with the query yet were missed by both
|
|
8
|
+
* legs never entered the candidate pool at all. This module closes that
|
|
9
|
+
* recall gap: every node's canonical entities are mirrored into the
|
|
10
|
+
* `entity_index` table at write time (following the `node_tags` join-table
|
|
11
|
+
* pattern), and `searchByEntities` turns query entities into a ranked
|
|
12
|
+
* candidate list that `MemOS.hybridSearch` fuses as a third RRF leg.
|
|
13
|
+
*
|
|
14
|
+
* This is the local, no-LLM analogue of the entity channel behind mem0
|
|
15
|
+
* v3's reported +20/+26 point jump and HippoRAG's entity-node gains: the
|
|
16
|
+
* extractor is the existing dependency-free lexical one
|
|
17
|
+
* (`src/entity-extraction.ts`), and the index is a plain B-tree — no
|
|
18
|
+
* embedding or graph traversal on the hot path.
|
|
19
|
+
*
|
|
20
|
+
* @module @mem-os/entity-index
|
|
21
|
+
*/
|
|
22
|
+
import type Database from "better-sqlite3";
|
|
23
|
+
/** One ranked hit from {@link searchByEntities}. */
|
|
24
|
+
export interface EntityIndexMatch {
|
|
25
|
+
/** The matched node's id. */
|
|
26
|
+
nodeId: string;
|
|
27
|
+
/** How many of the query entities this node matched. */
|
|
28
|
+
matches: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create the `entity_index` inverted table and its lookup index when
|
|
32
|
+
* missing. Migration-safe (`IF NOT EXISTS`): pre-existing databases gain
|
|
33
|
+
* the table on the next `init()` with no schema-version bump, and
|
|
34
|
+
* {@link backfillEntityIndex} populates it from existing rows.
|
|
35
|
+
*
|
|
36
|
+
* Node deletions cascade (`ON DELETE CASCADE` + `PRAGMA foreign_keys =
|
|
37
|
+
* ON`), so no explicit cleanup is needed on the delete path.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ensureEntityIndexTable(db: Database.Database): void;
|
|
40
|
+
/**
|
|
41
|
+
* Extract + canonicalize entities from free text (lowercase-normalized,
|
|
42
|
+
* deduped, first-appearance order). Used when no pre-canonicalized entity
|
|
43
|
+
* list is on hand — legacy rows in the backfill, or rows written straight
|
|
44
|
+
* through storage bypassing `MemOS.store`. `extraAliases` mirrors
|
|
45
|
+
* `MemOSConfig.entityAliases` (storage itself has no config, so the
|
|
46
|
+
* write/backfill paths pass none and get the built-in table).
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractContentEntities(content: string, extraAliases?: Readonly<Record<string, string>>): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Normalize raw entity strings for index storage: lowercase + trim +
|
|
51
|
+
* dedupe, preserving first-appearance order. The index is always
|
|
52
|
+
* lowercase, so lookups are case-insensitive by construction.
|
|
53
|
+
*/
|
|
54
|
+
export declare function normalizeEntities(entities: readonly unknown[]): string[];
|
|
55
|
+
/**
|
|
56
|
+
* Full re-sync of one node's entity rows: deletes stale rows, then
|
|
57
|
+
* inserts the normalized set (`INSERT OR IGNORE` keeps it idempotent).
|
|
58
|
+
* Runs inside the caller's transaction — call from `saveNode` /
|
|
59
|
+
* `updateNode` next to their `node_tags` mirrors.
|
|
60
|
+
*/
|
|
61
|
+
export declare function writeEntityIndexRows(db: Database.Database, nodeId: string, entities: readonly unknown[]): void;
|
|
62
|
+
/**
|
|
63
|
+
* Index one node's entities from its content: extracts with
|
|
64
|
+
* `extractQueryEntities` + `canonicalizeEntities` and stores
|
|
65
|
+
* lowercase-normalized rows. Prefer passing the write-time canonicalized
|
|
66
|
+
* `metadata.entities` via {@link writeEntityIndexRows} when available
|
|
67
|
+
* (it was canonicalized with the deployment's `entityAliases`); this
|
|
68
|
+
* content-based variant is the fallback for rows that lack it.
|
|
69
|
+
*/
|
|
70
|
+
export declare function indexNodeEntities(db: Database.Database, nodeId: string, content: string, extraAliases?: Readonly<Record<string, string>>): void;
|
|
71
|
+
/**
|
|
72
|
+
* Remove every index row for a node. Node deletes are normally handled by
|
|
73
|
+
* the `ON DELETE CASCADE` foreign key — this is for explicit re-syncs.
|
|
74
|
+
*/
|
|
75
|
+
export declare function removeNodeEntities(db: Database.Database, nodeId: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Ranked entity-leg lookup: node ids whose indexed entities intersect
|
|
78
|
+
* `entities`, ordered by matched-entity count (desc) with node id as the
|
|
79
|
+
* deterministic tiebreak. Returns `{ nodeId, matches }` pairs, best
|
|
80
|
+
* first — the caller (hybrid search) resolves ids to nodes and fuses the
|
|
81
|
+
* list as the third RRF leg.
|
|
82
|
+
*/
|
|
83
|
+
export declare function searchByEntities(db: Database.Database, entities: readonly string[], limit: number): EntityIndexMatch[];
|
|
84
|
+
/**
|
|
85
|
+
* One-time backfill for rows that predate the `entity_index` table (or
|
|
86
|
+
* lost their rows out-of-band). For each node missing from the index,
|
|
87
|
+
* prefers the stored `metadata.entities` — already canonicalized with the
|
|
88
|
+
* deployment's aliases at write time — and falls back to extracting from
|
|
89
|
+
* content. Idempotent: nodes already present in the index are skipped and
|
|
90
|
+
* inserts use `INSERT OR IGNORE`, so re-running is a no-op.
|
|
91
|
+
*/
|
|
92
|
+
export declare function backfillEntityIndex(db: Database.Database): void;
|
|
93
|
+
//# sourceMappingURL=entity-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-index.d.ts","sourceRoot":"","sources":["../src/entity-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAM3C,oDAAoD;AACpD,MAAM,WAAW,gBAAgB;IAC/B,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAUlE;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9C,MAAM,EAAE,CAEV;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,EAAE,CAUxE;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,IAAI,CAUN;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9C,IAAI,CAMN;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,MAAM,EAAE,MAAM,GACb,IAAI,CAEN;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,CAAC,QAAQ,EACrB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,KAAK,EAAE,MAAM,GACZ,gBAAgB,EAAE,CAkBpB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,GAAG,IAAI,CAuC/D"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-class entity index: an entity → memories inverted table in SQLite.
|
|
3
|
+
*
|
|
4
|
+
* MemOS already had an entity *signal* — the post-fusion entity-overlap
|
|
5
|
+
* boost in `fuseResults` (`src/retrieval.ts`) — but it could only re-score
|
|
6
|
+
* candidates the keyword (FTS5) and semantic legs had already retrieved.
|
|
7
|
+
* Memories that share entities with the query yet were missed by both
|
|
8
|
+
* legs never entered the candidate pool at all. This module closes that
|
|
9
|
+
* recall gap: every node's canonical entities are mirrored into the
|
|
10
|
+
* `entity_index` table at write time (following the `node_tags` join-table
|
|
11
|
+
* pattern), and `searchByEntities` turns query entities into a ranked
|
|
12
|
+
* candidate list that `MemOS.hybridSearch` fuses as a third RRF leg.
|
|
13
|
+
*
|
|
14
|
+
* This is the local, no-LLM analogue of the entity channel behind mem0
|
|
15
|
+
* v3's reported +20/+26 point jump and HippoRAG's entity-node gains: the
|
|
16
|
+
* extractor is the existing dependency-free lexical one
|
|
17
|
+
* (`src/entity-extraction.ts`), and the index is a plain B-tree — no
|
|
18
|
+
* embedding or graph traversal on the hot path.
|
|
19
|
+
*
|
|
20
|
+
* @module @mem-os/entity-index
|
|
21
|
+
*/
|
|
22
|
+
import { canonicalizeEntities, extractQueryEntities, } from "./entity-extraction.js";
|
|
23
|
+
/**
|
|
24
|
+
* Create the `entity_index` inverted table and its lookup index when
|
|
25
|
+
* missing. Migration-safe (`IF NOT EXISTS`): pre-existing databases gain
|
|
26
|
+
* the table on the next `init()` with no schema-version bump, and
|
|
27
|
+
* {@link backfillEntityIndex} populates it from existing rows.
|
|
28
|
+
*
|
|
29
|
+
* Node deletions cascade (`ON DELETE CASCADE` + `PRAGMA foreign_keys =
|
|
30
|
+
* ON`), so no explicit cleanup is needed on the delete path.
|
|
31
|
+
*/
|
|
32
|
+
export function ensureEntityIndexTable(db) {
|
|
33
|
+
db.exec(`
|
|
34
|
+
CREATE TABLE IF NOT EXISTS entity_index (
|
|
35
|
+
entity TEXT NOT NULL,
|
|
36
|
+
node_id TEXT NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
|
|
37
|
+
PRIMARY KEY (entity, node_id)
|
|
38
|
+
);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_entity_index_entity
|
|
40
|
+
ON entity_index(entity);
|
|
41
|
+
`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extract + canonicalize entities from free text (lowercase-normalized,
|
|
45
|
+
* deduped, first-appearance order). Used when no pre-canonicalized entity
|
|
46
|
+
* list is on hand — legacy rows in the backfill, or rows written straight
|
|
47
|
+
* through storage bypassing `MemOS.store`. `extraAliases` mirrors
|
|
48
|
+
* `MemOSConfig.entityAliases` (storage itself has no config, so the
|
|
49
|
+
* write/backfill paths pass none and get the built-in table).
|
|
50
|
+
*/
|
|
51
|
+
export function extractContentEntities(content, extraAliases) {
|
|
52
|
+
return canonicalizeEntities(extractQueryEntities(content), extraAliases);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Normalize raw entity strings for index storage: lowercase + trim +
|
|
56
|
+
* dedupe, preserving first-appearance order. The index is always
|
|
57
|
+
* lowercase, so lookups are case-insensitive by construction.
|
|
58
|
+
*/
|
|
59
|
+
export function normalizeEntities(entities) {
|
|
60
|
+
const out = [];
|
|
61
|
+
const seen = new Set();
|
|
62
|
+
for (const entity of entities) {
|
|
63
|
+
const normalized = String(entity).toLowerCase().trim();
|
|
64
|
+
if (normalized.length === 0 || seen.has(normalized))
|
|
65
|
+
continue;
|
|
66
|
+
seen.add(normalized);
|
|
67
|
+
out.push(normalized);
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Full re-sync of one node's entity rows: deletes stale rows, then
|
|
73
|
+
* inserts the normalized set (`INSERT OR IGNORE` keeps it idempotent).
|
|
74
|
+
* Runs inside the caller's transaction — call from `saveNode` /
|
|
75
|
+
* `updateNode` next to their `node_tags` mirrors.
|
|
76
|
+
*/
|
|
77
|
+
export function writeEntityIndexRows(db, nodeId, entities) {
|
|
78
|
+
const normalized = normalizeEntities(entities);
|
|
79
|
+
db.prepare("DELETE FROM entity_index WHERE node_id = ?").run(nodeId);
|
|
80
|
+
if (normalized.length === 0)
|
|
81
|
+
return;
|
|
82
|
+
const insert = db.prepare("INSERT OR IGNORE INTO entity_index (entity, node_id) VALUES (?, ?)");
|
|
83
|
+
for (const entity of normalized) {
|
|
84
|
+
insert.run(entity, nodeId);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Index one node's entities from its content: extracts with
|
|
89
|
+
* `extractQueryEntities` + `canonicalizeEntities` and stores
|
|
90
|
+
* lowercase-normalized rows. Prefer passing the write-time canonicalized
|
|
91
|
+
* `metadata.entities` via {@link writeEntityIndexRows} when available
|
|
92
|
+
* (it was canonicalized with the deployment's `entityAliases`); this
|
|
93
|
+
* content-based variant is the fallback for rows that lack it.
|
|
94
|
+
*/
|
|
95
|
+
export function indexNodeEntities(db, nodeId, content, extraAliases) {
|
|
96
|
+
writeEntityIndexRows(db, nodeId, extractContentEntities(content, extraAliases));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Remove every index row for a node. Node deletes are normally handled by
|
|
100
|
+
* the `ON DELETE CASCADE` foreign key — this is for explicit re-syncs.
|
|
101
|
+
*/
|
|
102
|
+
export function removeNodeEntities(db, nodeId) {
|
|
103
|
+
db.prepare("DELETE FROM entity_index WHERE node_id = ?").run(nodeId);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Ranked entity-leg lookup: node ids whose indexed entities intersect
|
|
107
|
+
* `entities`, ordered by matched-entity count (desc) with node id as the
|
|
108
|
+
* deterministic tiebreak. Returns `{ nodeId, matches }` pairs, best
|
|
109
|
+
* first — the caller (hybrid search) resolves ids to nodes and fuses the
|
|
110
|
+
* list as the third RRF leg.
|
|
111
|
+
*/
|
|
112
|
+
export function searchByEntities(db, entities, limit) {
|
|
113
|
+
const normalized = normalizeEntities(entities);
|
|
114
|
+
if (normalized.length === 0 || limit <= 0)
|
|
115
|
+
return [];
|
|
116
|
+
const placeholders = normalized.map(() => "?").join(", ");
|
|
117
|
+
const rows = db
|
|
118
|
+
.prepare(`SELECT node_id AS nodeId, COUNT(*) AS matches
|
|
119
|
+
FROM entity_index
|
|
120
|
+
WHERE entity IN (${placeholders})
|
|
121
|
+
GROUP BY node_id
|
|
122
|
+
ORDER BY matches DESC, node_id ASC
|
|
123
|
+
LIMIT ?`)
|
|
124
|
+
.all(...normalized, limit);
|
|
125
|
+
return rows.map((row) => ({
|
|
126
|
+
nodeId: String(row.nodeId),
|
|
127
|
+
matches: Number(row.matches),
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* One-time backfill for rows that predate the `entity_index` table (or
|
|
132
|
+
* lost their rows out-of-band). For each node missing from the index,
|
|
133
|
+
* prefers the stored `metadata.entities` — already canonicalized with the
|
|
134
|
+
* deployment's aliases at write time — and falls back to extracting from
|
|
135
|
+
* content. Idempotent: nodes already present in the index are skipped and
|
|
136
|
+
* inserts use `INSERT OR IGNORE`, so re-running is a no-op.
|
|
137
|
+
*/
|
|
138
|
+
export function backfillEntityIndex(db) {
|
|
139
|
+
const indexed = new Set(db.prepare("SELECT node_id FROM entity_index").all().map((row) => row.node_id));
|
|
140
|
+
const rows = db
|
|
141
|
+
.prepare("SELECT id, content, metadata FROM nodes")
|
|
142
|
+
.all();
|
|
143
|
+
if (rows.length === 0)
|
|
144
|
+
return;
|
|
145
|
+
const tx = db.transaction(() => {
|
|
146
|
+
for (const row of rows) {
|
|
147
|
+
if (indexed.has(row.id))
|
|
148
|
+
continue;
|
|
149
|
+
let entities = null;
|
|
150
|
+
try {
|
|
151
|
+
const metadata = JSON.parse(row.metadata);
|
|
152
|
+
if (Array.isArray(metadata?.entities)) {
|
|
153
|
+
entities = metadata.entities;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// Corrupt metadata JSON: fall through to content extraction.
|
|
158
|
+
}
|
|
159
|
+
writeEntityIndexRows(db, row.id, entities ?? extractContentEntities(row.content));
|
|
160
|
+
indexed.add(row.id);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
tx();
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=entity-index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity-index.js","sourceRoot":"","sources":["../src/entity-index.ts"],"names":[],"mappings":"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"}
|