@vortex-os/memory-extended 0.5.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/README.md +244 -0
- package/dist/consolidate/index.d.ts +7 -0
- package/dist/consolidate/index.d.ts.map +1 -0
- package/dist/consolidate/index.js +4 -0
- package/dist/consolidate/index.js.map +1 -0
- package/dist/consolidate/proposer.d.ts +43 -0
- package/dist/consolidate/proposer.d.ts.map +1 -0
- package/dist/consolidate/proposer.js +276 -0
- package/dist/consolidate/proposer.js.map +1 -0
- package/dist/consolidate/query.d.ts +32 -0
- package/dist/consolidate/query.d.ts.map +1 -0
- package/dist/consolidate/query.js +40 -0
- package/dist/consolidate/query.js.map +1 -0
- package/dist/consolidate/store.d.ts +21 -0
- package/dist/consolidate/store.d.ts.map +1 -0
- package/dist/consolidate/store.js +91 -0
- package/dist/consolidate/store.js.map +1 -0
- package/dist/consolidate/types.d.ts +68 -0
- package/dist/consolidate/types.d.ts.map +1 -0
- package/dist/consolidate/types.js +2 -0
- package/dist/consolidate/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/frontmatter.d.ts +6 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +38 -0
- package/dist/internal/frontmatter.js.map +1 -0
- package/dist/internal/proactive-curator-helpers.d.ts +171 -0
- package/dist/internal/proactive-curator-helpers.d.ts.map +1 -0
- package/dist/internal/proactive-curator-helpers.js +162 -0
- package/dist/internal/proactive-curator-helpers.js.map +1 -0
- package/dist/mcp/document-tools.d.ts +144 -0
- package/dist/mcp/document-tools.d.ts.map +1 -0
- package/dist/mcp/document-tools.js +319 -0
- package/dist/mcp/document-tools.js.map +1 -0
- package/dist/mcp/index.d.ts +34 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/install.d.ts +72 -0
- package/dist/mcp/install.d.ts.map +1 -0
- package/dist/mcp/install.js +92 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/memory-tools.d.ts +101 -0
- package/dist/mcp/memory-tools.d.ts.map +1 -0
- package/dist/mcp/memory-tools.js +105 -0
- package/dist/mcp/memory-tools.js.map +1 -0
- package/dist/mcp/recall-tool.d.ts +52 -0
- package/dist/mcp/recall-tool.d.ts.map +1 -0
- package/dist/mcp/recall-tool.js +60 -0
- package/dist/mcp/recall-tool.js.map +1 -0
- package/dist/mcp/server.d.ts +32 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +113 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/recall/engine.d.ts +38 -0
- package/dist/recall/engine.d.ts.map +1 -0
- package/dist/recall/engine.js +113 -0
- package/dist/recall/engine.js.map +1 -0
- package/dist/recall/index.d.ts +22 -0
- package/dist/recall/index.d.ts.map +1 -0
- package/dist/recall/index.js +20 -0
- package/dist/recall/index.js.map +1 -0
- package/dist/recall/intent.d.ts +24 -0
- package/dist/recall/intent.d.ts.map +1 -0
- package/dist/recall/intent.js +95 -0
- package/dist/recall/intent.js.map +1 -0
- package/dist/recall/render.d.ts +11 -0
- package/dist/recall/render.d.ts.map +1 -0
- package/dist/recall/render.js +23 -0
- package/dist/recall/render.js.map +1 -0
- package/dist/recall/types.d.ts +72 -0
- package/dist/recall/types.d.ts.map +1 -0
- package/dist/recall/types.js +2 -0
- package/dist/recall/types.js.map +1 -0
- package/dist/sessionArchive/adapters/claude-code.d.ts +3 -0
- package/dist/sessionArchive/adapters/claude-code.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/claude-code.js +276 -0
- package/dist/sessionArchive/adapters/claude-code.js.map +1 -0
- package/dist/sessionArchive/adapters/claude-desktop.d.ts +3 -0
- package/dist/sessionArchive/adapters/claude-desktop.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/claude-desktop.js +234 -0
- package/dist/sessionArchive/adapters/claude-desktop.js.map +1 -0
- package/dist/sessionArchive/adapters/codex.d.ts +3 -0
- package/dist/sessionArchive/adapters/codex.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/codex.js +322 -0
- package/dist/sessionArchive/adapters/codex.js.map +1 -0
- package/dist/sessionArchive/adapters/gemini.d.ts +3 -0
- package/dist/sessionArchive/adapters/gemini.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/gemini.js +248 -0
- package/dist/sessionArchive/adapters/gemini.js.map +1 -0
- package/dist/sessionArchive/adapters/index.d.ts +5 -0
- package/dist/sessionArchive/adapters/index.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/index.js +5 -0
- package/dist/sessionArchive/adapters/index.js.map +1 -0
- package/dist/sessionArchive/index.d.ts +9 -0
- package/dist/sessionArchive/index.d.ts.map +1 -0
- package/dist/sessionArchive/index.js +6 -0
- package/dist/sessionArchive/index.js.map +1 -0
- package/dist/sessionArchive/ingest.d.ts +68 -0
- package/dist/sessionArchive/ingest.d.ts.map +1 -0
- package/dist/sessionArchive/ingest.js +134 -0
- package/dist/sessionArchive/ingest.js.map +1 -0
- package/dist/sessionArchive/normalize.d.ts +15 -0
- package/dist/sessionArchive/normalize.d.ts.map +1 -0
- package/dist/sessionArchive/normalize.js +40 -0
- package/dist/sessionArchive/normalize.js.map +1 -0
- package/dist/sessionArchive/store.d.ts +118 -0
- package/dist/sessionArchive/store.d.ts.map +1 -0
- package/dist/sessionArchive/store.js +491 -0
- package/dist/sessionArchive/store.js.map +1 -0
- package/dist/sessionArchive/types.d.ts +124 -0
- package/dist/sessionArchive/types.d.ts.map +1 -0
- package/dist/sessionArchive/types.js +24 -0
- package/dist/sessionArchive/types.js.map +1 -0
- package/dist/sqlite/drift.d.ts +22 -0
- package/dist/sqlite/drift.d.ts.map +1 -0
- package/dist/sqlite/drift.js +69 -0
- package/dist/sqlite/drift.js.map +1 -0
- package/dist/sqlite/index.d.ts +22 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +4 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/rebuild.d.ts +32 -0
- package/dist/sqlite/rebuild.d.ts.map +1 -0
- package/dist/sqlite/rebuild.js +80 -0
- package/dist/sqlite/rebuild.js.map +1 -0
- package/dist/sqlite/schema.d.ts +15 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +41 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/dist/sqlite/store.d.ts +49 -0
- package/dist/sqlite/store.d.ts.map +1 -0
- package/dist/sqlite/store.js +179 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/sqlite/types.d.ts +57 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/dist/vector/backend-brute.d.ts +38 -0
- package/dist/vector/backend-brute.d.ts.map +1 -0
- package/dist/vector/backend-brute.js +112 -0
- package/dist/vector/backend-brute.js.map +1 -0
- package/dist/vector/embedder.d.ts +59 -0
- package/dist/vector/embedder.d.ts.map +1 -0
- package/dist/vector/embedder.js +47 -0
- package/dist/vector/embedder.js.map +1 -0
- package/dist/vector/index.d.ts +24 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/index.js +7 -0
- package/dist/vector/index.js.map +1 -0
- package/dist/vector/math.d.ts +21 -0
- package/dist/vector/math.d.ts.map +1 -0
- package/dist/vector/math.js +34 -0
- package/dist/vector/math.js.map +1 -0
- package/dist/vector/schema.d.ts +14 -0
- package/dist/vector/schema.d.ts.map +1 -0
- package/dist/vector/schema.js +24 -0
- package/dist/vector/schema.js.map +1 -0
- package/dist/vector/segment.d.ts +65 -0
- package/dist/vector/segment.d.ts.map +1 -0
- package/dist/vector/segment.js +72 -0
- package/dist/vector/segment.js.map +1 -0
- package/dist/vector/session.d.ts +90 -0
- package/dist/vector/session.d.ts.map +1 -0
- package/dist/vector/session.js +242 -0
- package/dist/vector/session.js.map +1 -0
- package/dist/vector/store.d.ts +69 -0
- package/dist/vector/store.d.ts.map +1 -0
- package/dist/vector/store.js +131 -0
- package/dist/vector/store.js.map +1 -0
- package/dist/vector/types.d.ts +109 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/dist/vector/types.js +24 -0
- package/dist/vector/types.js.map +1 -0
- package/package.json +96 -0
- package/scripts/mcp-stdio.mjs +143 -0
- package/scripts/rebuild-memory-sqlite.mjs +39 -0
- package/scripts/rebuild-memory-vector.mjs +64 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import Database from "better-sqlite3";
|
|
2
|
+
import type { VectorBackend, VectorRow, VectorSearchHit, VectorSearchOptions, VectorSource } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Default {@link VectorBackend} (operator decision 2 / 2026-05-29):
|
|
5
|
+
* in-process brute-force cosine over vectors persisted in a plain SQLite
|
|
6
|
+
* table. No `sqlite-vec`, no native extension to load — which is exactly
|
|
7
|
+
* why it is the default at this corpus size (tens to low hundreds of
|
|
8
|
+
* memories). A full scan of a few hundred unit vectors is sub-millisecond;
|
|
9
|
+
* the operational simplicity (zero extra deps, identical behavior on every
|
|
10
|
+
* platform) outweighs ANN indexing until the corpus reaches ~10k vectors.
|
|
11
|
+
*
|
|
12
|
+
* Swap in a `SqliteVecBackend` / `QdrantBackend` (same interface) when that
|
|
13
|
+
* day comes; `MemoryVectorStore` does not need to change.
|
|
14
|
+
*
|
|
15
|
+
* Storage detail: vectors are stored L2-normalized (the store normalizes
|
|
16
|
+
* before calling `upsert`), so the cosine similarity of two stored vectors
|
|
17
|
+
* is just their dot product. The query vector is normalized the same way,
|
|
18
|
+
* keeping {@link search} to one dot product per row.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BruteForceBackend implements VectorBackend {
|
|
21
|
+
readonly dbPath: string;
|
|
22
|
+
private db;
|
|
23
|
+
/**
|
|
24
|
+
* Open against a DB path (shares the file with `MemorySqliteStore` by
|
|
25
|
+
* design) or an already-open Database handle (lets the caller reuse a
|
|
26
|
+
* single connection). When given a handle, `close()` is a no-op — the
|
|
27
|
+
* owner closes it.
|
|
28
|
+
*/
|
|
29
|
+
constructor(db: string | Database.Database);
|
|
30
|
+
private readonly ownsHandle;
|
|
31
|
+
upsert(row: VectorRow): void;
|
|
32
|
+
delete(id: string, source?: VectorSource): boolean;
|
|
33
|
+
search(query: Float32Array, options?: VectorSearchOptions): readonly VectorSearchHit[];
|
|
34
|
+
listIds(source?: VectorSource): readonly string[];
|
|
35
|
+
count(source?: VectorSource): number;
|
|
36
|
+
close(): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=backend-brute.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend-brute.d.ts","sourceRoot":"","sources":["../../src/vector/backend-brute.ts"],"names":[],"mappings":"AAEA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAGtC,OAAO,KAAK,EACV,aAAa,EACb,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,YAAY,EACb,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,iBAAkB,YAAW,aAAa;IACrD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,CAAoB;IAE9B;;;;;OAKG;gBACS,EAAE,EAAE,MAAM,GAAG,QAAQ,CAAC,QAAQ;IAe1C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IAErC,MAAM,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI;IAe5B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,YAAY,GAAG,OAAO;IAOlD,MAAM,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,SAAS,eAAe,EAAE;IAqBtF,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,SAAS,MAAM,EAAE;IAOjD,KAAK,CAAC,MAAM,CAAC,EAAE,YAAY,GAAG,MAAM;IAOpC,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { VECTOR_SCHEMA } from "./schema.js";
|
|
5
|
+
import { dot } from "./math.js";
|
|
6
|
+
/**
|
|
7
|
+
* Default {@link VectorBackend} (operator decision 2 / 2026-05-29):
|
|
8
|
+
* in-process brute-force cosine over vectors persisted in a plain SQLite
|
|
9
|
+
* table. No `sqlite-vec`, no native extension to load — which is exactly
|
|
10
|
+
* why it is the default at this corpus size (tens to low hundreds of
|
|
11
|
+
* memories). A full scan of a few hundred unit vectors is sub-millisecond;
|
|
12
|
+
* the operational simplicity (zero extra deps, identical behavior on every
|
|
13
|
+
* platform) outweighs ANN indexing until the corpus reaches ~10k vectors.
|
|
14
|
+
*
|
|
15
|
+
* Swap in a `SqliteVecBackend` / `QdrantBackend` (same interface) when that
|
|
16
|
+
* day comes; `MemoryVectorStore` does not need to change.
|
|
17
|
+
*
|
|
18
|
+
* Storage detail: vectors are stored L2-normalized (the store normalizes
|
|
19
|
+
* before calling `upsert`), so the cosine similarity of two stored vectors
|
|
20
|
+
* is just their dot product. The query vector is normalized the same way,
|
|
21
|
+
* keeping {@link search} to one dot product per row.
|
|
22
|
+
*/
|
|
23
|
+
export class BruteForceBackend {
|
|
24
|
+
dbPath;
|
|
25
|
+
db;
|
|
26
|
+
/**
|
|
27
|
+
* Open against a DB path (shares the file with `MemorySqliteStore` by
|
|
28
|
+
* design) or an already-open Database handle (lets the caller reuse a
|
|
29
|
+
* single connection). When given a handle, `close()` is a no-op — the
|
|
30
|
+
* owner closes it.
|
|
31
|
+
*/
|
|
32
|
+
constructor(db) {
|
|
33
|
+
if (typeof db === "string") {
|
|
34
|
+
mkdirSync(dirname(db), { recursive: true });
|
|
35
|
+
this.dbPath = db;
|
|
36
|
+
this.db = new Database(db);
|
|
37
|
+
this.db.pragma("journal_mode = WAL");
|
|
38
|
+
this.ownsHandle = true;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.dbPath = db.name;
|
|
42
|
+
this.db = db;
|
|
43
|
+
this.ownsHandle = false;
|
|
44
|
+
}
|
|
45
|
+
this.db.exec(VECTOR_SCHEMA);
|
|
46
|
+
}
|
|
47
|
+
ownsHandle;
|
|
48
|
+
upsert(row) {
|
|
49
|
+
this.db
|
|
50
|
+
.prepare(`INSERT OR REPLACE INTO memory_vectors (id, source, dim, vector)
|
|
51
|
+
VALUES (@id, @source, @dim, @vector)`)
|
|
52
|
+
.run({
|
|
53
|
+
id: row.id,
|
|
54
|
+
source: row.source,
|
|
55
|
+
dim: row.dim,
|
|
56
|
+
// Float32Array → Buffer view over the same bytes (no copy of values).
|
|
57
|
+
vector: Buffer.from(row.vector.buffer, row.vector.byteOffset, row.vector.byteLength),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
delete(id, source) {
|
|
61
|
+
const res = source
|
|
62
|
+
? this.db.prepare(`DELETE FROM memory_vectors WHERE id = ? AND source = ?`).run(id, source)
|
|
63
|
+
: this.db.prepare(`DELETE FROM memory_vectors WHERE id = ?`).run(id);
|
|
64
|
+
return res.changes > 0;
|
|
65
|
+
}
|
|
66
|
+
search(query, options) {
|
|
67
|
+
const k = options?.k ?? 10;
|
|
68
|
+
const idFilter = options?.ids;
|
|
69
|
+
const sql = options?.source
|
|
70
|
+
? `SELECT id, source, dim, vector FROM memory_vectors WHERE source = ?`
|
|
71
|
+
: `SELECT id, source, dim, vector FROM memory_vectors`;
|
|
72
|
+
const rows = (options?.source
|
|
73
|
+
? this.db.prepare(sql).all(options.source)
|
|
74
|
+
: this.db.prepare(sql).all());
|
|
75
|
+
const hits = [];
|
|
76
|
+
for (const r of rows) {
|
|
77
|
+
if (idFilter && !idFilter.has(r.id))
|
|
78
|
+
continue;
|
|
79
|
+
if (r.dim !== query.length)
|
|
80
|
+
continue; // dimension mismatch → skip (model changed)
|
|
81
|
+
const vec = bufferToFloat32(r.vector);
|
|
82
|
+
hits.push({ id: r.id, source: r.source, score: dot(query, vec) });
|
|
83
|
+
}
|
|
84
|
+
hits.sort((a, b) => b.score - a.score);
|
|
85
|
+
return hits.slice(0, k);
|
|
86
|
+
}
|
|
87
|
+
listIds(source) {
|
|
88
|
+
const rows = (source
|
|
89
|
+
? this.db.prepare(`SELECT id FROM memory_vectors WHERE source = ? ORDER BY id`).all(source)
|
|
90
|
+
: this.db.prepare(`SELECT id FROM memory_vectors ORDER BY id`).all());
|
|
91
|
+
return rows.map((r) => r.id);
|
|
92
|
+
}
|
|
93
|
+
count(source) {
|
|
94
|
+
const row = (source
|
|
95
|
+
? this.db.prepare(`SELECT COUNT(*) AS n FROM memory_vectors WHERE source = ?`).get(source)
|
|
96
|
+
: this.db.prepare(`SELECT COUNT(*) AS n FROM memory_vectors`).get());
|
|
97
|
+
return row.n;
|
|
98
|
+
}
|
|
99
|
+
close() {
|
|
100
|
+
if (this.ownsHandle)
|
|
101
|
+
this.db.close();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Reinterpret a stored BLOB as Float32Array (copying to guarantee alignment). */
|
|
105
|
+
function bufferToFloat32(buf) {
|
|
106
|
+
// Copy into a fresh ArrayBuffer — a Buffer's underlying pool may be offset
|
|
107
|
+
// such that a zero-copy Float32Array view would be mis-aligned.
|
|
108
|
+
const copy = new Uint8Array(buf.byteLength);
|
|
109
|
+
copy.set(buf);
|
|
110
|
+
return new Float32Array(copy.buffer, 0, buf.byteLength / 4);
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=backend-brute.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend-brute.js","sourceRoot":"","sources":["../../src/vector/backend-brute.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { EmbedFn } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default local embedder (operator decision 1 / 2026-05-29).
|
|
4
|
+
*
|
|
5
|
+
* Ships with `@vortex-os/memory-extended` so installing the package gives
|
|
6
|
+
* you a working embedder with no separate manual step. The embedding
|
|
7
|
+
* *library* (`@huggingface/transformers`) is a declared dependency and
|
|
8
|
+
* installs with the package; the embedding *model weights* download once on
|
|
9
|
+
* first call and are cached by the library thereafter. Nothing the operator
|
|
10
|
+
* has to fetch by hand.
|
|
11
|
+
*
|
|
12
|
+
* The heavy library is loaded lazily — and via a runtime-resolved
|
|
13
|
+
* specifier so the TypeScript build does not require its types to be
|
|
14
|
+
* present. Hosts that prefer an API embedder (OpenAI / Voyage) simply pass
|
|
15
|
+
* their own `EmbedFn` and never trigger this code path.
|
|
16
|
+
*
|
|
17
|
+
* Default model: `Xenova/multilingual-e5-small` — 384-dim, 50+ languages
|
|
18
|
+
* (incl. Korean), 512-token input, runs locally on CPU, no API key. Same
|
|
19
|
+
* weight class as the older MiniLM default (~118M, ~470 MB) but it is a
|
|
20
|
+
* *retrieval-trained* model (better at query↔document matching than a
|
|
21
|
+
* paraphrase model) and accepts 512 tokens vs MiniLM's 128, easing the
|
|
22
|
+
* within-chunk truncation that hurt session recall.
|
|
23
|
+
*
|
|
24
|
+
* **e5 prefixes (asymmetric):** e5 expects `"query: "` before a search query
|
|
25
|
+
* and `"passage: "` before an indexed document. This embedder applies them
|
|
26
|
+
* automatically from `kind` (passed by the store: indexing → passage,
|
|
27
|
+
* recall → query). If you override `model` to a *symmetric* model
|
|
28
|
+
* (MiniLM, all-MiniLM-L6-v2) pass `prefixes: null` to disable them.
|
|
29
|
+
*
|
|
30
|
+
* NOTE: vectors are model-specific. Changing the model requires rebuilding
|
|
31
|
+
* the vector index (`npx rebuild-memory-vector`); even when the dimension
|
|
32
|
+
* matches, the embedding *spaces* differ, so stale vectors would produce
|
|
33
|
+
* meaningless similarities until rebuilt.
|
|
34
|
+
*/
|
|
35
|
+
export interface LocalEmbedderOptions {
|
|
36
|
+
/** HF model id. Default `Xenova/multilingual-e5-small`. */
|
|
37
|
+
readonly model?: string;
|
|
38
|
+
/**
|
|
39
|
+
* Pooling strategy passed to the feature-extraction pipeline. e5 and
|
|
40
|
+
* MiniLM both use mean pooling; leave as default unless using another model.
|
|
41
|
+
*/
|
|
42
|
+
readonly pooling?: "mean" | "cls";
|
|
43
|
+
/**
|
|
44
|
+
* Instruction prefixes for asymmetric retrieval models. Default = e5's
|
|
45
|
+
* `query:` / `passage:`. Pass `null` for symmetric models (no prefix).
|
|
46
|
+
*/
|
|
47
|
+
readonly prefixes?: {
|
|
48
|
+
readonly query: string;
|
|
49
|
+
readonly passage: string;
|
|
50
|
+
} | null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Build an {@link EmbedFn} backed by a local transformers pipeline. The
|
|
54
|
+
* pipeline is created on first invocation and reused for the lifetime of
|
|
55
|
+
* the returned function (so a `rebuild` over N memories loads the model
|
|
56
|
+
* once, not N times).
|
|
57
|
+
*/
|
|
58
|
+
export declare function createLocalEmbedder(options?: LocalEmbedderOptions): EmbedFn;
|
|
59
|
+
//# sourceMappingURL=embedder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedder.d.ts","sourceRoot":"","sources":["../../src/vector/embedder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,YAAY,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;IAClC;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACjF;AAKD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CA2C3E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const DEFAULT_MODEL = "Xenova/multilingual-e5-small";
|
|
2
|
+
const E5_PREFIXES = { query: "query: ", passage: "passage: " };
|
|
3
|
+
/**
|
|
4
|
+
* Build an {@link EmbedFn} backed by a local transformers pipeline. The
|
|
5
|
+
* pipeline is created on first invocation and reused for the lifetime of
|
|
6
|
+
* the returned function (so a `rebuild` over N memories loads the model
|
|
7
|
+
* once, not N times).
|
|
8
|
+
*/
|
|
9
|
+
export function createLocalEmbedder(options) {
|
|
10
|
+
const model = options?.model ?? DEFAULT_MODEL;
|
|
11
|
+
const pooling = options?.pooling ?? "mean";
|
|
12
|
+
// undefined → e5 defaults; null → disabled (symmetric models).
|
|
13
|
+
const prefixes = options?.prefixes === undefined ? E5_PREFIXES : options.prefixes;
|
|
14
|
+
// Lazily-created, memoized pipeline promise. Typed loosely because the
|
|
15
|
+
// dependency is optional-at-build-time and resolved at runtime.
|
|
16
|
+
let pipePromise = null;
|
|
17
|
+
async function getPipe() {
|
|
18
|
+
if (pipePromise)
|
|
19
|
+
return pipePromise;
|
|
20
|
+
pipePromise = (async () => {
|
|
21
|
+
// Runtime-resolved specifier: keeps tsc from requiring the module's
|
|
22
|
+
// types at build time. The package is a runtime dependency.
|
|
23
|
+
const specifier = "@huggingface/transformers";
|
|
24
|
+
let mod;
|
|
25
|
+
try {
|
|
26
|
+
mod = (await import(specifier));
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
throw new Error(`createLocalEmbedder: failed to load "@huggingface/transformers". ` +
|
|
30
|
+
`It ships as a dependency of @vortex-os/memory-extended; if it is missing, ` +
|
|
31
|
+
`run \`npm install\`, or pass your own EmbedFn (e.g. an OpenAI adapter) ` +
|
|
32
|
+
`to avoid the local model entirely. Original error: ${e.message}`);
|
|
33
|
+
}
|
|
34
|
+
const pipeline = mod.pipeline;
|
|
35
|
+
const extractor = (await pipeline("feature-extraction", model));
|
|
36
|
+
return extractor;
|
|
37
|
+
})();
|
|
38
|
+
return pipePromise;
|
|
39
|
+
}
|
|
40
|
+
return async (text, kind) => {
|
|
41
|
+
const extractor = await getPipe();
|
|
42
|
+
const prefix = prefixes ? (kind === "query" ? prefixes.query : prefixes.passage) : "";
|
|
43
|
+
const output = await extractor(prefix + text, { pooling, normalize: true });
|
|
44
|
+
return Float32Array.from(output.data);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=embedder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedder.js","sourceRoot":"","sources":["../../src/vector/embedder.ts"],"names":[],"mappings":"AAkDA,MAAM,aAAa,GAAG,8BAA8B,CAAC;AACrD,MAAM,WAAW,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAW,CAAC;AAExE;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAA8B;IAChE,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,aAAa,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,MAAM,CAAC;IAC3C,+DAA+D;IAC/D,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAElF,uEAAuE;IACvE,gEAAgE;IAChE,IAAI,WAAW,GAA0F,IAAI,CAAC;IAE9G,KAAK,UAAU,OAAO;QACpB,IAAI,WAAW;YAAE,OAAO,WAAW,CAAC;QACpC,WAAW,GAAG,CAAC,KAAK,IAAI,EAAE;YACxB,oEAAoE;YACpE,4DAA4D;YAC5D,MAAM,SAAS,GAAG,2BAA2B,CAAC;YAC9C,IAAI,GAAoE,CAAC;YACzE,IAAI,CAAC;gBACH,GAAG,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAe,CAAC;YAChD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CACb,mEAAmE;oBACjE,4EAA4E;oBAC5E,yEAAyE;oBACzE,sDAAuD,CAAW,CAAC,OAAO,EAAE,CAC/E,CAAC;YACJ,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC;YAC9B,MAAM,SAAS,GAAG,CAAC,MAAM,QAAQ,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAGrB,CAAC;YAC1C,OAAO,SAAS,CAAC;QACnB,CAAC,CAAC,EAAE,CAAC;QACL,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,OAAO,KAAK,EAAE,IAAY,EAAE,IAAgB,EAAE,EAAE;QAC9C,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtF,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5E,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vector` namespace — semantic similarity over markdown memories
|
|
3
|
+
* (Phase 11c, 2026-05-29).
|
|
4
|
+
*
|
|
5
|
+
* Default stack: host-injected `EmbedFn` (with a bundled local embedder),
|
|
6
|
+
* brute-force cosine backend, vectors persisted in the shared
|
|
7
|
+
* `memory.sqlite`. See `./types.ts` for the operator decisions behind each
|
|
8
|
+
* default and `docs/memory-extended-design.md` §vector for rationale.
|
|
9
|
+
*
|
|
10
|
+
* Consumers feature-check `MemoryVectorStore` to confirm the namespace is
|
|
11
|
+
* present in the installed version.
|
|
12
|
+
*/
|
|
13
|
+
export type { EmbedFn, EmbedKind, EmbedVector, VectorBackend, VectorRebuildResult, VectorRow, VectorSearchHit, VectorSearchOptions, VectorSource, } from "./types.js";
|
|
14
|
+
export { MemoryVectorStore } from "./store.js";
|
|
15
|
+
export type { MemoryVectorStoreOptions } from "./store.js";
|
|
16
|
+
export { BruteForceBackend } from "./backend-brute.js";
|
|
17
|
+
export { createLocalEmbedder } from "./embedder.js";
|
|
18
|
+
export type { LocalEmbedderOptions } from "./embedder.js";
|
|
19
|
+
export { normalize, toFloat32, dot, l2norm } from "./math.js";
|
|
20
|
+
export { segmentByEmbedding } from "./segment.js";
|
|
21
|
+
export type { Segment, SegmentUnit, SegmentOptions } from "./segment.js";
|
|
22
|
+
export { SessionChunkStore, rebuildSessionVectors, buildTurnUnits, sessionChunkId, SESSION_CHUNK_SCHEMA, } from "./session.js";
|
|
23
|
+
export type { SessionChunkRow, SessionRebuildOptions, SessionRebuildResult, } from "./session.js";
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,YAAY,EACV,OAAO,EACP,SAAS,EACT,WAAW,EACX,aAAa,EACb,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,mBAAmB,EACnB,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,YAAY,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { MemoryVectorStore } from "./store.js";
|
|
2
|
+
export { BruteForceBackend } from "./backend-brute.js";
|
|
3
|
+
export { createLocalEmbedder } from "./embedder.js";
|
|
4
|
+
export { normalize, toFloat32, dot, l2norm } from "./math.js";
|
|
5
|
+
export { segmentByEmbedding } from "./segment.js";
|
|
6
|
+
export { SessionChunkStore, rebuildSessionVectors, buildTurnUnits, sessionChunkId, SESSION_CHUNK_SCHEMA, } from "./session.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vector/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,cAAc,EACd,oBAAoB,GACrB,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector math helpers — kept dependency-free and tiny.
|
|
3
|
+
*
|
|
4
|
+
* Vectors are L2-normalized at store time and at query time, so cosine
|
|
5
|
+
* similarity reduces to a dot product. Normalizing once on write keeps the
|
|
6
|
+
* hot search loop to a single multiply-accumulate per dimension.
|
|
7
|
+
*/
|
|
8
|
+
import type { EmbedVector } from "./types.js";
|
|
9
|
+
/** Coerce any accepted embedding form to a Float32Array (copying if needed). */
|
|
10
|
+
export declare function toFloat32(v: EmbedVector): Float32Array;
|
|
11
|
+
/** L2 norm of a vector. */
|
|
12
|
+
export declare function l2norm(v: Float32Array): number;
|
|
13
|
+
/**
|
|
14
|
+
* Return a unit-length copy. A zero vector is returned unchanged (norm 0
|
|
15
|
+
* would divide by zero); such a vector scores 0 against everything, which
|
|
16
|
+
* is the correct "no signal" behavior.
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalize(v: EmbedVector): Float32Array;
|
|
19
|
+
/** Dot product. Assumes equal length (caller guarantees via dim check). */
|
|
20
|
+
export declare function dot(a: Float32Array, b: Float32Array): number;
|
|
21
|
+
//# sourceMappingURL=math.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/vector/math.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,GAAG,YAAY,CAEtD;AAED,2BAA2B;AAC3B,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,CAI9C;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,GAAG,YAAY,CAOtD;AAED,2EAA2E;AAC3E,wBAAgB,GAAG,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,YAAY,GAAG,MAAM,CAI5D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Coerce any accepted embedding form to a Float32Array (copying if needed). */
|
|
2
|
+
export function toFloat32(v) {
|
|
3
|
+
return v instanceof Float32Array ? v : Float32Array.from(v);
|
|
4
|
+
}
|
|
5
|
+
/** L2 norm of a vector. */
|
|
6
|
+
export function l2norm(v) {
|
|
7
|
+
let sum = 0;
|
|
8
|
+
for (let i = 0; i < v.length; i++)
|
|
9
|
+
sum += v[i] * v[i];
|
|
10
|
+
return Math.sqrt(sum);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Return a unit-length copy. A zero vector is returned unchanged (norm 0
|
|
14
|
+
* would divide by zero); such a vector scores 0 against everything, which
|
|
15
|
+
* is the correct "no signal" behavior.
|
|
16
|
+
*/
|
|
17
|
+
export function normalize(v) {
|
|
18
|
+
const f = toFloat32(v);
|
|
19
|
+
const n = l2norm(f);
|
|
20
|
+
if (n === 0)
|
|
21
|
+
return f;
|
|
22
|
+
const out = new Float32Array(f.length);
|
|
23
|
+
for (let i = 0; i < f.length; i++)
|
|
24
|
+
out[i] = f[i] / n;
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/** Dot product. Assumes equal length (caller guarantees via dim check). */
|
|
28
|
+
export function dot(a, b) {
|
|
29
|
+
let sum = 0;
|
|
30
|
+
for (let i = 0; i < a.length; i++)
|
|
31
|
+
sum += a[i] * b[i];
|
|
32
|
+
return sum;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=math.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/vector/math.ts"],"names":[],"mappings":"AASA,gFAAgF;AAChF,MAAM,UAAU,SAAS,CAAC,CAAc;IACtC,OAAO,CAAC,YAAY,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC9D,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,MAAM,CAAC,CAAe;IACpC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACxD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,CAAc;IACtC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACtB,MAAM,GAAG,GAAG,IAAI,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACvC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC;IACtD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,GAAG,CAAC,CAAe,EAAE,CAAe;IAClD,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAAE,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;IACxD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector table — lives in the *same* `memory.sqlite` as the `sqlite`
|
|
3
|
+
* namespace (operator decision 3 / 2026-05-29). No `sqlite-vec` extension:
|
|
4
|
+
* the vector is stored as a raw Float32 BLOB and similarity is computed in
|
|
5
|
+
* JS by {@link BruteForceBackend}. Keeping it in one DB means one rebuild
|
|
6
|
+
* path and one file to delete when regenerating derived indexes.
|
|
7
|
+
*
|
|
8
|
+
* Composite primary key `(id, source)` lets the same id exist once per
|
|
9
|
+
* corpus — a memory and a session-archive entry can both key on the same
|
|
10
|
+
* stem without colliding. `dim` is stored so a search can refuse to
|
|
11
|
+
* compare vectors produced by a different embedding model.
|
|
12
|
+
*/
|
|
13
|
+
export declare const VECTOR_SCHEMA = "\nCREATE TABLE IF NOT EXISTS memory_vectors (\n id TEXT NOT NULL,\n source TEXT NOT NULL DEFAULT 'memory',\n dim INTEGER NOT NULL,\n vector BLOB NOT NULL,\n PRIMARY KEY (id, source)\n);\n\nCREATE INDEX IF NOT EXISTS idx_memory_vectors_source ON memory_vectors(source);\n";
|
|
14
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/vector/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,wRAUzB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector table — lives in the *same* `memory.sqlite` as the `sqlite`
|
|
3
|
+
* namespace (operator decision 3 / 2026-05-29). No `sqlite-vec` extension:
|
|
4
|
+
* the vector is stored as a raw Float32 BLOB and similarity is computed in
|
|
5
|
+
* JS by {@link BruteForceBackend}. Keeping it in one DB means one rebuild
|
|
6
|
+
* path and one file to delete when regenerating derived indexes.
|
|
7
|
+
*
|
|
8
|
+
* Composite primary key `(id, source)` lets the same id exist once per
|
|
9
|
+
* corpus — a memory and a session-archive entry can both key on the same
|
|
10
|
+
* stem without colliding. `dim` is stored so a search can refuse to
|
|
11
|
+
* compare vectors produced by a different embedding model.
|
|
12
|
+
*/
|
|
13
|
+
export const VECTOR_SCHEMA = `
|
|
14
|
+
CREATE TABLE IF NOT EXISTS memory_vectors (
|
|
15
|
+
id TEXT NOT NULL,
|
|
16
|
+
source TEXT NOT NULL DEFAULT 'memory',
|
|
17
|
+
dim INTEGER NOT NULL,
|
|
18
|
+
vector BLOB NOT NULL,
|
|
19
|
+
PRIMARY KEY (id, source)
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_memory_vectors_source ON memory_vectors(source);
|
|
23
|
+
`;
|
|
24
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/vector/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;CAU5B,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { EmbedFn } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Embedding-similarity topic segmentation (Phase 11c follow-up, 2026-05-29).
|
|
4
|
+
*
|
|
5
|
+
* Splits an ordered list of turns into topic-coherent chunks by watching
|
|
6
|
+
* where the conversation's *meaning* shifts. Each turn is embedded once; a
|
|
7
|
+
* running centroid tracks "the current topic so far"; when a new turn's
|
|
8
|
+
* cosine similarity to that centroid drops below `threshold` (and the chunk
|
|
9
|
+
* already has at least `minUnits` turns), a boundary is cut. Min/max guards
|
|
10
|
+
* keep chunks from being a single "응" or growing unbounded.
|
|
11
|
+
*
|
|
12
|
+
* Boundary detection uses the per-turn embeddings (running centroid). The
|
|
13
|
+
* stored chunk vector, however, is a fresh embedding of the chunk's *joined
|
|
14
|
+
* text*, not the mean of its turn vectors: averaging turn vectors dilutes a
|
|
15
|
+
* chunk that spans two related sub-topics (a query for one sub-topic then
|
|
16
|
+
* loses to the averaged direction — observed in a 2026-05-29 smoke). One
|
|
17
|
+
* extra embedding per chunk; chunks are few. This is offline indexing work,
|
|
18
|
+
* not per-query.
|
|
19
|
+
*
|
|
20
|
+
* Why similarity, not time gaps: continuous multi-topic sessions (the
|
|
21
|
+
* operator's pattern — module A → module B back-to-back with no pause) have
|
|
22
|
+
* no time gap at the topic seam; only the meaning shifts. Time-gap
|
|
23
|
+
* heuristics miss those; embedding similarity catches them.
|
|
24
|
+
*/
|
|
25
|
+
/** One input turn to segment. */
|
|
26
|
+
export interface SegmentUnit {
|
|
27
|
+
/** Text of the turn (user + assistant concatenated, tool noise stripped). */
|
|
28
|
+
readonly text: string;
|
|
29
|
+
/** Timestamp of the turn, if known (for chunk start/end bookkeeping). */
|
|
30
|
+
readonly at: string | null;
|
|
31
|
+
}
|
|
32
|
+
/** One output chunk. */
|
|
33
|
+
export interface Segment {
|
|
34
|
+
/** Index of the first unit (inclusive) in the input array. */
|
|
35
|
+
readonly unitStart: number;
|
|
36
|
+
/** Index of the last unit (inclusive). */
|
|
37
|
+
readonly unitEnd: number;
|
|
38
|
+
/** Concatenated text of the chunk's turns. */
|
|
39
|
+
readonly text: string;
|
|
40
|
+
/** Chunk vector — normalized mean of the member turns' vectors. */
|
|
41
|
+
readonly vector: Float32Array;
|
|
42
|
+
readonly startedAt: string | null;
|
|
43
|
+
readonly endedAt: string | null;
|
|
44
|
+
}
|
|
45
|
+
export interface SegmentOptions {
|
|
46
|
+
/**
|
|
47
|
+
* Cosine-to-centroid below which a new turn is treated as a topic shift.
|
|
48
|
+
* Lower = fewer, larger chunks; higher = more, smaller chunks. Default
|
|
49
|
+
* 0.45 — a starting point to tune against real data.
|
|
50
|
+
*/
|
|
51
|
+
readonly threshold?: number;
|
|
52
|
+
/** Minimum turns before a boundary may be cut. Default 2. */
|
|
53
|
+
readonly minUnits?: number;
|
|
54
|
+
/** Hard cap on turns per chunk (forces a cut regardless of similarity). Default 12. */
|
|
55
|
+
readonly maxUnits?: number;
|
|
56
|
+
/** Hard cap on characters per chunk. Default 4000. */
|
|
57
|
+
readonly maxChars?: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Segment `units` into topic chunks. Empty-text units are dropped first.
|
|
61
|
+
* Returns at least one chunk when any non-empty unit exists; an empty input
|
|
62
|
+
* yields an empty array.
|
|
63
|
+
*/
|
|
64
|
+
export declare function segmentByEmbedding(units: readonly SegmentUnit[], embed: EmbedFn, options?: SegmentOptions): Promise<readonly Segment[]>;
|
|
65
|
+
//# sourceMappingURL=segment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment.d.ts","sourceRoot":"","sources":["../../src/vector/segment.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,iCAAiC;AACjC,MAAM,WAAW,WAAW;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,wBAAwB;AACxB,MAAM,WAAW,OAAO;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAED,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,6DAA6D;IAC7D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAID;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,KAAK,EAAE,OAAO,EACd,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAsE7B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { dot, normalize } from "./math.js";
|
|
2
|
+
const DEFAULTS = { threshold: 0.45, minUnits: 2, maxUnits: 12, maxChars: 4000 };
|
|
3
|
+
/**
|
|
4
|
+
* Segment `units` into topic chunks. Empty-text units are dropped first.
|
|
5
|
+
* Returns at least one chunk when any non-empty unit exists; an empty input
|
|
6
|
+
* yields an empty array.
|
|
7
|
+
*/
|
|
8
|
+
export async function segmentByEmbedding(units, embed, options) {
|
|
9
|
+
const opts = { ...DEFAULTS, ...options };
|
|
10
|
+
const live = units.filter((u) => u.text.trim().length > 0);
|
|
11
|
+
if (live.length === 0)
|
|
12
|
+
return [];
|
|
13
|
+
const vectors = [];
|
|
14
|
+
for (const u of live)
|
|
15
|
+
vectors.push(normalize(await embed(u.text)));
|
|
16
|
+
const raw = [];
|
|
17
|
+
let start = 0;
|
|
18
|
+
let centroidSum = new Float64Array(vectors[0].length);
|
|
19
|
+
let count = 0;
|
|
20
|
+
let chars = 0;
|
|
21
|
+
const addToCentroid = (v) => {
|
|
22
|
+
for (let i = 0; i < v.length; i++)
|
|
23
|
+
centroidSum[i] += v[i];
|
|
24
|
+
count++;
|
|
25
|
+
};
|
|
26
|
+
const centroidUnit = () => {
|
|
27
|
+
const mean = new Float32Array(centroidSum.length);
|
|
28
|
+
for (let i = 0; i < centroidSum.length; i++)
|
|
29
|
+
mean[i] = centroidSum[i] / count;
|
|
30
|
+
return normalize(mean);
|
|
31
|
+
};
|
|
32
|
+
const flush = (endExclusive) => {
|
|
33
|
+
const end = endExclusive - 1;
|
|
34
|
+
raw.push({
|
|
35
|
+
unitStart: start,
|
|
36
|
+
unitEnd: end,
|
|
37
|
+
text: live.slice(start, endExclusive).map((u) => u.text).join("\n\n"),
|
|
38
|
+
startedAt: live[start].at,
|
|
39
|
+
endedAt: live[end].at,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
const reset = (from) => {
|
|
43
|
+
start = from;
|
|
44
|
+
centroidSum = new Float64Array(vectors[0].length);
|
|
45
|
+
count = 0;
|
|
46
|
+
chars = 0;
|
|
47
|
+
};
|
|
48
|
+
for (let i = 0; i < live.length; i++) {
|
|
49
|
+
const v = vectors[i];
|
|
50
|
+
const unitChars = live[i].text.length;
|
|
51
|
+
if (count > 0) {
|
|
52
|
+
const sim = dot(v, centroidUnit());
|
|
53
|
+
const topicShift = sim < opts.threshold && count >= opts.minUnits;
|
|
54
|
+
const tooBig = count >= opts.maxUnits || chars + unitChars > opts.maxChars;
|
|
55
|
+
if (topicShift || tooBig) {
|
|
56
|
+
flush(i);
|
|
57
|
+
reset(i);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
addToCentroid(v);
|
|
61
|
+
chars += unitChars;
|
|
62
|
+
}
|
|
63
|
+
flush(live.length);
|
|
64
|
+
// Stored chunk vector = a fresh embedding of the chunk's joined text
|
|
65
|
+
// (not the diluted mean of turn vectors). One embed per chunk.
|
|
66
|
+
const segments = [];
|
|
67
|
+
for (const r of raw) {
|
|
68
|
+
segments.push({ ...r, vector: normalize(await embed(r.text)) });
|
|
69
|
+
}
|
|
70
|
+
return segments;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=segment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment.js","sourceRoot":"","sources":["../../src/vector/segment.ts"],"names":[],"mappings":"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"}
|