@amemhq/core 2.1.0 → 2.1.2
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 +23 -23
- package/dist/{chunk-XEMQZNLD.js → chunk-Z7G7KOJK.js} +77 -39
- package/dist/chunk-Z7G7KOJK.js.map +1 -0
- package/dist/cli-migrate.cjs +28 -16
- package/dist/cli-migrate.cjs.map +1 -1
- package/dist/cli-migrate.js +1 -1
- package/dist/index.cjs +64 -26
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/package.json +2 -3
- package/dist/chunk-XEMQZNLD.js.map +0 -1
package/dist/cli-migrate.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -81,12 +81,17 @@ module.exports = __toCommonJS(index_exports);
|
|
|
81
81
|
var os = __toESM(require("os"), 1);
|
|
82
82
|
var path = __toESM(require("path"), 1);
|
|
83
83
|
var _dataDir = process.env.AMEM_DATA_DIR || path.join(os.homedir(), ".amem");
|
|
84
|
+
var _warn = (msg) => console.warn(msg);
|
|
84
85
|
function configure(opts) {
|
|
85
86
|
if (opts.dataDir) _dataDir = opts.dataDir;
|
|
87
|
+
if (opts.warn) _warn = opts.warn;
|
|
86
88
|
}
|
|
87
89
|
function getDataDir() {
|
|
88
90
|
return _dataDir;
|
|
89
91
|
}
|
|
92
|
+
function warn(msg) {
|
|
93
|
+
_warn(msg);
|
|
94
|
+
}
|
|
90
95
|
|
|
91
96
|
// src/embedding.ts
|
|
92
97
|
var pipeline = null;
|
|
@@ -144,6 +149,12 @@ function applyModelPaths(env) {
|
|
|
144
149
|
env.allowLocalModels = true;
|
|
145
150
|
}
|
|
146
151
|
}
|
|
152
|
+
function humanBytes(n) {
|
|
153
|
+
if (n >= 1e9) return `${(n / 1e9).toFixed(2)} GB`;
|
|
154
|
+
if (n >= 1e6) return `${(n / 1e6).toFixed(1)} MB`;
|
|
155
|
+
if (n >= 1e3) return `${Math.round(n / 1e3)} kB`;
|
|
156
|
+
return `${n} B`;
|
|
157
|
+
}
|
|
147
158
|
function makeProgressReporter() {
|
|
148
159
|
const lastPct = /* @__PURE__ */ new Map();
|
|
149
160
|
return (e) => {
|
|
@@ -152,7 +163,7 @@ function makeProgressReporter() {
|
|
|
152
163
|
const pct = Math.floor(e.progress / 10) * 10;
|
|
153
164
|
if (lastPct.get(e.file) === pct) return;
|
|
154
165
|
lastPct.set(e.file, pct);
|
|
155
|
-
const size = e.total ? ` of ${(e.total
|
|
166
|
+
const size = e.total ? ` of ${humanBytes(e.total)}` : "";
|
|
156
167
|
console.log(`[amem] downloading ${e.file}: ${pct}%${size}`);
|
|
157
168
|
};
|
|
158
169
|
}
|
|
@@ -379,7 +390,7 @@ async function ensureCollection(collectionName) {
|
|
|
379
390
|
if (inUse !== null && inUse !== wanted) throw new MixedEmbeddingModelsError(col, wanted, inUse);
|
|
380
391
|
pinEmbeddingModel(wanted);
|
|
381
392
|
if (wanted === LEGACY_DEFAULT_EMBEDDING_MODEL) {
|
|
382
|
-
|
|
393
|
+
warn(
|
|
383
394
|
`[amem] "${col}" is on ${LEGACY_DEFAULT_EMBEDDING_MODEL} (${LEGACY_DEFAULT_DIM}-dim).
|
|
384
395
|
[amem] ${DEFAULT_EMBEDDING_MODEL} reads 8192 tokens where that one stops at 128, so anything longer is being truncated before it reaches the vector.
|
|
385
396
|
[amem] To move: npx --package=@amemhq/core amem-migrate --from-collection ${col}`
|
|
@@ -783,7 +794,7 @@ function makeCrud(collectionName, modeBIsolated = false) {
|
|
|
783
794
|
})
|
|
784
795
|
)
|
|
785
796
|
]).catch((err) => {
|
|
786
|
-
|
|
797
|
+
warn(`[amem] retrieval tracking patch failed: ${err.message}`);
|
|
787
798
|
});
|
|
788
799
|
for (const r of queryResults) {
|
|
789
800
|
r.note.retrieval_count = (r.note.retrieval_count || 0) + 1;
|
|
@@ -1150,7 +1161,7 @@ var _warned = /* @__PURE__ */ new Set();
|
|
|
1150
1161
|
function warnOnce(key, message) {
|
|
1151
1162
|
if (_warned.has(key)) return;
|
|
1152
1163
|
_warned.add(key);
|
|
1153
|
-
|
|
1164
|
+
warn(message);
|
|
1154
1165
|
}
|
|
1155
1166
|
function resolveProvider(role = "fast") {
|
|
1156
1167
|
const raw = role === "strong" ? process.env.AMEM_LLM_STRONG_PROVIDER || _override.strong?.provider || void 0 : void 0;
|
|
@@ -1222,9 +1233,11 @@ async function llmCall(prompt, maxTokens = 500, role = "fast") {
|
|
|
1222
1233
|
const isThinking = model.includes("gemini") || model.includes("pro-agent");
|
|
1223
1234
|
const effectiveMaxTokens = isThinking ? Math.max(maxTokens * 8, 4e3) : maxTokens;
|
|
1224
1235
|
try {
|
|
1225
|
-
|
|
1236
|
+
const text = provider === "openai" ? await openaiCall(prompt, model, effectiveMaxTokens, baseURL) : await anthropicCall(prompt, model, effectiveMaxTokens, baseURL);
|
|
1237
|
+
if (text === null) warn(`[amem] ${provider} answered with no text (model ${model})`);
|
|
1238
|
+
return text;
|
|
1226
1239
|
} catch (e) {
|
|
1227
|
-
|
|
1240
|
+
warn(`[amem] LLM call failed: ${e.message}`);
|
|
1228
1241
|
return null;
|
|
1229
1242
|
}
|
|
1230
1243
|
}
|
|
@@ -1323,7 +1336,8 @@ confidence guide (Story 27):
|
|
|
1323
1336
|
|
|
1324
1337
|
Text: ${content}`;
|
|
1325
1338
|
const raw = await llmCall(prompt, 400);
|
|
1326
|
-
if (!raw)
|
|
1339
|
+
if (!raw) {
|
|
1340
|
+
warn("[amem] note construction got nothing back; storing with no keywords, tags or context");
|
|
1327
1341
|
return {
|
|
1328
1342
|
keywords: [],
|
|
1329
1343
|
tags: [],
|
|
@@ -1333,6 +1347,7 @@ Text: ${content}`;
|
|
|
1333
1347
|
topics: [],
|
|
1334
1348
|
confidence: "medium"
|
|
1335
1349
|
};
|
|
1350
|
+
}
|
|
1336
1351
|
try {
|
|
1337
1352
|
const data = parseJsonLoose(raw);
|
|
1338
1353
|
const rawCategory = typeof data.category === "string" ? data.category : "General";
|
|
@@ -1351,7 +1366,7 @@ Text: ${content}`;
|
|
|
1351
1366
|
confidence
|
|
1352
1367
|
};
|
|
1353
1368
|
} catch (e) {
|
|
1354
|
-
|
|
1369
|
+
warn(`[amem] Note construction parse failed: ${e.message}`);
|
|
1355
1370
|
return {
|
|
1356
1371
|
keywords: [],
|
|
1357
1372
|
tags: [],
|
|
@@ -1380,9 +1395,15 @@ async function llmCrudDecision(userText, assistantText, existingMemories) {
|
|
|
1380
1395
|
const raw = await llmCall(prompt, 400, resolveCrudRole());
|
|
1381
1396
|
if (!raw) return [];
|
|
1382
1397
|
const match = stripReasoning(raw).match(/\[.*\]/s);
|
|
1383
|
-
if (!match)
|
|
1398
|
+
if (!match) {
|
|
1399
|
+
warn("[amem] llmCrudDecision found no array in the response; nothing from this turn is stored");
|
|
1400
|
+
return [];
|
|
1401
|
+
}
|
|
1384
1402
|
const parsed = JSON.parse(match[0]);
|
|
1385
|
-
if (!Array.isArray(parsed))
|
|
1403
|
+
if (!Array.isArray(parsed)) {
|
|
1404
|
+
warn("[amem] llmCrudDecision parsed a non-array; nothing from this turn is stored");
|
|
1405
|
+
return [];
|
|
1406
|
+
}
|
|
1386
1407
|
const ops = [];
|
|
1387
1408
|
for (const item of parsed) {
|
|
1388
1409
|
if (!item || typeof item !== "object") continue;
|
|
@@ -1401,7 +1422,7 @@ async function llmCrudDecision(userText, assistantText, existingMemories) {
|
|
|
1401
1422
|
}
|
|
1402
1423
|
return ops.slice(0, 3);
|
|
1403
1424
|
} catch (e) {
|
|
1404
|
-
|
|
1425
|
+
warn(`[amem] llmCrudDecision failed: ${e.message}`);
|
|
1405
1426
|
return [];
|
|
1406
1427
|
}
|
|
1407
1428
|
}
|
|
@@ -1411,13 +1432,16 @@ async function llmShouldMerge(contentA, contentB) {
|
|
|
1411
1432
|
if (!raw) return { shouldMerge: false };
|
|
1412
1433
|
try {
|
|
1413
1434
|
const data = parseJsonLoose(raw);
|
|
1414
|
-
if (typeof data.shouldMerge !== "boolean")
|
|
1435
|
+
if (typeof data.shouldMerge !== "boolean") {
|
|
1436
|
+
warn("[amem] llmShouldMerge got no boolean verdict; treating the pair as distinct");
|
|
1437
|
+
return { shouldMerge: false };
|
|
1438
|
+
}
|
|
1415
1439
|
if (data.shouldMerge && typeof data.merged === "string") {
|
|
1416
1440
|
return { shouldMerge: true, merged: data.merged };
|
|
1417
1441
|
}
|
|
1418
1442
|
return { shouldMerge: false };
|
|
1419
1443
|
} catch (e) {
|
|
1420
|
-
|
|
1444
|
+
warn(`[amem] llmShouldMerge parse failed: ${e.message}`);
|
|
1421
1445
|
return { shouldMerge: false };
|
|
1422
1446
|
}
|
|
1423
1447
|
}
|
|
@@ -1425,7 +1449,10 @@ var VALID_EVOLUTION_TYPES = /* @__PURE__ */ new Set(["EVOLVE", "CONFLICT", "EXPA
|
|
|
1425
1449
|
async function llmEvolutionJudge(oldContent, newContent) {
|
|
1426
1450
|
const prompt = t.evolutionJudge(oldContent, newContent);
|
|
1427
1451
|
const raw = await llmCall(prompt, 300, "strong");
|
|
1428
|
-
if (!raw)
|
|
1452
|
+
if (!raw) {
|
|
1453
|
+
warn("[amem] evolution judge got nothing back; defaulting the pair to NEW");
|
|
1454
|
+
return { type: "NEW" };
|
|
1455
|
+
}
|
|
1429
1456
|
try {
|
|
1430
1457
|
const data = parseJsonLoose(raw);
|
|
1431
1458
|
const type = VALID_EVOLUTION_TYPES.has(data.type) ? data.type : "NEW";
|
|
@@ -1434,7 +1461,7 @@ async function llmEvolutionJudge(oldContent, newContent) {
|
|
|
1434
1461
|
mergedContent: typeof data.mergedContent === "string" ? data.mergedContent : void 0
|
|
1435
1462
|
};
|
|
1436
1463
|
} catch (e) {
|
|
1437
|
-
|
|
1464
|
+
warn(`[amem] llmEvolutionJudge parse failed: ${e.message}`);
|
|
1438
1465
|
return { type: "NEW" };
|
|
1439
1466
|
}
|
|
1440
1467
|
}
|
|
@@ -1473,7 +1500,7 @@ ${linkedStr}`;
|
|
|
1473
1500
|
tagsToUpdate: Array.isArray(data.tags_to_update) ? data.tags_to_update.map(String) : []
|
|
1474
1501
|
};
|
|
1475
1502
|
} catch (e) {
|
|
1476
|
-
|
|
1503
|
+
warn(`[amem] Evolution parse failed: ${e.message}`);
|
|
1477
1504
|
return { tags: null, context: null, shouldStrengthen: false, suggestedConnections: [], tagsToUpdate: [] };
|
|
1478
1505
|
}
|
|
1479
1506
|
}
|
|
@@ -1485,9 +1512,15 @@ async function llmConflictScan(contents) {
|
|
|
1485
1512
|
if (!raw) return [];
|
|
1486
1513
|
const cleaned = stripReasoning(raw);
|
|
1487
1514
|
const match = cleaned.match(/\[[\s\S]*\]/);
|
|
1488
|
-
if (!match)
|
|
1515
|
+
if (!match) {
|
|
1516
|
+
warn("[amem] llmConflictScan found no array in the response; this sweep reports no pairs");
|
|
1517
|
+
return [];
|
|
1518
|
+
}
|
|
1489
1519
|
const parsed = JSON.parse(match[0]);
|
|
1490
|
-
if (!Array.isArray(parsed))
|
|
1520
|
+
if (!Array.isArray(parsed)) {
|
|
1521
|
+
warn("[amem] llmConflictScan parsed a non-array; this sweep reports no pairs");
|
|
1522
|
+
return [];
|
|
1523
|
+
}
|
|
1491
1524
|
const pairs = [];
|
|
1492
1525
|
const seen = /* @__PURE__ */ new Set();
|
|
1493
1526
|
for (const item of parsed) {
|
|
@@ -1511,7 +1544,7 @@ async function llmConflictScan(contents) {
|
|
|
1511
1544
|
}
|
|
1512
1545
|
return pairs;
|
|
1513
1546
|
} catch (e) {
|
|
1514
|
-
|
|
1547
|
+
warn(`[amem] llmConflictScan failed: ${e.message}`);
|
|
1515
1548
|
return [];
|
|
1516
1549
|
}
|
|
1517
1550
|
}
|
|
@@ -1825,7 +1858,7 @@ async function addMemory(content, agentId = "main", opts) {
|
|
|
1825
1858
|
}
|
|
1826
1859
|
}
|
|
1827
1860
|
} catch (e) {
|
|
1828
|
-
|
|
1861
|
+
warn(`[amem] link/evolution phase failed: ${e.message}`);
|
|
1829
1862
|
}
|
|
1830
1863
|
console.log(`[done] Note added: ${note.id}`);
|
|
1831
1864
|
return note.id;
|
|
@@ -1950,7 +1983,12 @@ async function searchMemory(query, topK = 5, agentId = "main", opts) {
|
|
|
1950
1983
|
keywords: note.keywords,
|
|
1951
1984
|
links: note.links,
|
|
1952
1985
|
timestamp: note.timestamp,
|
|
1953
|
-
|
|
1986
|
+
// Neither map covers a note that got here on BM25 alone: it was never in
|
|
1987
|
+
// the dense results, and it was not expanded into. That is a real cosine
|
|
1988
|
+
// nobody had measured, not a zero — and reporting 0 made a lexical match
|
|
1989
|
+
// look like the least relevant row in the list. Both vectors are already
|
|
1990
|
+
// in hand, so measuring it is one dot product.
|
|
1991
|
+
similarity: embSimMap.get(id) ?? bfsSimMap.get(id) ?? cosineSimilarity(queryEmbedding, note.embedding),
|
|
1954
1992
|
rrf: rrfMap.get(id) ?? 0,
|
|
1955
1993
|
via,
|
|
1956
1994
|
topics: note.topics ?? [],
|
|
@@ -2086,8 +2124,8 @@ async function consolidateMemories(agentId, logger, storageCtx) {
|
|
|
2086
2124
|
const ctx = storageCtx ?? defaultCtx();
|
|
2087
2125
|
const log = {
|
|
2088
2126
|
info: (msg) => logger ? logger.info(msg) : console.log(msg),
|
|
2089
|
-
warn: (msg) => logger ? logger.warn(msg) :
|
|
2090
|
-
error: (msg) => logger ? logger.error(msg) :
|
|
2127
|
+
warn: (msg) => logger ? logger.warn(msg) : warn(msg),
|
|
2128
|
+
error: (msg) => logger ? logger.error(msg) : warn(msg)
|
|
2091
2129
|
};
|
|
2092
2130
|
log.info(`[Consolidation] Starting consolidation for agentId: ${agentId}`);
|
|
2093
2131
|
const rawNotes = await ctx.listNotes(agentId);
|
|
@@ -2463,7 +2501,7 @@ async function migrateCollection(opts) {
|
|
|
2463
2501
|
const refreshFields = opts.refreshFields !== false;
|
|
2464
2502
|
const dryRun = opts.dryRun !== false;
|
|
2465
2503
|
const log = opts.logger?.info ?? ((m) => console.log(m));
|
|
2466
|
-
const
|
|
2504
|
+
const warn2 = opts.logger?.warn ?? warn;
|
|
2467
2505
|
if (from === to) throw new Error(`migrate: source and target are the same collection ("${from}")`);
|
|
2468
2506
|
const model = getEmbeddingModel();
|
|
2469
2507
|
const targetDim = await getEmbeddingDim();
|
|
@@ -2531,7 +2569,7 @@ async function migrateCollection(opts) {
|
|
|
2531
2569
|
if (!note.context) note.context = built.context;
|
|
2532
2570
|
refreshed++;
|
|
2533
2571
|
} catch (e) {
|
|
2534
|
-
|
|
2572
|
+
warn2(`[migrate] re-extract failed for ${note.id.slice(0, 8)} \u2014 keeping as-is: ${e.message}`);
|
|
2535
2573
|
}
|
|
2536
2574
|
}
|
|
2537
2575
|
const point = noteToPoint({ ...note, embedding: await encode(buildEmbedText(note)) });
|
|
@@ -2544,7 +2582,7 @@ async function migrateCollection(opts) {
|
|
|
2544
2582
|
await flush();
|
|
2545
2583
|
const finalCount = await countPointsRaw(to);
|
|
2546
2584
|
if (finalCount !== notes.length) {
|
|
2547
|
-
|
|
2585
|
+
warn2(`[migrate] target holds ${finalCount} point(s) but the source had ${notes.length} \u2014 check before switching`);
|
|
2548
2586
|
}
|
|
2549
2587
|
log(`[migrate] done: ${migrated} written, ${alreadyDone.size} already present, ${refreshed} re-extracted.`);
|
|
2550
2588
|
return {
|