openclaw-amem 1.4.0 → 1.4.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 +6 -6
- package/dist/index.js +37 -12
- package/openclaw.plugin.json +2 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# openclaw-amem
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="https://raw.githubusercontent.com/
|
|
4
|
+
<img src="https://raw.githubusercontent.com/amemhq/amem/main/docs/public/logo.webp" width="120" alt="amem logo" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
7
|
[](../../LICENSE)
|
|
@@ -16,11 +16,11 @@ Runs on a small model — `gpt-4o-mini`, `haiku`, a local Ollama. No Python.
|
|
|
16
16
|
|
|
17
17
|
中文走 [jieba](https://github.com/messense/node-jieba) 分词,提示词有完整中文版(`AMEM_PROMPT_LOCALE=zh`),embedding 模型本身多语言。
|
|
18
18
|
|
|
19
|
-
Requires a local [Qdrant](https://qdrant.tech). Implements [A-MEM](https://arxiv.org/abs/2502.12110) (arXiv 2502.12110); the engine itself is [`@
|
|
19
|
+
Requires a local [Qdrant](https://qdrant.tech). Implements [A-MEM](https://arxiv.org/abs/2502.12110) (arXiv 2502.12110); the engine itself is [`@amemhq/core`](../amem-core).
|
|
20
20
|
|
|
21
21
|
> 📖 Full guides, architecture & references: **[amem.owo.lc](https://amem.owo.lc)**.
|
|
22
22
|
|
|
23
|
-
⭐ Useful? [Star it on GitHub](https://github.com/
|
|
23
|
+
⭐ Useful? [Star it on GitHub](https://github.com/amemhq/amem).
|
|
24
24
|
|
|
25
25
|
## Highlights
|
|
26
26
|
|
|
@@ -31,7 +31,7 @@ Requires a local [Qdrant](https://qdrant.tech). Implements [A-MEM](https://arxiv
|
|
|
31
31
|
- 🔐 **Per-agent isolation** — private by default; explicit `owner`/`readers`/`writers`; Mode A (shared collection) or Mode B (dedicated collection).
|
|
32
32
|
- 🀄 **Chinese-optimized** & local embeddings (Transformers.js, 384-dim) — no Python, no external embedding API.
|
|
33
33
|
|
|
34
|
-
→ Full feature list & internals: **[
|
|
34
|
+
→ Full feature list & internals: **[@amemhq/core README](../amem-core)** · **[docs](https://amem.owo.lc)**.
|
|
35
35
|
|
|
36
36
|
## Requirements
|
|
37
37
|
|
|
@@ -46,7 +46,7 @@ Requires a local [Qdrant](https://qdrant.tech). Implements [A-MEM](https://arxiv
|
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
48
|
# From ClawHub (recommended)
|
|
49
|
-
openclaw plugins install clawhub
|
|
49
|
+
openclaw plugins install clawhub:openclaw-amem
|
|
50
50
|
|
|
51
51
|
# From npm
|
|
52
52
|
openclaw plugins install openclaw-amem
|
|
@@ -141,7 +141,7 @@ pnpm --filter openclaw-amem test # vitest (needs Qdrant on :6333)
|
|
|
141
141
|
|
|
142
142
|
## Docs & References
|
|
143
143
|
|
|
144
|
-
Full guides, architecture, and academic references: **[amem.owo.lc](https://amem.owo.lc)** · engine: **[
|
|
144
|
+
Full guides, architecture, and academic references: **[amem.owo.lc](https://amem.owo.lc)** · engine: **[@amemhq/core](../amem-core)** · paper: [A-MEM (arXiv:2502.12110)](https://arxiv.org/abs/2502.12110).
|
|
145
145
|
|
|
146
146
|
## License
|
|
147
147
|
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,12 @@ var init_config = __esm({
|
|
|
56
56
|
function getEmbeddingModel() {
|
|
57
57
|
return process.env.AMEM_EMBED_MODEL?.trim() || DEFAULT_EMBEDDING_MODEL;
|
|
58
58
|
}
|
|
59
|
+
function getEmbeddingPooling() {
|
|
60
|
+
const explicit = process.env.AMEM_EMBED_POOLING?.trim().toLowerCase();
|
|
61
|
+
if (explicit === "mean" || explicit === "cls") return explicit;
|
|
62
|
+
const basename2 = getEmbeddingModel().split("/").pop()?.toLowerCase() ?? "";
|
|
63
|
+
return CLS_POOLED_MODELS.has(basename2) ? "cls" : "mean";
|
|
64
|
+
}
|
|
59
65
|
async function getExtractor() {
|
|
60
66
|
const wanted = getEmbeddingModel();
|
|
61
67
|
if (extractor && loadedModelName === wanted) return extractor;
|
|
@@ -76,21 +82,25 @@ async function getEmbeddingDim() {
|
|
|
76
82
|
cachedDim = probe.length;
|
|
77
83
|
return cachedDim;
|
|
78
84
|
}
|
|
79
|
-
function
|
|
85
|
+
function poolNormalize(output, attentionMask, mode) {
|
|
80
86
|
const seqLen = output.length;
|
|
81
87
|
const dim = output[0].length;
|
|
82
88
|
const pooled = new Array(dim).fill(0);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
maskSum
|
|
89
|
+
if (mode === "cls") {
|
|
90
|
+
for (let j = 0; j < dim; j++) pooled[j] = output[0][j];
|
|
91
|
+
} else {
|
|
92
|
+
let maskSum = 0;
|
|
93
|
+
for (let i = 0; i < seqLen; i++) {
|
|
94
|
+
const m = attentionMask[i];
|
|
95
|
+
maskSum += m;
|
|
96
|
+
for (let j = 0; j < dim; j++) {
|
|
97
|
+
pooled[j] += output[i][j] * m;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
87
100
|
for (let j = 0; j < dim; j++) {
|
|
88
|
-
pooled[j]
|
|
101
|
+
pooled[j] /= Math.max(maskSum, 1e-9);
|
|
89
102
|
}
|
|
90
103
|
}
|
|
91
|
-
for (let j = 0; j < dim; j++) {
|
|
92
|
-
pooled[j] /= Math.max(maskSum, 1e-9);
|
|
93
|
-
}
|
|
94
104
|
let norm = 0;
|
|
95
105
|
for (const v of pooled) norm += v * v;
|
|
96
106
|
norm = Math.sqrt(norm);
|
|
@@ -98,7 +108,8 @@ function meanPoolingNormalize(output, attentionMask) {
|
|
|
98
108
|
}
|
|
99
109
|
async function encode(text) {
|
|
100
110
|
const ext = await getExtractor();
|
|
101
|
-
const
|
|
111
|
+
const pooling = getEmbeddingPooling();
|
|
112
|
+
const result = await ext(text, { pooling, normalize: true });
|
|
102
113
|
if (result && result.data) {
|
|
103
114
|
return Array.from(result.data);
|
|
104
115
|
}
|
|
@@ -114,7 +125,7 @@ async function encode(text) {
|
|
|
114
125
|
}
|
|
115
126
|
raw.push(row);
|
|
116
127
|
}
|
|
117
|
-
return
|
|
128
|
+
return poolNormalize(raw, new Array(seqLen).fill(1), pooling);
|
|
118
129
|
}
|
|
119
130
|
throw new Error("Unexpected embedding output shape");
|
|
120
131
|
}
|
|
@@ -129,7 +140,7 @@ function cosineSimilarity(a, b) {
|
|
|
129
140
|
for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
|
|
130
141
|
return dot;
|
|
131
142
|
}
|
|
132
|
-
var pipeline, extractor, loadedModelName, cachedDim, DEFAULT_EMBEDDING_MODEL;
|
|
143
|
+
var pipeline, extractor, loadedModelName, cachedDim, DEFAULT_EMBEDDING_MODEL, CLS_POOLED_MODELS;
|
|
133
144
|
var init_embedding = __esm({
|
|
134
145
|
"../amem-core/src/embedding.ts"() {
|
|
135
146
|
"use strict";
|
|
@@ -138,6 +149,19 @@ var init_embedding = __esm({
|
|
|
138
149
|
loadedModelName = null;
|
|
139
150
|
cachedDim = null;
|
|
140
151
|
DEFAULT_EMBEDDING_MODEL = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
152
|
+
CLS_POOLED_MODELS = /* @__PURE__ */ new Set([
|
|
153
|
+
"bge-m3",
|
|
154
|
+
"bge-base-zh-v1.5",
|
|
155
|
+
"bge-small-zh-v1.5",
|
|
156
|
+
"bge-base-en-v1.5",
|
|
157
|
+
"bge-small-en-v1.5",
|
|
158
|
+
"bge-large-en-v1.5",
|
|
159
|
+
"gte-multilingual-base",
|
|
160
|
+
"gte-modernbert-base",
|
|
161
|
+
"gte-large-en-v1.5",
|
|
162
|
+
"snowflake-arctic-embed-m",
|
|
163
|
+
"snowflake-arctic-embed-l"
|
|
164
|
+
]);
|
|
141
165
|
}
|
|
142
166
|
});
|
|
143
167
|
|
|
@@ -2395,6 +2419,7 @@ __export(src_exports, {
|
|
|
2395
2419
|
generateReviewBatch: () => generateReviewBatch,
|
|
2396
2420
|
getEmbeddingDim: () => getEmbeddingDim,
|
|
2397
2421
|
getEmbeddingModel: () => getEmbeddingModel,
|
|
2422
|
+
getEmbeddingPooling: () => getEmbeddingPooling,
|
|
2398
2423
|
getNote: () => getNote,
|
|
2399
2424
|
invalidateNote: () => invalidateNote,
|
|
2400
2425
|
isModelLoaded: () => isModelLoaded,
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "openclaw-amem",
|
|
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
|
-
"version": "1.4.
|
|
5
|
+
"version": "1.4.2",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"openclaw": {
|
|
8
8
|
"compat": {
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"AMEM_LLM_TIMEOUT",
|
|
40
40
|
"AMEM_CRUD_UPDATE_MIN_SIM",
|
|
41
41
|
"AMEM_EMBED_MODEL",
|
|
42
|
+
"AMEM_EMBED_POOLING",
|
|
42
43
|
"AMEM_COLLECTION",
|
|
43
44
|
"AMEM_DATA_DIR",
|
|
44
45
|
"AMEM_EVO_COUNTER_PATH",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-amem",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
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",
|
|
@@ -16,19 +16,19 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@anthropic-ai/sdk": "^0.112.
|
|
19
|
+
"@anthropic-ai/sdk": "^0.112.5",
|
|
20
20
|
"@huggingface/transformers": "^4.2.0",
|
|
21
21
|
"@node-rs/jieba": "^2.0.1",
|
|
22
22
|
"@qdrant/js-client-rest": "^1.18.0",
|
|
23
|
-
"@types/uuid": "^11.0.0",
|
|
24
23
|
"openai": "^6.48.0",
|
|
25
24
|
"uuid": "^14.0.0"
|
|
26
25
|
},
|
|
27
26
|
"devDependencies": {
|
|
28
27
|
"@eslint/js": "^10.0.1",
|
|
29
28
|
"@types/node": "^26.1.1",
|
|
29
|
+
"@types/uuid": "^11.0.0",
|
|
30
30
|
"eslint": "^10.7.0",
|
|
31
|
-
"prettier": "^3.9.
|
|
31
|
+
"prettier": "^3.9.6",
|
|
32
32
|
"tsup": "^8.4.0",
|
|
33
33
|
"tsx": "^4.23.1",
|
|
34
34
|
"typescript": "^6.0.3",
|
|
@@ -41,12 +41,12 @@
|
|
|
41
41
|
},
|
|
42
42
|
"repository": {
|
|
43
43
|
"type": "git",
|
|
44
|
-
"url": "git+https://github.com/
|
|
44
|
+
"url": "git+https://github.com/amemhq/amem.git",
|
|
45
45
|
"directory": "packages/openclaw-amem"
|
|
46
46
|
},
|
|
47
47
|
"homepage": "https://amem.owo.lc",
|
|
48
48
|
"bugs": {
|
|
49
|
-
"url": "https://github.com/
|
|
49
|
+
"url": "https://github.com/amemhq/amem/issues"
|
|
50
50
|
},
|
|
51
51
|
"keywords": [
|
|
52
52
|
"openclaw",
|