@amemhq/core 1.0.1 → 1.1.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/dist/chunk-B2NS7WAM.js +2182 -0
- package/dist/chunk-B2NS7WAM.js.map +1 -0
- package/dist/cli-migrate.cjs +932 -0
- package/dist/cli-migrate.cjs.map +1 -0
- package/dist/cli-migrate.d.cts +16 -0
- package/dist/cli-migrate.d.ts +16 -0
- package/dist/cli-migrate.js +86 -0
- package/dist/cli-migrate.js.map +1 -0
- package/dist/index.cjs +68 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -2
- package/dist/index.d.ts +39 -2
- package/dist/index.js +72 -2120
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.cjs
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
DEFAULT_CRUD_UPDATE_MIN_SIM: () => DEFAULT_CRUD_UPDATE_MIN_SIM,
|
|
34
34
|
DEFAULT_EMBEDDING_MODEL: () => DEFAULT_EMBEDDING_MODEL,
|
|
35
35
|
EmbeddingDimensionMismatchError: () => EmbeddingDimensionMismatchError,
|
|
36
|
+
EmbeddingModelMismatchError: () => EmbeddingModelMismatchError,
|
|
36
37
|
addEpisodic: () => addEpisodic,
|
|
37
38
|
addMemory: () => addMemory,
|
|
38
39
|
canRead: () => canRead,
|
|
@@ -47,7 +48,9 @@ __export(index_exports, {
|
|
|
47
48
|
encode: () => encode,
|
|
48
49
|
ensureCollection: () => ensureCollection,
|
|
49
50
|
generateReviewBatch: () => generateReviewBatch,
|
|
51
|
+
getEmbeddingDevice: () => getEmbeddingDevice,
|
|
50
52
|
getEmbeddingDim: () => getEmbeddingDim,
|
|
53
|
+
getEmbeddingDtype: () => getEmbeddingDtype,
|
|
51
54
|
getEmbeddingModel: () => getEmbeddingModel,
|
|
52
55
|
getEmbeddingPooling: () => getEmbeddingPooling,
|
|
53
56
|
getNote: () => getNote,
|
|
@@ -83,7 +86,7 @@ function getDataDir() {
|
|
|
83
86
|
// src/embedding.ts
|
|
84
87
|
var pipeline = null;
|
|
85
88
|
var extractor = null;
|
|
86
|
-
var
|
|
89
|
+
var loadedKey = null;
|
|
87
90
|
var cachedDim = null;
|
|
88
91
|
var DEFAULT_EMBEDDING_MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
89
92
|
function getEmbeddingModel() {
|
|
@@ -108,22 +111,37 @@ function getEmbeddingPooling() {
|
|
|
108
111
|
const basename2 = getEmbeddingModel().split("/").pop()?.toLowerCase() ?? "";
|
|
109
112
|
return CLS_POOLED_MODELS.has(basename2) ? "cls" : "mean";
|
|
110
113
|
}
|
|
114
|
+
function getEmbeddingDevice() {
|
|
115
|
+
return process.env.AMEM_EMBED_DEVICE?.trim() || void 0;
|
|
116
|
+
}
|
|
117
|
+
function getEmbeddingDtype() {
|
|
118
|
+
return process.env.AMEM_EMBED_DTYPE?.trim() || void 0;
|
|
119
|
+
}
|
|
120
|
+
function extractorKey() {
|
|
121
|
+
return `${getEmbeddingModel()}|${getEmbeddingDevice() ?? ""}|${getEmbeddingDtype() ?? ""}`;
|
|
122
|
+
}
|
|
111
123
|
async function getExtractor() {
|
|
112
|
-
const wanted =
|
|
113
|
-
if (extractor &&
|
|
124
|
+
const wanted = extractorKey();
|
|
125
|
+
if (extractor && loadedKey === wanted) return extractor;
|
|
114
126
|
if (!pipeline) {
|
|
115
127
|
const mod = await import("@huggingface/transformers");
|
|
116
128
|
pipeline = mod.pipeline;
|
|
117
129
|
}
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
const device = getEmbeddingDevice();
|
|
131
|
+
const dtype = getEmbeddingDtype();
|
|
132
|
+
extractor = await pipeline("feature-extraction", getEmbeddingModel(), {
|
|
133
|
+
revision: "main",
|
|
134
|
+
// Omitted entirely when unset, so an unconfigured install gets exactly the
|
|
135
|
+
// library defaults it got before these existed.
|
|
136
|
+
...device ? { device } : {},
|
|
137
|
+
...dtype ? { dtype } : {}
|
|
120
138
|
});
|
|
121
|
-
|
|
139
|
+
loadedKey = wanted;
|
|
122
140
|
cachedDim = null;
|
|
123
141
|
return extractor;
|
|
124
142
|
}
|
|
125
143
|
async function getEmbeddingDim() {
|
|
126
|
-
if (cachedDim !== null &&
|
|
144
|
+
if (cachedDim !== null && loadedKey === extractorKey()) return cachedDim;
|
|
127
145
|
const probe = await encode("dimension probe");
|
|
128
146
|
cachedDim = probe.length;
|
|
129
147
|
return cachedDim;
|
|
@@ -208,7 +226,7 @@ var EmbeddingDimensionMismatchError = class extends Error {
|
|
|
208
226
|
constructor(collection, collectionDim, modelDim, model) {
|
|
209
227
|
super(
|
|
210
228
|
`Collection "${collection}" stores ${collectionDim}-dimension vectors, but the embedding model "${model}" produces ${modelDim}. Qdrant fixes a collection's vector size at creation and cannot change it, so writes and searches would both fail.
|
|
211
|
-
Either set AMEM_EMBED_MODEL back to the model this collection was built with, or
|
|
229
|
+
Either set AMEM_EMBED_MODEL back to the model this collection was built with, or ${migrationHint(collection, model)}`
|
|
212
230
|
);
|
|
213
231
|
this.collection = collection;
|
|
214
232
|
this.collectionDim = collectionDim;
|
|
@@ -221,6 +239,35 @@ Either set AMEM_EMBED_MODEL back to the model this collection was built with, or
|
|
|
221
239
|
modelDim;
|
|
222
240
|
model;
|
|
223
241
|
};
|
|
242
|
+
function migrationHint(collection, targetModel) {
|
|
243
|
+
return `migrate to a new collection:
|
|
244
|
+
|
|
245
|
+
AMEM_EMBED_MODEL=${targetModel} \\
|
|
246
|
+
npx --package=@amemhq/core amem-migrate --to ${collection}_v2
|
|
247
|
+
|
|
248
|
+
That is a dry run; add --apply to write. "${collection}" is only read, so nothing is lost either way. When it looks right, point whatever names this collection at the new one \u2014 AMEM_COLLECTION, or the plugin's "collection" setting if this agent has its own. See https://amem.owo.lc/reference/embedding-models.`;
|
|
249
|
+
}
|
|
250
|
+
var EmbeddingModelMismatchError = class extends Error {
|
|
251
|
+
constructor(collection, collectionModel, configuredModel) {
|
|
252
|
+
super(
|
|
253
|
+
`Collection "${collection}" was built with the embedding model "${collectionModel}", but this process is configured for "${configuredModel}". Both produce vectors of the same width, so nothing would fail \u2014 searches would just quietly compare vectors from two different models.
|
|
254
|
+
Either set AMEM_EMBED_MODEL back to "${collectionModel}", or ` + migrationHint(collection, configuredModel)
|
|
255
|
+
);
|
|
256
|
+
this.collection = collection;
|
|
257
|
+
this.collectionModel = collectionModel;
|
|
258
|
+
this.configuredModel = configuredModel;
|
|
259
|
+
this.name = "EmbeddingModelMismatchError";
|
|
260
|
+
}
|
|
261
|
+
collection;
|
|
262
|
+
collectionModel;
|
|
263
|
+
configuredModel;
|
|
264
|
+
};
|
|
265
|
+
async function recordCollectionModel(collection, model) {
|
|
266
|
+
try {
|
|
267
|
+
await qdrant("PATCH", `/collections/${collection}`, { metadata: { embedding_model: model } });
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
224
271
|
async function qdrant(method, path5, body) {
|
|
225
272
|
const res = await fetch(`${QDRANT_URL}${path5}`, {
|
|
226
273
|
method,
|
|
@@ -263,6 +310,14 @@ async function ensureCollection(collectionName) {
|
|
|
263
310
|
throw new EmbeddingDimensionMismatchError(col, collectionDim, modelDim, getEmbeddingModel());
|
|
264
311
|
}
|
|
265
312
|
}
|
|
313
|
+
const recorded = existing.config?.metadata?.embedding_model;
|
|
314
|
+
const current = getEmbeddingModel();
|
|
315
|
+
if (typeof recorded === "string" && recorded !== current) {
|
|
316
|
+
throw new EmbeddingModelMismatchError(col, recorded, current);
|
|
317
|
+
}
|
|
318
|
+
if (recorded === void 0) {
|
|
319
|
+
await recordCollectionModel(col, current);
|
|
320
|
+
}
|
|
266
321
|
markReady();
|
|
267
322
|
return;
|
|
268
323
|
}
|
|
@@ -274,6 +329,7 @@ async function ensureCollection(collectionName) {
|
|
|
274
329
|
} catch (err) {
|
|
275
330
|
if (!(err instanceof Error) || !err.message.includes("already exists")) throw err;
|
|
276
331
|
}
|
|
332
|
+
await recordCollectionModel(col, getEmbeddingModel());
|
|
277
333
|
await qdrant("PUT", `/collections/${col}/index`, {
|
|
278
334
|
field_name: "agent_id",
|
|
279
335
|
field_schema: "keyword"
|
|
@@ -319,6 +375,7 @@ async function collectionDimRaw(collection) {
|
|
|
319
375
|
}
|
|
320
376
|
async function createCollectionRaw(collection, size) {
|
|
321
377
|
await qdrant("PUT", `/collections/${collection}`, { vectors: { size, distance: "Cosine" } });
|
|
378
|
+
await recordCollectionModel(collection, getEmbeddingModel());
|
|
322
379
|
for (const field_name of ["agent_id", "hash", "topics", "subjects"]) {
|
|
323
380
|
await qdrant("PUT", `/collections/${collection}/index`, { field_name, field_schema: "keyword" });
|
|
324
381
|
}
|
|
@@ -2358,6 +2415,7 @@ function isPlausibleUpdateTarget(newEmbedding, targetEmbedding, minSimilarity) {
|
|
|
2358
2415
|
DEFAULT_CRUD_UPDATE_MIN_SIM,
|
|
2359
2416
|
DEFAULT_EMBEDDING_MODEL,
|
|
2360
2417
|
EmbeddingDimensionMismatchError,
|
|
2418
|
+
EmbeddingModelMismatchError,
|
|
2361
2419
|
addEpisodic,
|
|
2362
2420
|
addMemory,
|
|
2363
2421
|
canRead,
|
|
@@ -2372,7 +2430,9 @@ function isPlausibleUpdateTarget(newEmbedding, targetEmbedding, minSimilarity) {
|
|
|
2372
2430
|
encode,
|
|
2373
2431
|
ensureCollection,
|
|
2374
2432
|
generateReviewBatch,
|
|
2433
|
+
getEmbeddingDevice,
|
|
2375
2434
|
getEmbeddingDim,
|
|
2435
|
+
getEmbeddingDtype,
|
|
2376
2436
|
getEmbeddingModel,
|
|
2377
2437
|
getEmbeddingPooling,
|
|
2378
2438
|
getNote,
|