@zukhruf/embeddings 0.3.47 → 0.3.48
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 +22 -1
- package/dist/collection.d.ts +1 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +12 -77
- package/dist/collection.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/unknown-document-error.js +2 -2
- package/dist/unknown-document-error.js.map +1 -1
- package/dist/vector-search.d.ts +25 -0
- package/dist/vector-search.d.ts.map +1 -0
- package/dist/vector-search.js +96 -0
- package/dist/vector-search.js.map +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -47,6 +47,23 @@ collection.nearest('a', { k: 10, withinDays: 7 }); // [{ id: 'b', similarity:
|
|
|
47
47
|
collection.similarity('a', 'c');
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
+
### Search a vector table of your own
|
|
51
|
+
|
|
52
|
+
Another program can write the vectors into its own table. A `VectorSearch` searches that table with the same rules as a collection. The table or view must have the columns `id` (text), `vector` and `published_at` (milliseconds since the Unix epoch). Each `vector` must be a unit vector, as a BLOB of float32 values in the byte order of the platform ([ADR 0005](./docs/adr/0005-a-vector-search-reads-a-vector-table-of-unit-vectors.md)).
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
56
|
+
|
|
57
|
+
import { VectorSearch } from '@zukhruf/embeddings';
|
|
58
|
+
|
|
59
|
+
using database = new DatabaseSync('listings.sqlite');
|
|
60
|
+
const search = new VectorSearch({ database, table: 'listing_vectors' });
|
|
61
|
+
|
|
62
|
+
search.nearest('listing-42', { k: 10, withinDays: 30 });
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
To search a table with other column names, make a view that renames the columns, and give the name of the view.
|
|
66
|
+
|
|
50
67
|
## API
|
|
51
68
|
|
|
52
69
|
- `new Collection({ database, model })`. The caller opens and closes `database`. The collection makes its table `embedding_document` in it. `model` is an AI SDK embedding model object.
|
|
@@ -54,10 +71,12 @@ collection.similarity('a', 'c');
|
|
|
54
71
|
- `nearest(id, { k, withinDays })` gives at most `k` neighbours of the document, with the highest similarity first. A neighbour has a publish date at most `withinDays` days before or after the publish date of the document. Equal similarities sort by id.
|
|
55
72
|
- `neighbourGraph({ from, to, k, withinDays })` gives an iterator of `[id, neighbours]` entries, one for each document published from `from` to `to`, in the order of the publish dates and then of the ids. The neighbours of each entry are equal to the result of `nearest(id, { k, withinDays })`. A document outside the range has no entry, but it can be a neighbour of a document in the range. The call reads and decodes each vector one time ([ADR 0004](./docs/adr/0004-a-neighbour-graph-decodes-each-vector-once.md)). Use it when you need the neighbours of many documents.
|
|
56
73
|
- `similarity(a, b)` gives the cosine similarity of two stored documents.
|
|
74
|
+
- `new VectorSearch({ database, table })`. The caller opens and closes `database`. `table` is the name of a table or a view in it, with the columns `id`, `vector` and `published_at`. The vector search only reads it. Each call reads the table again, so the table can change between two calls.
|
|
75
|
+
- `VectorSearch` gives `nearest`, `neighbourGraph` and `similarity`, with the same rules and results as these calls of a collection. A collection gives these calls to a `VectorSearch` over its table `embedding_document`.
|
|
57
76
|
|
|
58
77
|
## Errors
|
|
59
78
|
|
|
60
|
-
- `UnknownDocumentError`: `nearest` or `similarity` got an id that the
|
|
79
|
+
- `UnknownDocumentError`: `nearest` or `similarity` got an id that the vector table does not contain.
|
|
61
80
|
- `DimensionMismatchError`: the model gave a vector with a dimension that is not the dimension of the stored vectors. The upsert stores none of its documents. To change to a model with another dimension, upsert all documents in one call.
|
|
62
81
|
|
|
63
82
|
## Limits
|
|
@@ -65,6 +84,7 @@ collection.similarity('a', 'c');
|
|
|
65
84
|
- One database holds one collection.
|
|
66
85
|
- The collection stores the vector of the text, not the text. The caller chooses the text, for example a title and the first sentences.
|
|
67
86
|
- A document has one vector. A long text gives one mean vector, and its details become a small part of it.
|
|
87
|
+
- A vector search requires unit vectors. It does not examine the length or the dimension of a vector.
|
|
68
88
|
- The search reads each document in the window ([ADR 0001](./docs/adr/0001-search-compares-each-document-in-the-window.md)). A window of many thousand documents is slow. A neighbour graph decodes each vector once, but it still calculates a similarity for each pair of documents in a window.
|
|
69
89
|
- After a model change, upsert all documents again ([ADR 0002](./docs/adr/0002-the-hash-includes-the-model.md)).
|
|
70
90
|
|
|
@@ -74,4 +94,5 @@ collection.similarity('a', 'c');
|
|
|
74
94
|
- [ADR 0002: The hash includes the model](./docs/adr/0002-the-hash-includes-the-model.md)
|
|
75
95
|
- [ADR 0003: All vectors of a collection have one dimension](./docs/adr/0003-all-vectors-of-a-collection-have-one-dimension.md)
|
|
76
96
|
- [ADR 0004: A neighbour graph decodes each vector once](./docs/adr/0004-a-neighbour-graph-decodes-each-vector-once.md)
|
|
97
|
+
- [ADR 0005: A vector search reads a vector table of unit vectors](./docs/adr/0005-a-vector-search-reads-a-vector-table-of-unit-vectors.md)
|
|
77
98
|
- [Code copied from @deepagents/retrieval](./docs/copied-from-deepagents-retrieval.md)
|
package/dist/collection.d.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
import type { DatabaseSync } from 'node:sqlite';
|
|
2
2
|
import { type EmbeddingModel } from 'ai';
|
|
3
|
+
import { type Neighbour } from './vector-search.ts';
|
|
3
4
|
export type Document = {
|
|
4
5
|
readonly id: string;
|
|
5
6
|
readonly text: string;
|
|
6
7
|
readonly publishedAt: Date;
|
|
7
8
|
};
|
|
8
|
-
export type Neighbour = {
|
|
9
|
-
readonly id: string;
|
|
10
|
-
readonly similarity: number;
|
|
11
|
-
};
|
|
12
9
|
export type CollectionOptions = {
|
|
13
10
|
readonly database: DatabaseSync;
|
|
14
11
|
readonly model: Exclude<EmbeddingModel, string>;
|
package/dist/collection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,KAAK,cAAc,EAAa,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAAE,KAAK,cAAc,EAAa,MAAM,IAAI,CAAC;AAMpD,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,oBAAoB,CAAC;AAGlE,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAE9B,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAGhC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;CACjD,CAAC;AAIF,qBAAa,UAAU;;gBAKT,OAAO,EAAE,iBAAiB;IAYhC,MAAM,CAAC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoD3D,OAAO,CACL,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAC3D,SAAS,EAAE;IAKd,cAAc,CAAC,OAAO,EAAE;QACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;QACpB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;QAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;KAC7B,GAAG,cAAc,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAKzC,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;CAwCzC"}
|
package/dist/collection.js
CHANGED
|
@@ -3,19 +3,22 @@ import { embedMany } from 'ai';
|
|
|
3
3
|
import { transaction } from '@zukhruf/sqlite-transaction';
|
|
4
4
|
import { DimensionMismatchError } from "./dimension-mismatch-error.js";
|
|
5
5
|
import { collectionSchema } from "./schema.js";
|
|
6
|
-
import {
|
|
6
|
+
import { VectorSearch } from "./vector-search.js";
|
|
7
7
|
import { UnitVector } from "./vector.js";
|
|
8
|
-
const DAY_MS = 86_400_000;
|
|
9
8
|
// The embeddings of documents, one vector for each document, in a SQLite
|
|
10
|
-
// database.
|
|
11
|
-
// inside the window, with no index (docs/adr/0001).
|
|
9
|
+
// database. The collection searches its table through a VectorSearch.
|
|
12
10
|
export class Collection {
|
|
13
11
|
#database;
|
|
14
12
|
#model;
|
|
13
|
+
#search;
|
|
15
14
|
constructor(options) {
|
|
16
15
|
this.#database = options.database;
|
|
17
16
|
this.#model = options.model;
|
|
18
17
|
collectionSchema.apply(this.#database);
|
|
18
|
+
this.#search = new VectorSearch({
|
|
19
|
+
database: this.#database,
|
|
20
|
+
table: 'embedding_document',
|
|
21
|
+
});
|
|
19
22
|
}
|
|
20
23
|
// Embeds the documents whose text or model changed since their last upsert,
|
|
21
24
|
// and stores the publish date of each document.
|
|
@@ -42,50 +45,17 @@ export class Collection {
|
|
|
42
45
|
this.#checkDimension(changed.map(({ document }) => document.id), vectors);
|
|
43
46
|
}, { mode: 'immediate' });
|
|
44
47
|
}
|
|
45
|
-
// The
|
|
46
|
-
// those published at most withinDays before or after it. The document
|
|
47
|
-
// itself is not one of them. Equal similarities sort by id.
|
|
48
|
+
// The neighbours of a document (docs/adr/0005).
|
|
48
49
|
nearest(id, options) {
|
|
49
|
-
|
|
50
|
-
const window = options.withinDays * DAY_MS;
|
|
51
|
-
const candidates = this.#database
|
|
52
|
-
.prepare('SELECT "id", "vector" FROM "embedding_document" WHERE "published_at" BETWEEN ? AND ? AND "id" != ?')
|
|
53
|
-
.all(publishedAt - window, publishedAt + window, id)
|
|
54
|
-
.map((row) => ({
|
|
55
|
-
id: String(row.id),
|
|
56
|
-
vector: UnitVector.fromBlob(blob(row.vector)),
|
|
57
|
-
}));
|
|
58
|
-
return ranked(vector, candidates, options.k);
|
|
50
|
+
return this.#search.nearest(id, options);
|
|
59
51
|
}
|
|
60
|
-
// The
|
|
61
|
-
// Each entry is what nearest gives for the document with the same k and
|
|
62
|
-
// withinDays. The call reads and decodes each vector once
|
|
63
|
-
// (docs/adr/0004). The entries come in the order of the publish dates, then
|
|
64
|
-
// of the ids.
|
|
52
|
+
// The neighbours of each document published from `from` to `to`.
|
|
65
53
|
neighbourGraph(options) {
|
|
66
|
-
|
|
67
|
-
const to = options.to.getTime();
|
|
68
|
-
const window = options.withinDays * DAY_MS;
|
|
69
|
-
const documents = this.#database
|
|
70
|
-
.prepare('SELECT "id", "published_at", "vector" FROM "embedding_document" WHERE "published_at" BETWEEN ? AND ? ORDER BY "published_at", "id"')
|
|
71
|
-
.all(from - window, to + window)
|
|
72
|
-
.map((row) => ({
|
|
73
|
-
id: String(row.id),
|
|
74
|
-
publishedAt: Number(row.published_at),
|
|
75
|
-
vector: UnitVector.fromBlob(blob(row.vector)),
|
|
76
|
-
}));
|
|
77
|
-
return documents
|
|
78
|
-
.values()
|
|
79
|
-
.filter(({ publishedAt }) => publishedAt >= from && publishedAt <= to)
|
|
80
|
-
.map((document) => [
|
|
81
|
-
document.id,
|
|
82
|
-
ranked(document.vector, documents.filter((other) => other.id !== document.id &&
|
|
83
|
-
Math.abs(other.publishedAt - document.publishedAt) <= window), options.k),
|
|
84
|
-
]);
|
|
54
|
+
return this.#search.neighbourGraph(options);
|
|
85
55
|
}
|
|
86
56
|
// The cosine similarity of two stored documents.
|
|
87
57
|
similarity(a, b) {
|
|
88
|
-
return this.#
|
|
58
|
+
return this.#search.similarity(a, b);
|
|
89
59
|
}
|
|
90
60
|
// No texts make no call to the model.
|
|
91
61
|
async #embed(texts) {
|
|
@@ -103,17 +73,6 @@ export class Collection {
|
|
|
103
73
|
.update(JSON.stringify([this.#model.provider, this.#model.modelId, text]))
|
|
104
74
|
.digest('hex');
|
|
105
75
|
}
|
|
106
|
-
#read(id) {
|
|
107
|
-
const row = this.#database
|
|
108
|
-
.prepare('SELECT "vector", "published_at" FROM "embedding_document" WHERE "id" = ?')
|
|
109
|
-
.get(id);
|
|
110
|
-
if (row === undefined)
|
|
111
|
-
throw new UnknownDocumentError(id);
|
|
112
|
-
return {
|
|
113
|
-
vector: UnitVector.fromBlob(blob(row.vector)),
|
|
114
|
-
publishedAt: Number(row.published_at),
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
76
|
// Before the upsert, each stored vector has one dimension. The new vectors
|
|
118
77
|
// must have the dimension of a vector that the upsert did not replace, and
|
|
119
78
|
// of each other.
|
|
@@ -132,28 +91,4 @@ export class Collection {
|
|
|
132
91
|
throw new DimensionMismatchError(expected, vector.dimension);
|
|
133
92
|
}
|
|
134
93
|
}
|
|
135
|
-
// The k candidates most similar to the vector. Equal similarities sort by id.
|
|
136
|
-
function ranked(vector, candidates, k) {
|
|
137
|
-
return candidates
|
|
138
|
-
.map((candidate) => ({
|
|
139
|
-
id: candidate.id,
|
|
140
|
-
similarity: vector.cosine(candidate.vector),
|
|
141
|
-
}))
|
|
142
|
-
.toSorted((left, right) => right.similarity - left.similarity || compareIds(left.id, right.id))
|
|
143
|
-
.slice(0, k);
|
|
144
|
-
}
|
|
145
|
-
// Compares by code unit, not by locale, so the order is the same on every
|
|
146
|
-
// machine.
|
|
147
|
-
function compareIds(left, right) {
|
|
148
|
-
if (left < right)
|
|
149
|
-
return -1;
|
|
150
|
-
if (left > right)
|
|
151
|
-
return 1;
|
|
152
|
-
return 0;
|
|
153
|
-
}
|
|
154
|
-
function blob(value) {
|
|
155
|
-
if (!(value instanceof Uint8Array))
|
|
156
|
-
throw new TypeError('A stored vector must be a BLOB.');
|
|
157
|
-
return value;
|
|
158
|
-
}
|
|
159
94
|
//# sourceMappingURL=collection.js.map
|
package/dist/collection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,OAAO,EAAuB,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../src/collection.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { Collection, type CollectionOptions, type Document,
|
|
1
|
+
export { Collection, type CollectionOptions, type Document, } from './collection.ts';
|
|
2
2
|
export { DimensionMismatchError } from './dimension-mismatch-error.ts';
|
|
3
3
|
export { UnknownDocumentError } from './unknown-document-error.ts';
|
|
4
|
+
export { type Neighbour, VectorSearch, type VectorSearchOptions, } from './vector-search.ts';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,KAAK,iBAAiB,EACtB,KAAK,QAAQ,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EACL,KAAK,SAAS,EACd,YAAY,EACZ,KAAK,mBAAmB,GACzB,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { Collection, } from "./collection.js";
|
|
2
2
|
export { DimensionMismatchError } from "./dimension-mismatch-error.js";
|
|
3
3
|
export { UnknownDocumentError } from "./unknown-document-error.js";
|
|
4
|
+
export { VectorSearch, } from "./vector-search.js";
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,GAGX,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAEL,YAAY,GAEb,MAAM,oBAAoB,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
// The
|
|
1
|
+
// The vector table has no document with this id.
|
|
2
2
|
export class UnknownDocumentError extends Error {
|
|
3
3
|
id;
|
|
4
4
|
constructor(id) {
|
|
5
|
-
super(`The
|
|
5
|
+
super(`The vector table has no document with the id ${id}.`);
|
|
6
6
|
this.name = 'UnknownDocumentError';
|
|
7
7
|
this.id = id;
|
|
8
8
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unknown-document-error.js","sourceRoot":"","sources":["../src/unknown-document-error.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"unknown-document-error.js","sourceRoot":"","sources":["../src/unknown-document-error.ts"],"names":[],"mappings":"AAAA,iDAAiD;AACjD,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACpC,EAAE,CAAS;IAEpB,YAAY,EAAU;QACpB,KAAK,CAAC,gDAAgD,EAAE,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;CACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
export type Neighbour = {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
readonly similarity: number;
|
|
5
|
+
};
|
|
6
|
+
export type VectorSearchOptions = {
|
|
7
|
+
readonly database: DatabaseSync;
|
|
8
|
+
readonly table: string;
|
|
9
|
+
};
|
|
10
|
+
export declare class VectorSearch {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(options: VectorSearchOptions);
|
|
13
|
+
nearest(id: string, options: {
|
|
14
|
+
readonly k: number;
|
|
15
|
+
readonly withinDays: number;
|
|
16
|
+
}): Neighbour[];
|
|
17
|
+
neighbourGraph(options: {
|
|
18
|
+
readonly from: Date;
|
|
19
|
+
readonly to: Date;
|
|
20
|
+
readonly k: number;
|
|
21
|
+
readonly withinDays: number;
|
|
22
|
+
}): IteratorObject<[string, Neighbour[]]>;
|
|
23
|
+
similarity(a: string, b: string): number;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=vector-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-search.d.ts","sourceRoot":"","sources":["../src/vector-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAOhD,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAEhC,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC;IAIhC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,CAAC;AAMF,qBAAa,YAAY;;gBAIX,OAAO,EAAE,mBAAmB;IAQxC,OAAO,CACL,EAAE,EAAE,MAAM,EACV,OAAO,EAAE;QAAE,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAC3D,SAAS,EAAE;IAoBd,cAAc,CAAC,OAAO,EAAE;QACtB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;QACpB,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;QAClB,QAAQ,CAAC,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;KAC7B,GAAG,cAAc,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAgCzC,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;CAgBzC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { UnknownDocumentError } from "./unknown-document-error.js";
|
|
2
|
+
import { UnitVector } from "./vector.js";
|
|
3
|
+
const DAY_MS = 86_400_000;
|
|
4
|
+
// The neighbours of the documents in a vector table. A search compares the
|
|
5
|
+
// document with each document published inside the window, with no index
|
|
6
|
+
// (docs/adr/0001). Each call prepares its statements, so the table can come
|
|
7
|
+
// after the search.
|
|
8
|
+
export class VectorSearch {
|
|
9
|
+
#database;
|
|
10
|
+
#table;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
this.#database = options.database;
|
|
13
|
+
this.#table = `"${options.table.replaceAll('"', '""')}"`;
|
|
14
|
+
}
|
|
15
|
+
// The k documents most similar to the document, by cosine similarity, from
|
|
16
|
+
// those published at most withinDays before or after it. The document
|
|
17
|
+
// itself is not one of them. Equal similarities sort by id.
|
|
18
|
+
nearest(id, options) {
|
|
19
|
+
const { vector, publishedAt } = this.#read(id);
|
|
20
|
+
const window = options.withinDays * DAY_MS;
|
|
21
|
+
const candidates = this.#database
|
|
22
|
+
.prepare(`SELECT "id", "vector" FROM ${this.#table} WHERE "published_at" BETWEEN ? AND ? AND "id" != ?`)
|
|
23
|
+
.all(publishedAt - window, publishedAt + window, id)
|
|
24
|
+
.map((row) => ({
|
|
25
|
+
id: String(row.id),
|
|
26
|
+
vector: UnitVector.fromBlob(blob(row.vector)),
|
|
27
|
+
}));
|
|
28
|
+
return ranked(vector, candidates, options.k);
|
|
29
|
+
}
|
|
30
|
+
// The k nearest neighbours of each document published from `from` to `to`.
|
|
31
|
+
// Each entry is what nearest gives for the document with the same k and
|
|
32
|
+
// withinDays. The call reads and decodes each vector once
|
|
33
|
+
// (docs/adr/0004). The entries come in the order of the publish dates, then
|
|
34
|
+
// of the ids.
|
|
35
|
+
neighbourGraph(options) {
|
|
36
|
+
const from = options.from.getTime();
|
|
37
|
+
const to = options.to.getTime();
|
|
38
|
+
const window = options.withinDays * DAY_MS;
|
|
39
|
+
const documents = this.#database
|
|
40
|
+
.prepare(`SELECT "id", "published_at", "vector" FROM ${this.#table} WHERE "published_at" BETWEEN ? AND ? ORDER BY "published_at", "id"`)
|
|
41
|
+
.all(from - window, to + window)
|
|
42
|
+
.map((row) => ({
|
|
43
|
+
id: String(row.id),
|
|
44
|
+
publishedAt: Number(row.published_at),
|
|
45
|
+
vector: UnitVector.fromBlob(blob(row.vector)),
|
|
46
|
+
}));
|
|
47
|
+
return documents
|
|
48
|
+
.values()
|
|
49
|
+
.filter(({ publishedAt }) => publishedAt >= from && publishedAt <= to)
|
|
50
|
+
.map((document) => [
|
|
51
|
+
document.id,
|
|
52
|
+
ranked(document.vector, documents.filter((other) => other.id !== document.id &&
|
|
53
|
+
Math.abs(other.publishedAt - document.publishedAt) <= window), options.k),
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
// The cosine similarity of two documents.
|
|
57
|
+
similarity(a, b) {
|
|
58
|
+
return this.#read(a).vector.cosine(this.#read(b).vector);
|
|
59
|
+
}
|
|
60
|
+
#read(id) {
|
|
61
|
+
const row = this.#database
|
|
62
|
+
.prepare(`SELECT "vector", "published_at" FROM ${this.#table} WHERE "id" = ?`)
|
|
63
|
+
.get(id);
|
|
64
|
+
if (row === undefined)
|
|
65
|
+
throw new UnknownDocumentError(id);
|
|
66
|
+
return {
|
|
67
|
+
vector: UnitVector.fromBlob(blob(row.vector)),
|
|
68
|
+
publishedAt: Number(row.published_at),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// The k candidates most similar to the vector. Equal similarities sort by id.
|
|
73
|
+
function ranked(vector, candidates, k) {
|
|
74
|
+
return candidates
|
|
75
|
+
.map((candidate) => ({
|
|
76
|
+
id: candidate.id,
|
|
77
|
+
similarity: vector.cosine(candidate.vector),
|
|
78
|
+
}))
|
|
79
|
+
.toSorted((left, right) => right.similarity - left.similarity || compareIds(left.id, right.id))
|
|
80
|
+
.slice(0, k);
|
|
81
|
+
}
|
|
82
|
+
// Compares by code unit, not by locale, so the order is the same on every
|
|
83
|
+
// machine.
|
|
84
|
+
function compareIds(left, right) {
|
|
85
|
+
if (left < right)
|
|
86
|
+
return -1;
|
|
87
|
+
if (left > right)
|
|
88
|
+
return 1;
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
function blob(value) {
|
|
92
|
+
if (!(value instanceof Uint8Array))
|
|
93
|
+
throw new TypeError('A stored vector must be a BLOB.');
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=vector-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vector-search.js","sourceRoot":"","sources":["../src/vector-search.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zukhruf/embeddings",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.48",
|
|
4
4
|
"description": "A collection of document embeddings in SQLite. It embeds only new or changed text through an AI SDK embedding model, and finds the nearest documents of a document by exact cosine similarity inside a publish-date window.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@zukhruf/sqlite-drift": "^0.3.
|
|
45
|
-
"@zukhruf/sqlite-transaction": "^0.3.
|
|
44
|
+
"@zukhruf/sqlite-drift": "^0.3.48",
|
|
45
|
+
"@zukhruf/sqlite-transaction": "^0.3.48",
|
|
46
46
|
"ai": "^7.0.154"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@zukhruf/testing": "^0.3.
|
|
49
|
+
"@zukhruf/testing": "^0.3.48"
|
|
50
50
|
}
|
|
51
51
|
}
|