@lov3kaizen/agentsea-embeddings 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/LICENSE +21 -0
- package/README.md +475 -0
- package/dist/caching/index.d.mts +286 -0
- package/dist/caching/index.d.ts +286 -0
- package/dist/caching/index.js +1005 -0
- package/dist/caching/index.mjs +27 -0
- package/dist/chunk-3KM32UQK.mjs +207 -0
- package/dist/chunk-DJAURHAS.mjs +1117 -0
- package/dist/chunk-NBHIRTJT.mjs +895 -0
- package/dist/chunk-QAITLJ2E.mjs +259 -0
- package/dist/chunk-TER262ST.mjs +877 -0
- package/dist/chunk-VPSMDBHH.mjs +957 -0
- package/dist/chunking/index.d.mts +1 -0
- package/dist/chunking/index.d.ts +1 -0
- package/dist/chunking/index.js +1408 -0
- package/dist/chunking/index.mjs +37 -0
- package/dist/embedding.types-CCgPVxt1.d.mts +102 -0
- package/dist/embedding.types-CCgPVxt1.d.ts +102 -0
- package/dist/index-CeG6God2.d.mts +297 -0
- package/dist/index-DMaQRn2w.d.mts +172 -0
- package/dist/index-DMaQRn2w.d.ts +172 -0
- package/dist/index-DWddsKRi.d.ts +297 -0
- package/dist/index.d.mts +647 -0
- package/dist/index.d.ts +647 -0
- package/dist/index.js +5259 -0
- package/dist/index.mjs +1028 -0
- package/dist/providers/index.d.mts +2 -0
- package/dist/providers/index.d.ts +2 -0
- package/dist/providers/index.js +1235 -0
- package/dist/providers/index.mjs +32 -0
- package/dist/stores/index.d.mts +298 -0
- package/dist/stores/index.d.ts +298 -0
- package/dist/stores/index.js +1178 -0
- package/dist/stores/index.mjs +26 -0
- package/package.json +102 -0
|
@@ -0,0 +1,877 @@
|
|
|
1
|
+
import {
|
|
2
|
+
batch
|
|
3
|
+
} from "./chunk-3KM32UQK.mjs";
|
|
4
|
+
import {
|
|
5
|
+
EmbeddingModel
|
|
6
|
+
} from "./chunk-QAITLJ2E.mjs";
|
|
7
|
+
|
|
8
|
+
// src/stores/BaseStore.ts
|
|
9
|
+
var BaseStore = class {
|
|
10
|
+
/** Store configuration */
|
|
11
|
+
config;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = {
|
|
14
|
+
namespace: config.namespace ?? "default",
|
|
15
|
+
dimensions: config.dimensions,
|
|
16
|
+
metric: config.metric ?? "cosine",
|
|
17
|
+
...config
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Get namespace
|
|
22
|
+
*/
|
|
23
|
+
get namespace() {
|
|
24
|
+
return this.config.namespace ?? "default";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Get dimensions
|
|
28
|
+
*/
|
|
29
|
+
get dimensions() {
|
|
30
|
+
return this.config.dimensions;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Get distance metric
|
|
34
|
+
*/
|
|
35
|
+
get metric() {
|
|
36
|
+
return this.config.metric ?? "cosine";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Calculate similarity/distance between vectors
|
|
40
|
+
*/
|
|
41
|
+
calculateScore(a, b) {
|
|
42
|
+
switch (this.metric) {
|
|
43
|
+
case "cosine":
|
|
44
|
+
return EmbeddingModel.cosineSimilarity(a, b);
|
|
45
|
+
case "euclidean": {
|
|
46
|
+
const dist = EmbeddingModel.euclideanDistance(a, b);
|
|
47
|
+
return 1 / (1 + dist);
|
|
48
|
+
}
|
|
49
|
+
case "dot_product":
|
|
50
|
+
return EmbeddingModel.dotProduct(a, b);
|
|
51
|
+
default:
|
|
52
|
+
return EmbeddingModel.cosineSimilarity(a, b);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Filter records by metadata
|
|
57
|
+
*/
|
|
58
|
+
filterByMetadata(records, filter) {
|
|
59
|
+
if (!filter) return records;
|
|
60
|
+
return records.filter((record) => {
|
|
61
|
+
if (!record.metadata) return false;
|
|
62
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
63
|
+
if (record.metadata[key] !== value) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Convert records to search results
|
|
72
|
+
*/
|
|
73
|
+
toSearchResults(records, options) {
|
|
74
|
+
return records.map((record) => ({
|
|
75
|
+
id: record.id,
|
|
76
|
+
text: options?.includeText !== false ? record.text ?? "" : "",
|
|
77
|
+
score: record.score,
|
|
78
|
+
metadata: options?.includeMetadata !== false ? record.metadata ?? {} : {},
|
|
79
|
+
distance: this.metric !== "cosine" ? 1 - record.score : void 0
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// src/stores/MemoryStore.ts
|
|
85
|
+
import * as fs from "fs/promises";
|
|
86
|
+
var MemoryStore = class extends BaseStore {
|
|
87
|
+
storeType = "memory";
|
|
88
|
+
vectors = /* @__PURE__ */ new Map();
|
|
89
|
+
namespaces = /* @__PURE__ */ new Map();
|
|
90
|
+
persistPath;
|
|
91
|
+
persistInterval;
|
|
92
|
+
maxVectors;
|
|
93
|
+
constructor(config = { type: "memory" }) {
|
|
94
|
+
super(config);
|
|
95
|
+
this.maxVectors = config.maxVectors ?? 1e5;
|
|
96
|
+
this.persistPath = config.persistPath;
|
|
97
|
+
if (config.persistInterval && config.persistPath) {
|
|
98
|
+
this.persistInterval = setInterval(
|
|
99
|
+
() => void this.persist().catch(() => {
|
|
100
|
+
}),
|
|
101
|
+
config.persistInterval
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async upsert(records, options) {
|
|
106
|
+
const startTime = performance.now();
|
|
107
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
108
|
+
const upsertedIds = [];
|
|
109
|
+
const errors = [];
|
|
110
|
+
if (!this.namespaces.has(namespace)) {
|
|
111
|
+
this.namespaces.set(namespace, /* @__PURE__ */ new Set());
|
|
112
|
+
}
|
|
113
|
+
const nsIds = this.namespaces.get(namespace);
|
|
114
|
+
for (const record of records) {
|
|
115
|
+
try {
|
|
116
|
+
if (this.vectors.size >= this.maxVectors && !this.vectors.has(record.id)) {
|
|
117
|
+
const oldestId = this.vectors.keys().next().value;
|
|
118
|
+
if (oldestId) {
|
|
119
|
+
this.vectors.delete(oldestId);
|
|
120
|
+
for (const ns of this.namespaces.values()) {
|
|
121
|
+
ns.delete(oldestId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
this.vectors.set(record.id, record);
|
|
126
|
+
nsIds.add(record.id);
|
|
127
|
+
upsertedIds.push(record.id);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
errors.push({ id: record.id, error: error.message });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return Promise.resolve({
|
|
133
|
+
upsertedIds,
|
|
134
|
+
upsertedCount: upsertedIds.length,
|
|
135
|
+
errors,
|
|
136
|
+
durationMs: performance.now() - startTime
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
async query(vector, options) {
|
|
140
|
+
const startTime = performance.now();
|
|
141
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
142
|
+
const topK = options?.topK ?? 10;
|
|
143
|
+
const minScore = options?.minScore ?? 0;
|
|
144
|
+
const nsIds = this.namespaces.get(namespace);
|
|
145
|
+
if (!nsIds || nsIds.size === 0) {
|
|
146
|
+
return {
|
|
147
|
+
matches: [],
|
|
148
|
+
namespace,
|
|
149
|
+
durationMs: performance.now() - startTime
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
let scoredRecords = [];
|
|
153
|
+
for (const id of nsIds) {
|
|
154
|
+
const record = this.vectors.get(id);
|
|
155
|
+
if (!record) continue;
|
|
156
|
+
const score = this.calculateScore(vector, record.vector);
|
|
157
|
+
if (score >= minScore) {
|
|
158
|
+
scoredRecords.push({ ...record, score });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
if (options?.filter) {
|
|
162
|
+
scoredRecords = this.filterByMetadata(
|
|
163
|
+
scoredRecords,
|
|
164
|
+
options.filter
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
scoredRecords.sort((a, b) => b.score - a.score);
|
|
168
|
+
const topResults = scoredRecords.slice(0, topK);
|
|
169
|
+
return Promise.resolve({
|
|
170
|
+
matches: this.toSearchResults(topResults, options),
|
|
171
|
+
namespace,
|
|
172
|
+
durationMs: performance.now() - startTime
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
async delete(ids, options) {
|
|
176
|
+
const startTime = performance.now();
|
|
177
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
178
|
+
let deletedCount = 0;
|
|
179
|
+
const nsIds = this.namespaces.get(namespace);
|
|
180
|
+
for (const id of ids) {
|
|
181
|
+
if (this.vectors.has(id)) {
|
|
182
|
+
this.vectors.delete(id);
|
|
183
|
+
nsIds?.delete(id);
|
|
184
|
+
deletedCount++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return Promise.resolve({
|
|
188
|
+
deletedCount,
|
|
189
|
+
durationMs: performance.now() - startTime
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
async deleteAll(options) {
|
|
193
|
+
const startTime = performance.now();
|
|
194
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
195
|
+
if (options?.deleteAll) {
|
|
196
|
+
const count2 = this.vectors.size;
|
|
197
|
+
this.vectors.clear();
|
|
198
|
+
this.namespaces.clear();
|
|
199
|
+
return Promise.resolve({
|
|
200
|
+
deletedCount: count2,
|
|
201
|
+
durationMs: performance.now() - startTime
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
const nsIds = this.namespaces.get(namespace);
|
|
205
|
+
if (!nsIds) {
|
|
206
|
+
return Promise.resolve({
|
|
207
|
+
deletedCount: 0,
|
|
208
|
+
durationMs: performance.now() - startTime
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
const count = nsIds.size;
|
|
212
|
+
for (const id of nsIds) {
|
|
213
|
+
this.vectors.delete(id);
|
|
214
|
+
}
|
|
215
|
+
this.namespaces.delete(namespace);
|
|
216
|
+
return Promise.resolve({
|
|
217
|
+
deletedCount: count,
|
|
218
|
+
durationMs: performance.now() - startTime
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
getStats() {
|
|
222
|
+
return Promise.resolve({
|
|
223
|
+
type: this.storeType,
|
|
224
|
+
vectorCount: this.vectors.size,
|
|
225
|
+
namespaceCount: this.namespaces.size,
|
|
226
|
+
dimensions: this.dimensions ?? 0,
|
|
227
|
+
metric: this.metric,
|
|
228
|
+
lastUpdated: Date.now()
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
checkHealth() {
|
|
232
|
+
return Promise.resolve({
|
|
233
|
+
healthy: true,
|
|
234
|
+
latencyMs: 0,
|
|
235
|
+
lastCheck: Date.now()
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
async close() {
|
|
239
|
+
if (this.persistInterval) {
|
|
240
|
+
clearInterval(this.persistInterval);
|
|
241
|
+
}
|
|
242
|
+
if (this.persistPath) {
|
|
243
|
+
await this.persist();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Persist store to file
|
|
248
|
+
*/
|
|
249
|
+
async persist() {
|
|
250
|
+
if (!this.persistPath) return;
|
|
251
|
+
const data = {
|
|
252
|
+
vectors: Array.from(this.vectors.entries()),
|
|
253
|
+
namespaces: Array.from(this.namespaces.entries()).map(([k, v]) => [
|
|
254
|
+
k,
|
|
255
|
+
Array.from(v)
|
|
256
|
+
])
|
|
257
|
+
};
|
|
258
|
+
await fs.writeFile(this.persistPath, JSON.stringify(data), "utf-8");
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Load store from file
|
|
262
|
+
*/
|
|
263
|
+
async load() {
|
|
264
|
+
if (!this.persistPath) return;
|
|
265
|
+
try {
|
|
266
|
+
const data = JSON.parse(await fs.readFile(this.persistPath, "utf-8"));
|
|
267
|
+
this.vectors = new Map(data.vectors);
|
|
268
|
+
this.namespaces = new Map(
|
|
269
|
+
data.namespaces.map(([k, v]) => [k, new Set(v)])
|
|
270
|
+
);
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get all vectors
|
|
276
|
+
*/
|
|
277
|
+
getAll() {
|
|
278
|
+
return Array.from(this.vectors.values());
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Get vector by ID
|
|
282
|
+
*/
|
|
283
|
+
getById(id) {
|
|
284
|
+
return this.vectors.get(id);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function createMemoryStore(config) {
|
|
288
|
+
return new MemoryStore(config);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// src/stores/PineconeStore.ts
|
|
292
|
+
var PineconeStore = class extends BaseStore {
|
|
293
|
+
storeType = "pinecone";
|
|
294
|
+
client;
|
|
295
|
+
index;
|
|
296
|
+
apiKey;
|
|
297
|
+
indexName;
|
|
298
|
+
initialized = false;
|
|
299
|
+
constructor(config) {
|
|
300
|
+
super(config);
|
|
301
|
+
if (!config.apiKey) {
|
|
302
|
+
throw new Error("Pinecone API key is required");
|
|
303
|
+
}
|
|
304
|
+
if (!config.indexName) {
|
|
305
|
+
throw new Error("Pinecone index name is required");
|
|
306
|
+
}
|
|
307
|
+
this.apiKey = config.apiKey;
|
|
308
|
+
this.indexName = config.indexName;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Initialize Pinecone client
|
|
312
|
+
*/
|
|
313
|
+
async init() {
|
|
314
|
+
if (this.initialized) return;
|
|
315
|
+
try {
|
|
316
|
+
const { Pinecone } = await import("@pinecone-database/pinecone");
|
|
317
|
+
this.client = new Pinecone({ apiKey: this.apiKey });
|
|
318
|
+
this.index = this.client.index(
|
|
319
|
+
this.indexName
|
|
320
|
+
);
|
|
321
|
+
this.initialized = true;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
`Failed to initialize Pinecone: ${error.message}`
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async ensureInitialized() {
|
|
329
|
+
if (!this.initialized) {
|
|
330
|
+
await this.init();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async upsert(records, options) {
|
|
334
|
+
await this.ensureInitialized();
|
|
335
|
+
const startTime = performance.now();
|
|
336
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
337
|
+
const batchSize = options?.batchSize ?? 100;
|
|
338
|
+
const upsertedIds = [];
|
|
339
|
+
const errors = [];
|
|
340
|
+
const vectors = records.map((record) => ({
|
|
341
|
+
id: record.id,
|
|
342
|
+
values: record.vector,
|
|
343
|
+
metadata: {
|
|
344
|
+
...record.metadata,
|
|
345
|
+
text: record.text
|
|
346
|
+
}
|
|
347
|
+
}));
|
|
348
|
+
const batches = batch(vectors, batchSize);
|
|
349
|
+
let completed = 0;
|
|
350
|
+
for (const batchVectors of batches) {
|
|
351
|
+
try {
|
|
352
|
+
const ns = this.index.namespace(namespace);
|
|
353
|
+
await ns.upsert(
|
|
354
|
+
batchVectors
|
|
355
|
+
);
|
|
356
|
+
upsertedIds.push(...batchVectors.map((v) => v.id));
|
|
357
|
+
} catch (error) {
|
|
358
|
+
for (const v of batchVectors) {
|
|
359
|
+
errors.push({ id: v.id, error: error.message });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
completed += batchVectors.length;
|
|
363
|
+
options?.onProgress?.({ completed, total: records.length });
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
upsertedIds,
|
|
367
|
+
upsertedCount: upsertedIds.length,
|
|
368
|
+
errors,
|
|
369
|
+
durationMs: performance.now() - startTime
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
async query(vector, options) {
|
|
373
|
+
await this.ensureInitialized();
|
|
374
|
+
const startTime = performance.now();
|
|
375
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
376
|
+
const topK = options?.topK ?? 10;
|
|
377
|
+
const ns = this.index.namespace(
|
|
378
|
+
namespace
|
|
379
|
+
);
|
|
380
|
+
const result = await ns.query({
|
|
381
|
+
vector,
|
|
382
|
+
topK,
|
|
383
|
+
filter: options?.filter,
|
|
384
|
+
includeValues: options?.includeVectors ?? false,
|
|
385
|
+
includeMetadata: options?.includeMetadata ?? true
|
|
386
|
+
});
|
|
387
|
+
const matches = result.matches.map((match) => ({
|
|
388
|
+
id: match.id,
|
|
389
|
+
text: match.metadata?.text ?? "",
|
|
390
|
+
score: match.score,
|
|
391
|
+
metadata: match.metadata ?? {}
|
|
392
|
+
}));
|
|
393
|
+
const filtered = options?.minScore ? matches.filter((m) => m.score >= options.minScore) : matches;
|
|
394
|
+
return {
|
|
395
|
+
matches: filtered,
|
|
396
|
+
namespace,
|
|
397
|
+
durationMs: performance.now() - startTime
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
async delete(ids, options) {
|
|
401
|
+
await this.ensureInitialized();
|
|
402
|
+
const startTime = performance.now();
|
|
403
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
404
|
+
const ns = this.index.namespace(
|
|
405
|
+
namespace
|
|
406
|
+
);
|
|
407
|
+
await ns.deleteMany(
|
|
408
|
+
ids
|
|
409
|
+
);
|
|
410
|
+
return {
|
|
411
|
+
deletedCount: ids.length,
|
|
412
|
+
durationMs: performance.now() - startTime
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
async deleteAll(options) {
|
|
416
|
+
await this.ensureInitialized();
|
|
417
|
+
const startTime = performance.now();
|
|
418
|
+
const namespace = options?.namespace ?? this.namespace;
|
|
419
|
+
const ns = this.index.namespace(
|
|
420
|
+
namespace
|
|
421
|
+
);
|
|
422
|
+
await ns.deleteAll();
|
|
423
|
+
return {
|
|
424
|
+
deletedCount: -1,
|
|
425
|
+
// Unknown count
|
|
426
|
+
durationMs: performance.now() - startTime
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
async getStats() {
|
|
430
|
+
await this.ensureInitialized();
|
|
431
|
+
const stats = await this.index.describeIndexStats();
|
|
432
|
+
return {
|
|
433
|
+
type: this.storeType,
|
|
434
|
+
vectorCount: stats.totalVectorCount ?? 0,
|
|
435
|
+
namespaceCount: Object.keys(stats.namespaces ?? {}).length,
|
|
436
|
+
dimensions: stats.dimension,
|
|
437
|
+
metric: this.metric,
|
|
438
|
+
lastUpdated: Date.now()
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
async checkHealth() {
|
|
442
|
+
const startTime = performance.now();
|
|
443
|
+
try {
|
|
444
|
+
await this.ensureInitialized();
|
|
445
|
+
await this.index.describeIndexStats();
|
|
446
|
+
return {
|
|
447
|
+
healthy: true,
|
|
448
|
+
latencyMs: performance.now() - startTime,
|
|
449
|
+
lastCheck: Date.now()
|
|
450
|
+
};
|
|
451
|
+
} catch (error) {
|
|
452
|
+
return {
|
|
453
|
+
healthy: false,
|
|
454
|
+
latencyMs: performance.now() - startTime,
|
|
455
|
+
lastCheck: Date.now(),
|
|
456
|
+
error: error.message
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
async close() {
|
|
461
|
+
this.initialized = false;
|
|
462
|
+
return Promise.resolve();
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
function createPineconeStore(config) {
|
|
466
|
+
return new PineconeStore(config);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/stores/ChromaStore.ts
|
|
470
|
+
var ChromaStore = class extends BaseStore {
|
|
471
|
+
storeType = "chroma";
|
|
472
|
+
client;
|
|
473
|
+
collection;
|
|
474
|
+
collectionName;
|
|
475
|
+
url;
|
|
476
|
+
initialized = false;
|
|
477
|
+
constructor(config) {
|
|
478
|
+
super(config);
|
|
479
|
+
if (!config.collectionName) {
|
|
480
|
+
throw new Error("Chroma collection name is required");
|
|
481
|
+
}
|
|
482
|
+
this.collectionName = config.collectionName;
|
|
483
|
+
this.url = config.url;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Initialize ChromaDB client
|
|
487
|
+
*/
|
|
488
|
+
async init() {
|
|
489
|
+
if (this.initialized) return;
|
|
490
|
+
try {
|
|
491
|
+
const { ChromaClient } = await import("chromadb");
|
|
492
|
+
this.client = this.url ? new ChromaClient({ path: this.url }) : new ChromaClient();
|
|
493
|
+
this.collection = await this.client.getOrCreateCollection({
|
|
494
|
+
name: this.collectionName,
|
|
495
|
+
metadata: {
|
|
496
|
+
"hnsw:space": this.metricToChroma(this.metric)
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
this.initialized = true;
|
|
500
|
+
} catch (error) {
|
|
501
|
+
throw new Error(
|
|
502
|
+
`Failed to initialize ChromaDB: ${error.message}`
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
metricToChroma(metric) {
|
|
507
|
+
switch (metric) {
|
|
508
|
+
case "cosine":
|
|
509
|
+
return "cosine";
|
|
510
|
+
case "euclidean":
|
|
511
|
+
return "l2";
|
|
512
|
+
case "dot_product":
|
|
513
|
+
return "ip";
|
|
514
|
+
default:
|
|
515
|
+
return "cosine";
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
async ensureInitialized() {
|
|
519
|
+
if (!this.initialized) {
|
|
520
|
+
await this.init();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
async upsert(records, _options) {
|
|
524
|
+
await this.ensureInitialized();
|
|
525
|
+
const startTime = performance.now();
|
|
526
|
+
const ids = records.map((r) => r.id);
|
|
527
|
+
const embeddings = records.map((r) => r.vector);
|
|
528
|
+
const documents = records.map((r) => r.text ?? "");
|
|
529
|
+
const metadatas = records.map((r) => r.metadata ?? {});
|
|
530
|
+
const upsertedIds = [];
|
|
531
|
+
const errors = [];
|
|
532
|
+
try {
|
|
533
|
+
await this.collection.upsert({
|
|
534
|
+
ids,
|
|
535
|
+
embeddings,
|
|
536
|
+
documents,
|
|
537
|
+
metadatas
|
|
538
|
+
});
|
|
539
|
+
upsertedIds.push(...ids);
|
|
540
|
+
} catch (error) {
|
|
541
|
+
for (const id of ids) {
|
|
542
|
+
errors.push({ id, error: error.message });
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
upsertedIds,
|
|
547
|
+
upsertedCount: upsertedIds.length,
|
|
548
|
+
errors,
|
|
549
|
+
durationMs: performance.now() - startTime
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
async query(vector, options) {
|
|
553
|
+
await this.ensureInitialized();
|
|
554
|
+
const startTime = performance.now();
|
|
555
|
+
const topK = options?.topK ?? 10;
|
|
556
|
+
const result = await this.collection.query({
|
|
557
|
+
queryEmbeddings: [vector],
|
|
558
|
+
nResults: topK,
|
|
559
|
+
where: options?.filter,
|
|
560
|
+
include: ["documents", "metadatas", "distances"]
|
|
561
|
+
});
|
|
562
|
+
const matches = (result.ids[0] ?? []).map((id, i) => {
|
|
563
|
+
const distance = result.distances?.[0]?.[i] ?? 0;
|
|
564
|
+
const score = 1 / (1 + distance);
|
|
565
|
+
return {
|
|
566
|
+
id,
|
|
567
|
+
text: result.documents?.[0]?.[i] ?? "",
|
|
568
|
+
score,
|
|
569
|
+
metadata: result.metadatas?.[0]?.[i] ?? {},
|
|
570
|
+
distance
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
const filtered = options?.minScore ? matches.filter((m) => m.score >= options.minScore) : matches;
|
|
574
|
+
return {
|
|
575
|
+
matches: filtered,
|
|
576
|
+
namespace: this.collectionName,
|
|
577
|
+
durationMs: performance.now() - startTime
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
async delete(ids, _options) {
|
|
581
|
+
await this.ensureInitialized();
|
|
582
|
+
const startTime = performance.now();
|
|
583
|
+
await this.collection.delete({ ids });
|
|
584
|
+
return {
|
|
585
|
+
deletedCount: ids.length,
|
|
586
|
+
durationMs: performance.now() - startTime
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
async deleteAll(_options) {
|
|
590
|
+
await this.ensureInitialized();
|
|
591
|
+
const startTime = performance.now();
|
|
592
|
+
await this.client.deleteCollection({ name: this.collectionName });
|
|
593
|
+
this.collection = await this.client.createCollection({
|
|
594
|
+
name: this.collectionName,
|
|
595
|
+
metadata: {
|
|
596
|
+
"hnsw:space": this.metricToChroma(this.metric)
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
return {
|
|
600
|
+
deletedCount: -1,
|
|
601
|
+
durationMs: performance.now() - startTime
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
async getStats() {
|
|
605
|
+
await this.ensureInitialized();
|
|
606
|
+
const count = await this.collection.count();
|
|
607
|
+
return {
|
|
608
|
+
type: this.storeType,
|
|
609
|
+
vectorCount: count,
|
|
610
|
+
namespaceCount: 1,
|
|
611
|
+
dimensions: this.dimensions ?? 0,
|
|
612
|
+
metric: this.metric,
|
|
613
|
+
lastUpdated: Date.now()
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
async checkHealth() {
|
|
617
|
+
const startTime = performance.now();
|
|
618
|
+
try {
|
|
619
|
+
await this.ensureInitialized();
|
|
620
|
+
await this.collection.count();
|
|
621
|
+
return {
|
|
622
|
+
healthy: true,
|
|
623
|
+
latencyMs: performance.now() - startTime,
|
|
624
|
+
lastCheck: Date.now()
|
|
625
|
+
};
|
|
626
|
+
} catch (error) {
|
|
627
|
+
return {
|
|
628
|
+
healthy: false,
|
|
629
|
+
latencyMs: performance.now() - startTime,
|
|
630
|
+
lastCheck: Date.now(),
|
|
631
|
+
error: error.message
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
async close() {
|
|
636
|
+
this.initialized = false;
|
|
637
|
+
return Promise.resolve();
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
function createChromaStore(config) {
|
|
641
|
+
return new ChromaStore(config);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// src/stores/QdrantStore.ts
|
|
645
|
+
var QdrantStore = class extends BaseStore {
|
|
646
|
+
storeType = "qdrant";
|
|
647
|
+
client;
|
|
648
|
+
collectionName;
|
|
649
|
+
url;
|
|
650
|
+
apiKey;
|
|
651
|
+
initialized = false;
|
|
652
|
+
constructor(config) {
|
|
653
|
+
super(config);
|
|
654
|
+
if (!config.url) {
|
|
655
|
+
throw new Error("Qdrant URL is required");
|
|
656
|
+
}
|
|
657
|
+
if (!config.collectionName) {
|
|
658
|
+
throw new Error("Qdrant collection name is required");
|
|
659
|
+
}
|
|
660
|
+
this.url = config.url;
|
|
661
|
+
this.collectionName = config.collectionName;
|
|
662
|
+
this.apiKey = config.apiKey;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Initialize Qdrant client
|
|
666
|
+
*/
|
|
667
|
+
async init() {
|
|
668
|
+
if (this.initialized) return;
|
|
669
|
+
try {
|
|
670
|
+
const { QdrantClient } = await import("@qdrant/js-client-rest");
|
|
671
|
+
this.client = new QdrantClient({
|
|
672
|
+
url: this.url,
|
|
673
|
+
apiKey: this.apiKey
|
|
674
|
+
});
|
|
675
|
+
const collections = await this.client.getCollections();
|
|
676
|
+
const exists = collections.collections.some(
|
|
677
|
+
(c) => c.name === this.collectionName
|
|
678
|
+
);
|
|
679
|
+
if (!exists && this.dimensions) {
|
|
680
|
+
await this.client.createCollection(this.collectionName, {
|
|
681
|
+
vectors: {
|
|
682
|
+
size: this.dimensions,
|
|
683
|
+
distance: this.metricToQdrant(this.metric)
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
this.initialized = true;
|
|
688
|
+
} catch (error) {
|
|
689
|
+
throw new Error(
|
|
690
|
+
`Failed to initialize Qdrant: ${error.message}`
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
metricToQdrant(metric) {
|
|
695
|
+
switch (metric) {
|
|
696
|
+
case "cosine":
|
|
697
|
+
return "Cosine";
|
|
698
|
+
case "euclidean":
|
|
699
|
+
return "Euclid";
|
|
700
|
+
case "dot_product":
|
|
701
|
+
return "Dot";
|
|
702
|
+
default:
|
|
703
|
+
return "Cosine";
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
async ensureInitialized() {
|
|
707
|
+
if (!this.initialized) {
|
|
708
|
+
await this.init();
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
async upsert(records, options) {
|
|
712
|
+
await this.ensureInitialized();
|
|
713
|
+
const startTime = performance.now();
|
|
714
|
+
const batchSize = options?.batchSize ?? 100;
|
|
715
|
+
const upsertedIds = [];
|
|
716
|
+
const errors = [];
|
|
717
|
+
const points = records.map((record) => ({
|
|
718
|
+
id: record.id,
|
|
719
|
+
vector: record.vector,
|
|
720
|
+
payload: {
|
|
721
|
+
...record.metadata,
|
|
722
|
+
text: record.text
|
|
723
|
+
}
|
|
724
|
+
}));
|
|
725
|
+
const batches = batch(points, batchSize);
|
|
726
|
+
let completed = 0;
|
|
727
|
+
for (const batchPoints of batches) {
|
|
728
|
+
try {
|
|
729
|
+
await this.client.upsert(this.collectionName, {
|
|
730
|
+
points: batchPoints
|
|
731
|
+
});
|
|
732
|
+
upsertedIds.push(...batchPoints.map((p) => p.id));
|
|
733
|
+
} catch (error) {
|
|
734
|
+
for (const p of batchPoints) {
|
|
735
|
+
errors.push({ id: p.id, error: error.message });
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
completed += batchPoints.length;
|
|
739
|
+
options?.onProgress?.({ completed, total: records.length });
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
upsertedIds,
|
|
743
|
+
upsertedCount: upsertedIds.length,
|
|
744
|
+
errors,
|
|
745
|
+
durationMs: performance.now() - startTime
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
async query(vector, options) {
|
|
749
|
+
await this.ensureInitialized();
|
|
750
|
+
const startTime = performance.now();
|
|
751
|
+
const topK = options?.topK ?? 10;
|
|
752
|
+
const result = await this.client.search(this.collectionName, {
|
|
753
|
+
vector,
|
|
754
|
+
limit: topK,
|
|
755
|
+
filter: options?.filter ? this.buildQdrantFilter(options.filter) : void 0,
|
|
756
|
+
with_payload: options?.includeMetadata ?? true,
|
|
757
|
+
with_vector: options?.includeVectors ?? false,
|
|
758
|
+
score_threshold: options?.minScore
|
|
759
|
+
});
|
|
760
|
+
const matches = result.map((point) => ({
|
|
761
|
+
id: point.id.toString(),
|
|
762
|
+
text: point.payload?.text ?? "",
|
|
763
|
+
score: point.score,
|
|
764
|
+
metadata: point.payload ?? {}
|
|
765
|
+
}));
|
|
766
|
+
return {
|
|
767
|
+
matches,
|
|
768
|
+
namespace: this.collectionName,
|
|
769
|
+
durationMs: performance.now() - startTime
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
buildQdrantFilter(filter) {
|
|
773
|
+
const must = [];
|
|
774
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
775
|
+
must.push({
|
|
776
|
+
key,
|
|
777
|
+
match: { value }
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
return { must };
|
|
781
|
+
}
|
|
782
|
+
async delete(ids, _options) {
|
|
783
|
+
await this.ensureInitialized();
|
|
784
|
+
const startTime = performance.now();
|
|
785
|
+
await this.client.delete(this.collectionName, {
|
|
786
|
+
points: ids
|
|
787
|
+
});
|
|
788
|
+
return {
|
|
789
|
+
deletedCount: ids.length,
|
|
790
|
+
durationMs: performance.now() - startTime
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
async deleteAll(_options) {
|
|
794
|
+
await this.ensureInitialized();
|
|
795
|
+
const startTime = performance.now();
|
|
796
|
+
await this.client.deleteCollection(this.collectionName);
|
|
797
|
+
if (this.dimensions) {
|
|
798
|
+
await this.client.createCollection(this.collectionName, {
|
|
799
|
+
vectors: {
|
|
800
|
+
size: this.dimensions,
|
|
801
|
+
distance: this.metricToQdrant(this.metric)
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
return {
|
|
806
|
+
deletedCount: -1,
|
|
807
|
+
durationMs: performance.now() - startTime
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
async getStats() {
|
|
811
|
+
await this.ensureInitialized();
|
|
812
|
+
const info = await this.client.getCollection(this.collectionName);
|
|
813
|
+
return {
|
|
814
|
+
type: this.storeType,
|
|
815
|
+
vectorCount: info.vectors_count,
|
|
816
|
+
namespaceCount: 1,
|
|
817
|
+
dimensions: info.config?.params?.vectors?.size,
|
|
818
|
+
metric: this.metric,
|
|
819
|
+
lastUpdated: Date.now()
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
async checkHealth() {
|
|
823
|
+
const startTime = performance.now();
|
|
824
|
+
try {
|
|
825
|
+
await this.ensureInitialized();
|
|
826
|
+
await this.client.getCollection(this.collectionName);
|
|
827
|
+
return {
|
|
828
|
+
healthy: true,
|
|
829
|
+
latencyMs: performance.now() - startTime,
|
|
830
|
+
lastCheck: Date.now()
|
|
831
|
+
};
|
|
832
|
+
} catch (error) {
|
|
833
|
+
return {
|
|
834
|
+
healthy: false,
|
|
835
|
+
latencyMs: performance.now() - startTime,
|
|
836
|
+
lastCheck: Date.now(),
|
|
837
|
+
error: error.message
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
async close() {
|
|
842
|
+
this.initialized = false;
|
|
843
|
+
return Promise.resolve();
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
function createQdrantStore(config) {
|
|
847
|
+
return new QdrantStore(config);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// src/stores/index.ts
|
|
851
|
+
function createStore(type, config) {
|
|
852
|
+
switch (type) {
|
|
853
|
+
case "memory":
|
|
854
|
+
return new MemoryStore(config);
|
|
855
|
+
case "pinecone":
|
|
856
|
+
return new PineconeStore(config);
|
|
857
|
+
case "chroma":
|
|
858
|
+
return new ChromaStore(config);
|
|
859
|
+
case "qdrant":
|
|
860
|
+
return new QdrantStore(config);
|
|
861
|
+
default:
|
|
862
|
+
return new MemoryStore(config);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export {
|
|
867
|
+
BaseStore,
|
|
868
|
+
MemoryStore,
|
|
869
|
+
createMemoryStore,
|
|
870
|
+
PineconeStore,
|
|
871
|
+
createPineconeStore,
|
|
872
|
+
ChromaStore,
|
|
873
|
+
createChromaStore,
|
|
874
|
+
QdrantStore,
|
|
875
|
+
createQdrantStore,
|
|
876
|
+
createStore
|
|
877
|
+
};
|