@openclaw/memory-lancedb 2026.5.27 → 2026.5.28-beta.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/dist/config.js +38 -10
- package/dist/index.js +24 -9
- package/npm-shrinkwrap.json +2 -2
- package/openclaw.plugin.json +2 -1
- package/package.json +4 -4
- package/dist/test-helpers.js +0 -3198
package/dist/config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { parseFiniteNumber } from "openclaw/plugin-sdk/number-runtime";
|
|
4
5
|
//#region extensions/memory-lancedb/config.ts
|
|
5
6
|
const MEMORY_CATEGORIES = [
|
|
6
7
|
"preference",
|
|
@@ -56,11 +57,27 @@ function resolveEnvVars(value) {
|
|
|
56
57
|
return envValue;
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
|
-
function resolveEmbeddingModel(embedding) {
|
|
60
|
+
function resolveEmbeddingModel(embedding, dimensions) {
|
|
60
61
|
const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL;
|
|
61
|
-
if (
|
|
62
|
+
if (dimensions === void 0) vectorDimsForModel(model);
|
|
62
63
|
return model;
|
|
63
64
|
}
|
|
65
|
+
function resolveFiniteIntegerConfig(value) {
|
|
66
|
+
if (typeof value !== "number") return;
|
|
67
|
+
const parsed = parseFiniteNumber(value);
|
|
68
|
+
return parsed === void 0 ? void 0 : Math.floor(parsed);
|
|
69
|
+
}
|
|
70
|
+
function resolveBoundedIntegerConfig(params) {
|
|
71
|
+
const resolved = resolveFiniteIntegerConfig(params.value) ?? params.fallback;
|
|
72
|
+
if (resolved < params.min || resolved > params.max) throw new Error(`${params.label} must be between ${params.min} and ${params.max}`);
|
|
73
|
+
return resolved;
|
|
74
|
+
}
|
|
75
|
+
function resolveEmbeddingDimensions(embedding) {
|
|
76
|
+
if (embedding.dimensions === void 0) return;
|
|
77
|
+
const dimensions = typeof embedding.dimensions === "number" ? parseFiniteNumber(embedding.dimensions) : void 0;
|
|
78
|
+
if (dimensions === void 0 || !Number.isInteger(dimensions) || dimensions < 1) throw new Error("embedding.dimensions must be a positive integer");
|
|
79
|
+
return dimensions;
|
|
80
|
+
}
|
|
64
81
|
const memoryConfigSchema = {
|
|
65
82
|
parse(value) {
|
|
66
83
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("memory config required");
|
|
@@ -80,13 +97,24 @@ const memoryConfigSchema = {
|
|
|
80
97
|
if (!embedding || typeof embedding !== "object" || Array.isArray(embedding)) throw new Error("embedding config required");
|
|
81
98
|
assertAllowedKeys(embedding, [...EMBEDDING_CONFIG_KEYS], "embedding config");
|
|
82
99
|
if (Object.keys(embedding).length === 0) throw new Error("embedding config must include at least one setting");
|
|
83
|
-
const
|
|
100
|
+
const dimensions = resolveEmbeddingDimensions(embedding);
|
|
101
|
+
const model = resolveEmbeddingModel(embedding, dimensions);
|
|
84
102
|
const provider = typeof embedding.provider === "string" ? embedding.provider.trim() : "openai";
|
|
85
103
|
if (!provider) throw new Error("embedding.provider must not be empty");
|
|
86
|
-
const captureMaxChars =
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
const captureMaxChars = resolveBoundedIntegerConfig({
|
|
105
|
+
value: cfg.captureMaxChars,
|
|
106
|
+
fallback: 500,
|
|
107
|
+
min: 100,
|
|
108
|
+
max: 1e4,
|
|
109
|
+
label: "captureMaxChars"
|
|
110
|
+
});
|
|
111
|
+
const recallMaxChars = resolveBoundedIntegerConfig({
|
|
112
|
+
value: cfg.recallMaxChars,
|
|
113
|
+
fallback: DEFAULT_RECALL_MAX_CHARS,
|
|
114
|
+
min: 100,
|
|
115
|
+
max: 1e4,
|
|
116
|
+
label: "recallMaxChars"
|
|
117
|
+
});
|
|
90
118
|
let customTriggers;
|
|
91
119
|
if (cfg.customTriggers !== void 0) {
|
|
92
120
|
if (!Array.isArray(cfg.customTriggers)) throw new Error("customTriggers must be an array of strings");
|
|
@@ -118,15 +146,15 @@ const memoryConfigSchema = {
|
|
|
118
146
|
model,
|
|
119
147
|
apiKey: typeof embedding.apiKey === "string" ? resolveEnvVars(embedding.apiKey) : void 0,
|
|
120
148
|
baseUrl: typeof embedding.baseUrl === "string" ? resolveEnvVars(embedding.baseUrl) : void 0,
|
|
121
|
-
dimensions
|
|
149
|
+
dimensions
|
|
122
150
|
},
|
|
123
151
|
dreaming,
|
|
124
152
|
dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH,
|
|
125
153
|
autoCapture: cfg.autoCapture === true,
|
|
126
154
|
autoRecall: cfg.autoRecall !== false,
|
|
127
|
-
captureMaxChars
|
|
155
|
+
captureMaxChars,
|
|
128
156
|
...customTriggers ? { customTriggers } : {},
|
|
129
|
-
recallMaxChars
|
|
157
|
+
recallMaxChars,
|
|
130
158
|
...storageOptions ? { storageOptions } : {}
|
|
131
159
|
};
|
|
132
160
|
},
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { DEFAULT_RECALL_MAX_CHARS, MEMORY_CATEGORIES, memoryConfigSchema, vector
|
|
|
3
3
|
import { loadLanceDbModule } from "./lancedb-runtime.js";
|
|
4
4
|
import { Buffer } from "node:buffer";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { optionalFiniteNumberSchema, optionalPositiveIntegerSchema } from "openclaw/plugin-sdk/channel-actions";
|
|
7
|
+
import { readFiniteNumberParam, readPositiveIntegerParam } from "openclaw/plugin-sdk/param-readers";
|
|
6
8
|
import { resolveLivePluginConfigObject } from "openclaw/plugin-sdk/plugin-config-runtime";
|
|
7
9
|
import { ensureGlobalUndiciEnvProxyDispatcher } from "openclaw/plugin-sdk/runtime-env";
|
|
8
10
|
import { asOptionalRecord, normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -52,9 +54,12 @@ function extractLatestUserText(messages) {
|
|
|
52
54
|
}
|
|
53
55
|
function normalizeRecallQuery(text, maxChars = DEFAULT_RECALL_MAX_CHARS) {
|
|
54
56
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
55
|
-
const limit =
|
|
57
|
+
const limit = normalizeMaxChars(maxChars, DEFAULT_RECALL_MAX_CHARS);
|
|
56
58
|
return normalized.length > limit ? truncateUtf16Safe(normalized, limit).trimEnd() : normalized;
|
|
57
59
|
}
|
|
60
|
+
function normalizeMaxChars(value, fallback) {
|
|
61
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : fallback;
|
|
62
|
+
}
|
|
58
63
|
function messageFingerprint(message) {
|
|
59
64
|
const msgObj = asOptionalRecord(message);
|
|
60
65
|
if (!msgObj) return `${typeof message}:${String(message)}`;
|
|
@@ -334,7 +339,7 @@ function matchesCustomTrigger(text, customTriggers) {
|
|
|
334
339
|
return customTriggers.some((trigger) => lower.includes(trigger.toLocaleLowerCase()));
|
|
335
340
|
}
|
|
336
341
|
function shouldCapture(text, options) {
|
|
337
|
-
const maxChars = options?.maxChars
|
|
342
|
+
const maxChars = normalizeMaxChars(options?.maxChars, 500);
|
|
338
343
|
if (text.length > maxChars) return false;
|
|
339
344
|
if (text.includes("<relevant-memories>")) return false;
|
|
340
345
|
if (text.startsWith("<") && text.includes("</")) return false;
|
|
@@ -417,10 +422,12 @@ var memory_lancedb_default = definePluginEntry({
|
|
|
417
422
|
description: "Search through long-term memories. Use when you need context about user preferences, past decisions, or previously discussed topics.",
|
|
418
423
|
parameters: Type.Object({
|
|
419
424
|
query: Type.String({ description: "Search query" }),
|
|
420
|
-
limit:
|
|
425
|
+
limit: optionalPositiveIntegerSchema({ description: "Max results (default: 5)" })
|
|
421
426
|
}),
|
|
422
427
|
async execute(_toolCallId, params) {
|
|
423
|
-
const
|
|
428
|
+
const rawParams = params;
|
|
429
|
+
const query = rawParams.query;
|
|
430
|
+
const limit = readPositiveIntegerParam(rawParams, "limit") ?? 5;
|
|
424
431
|
const currentCfg = resolveCurrentHookConfig();
|
|
425
432
|
const vector = await embeddings.embed(normalizeRecallQuery(query, currentCfg.recallMaxChars));
|
|
426
433
|
const results = await db.search(vector, limit, .1);
|
|
@@ -457,14 +464,22 @@ var memory_lancedb_default = definePluginEntry({
|
|
|
457
464
|
description: "Save important information in long-term memory. Use for preferences, facts, decisions.",
|
|
458
465
|
parameters: Type.Object({
|
|
459
466
|
text: Type.String({ description: "Information to remember" }),
|
|
460
|
-
importance:
|
|
467
|
+
importance: optionalFiniteNumberSchema({
|
|
468
|
+
description: "Importance 0-1 (default: 0.7)",
|
|
469
|
+
minimum: 0,
|
|
470
|
+
maximum: 1
|
|
471
|
+
}),
|
|
461
472
|
category: Type.Optional(Type.Unsafe({
|
|
462
473
|
type: "string",
|
|
463
474
|
enum: [...MEMORY_CATEGORIES]
|
|
464
475
|
}))
|
|
465
476
|
}),
|
|
466
477
|
async execute(_toolCallId, params) {
|
|
467
|
-
const { text,
|
|
478
|
+
const { text, category = "other" } = params;
|
|
479
|
+
const importance = readFiniteNumberParam(params, "importance", {
|
|
480
|
+
min: 0,
|
|
481
|
+
max: 1
|
|
482
|
+
}) ?? .7;
|
|
468
483
|
if (looksLikePromptInjection(text)) return {
|
|
469
484
|
content: [{
|
|
470
485
|
type: "text",
|
|
@@ -589,7 +604,8 @@ var memory_lancedb_default = definePluginEntry({
|
|
|
589
604
|
});
|
|
590
605
|
memory.command("search").description("Search memories").argument("<query>", "Search query").option("--limit <n>", "Max results", "5").action(async (query, opts) => {
|
|
591
606
|
const vector = await embeddings.embed(normalizeRecallQuery(query, cfg.recallMaxChars));
|
|
592
|
-
const
|
|
607
|
+
const limit = parsePositiveIntegerOption(opts.limit, "--limit");
|
|
608
|
+
const output = (await db.search(vector, limit, .3)).map((r) => ({
|
|
593
609
|
id: r.entry.id,
|
|
594
610
|
text: r.entry.text,
|
|
595
611
|
category: r.entry.category,
|
|
@@ -626,8 +642,7 @@ var memory_lancedb_default = definePluginEntry({
|
|
|
626
642
|
if (!/^[a-zA-Z0-9_\-\s='"><!.,()%*]+$/.test(filterCondition)) throw new Error("Filter condition contains invalid characters");
|
|
627
643
|
query = query.where(filterCondition);
|
|
628
644
|
}
|
|
629
|
-
const limit =
|
|
630
|
-
if (Number.isNaN(limit) || limit <= 0) throw new Error("Invalid limit: must be a positive integer");
|
|
645
|
+
const limit = parsePositiveIntegerOption(opts.limit, "--limit") ?? 10;
|
|
631
646
|
if (!opts.orderBy) query = query.limit(limit);
|
|
632
647
|
let rows = await query.toArray();
|
|
633
648
|
if (opts.orderBy) {
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/memory-lancedb",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.28-beta.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@openclaw/memory-lancedb",
|
|
9
|
-
"version": "2026.5.
|
|
9
|
+
"version": "2026.5.28-beta.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@lancedb/lancedb": "0.29.0",
|
|
12
12
|
"apache-arrow": "18.1.0",
|
package/openclaw.plugin.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/memory-lancedb",
|
|
3
|
-
"version": "2026.5.
|
|
3
|
+
"version": "2026.5.28-beta.2",
|
|
4
4
|
"description": "OpenClaw LanceDB-backed long-term memory plugin with auto-recall, auto-capture, and vector search.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"minHostVersion": ">=2026.4.10"
|
|
27
27
|
},
|
|
28
28
|
"compat": {
|
|
29
|
-
"pluginApi": ">=2026.5.
|
|
29
|
+
"pluginApi": ">=2026.5.28-beta.2"
|
|
30
30
|
},
|
|
31
31
|
"build": {
|
|
32
|
-
"openclawVersion": "2026.5.
|
|
32
|
+
"openclawVersion": "2026.5.28-beta.2"
|
|
33
33
|
},
|
|
34
34
|
"release": {
|
|
35
35
|
"bundleRuntimeDependencies": false,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"npm-shrinkwrap.json"
|
|
47
47
|
],
|
|
48
48
|
"peerDependencies": {
|
|
49
|
-
"openclaw": ">=2026.5.
|
|
49
|
+
"openclaw": ">=2026.5.28-beta.2"
|
|
50
50
|
},
|
|
51
51
|
"peerDependenciesMeta": {
|
|
52
52
|
"openclaw": {
|