@juspay/neurolink 9.89.0 → 9.90.0
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/CHANGELOG.md +10 -0
- package/dist/adapters/tts/googleTTSHandler.d.ts +10 -0
- package/dist/adapters/tts/googleTTSHandler.js +27 -18
- package/dist/agent/directTools.js +1 -1
- package/dist/browser/neurolink.min.js +370 -356
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/adapters/tts/googleTTSHandler.d.ts +10 -0
- package/dist/lib/adapters/tts/googleTTSHandler.js +27 -18
- package/dist/lib/agent/directTools.js +1 -1
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +1 -1
- package/dist/lib/rag/index.d.ts +1 -0
- package/dist/lib/rag/index.js +2 -0
- package/dist/lib/rag/stores/chroma.d.ts +90 -0
- package/dist/lib/rag/stores/chroma.js +281 -0
- package/dist/lib/rag/stores/index.d.ts +21 -0
- package/dist/lib/rag/stores/index.js +22 -0
- package/dist/lib/rag/stores/pgvector.d.ts +95 -0
- package/dist/lib/rag/stores/pgvector.js +400 -0
- package/dist/lib/rag/stores/pinecone.d.ts +85 -0
- package/dist/lib/rag/stores/pinecone.js +159 -0
- package/dist/lib/types/index.d.ts +2 -0
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/rag.d.ts +30 -0
- package/dist/lib/types/vectorStoreChroma.d.ts +67 -0
- package/dist/lib/types/vectorStoreChroma.js +12 -0
- package/dist/lib/types/vectorStorePinecone.d.ts +48 -0
- package/dist/lib/types/vectorStorePinecone.js +12 -0
- package/dist/rag/index.d.ts +1 -0
- package/dist/rag/index.js +2 -0
- package/dist/rag/stores/chroma.d.ts +90 -0
- package/dist/rag/stores/chroma.js +280 -0
- package/dist/rag/stores/index.d.ts +21 -0
- package/dist/rag/stores/index.js +21 -0
- package/dist/rag/stores/pgvector.d.ts +95 -0
- package/dist/rag/stores/pgvector.js +399 -0
- package/dist/rag/stores/pinecone.d.ts +85 -0
- package/dist/rag/stores/pinecone.js +158 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.js +2 -0
- package/dist/types/rag.d.ts +30 -0
- package/dist/types/vectorStoreChroma.d.ts +67 -0
- package/dist/types/vectorStoreChroma.js +11 -0
- package/dist/types/vectorStorePinecone.d.ts +48 -0
- package/dist/types/vectorStorePinecone.js +11 -0
- package/package.json +11 -7
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chroma vector store adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the `VectorStore` contract (see `src/lib/types/rag.ts`) against
|
|
5
|
+
* a Chroma collection. This module has zero runtime dependency on the
|
|
6
|
+
* `chromadb` package: callers construct their own `ChromaClient` (or any
|
|
7
|
+
* compatible object) and inject it here. The structural interfaces below
|
|
8
|
+
* mirror the subset of the `chromadb` API this adapter relies on
|
|
9
|
+
* (`getOrCreateCollection`, `collection.upsert/query/delete`).
|
|
10
|
+
*
|
|
11
|
+
* ## Distance -> similarity scoring
|
|
12
|
+
*
|
|
13
|
+
* Chroma returns *distances*, not similarities, and the meaning of that
|
|
14
|
+
* distance depends entirely on the collection's configured `hnsw:space`
|
|
15
|
+
* (this adapter does not — and cannot — set that; it is chosen when the
|
|
16
|
+
* collection is created on the Chroma side). `distanceMetric` tells this
|
|
17
|
+
* adapter which space the collection uses so it can invert the distance
|
|
18
|
+
* into a `score` consistent with `InMemoryVectorStore` (higher = more
|
|
19
|
+
* similar):
|
|
20
|
+
*
|
|
21
|
+
* - `"cosine"` (default): Chroma returns `distance = 1 - cosineSimilarity`,
|
|
22
|
+
* so `score = 1 - distance` reproduces `InMemoryVectorStore`'s cosine
|
|
23
|
+
* score exactly.
|
|
24
|
+
* - `"ip"`: Chroma returns `distance = 1 - dot(a, b)`, so
|
|
25
|
+
* `score = 1 - distance` yields the raw dot product — only equal to
|
|
26
|
+
* cosine similarity if both vectors are unit-normalized.
|
|
27
|
+
* - `"l2"`: Chroma returns squared Euclidean distance, range
|
|
28
|
+
* `[0, Infinity)`, which has no exact inverse into a cosine-similarity
|
|
29
|
+
* scale. It is converted via `score = 1 / (1 + distance)`, a
|
|
30
|
+
* monotonically decreasing-in-distance map into `(0, 1]`. This preserves
|
|
31
|
+
* result ordering but is NOT numerically comparable to cosine scores.
|
|
32
|
+
*
|
|
33
|
+
* Passing a `distanceMetric` that does not match the collection's actual
|
|
34
|
+
* `hnsw:space` will silently produce meaningless scores (Chroma has no way
|
|
35
|
+
* to report its configured space back to the client), so callers must keep
|
|
36
|
+
* the two in sync.
|
|
37
|
+
*
|
|
38
|
+
* ## Metadata round-tripping
|
|
39
|
+
*
|
|
40
|
+
* Chroma metadata values must be strings, numbers, or booleans. Values that
|
|
41
|
+
* are `undefined` are dropped (Chroma rejects `undefined`); any other
|
|
42
|
+
* non-primitive value (object, array, `null`) is `JSON.stringify`-ed on
|
|
43
|
+
* write. This is lossy: such values come back as JSON-encoded strings on
|
|
44
|
+
* read, not their original type. Store only primitive metadata values if
|
|
45
|
+
* you need exact round-tripping.
|
|
46
|
+
*
|
|
47
|
+
* ## indexName -> collection mapping
|
|
48
|
+
*
|
|
49
|
+
* `indexName` is used verbatim as the Chroma collection name (1:1,
|
|
50
|
+
* lazily created via `getOrCreateCollection` and cached for the lifetime of
|
|
51
|
+
* this adapter instance).
|
|
52
|
+
*/
|
|
53
|
+
// Structural Chroma client/collection types (`ChromaClientLike`,
|
|
54
|
+
// `ChromaCollectionLike`, `ChromaUpsertParams`, etc.) live in
|
|
55
|
+
// `src/lib/types/vectorStoreChroma.ts` per this repo's canonical-type-
|
|
56
|
+
// location rule (see CLAUDE.md Critical Rules 2 & 12) and are imported from
|
|
57
|
+
// the types barrel above. This file only re-exports runtime values
|
|
58
|
+
// (`ChromaVectorStore`, `translateMetadataFilter`) — consumers who need the
|
|
59
|
+
// structural types should import them from `@juspay/neurolink` (root) or
|
|
60
|
+
// the types barrel directly, per Critical Rule 13.
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// MetadataFilter -> Chroma `where` translation
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
const CHROMA_SUPPORTED_FIELD_OPS = new Set([
|
|
65
|
+
"$eq",
|
|
66
|
+
"$ne",
|
|
67
|
+
"$gt",
|
|
68
|
+
"$gte",
|
|
69
|
+
"$lt",
|
|
70
|
+
"$lte",
|
|
71
|
+
"$in",
|
|
72
|
+
"$nin",
|
|
73
|
+
]);
|
|
74
|
+
const CHROMA_UNSUPPORTED_FIELD_OP_REASONS = {
|
|
75
|
+
$exists: 'Chroma metadata filtering has no existence check on the "where" clause.',
|
|
76
|
+
$contains: 'Chroma metadata filtering has no substring-match operator on the "where" clause (Chroma\'s substring matching only applies to document text via "where_document", not metadata field values).',
|
|
77
|
+
$regex: 'Chroma metadata filtering has no regular-expression operator on the "where" clause.',
|
|
78
|
+
$size: 'Chroma metadata filtering has no array-length operator on the "where" clause.',
|
|
79
|
+
};
|
|
80
|
+
function unsupportedFieldOpError(opKey, field) {
|
|
81
|
+
const reason = CHROMA_UNSUPPORTED_FIELD_OP_REASONS[opKey] ??
|
|
82
|
+
`Chroma metadata filtering does not support the "${opKey}" operator on the "where" clause.`;
|
|
83
|
+
return new Error(`Unsupported operator "${opKey}" (field "${field}"): ${reason}`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Translates a field-level filter value (either a direct-equality primitive
|
|
87
|
+
* or an operator bag) into a Chroma `where` clause fragment for that field.
|
|
88
|
+
*
|
|
89
|
+
* Returns `undefined` when the filter value carries no enforceable
|
|
90
|
+
* constraint (an array, or an operator bag with no recognized keys) — this
|
|
91
|
+
* mirrors `InMemoryVectorStore.matchesFilter`, which treats any non-null
|
|
92
|
+
* object value as an "operator bag" and silently applies none of its checks
|
|
93
|
+
* when no recognized `$`-key is present, i.e. the field matches
|
|
94
|
+
* unconditionally. Omitting the field from the Chroma `where` clause
|
|
95
|
+
* reproduces that same "always matches" behavior.
|
|
96
|
+
*/
|
|
97
|
+
function translateFieldFilter(field, value) {
|
|
98
|
+
if (Array.isArray(value)) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
if (value === null || typeof value !== "object") {
|
|
102
|
+
return { [field]: { $eq: value } };
|
|
103
|
+
}
|
|
104
|
+
const ops = value;
|
|
105
|
+
const opKeys = Object.keys(ops);
|
|
106
|
+
if (opKeys.length === 0) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
const chromaOps = {};
|
|
110
|
+
for (const opKey of opKeys) {
|
|
111
|
+
if (!CHROMA_SUPPORTED_FIELD_OPS.has(opKey)) {
|
|
112
|
+
throw unsupportedFieldOpError(opKey, field);
|
|
113
|
+
}
|
|
114
|
+
chromaOps[opKey] = ops[opKey];
|
|
115
|
+
}
|
|
116
|
+
return { [field]: chromaOps };
|
|
117
|
+
}
|
|
118
|
+
function translateLogicalOperator(key, value) {
|
|
119
|
+
switch (key) {
|
|
120
|
+
case "$and":
|
|
121
|
+
return {
|
|
122
|
+
$and: value.map(translateMetadataFilter),
|
|
123
|
+
};
|
|
124
|
+
case "$or":
|
|
125
|
+
return {
|
|
126
|
+
$or: value.map(translateMetadataFilter),
|
|
127
|
+
};
|
|
128
|
+
case "$not":
|
|
129
|
+
throw new Error('Chroma metadata filtering has no negation operator on the "where" clause; "$not" cannot be translated. Rewrite the filter using $ne/$nin instead.');
|
|
130
|
+
case "$nor":
|
|
131
|
+
throw new Error('"$nor" is declared on MetadataFilter but is not implemented by InMemoryVectorStore either, and Chroma has no negation operator on the "where" clause. Rewrite using $and of $ne/$nin clauses.');
|
|
132
|
+
default:
|
|
133
|
+
throw new Error(`Unknown logical operator "${key}" in MetadataFilter.`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Translates a `MetadataFilter` into a Chroma `where` clause.
|
|
138
|
+
*
|
|
139
|
+
* Throws a clear error for any operator Chroma's metadata `where` clause
|
|
140
|
+
* cannot express ($not, $nor, $exists, $contains, $regex, $size) rather than
|
|
141
|
+
* silently dropping the constraint and mis-filtering results.
|
|
142
|
+
*/
|
|
143
|
+
export function translateMetadataFilter(filter) {
|
|
144
|
+
const clauses = [];
|
|
145
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
146
|
+
const clause = key.startsWith("$")
|
|
147
|
+
? translateLogicalOperator(key, value)
|
|
148
|
+
: translateFieldFilter(key, value);
|
|
149
|
+
if (clause !== undefined) {
|
|
150
|
+
clauses.push(clause);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (clauses.length === 0) {
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
if (clauses.length === 1) {
|
|
157
|
+
return clauses[0];
|
|
158
|
+
}
|
|
159
|
+
// Chroma requires a single top-level operator; combine multiple field/
|
|
160
|
+
// logical clauses with an explicit $and (mirrors matchesFilter ANDing
|
|
161
|
+
// every top-level entry).
|
|
162
|
+
return { $and: clauses };
|
|
163
|
+
}
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// Metadata <-> Chroma metadata conversion
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
function toChromaMetadataValue(value) {
|
|
168
|
+
if (typeof value === "string" ||
|
|
169
|
+
typeof value === "number" ||
|
|
170
|
+
typeof value === "boolean") {
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
// Chroma metadata values must be string | number | boolean. Non-primitive
|
|
174
|
+
// values (object, array, null) are JSON-stringified — lossy on read back,
|
|
175
|
+
// see module doc comment.
|
|
176
|
+
return JSON.stringify(value);
|
|
177
|
+
}
|
|
178
|
+
function toChromaMetadata(metadata) {
|
|
179
|
+
const result = {};
|
|
180
|
+
for (const [key, value] of Object.entries(metadata)) {
|
|
181
|
+
if (value === undefined) {
|
|
182
|
+
// Chroma rejects `undefined` metadata values outright.
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
result[key] = toChromaMetadataValue(value);
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
}
|
|
189
|
+
function distanceToScore(distance, metric) {
|
|
190
|
+
switch (metric) {
|
|
191
|
+
case "cosine":
|
|
192
|
+
case "ip":
|
|
193
|
+
return 1 - distance;
|
|
194
|
+
case "l2":
|
|
195
|
+
return 1 / (1 + distance);
|
|
196
|
+
default: {
|
|
197
|
+
const exhaustive = metric;
|
|
198
|
+
throw new Error(`Unknown Chroma distance metric: ${String(exhaustive)}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// ChromaVectorStore
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
export class ChromaVectorStore {
|
|
206
|
+
client;
|
|
207
|
+
collections = new Map();
|
|
208
|
+
distanceMetric;
|
|
209
|
+
constructor(client, options = {}) {
|
|
210
|
+
this.client = client;
|
|
211
|
+
this.distanceMetric = options.distanceMetric ?? "cosine";
|
|
212
|
+
}
|
|
213
|
+
getCollection(indexName) {
|
|
214
|
+
let collection = this.collections.get(indexName);
|
|
215
|
+
if (!collection) {
|
|
216
|
+
collection = this.client.getOrCreateCollection({ name: indexName });
|
|
217
|
+
this.collections.set(indexName, collection);
|
|
218
|
+
}
|
|
219
|
+
return collection;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Add or update vectors in an index (Chroma collection).
|
|
223
|
+
*/
|
|
224
|
+
async upsert(indexName, items) {
|
|
225
|
+
if (items.length === 0) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const collection = await this.getCollection(indexName);
|
|
229
|
+
await collection.upsert({
|
|
230
|
+
ids: items.map((item) => item.id),
|
|
231
|
+
embeddings: items.map((item) => item.vector),
|
|
232
|
+
metadatas: items.map((item) => toChromaMetadata(item.metadata ?? {})),
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Query vectors by similarity.
|
|
237
|
+
*/
|
|
238
|
+
async query(params) {
|
|
239
|
+
const { indexName, queryVector, topK = 10, filter, includeVectors = false, } = params;
|
|
240
|
+
const collection = await this.getCollection(indexName);
|
|
241
|
+
const include = ["metadatas", "distances"];
|
|
242
|
+
if (includeVectors) {
|
|
243
|
+
include.push("embeddings");
|
|
244
|
+
}
|
|
245
|
+
const response = await collection.query({
|
|
246
|
+
queryEmbeddings: [queryVector],
|
|
247
|
+
nResults: topK,
|
|
248
|
+
...(filter ? { where: translateMetadataFilter(filter) } : {}),
|
|
249
|
+
include,
|
|
250
|
+
});
|
|
251
|
+
const ids = response.ids[0] ?? [];
|
|
252
|
+
const metadatasRow = response.metadatas?.[0] ?? [];
|
|
253
|
+
const distancesRow = response.distances?.[0] ?? [];
|
|
254
|
+
const embeddingsRow = response.embeddings?.[0] ?? [];
|
|
255
|
+
return ids.map((id, i) => {
|
|
256
|
+
const metadata = (metadatasRow[i] ?? {});
|
|
257
|
+
const distance = distancesRow[i];
|
|
258
|
+
const score = distance === null || distance === undefined
|
|
259
|
+
? undefined
|
|
260
|
+
: distanceToScore(distance, this.distanceMetric);
|
|
261
|
+
return {
|
|
262
|
+
id,
|
|
263
|
+
score,
|
|
264
|
+
text: metadata.text,
|
|
265
|
+
metadata,
|
|
266
|
+
...(includeVectors ? { vector: embeddingsRow[i] ?? [] } : {}),
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Delete vectors from an index (Chroma collection) by id.
|
|
272
|
+
*/
|
|
273
|
+
async delete(indexName, ids) {
|
|
274
|
+
if (ids.length === 0) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const collection = await this.getCollection(indexName);
|
|
278
|
+
await collection.delete({ ids });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector Store Adapters
|
|
3
|
+
*
|
|
4
|
+
* Client-injection adapters implementing the `VectorStore` contract
|
|
5
|
+
* (see `src/lib/types/rag.ts`) against real vector database backends.
|
|
6
|
+
* Each adapter accepts an already-constructed vendor client typed as a
|
|
7
|
+
* minimal structural interface — no vendor SDK is a runtime dependency
|
|
8
|
+
* of this package; callers bring their own client instance.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { PineconeVectorStore } from '@juspay/neurolink/rag';
|
|
13
|
+
* import { Pinecone } from '@pinecone-database/pinecone';
|
|
14
|
+
*
|
|
15
|
+
* const client = new Pinecone({ apiKey: '...' });
|
|
16
|
+
* const store = new PineconeVectorStore(client.index('my-index'));
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export * from "./pinecone.js";
|
|
20
|
+
export * from "./pgvector.js";
|
|
21
|
+
export * from "./chroma.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vector Store Adapters
|
|
3
|
+
*
|
|
4
|
+
* Client-injection adapters implementing the `VectorStore` contract
|
|
5
|
+
* (see `src/lib/types/rag.ts`) against real vector database backends.
|
|
6
|
+
* Each adapter accepts an already-constructed vendor client typed as a
|
|
7
|
+
* minimal structural interface — no vendor SDK is a runtime dependency
|
|
8
|
+
* of this package; callers bring their own client instance.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { PineconeVectorStore } from '@juspay/neurolink/rag';
|
|
13
|
+
* import { Pinecone } from '@pinecone-database/pinecone';
|
|
14
|
+
*
|
|
15
|
+
* const client = new Pinecone({ apiKey: '...' });
|
|
16
|
+
* const store = new PineconeVectorStore(client.index('my-index'));
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export * from "./pinecone.js";
|
|
20
|
+
export * from "./pgvector.js";
|
|
21
|
+
export * from "./chroma.js";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pgvector Vector Store Adapter
|
|
3
|
+
*
|
|
4
|
+
* Client-injection `VectorStore` implementation backed by Postgres +
|
|
5
|
+
* the `pgvector` extension. Callers construct and own the Postgres client
|
|
6
|
+
* (e.g. a `pg.Pool`, or an in-process `@electric-sql/pglite` instance) and
|
|
7
|
+
* pass it in — this module adds zero vendor SDKs as runtime dependencies.
|
|
8
|
+
*
|
|
9
|
+
* Storage model: one table per `indexName`, created lazily on first
|
|
10
|
+
* `upsert()` (`CREATE TABLE IF NOT EXISTS`, so it is safe to call
|
|
11
|
+
* repeatedly). Schema:
|
|
12
|
+
* id text PRIMARY KEY
|
|
13
|
+
* embedding vector(n) -- n fixed to the dimension of the first upsert
|
|
14
|
+
* metadata jsonb NOT NULL DEFAULT '{}'::jsonb
|
|
15
|
+
*
|
|
16
|
+
* Scoring: matches `InMemoryVectorStore`'s cosine-similarity convention
|
|
17
|
+
* (higher is better, 1.0 = identical direction) by converting pgvector's
|
|
18
|
+
* `<=>` cosine *distance* operator (0 = identical) via `score = 1 - distance`.
|
|
19
|
+
*
|
|
20
|
+
* `MetadataFilter` -> SQL: every value that flows into a query is a bound
|
|
21
|
+
* parameter (`$1`, `$2`, ...), including metadata field *names* — nothing
|
|
22
|
+
* from the filter is ever string-concatenated into SQL text. The one thing
|
|
23
|
+
* that IS textually embedded is the derived table name, and only after it
|
|
24
|
+
* passes a strict identifier allow-list (see `IDENTIFIER_RE`), because
|
|
25
|
+
* Postgres has no way to bind an identifier as a query parameter.
|
|
26
|
+
*
|
|
27
|
+
* Operators not expressible without ambiguity are rejected loudly instead of
|
|
28
|
+
* silently no-op'ing:
|
|
29
|
+
* - `$nor` (top-level logical operator) and `$size` (field-level operator)
|
|
30
|
+
* are declared on `MetadataFilter` but (per the reference
|
|
31
|
+
* `InMemoryVectorStore.matchesFilter`) are not actually implemented
|
|
32
|
+
* there either — rather than guess at semantics that would silently
|
|
33
|
+
* diverge from any caller's expectations, this adapter throws.
|
|
34
|
+
* - Any other unrecognized `$foo` key, top-level or field-level, throws
|
|
35
|
+
* for the same reason.
|
|
36
|
+
*
|
|
37
|
+
* Known, documented behavioral differences from `InMemoryVectorStore`:
|
|
38
|
+
* - `$eq` / bare-value equality compares via Postgres jsonb structural
|
|
39
|
+
* equality, whereas the in-memory reference uses JS `!==` (so it can
|
|
40
|
+
* never match on array/object metadata values — two distinct array
|
|
41
|
+
* instances are never `===`). This adapter is strictly more capable
|
|
42
|
+
* for those value types.
|
|
43
|
+
* - `$regex` runs Postgres POSIX regex (`~`) rather than a JS `RegExp`.
|
|
44
|
+
* For non-string or missing fields the in-memory store tests the
|
|
45
|
+
* pattern against `""` (so e.g. `^$` or `.*` can still match); this
|
|
46
|
+
* adapter instead requires the field to be a JSON string, so those
|
|
47
|
+
* edge-case patterns will not match a missing/non-string field here.
|
|
48
|
+
*/
|
|
49
|
+
import type { MetadataFilter, PgClientLike, PgVectorStoreOptions, VectorQueryResult, VectorStore } from "../../types/index.js";
|
|
50
|
+
export declare class PgVectorStore implements VectorStore {
|
|
51
|
+
private readonly client;
|
|
52
|
+
private readonly tablePrefix;
|
|
53
|
+
/** Tables we've already run `CREATE TABLE IF NOT EXISTS` for, this process. */
|
|
54
|
+
private readonly ensuredTables;
|
|
55
|
+
constructor(client: PgClientLike, options?: PgVectorStoreOptions);
|
|
56
|
+
/**
|
|
57
|
+
* Derive and validate the backing table name for an index. Throws on
|
|
58
|
+
* anything that isn't a safe bare SQL identifier — this is the injection
|
|
59
|
+
* guard for `indexName`.
|
|
60
|
+
*/
|
|
61
|
+
private tableName;
|
|
62
|
+
/**
|
|
63
|
+
* Ensure the `vector` extension and the index's table exist. Safe to call
|
|
64
|
+
* repeatedly (`IF NOT EXISTS` throughout). `dimension` is fixed by the
|
|
65
|
+
* first upsert into a fresh index; it is a validated positive integer
|
|
66
|
+
* embedded directly in DDL (Postgres cannot bind type modifiers either).
|
|
67
|
+
*/
|
|
68
|
+
private ensureTable;
|
|
69
|
+
/**
|
|
70
|
+
* Upsert vectors into an index, creating its table on first use.
|
|
71
|
+
* Mirrors `InMemoryVectorStore.upsert` (metadata defaults to `{}`).
|
|
72
|
+
*/
|
|
73
|
+
upsert(indexName: string, items: Array<{
|
|
74
|
+
id: string;
|
|
75
|
+
vector: number[];
|
|
76
|
+
metadata?: Record<string, unknown>;
|
|
77
|
+
}>): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Query by cosine similarity. Returns `[]` if the index's table doesn't
|
|
80
|
+
* exist yet (mirrors `InMemoryVectorStore.query` on an unknown index)
|
|
81
|
+
* rather than throwing.
|
|
82
|
+
*/
|
|
83
|
+
query(params: {
|
|
84
|
+
indexName: string;
|
|
85
|
+
queryVector: number[];
|
|
86
|
+
topK?: number;
|
|
87
|
+
filter?: MetadataFilter;
|
|
88
|
+
includeVectors?: boolean;
|
|
89
|
+
}): Promise<VectorQueryResult[]>;
|
|
90
|
+
/**
|
|
91
|
+
* Delete vectors by id. No-op if the index's table doesn't exist yet
|
|
92
|
+
* (mirrors `InMemoryVectorStore.delete`).
|
|
93
|
+
*/
|
|
94
|
+
delete(indexName: string, ids: string[]): Promise<void>;
|
|
95
|
+
}
|