openclaw-amem 2.0.1 → 2.1.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/README.md +16 -4
- package/dist/index.js +41 -7
- package/openclaw.plugin.json +3 -1
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -57,6 +57,17 @@ pnpm --filter openclaw-amem build
|
|
|
57
57
|
openclaw plugins install --link ./packages/openclaw-amem
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
+
Updating:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
openclaw plugins update openclaw-amem
|
|
64
|
+
openclaw gateway restart
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
An update rebuilds `node_modules`, and the model cache lives there by default — so
|
|
68
|
+
it re-downloads 2.27 GB unless `AMEM_MODEL_CACHE` points somewhere outside the
|
|
69
|
+
plugin directory. Set that once and updates cost nothing.
|
|
70
|
+
|
|
60
71
|
### 2. Configure `~/.openclaw/openclaw.json`
|
|
61
72
|
|
|
62
73
|
Add `openclaw-amem` to your allowed plugins and hook it into the `memory` slot:
|
|
@@ -89,10 +100,11 @@ Add `openclaw-amem` to your allowed plugins and hook it into the `memory` slot:
|
|
|
89
100
|
openclaw gateway restart
|
|
90
101
|
```
|
|
91
102
|
|
|
92
|
-
First run downloads the embedding model (`bge-m3`,
|
|
93
|
-
restarts are instant.
|
|
94
|
-
|
|
95
|
-
memories, since changing it afterwards means
|
|
103
|
+
First run downloads the embedding model (`bge-m3`, 2.27 GB) and caches it. Later
|
|
104
|
+
restarts are instant. If that is too much, the only smaller model worth setting is
|
|
105
|
+
`AMEM_EMBED_MODEL=Xenova/bge-small-zh-v1.5` — 25 MB, Chinese only, and it caps at
|
|
106
|
+
512 tokens. Set it **before** you have memories, since changing it afterwards means
|
|
107
|
+
a [migration](https://amem.owo.lc/reference/embedding-models#changing-the-model-on-a-store-you-already-have).
|
|
96
108
|
|
|
97
109
|
Upgrading from 1.x downloads nothing. Your existing memories keep the model that
|
|
98
110
|
built them, and the plugin says so at startup along with the one command that
|
package/dist/index.js
CHANGED
|
@@ -76,9 +76,28 @@ function getEmbeddingDevice() {
|
|
|
76
76
|
return process.env.AMEM_EMBED_DEVICE?.trim() || void 0;
|
|
77
77
|
}
|
|
78
78
|
function getEmbeddingDtype() {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
return process.env.AMEM_EMBED_DTYPE?.trim() || void 0;
|
|
80
|
+
}
|
|
81
|
+
function applyModelPaths(env) {
|
|
82
|
+
const cache = process.env.AMEM_MODEL_CACHE?.trim();
|
|
83
|
+
if (cache) env.cacheDir = cache;
|
|
84
|
+
const local = process.env.AMEM_MODEL_DIR?.trim();
|
|
85
|
+
if (local) {
|
|
86
|
+
env.localModelPath = local;
|
|
87
|
+
env.allowLocalModels = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function makeProgressReporter() {
|
|
91
|
+
const lastPct = /* @__PURE__ */ new Map();
|
|
92
|
+
return (e) => {
|
|
93
|
+
if (e.status !== "progress" || !e.file || typeof e.progress !== "number") return;
|
|
94
|
+
if (!e.file.endsWith(".onnx") && !e.file.endsWith(".onnx_data")) return;
|
|
95
|
+
const pct = Math.floor(e.progress / 10) * 10;
|
|
96
|
+
if (lastPct.get(e.file) === pct) return;
|
|
97
|
+
lastPct.set(e.file, pct);
|
|
98
|
+
const size = e.total ? ` of ${(e.total / 1e9).toFixed(2)} GB` : "";
|
|
99
|
+
console.log(`[amem] downloading ${e.file}: ${pct}%${size}`);
|
|
100
|
+
};
|
|
82
101
|
}
|
|
83
102
|
function extractorKey() {
|
|
84
103
|
return `${getEmbeddingModel()}|${getEmbeddingDevice() ?? ""}|${getEmbeddingDtype() ?? ""}`;
|
|
@@ -89,11 +108,13 @@ async function getExtractor() {
|
|
|
89
108
|
if (!pipeline) {
|
|
90
109
|
const mod = await import("@huggingface/transformers");
|
|
91
110
|
pipeline = mod.pipeline;
|
|
111
|
+
applyModelPaths(mod.env);
|
|
92
112
|
}
|
|
93
113
|
const device = getEmbeddingDevice();
|
|
94
114
|
const dtype = getEmbeddingDtype();
|
|
95
115
|
extractor = await pipeline("feature-extraction", getEmbeddingModel(), {
|
|
96
116
|
revision: "main",
|
|
117
|
+
progress_callback: makeProgressReporter(),
|
|
97
118
|
// Omitted entirely when unset, so an unconfigured install gets exactly the
|
|
98
119
|
// library defaults it got before these existed.
|
|
99
120
|
...device ? { device } : {},
|
|
@@ -167,7 +188,7 @@ function cosineSimilarity(a, b) {
|
|
|
167
188
|
for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
|
|
168
189
|
return dot;
|
|
169
190
|
}
|
|
170
|
-
var pipeline, extractor, loadedKey, cachedDim, DEFAULT_EMBEDDING_MODEL, LEGACY_DEFAULT_EMBEDDING_MODEL, LEGACY_DEFAULT_DIM,
|
|
191
|
+
var pipeline, extractor, loadedKey, cachedDim, DEFAULT_EMBEDDING_MODEL, LEGACY_DEFAULT_EMBEDDING_MODEL, LEGACY_DEFAULT_DIM, pinnedModel, CLS_POOLED_MODELS;
|
|
171
192
|
var init_embedding = __esm({
|
|
172
193
|
"../amem-core/src/embedding.ts"() {
|
|
173
194
|
"use strict";
|
|
@@ -178,7 +199,6 @@ var init_embedding = __esm({
|
|
|
178
199
|
DEFAULT_EMBEDDING_MODEL = "Xenova/bge-m3";
|
|
179
200
|
LEGACY_DEFAULT_EMBEDDING_MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
180
201
|
LEGACY_DEFAULT_DIM = 384;
|
|
181
|
-
DEFAULT_MODEL_DTYPE = "fp16";
|
|
182
202
|
pinnedModel = null;
|
|
183
203
|
CLS_POOLED_MODELS = /* @__PURE__ */ new Set([
|
|
184
204
|
"bge-m3",
|
|
@@ -377,6 +397,10 @@ async function scrollIdsRaw(collection, limit = 1e4) {
|
|
|
377
397
|
async function deleteCollectionRaw(collection) {
|
|
378
398
|
await qdrant("DELETE", `/collections/${collection}`);
|
|
379
399
|
}
|
|
400
|
+
async function snapshotCollectionRaw(collection) {
|
|
401
|
+
const r = await qdrant("POST", `/collections/${collection}/snapshots`);
|
|
402
|
+
return { name: r.name, size: r.size ?? 0 };
|
|
403
|
+
}
|
|
380
404
|
async function resolveAliasRaw(alias) {
|
|
381
405
|
try {
|
|
382
406
|
const res = await qdrant("GET", `/aliases`);
|
|
@@ -1918,7 +1942,12 @@ async function searchMemory(query, topK = 5, agentId = "main", opts) {
|
|
|
1918
1942
|
keywords: note.keywords,
|
|
1919
1943
|
links: note.links,
|
|
1920
1944
|
timestamp: note.timestamp,
|
|
1921
|
-
|
|
1945
|
+
// Neither map covers a note that got here on BM25 alone: it was never in
|
|
1946
|
+
// the dense results, and it was not expanded into. That is a real cosine
|
|
1947
|
+
// nobody had measured, not a zero — and reporting 0 made a lexical match
|
|
1948
|
+
// look like the least relevant row in the list. Both vectors are already
|
|
1949
|
+
// in hand, so measuring it is one dot product.
|
|
1950
|
+
similarity: embSimMap.get(id) ?? bfsSimMap.get(id) ?? cosineSimilarity(queryEmbedding, note.embedding),
|
|
1922
1951
|
rrf: rrfMap.get(id) ?? 0,
|
|
1923
1952
|
via,
|
|
1924
1953
|
topics: note.topics ?? [],
|
|
@@ -2559,6 +2588,7 @@ async function switchToMigrated(opts) {
|
|
|
2559
2588
|
const { name, to } = opts;
|
|
2560
2589
|
const log = opts.logger?.info ?? ((m) => console.log(m));
|
|
2561
2590
|
if (name === to) throw new Error(`switch: "${name}" and "${to}" are the same collection`);
|
|
2591
|
+
let snap;
|
|
2562
2592
|
const already = await resolveAliasRaw(name);
|
|
2563
2593
|
if (already === to) {
|
|
2564
2594
|
log(`[switch] "${name}" already points at "${to}" \u2014 nothing to do`);
|
|
@@ -2574,6 +2604,10 @@ async function switchToMigrated(opts) {
|
|
|
2574
2604
|
);
|
|
2575
2605
|
}
|
|
2576
2606
|
log(`[switch] verified ${targetCount} in "${to}" against ${sourceCount} in "${name}"`);
|
|
2607
|
+
if (opts.snapshot !== false) {
|
|
2608
|
+
snap = await snapshotCollectionRaw(name);
|
|
2609
|
+
log(`[switch] snapshotted "${name}" \u2192 ${snap.name} (${(snap.size / 1e6).toFixed(0)} MB)`);
|
|
2610
|
+
}
|
|
2577
2611
|
await deleteCollectionRaw(name);
|
|
2578
2612
|
log(`[switch] dropped "${name}"`);
|
|
2579
2613
|
await createAliasRaw(name, to);
|
|
@@ -2581,7 +2615,7 @@ async function switchToMigrated(opts) {
|
|
|
2581
2615
|
await setAliasRaw(name, to);
|
|
2582
2616
|
}
|
|
2583
2617
|
log(`[switch] "${name}" now resolves to "${to}"`);
|
|
2584
|
-
return { name, to, moved: targetCount };
|
|
2618
|
+
return { name, to, moved: targetCount, snapshot: snap };
|
|
2585
2619
|
}
|
|
2586
2620
|
var init_migrate = __esm({
|
|
2587
2621
|
"../amem-core/src/migrate.ts"() {
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "amem",
|
|
4
4
|
"description": "Catches memories that contradict each other. Notes rewrite themselves as new ones arrive, link into a graph, and stay separated per agent and per person. Runs on a small model. Requires a local Qdrant. 中文走 jieba 分词,提示词有中文版。",
|
|
5
5
|
"icon": "https://amem.owo.lc/logo.png",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.1.1",
|
|
7
7
|
"kind": "memory",
|
|
8
8
|
"contracts": {
|
|
9
9
|
"tools": [
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
"AMEM_EMBED_POOLING",
|
|
36
36
|
"AMEM_EMBED_DEVICE",
|
|
37
37
|
"AMEM_EMBED_DTYPE",
|
|
38
|
+
"AMEM_MODEL_CACHE",
|
|
39
|
+
"AMEM_MODEL_DIR",
|
|
38
40
|
"AMEM_COLLECTION",
|
|
39
41
|
"AMEM_DATA_DIR",
|
|
40
42
|
"AMEM_EVO_COUNTER_PATH",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-amem",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "Catches memories that contradict each other. Notes rewrite themselves as new ones arrive, link into a graph, and stay separated per agent and per person. Runs on a small model. Requires a local Qdrant. 中文走 jieba 分词,提示词有中文版。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@eslint/js": "^10.0.1",
|
|
28
28
|
"@types/node": "^26.1.1",
|
|
29
|
-
"@types/uuid": "^11.0.0",
|
|
30
29
|
"eslint": "^10.7.0",
|
|
31
30
|
"prettier": "^3.9.6",
|
|
32
31
|
"tsup": "^8.4.0",
|