memory-lancedb-pro 1.0.1 → 1.0.4
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/.github/workflows/ci.yml +24 -0
- package/CHANGELOG.md +22 -0
- package/README.md +2 -2
- package/README_CN.md +1 -1
- package/cli.ts +25 -1
- package/index.ts +35 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -1
- package/scripts/smoke-openclaw.sh +31 -0
- package/src/embedder.ts +11 -0
- package/test/cli-smoke.mjs +92 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
cli-smoke:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout
|
|
12
|
+
uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Setup Node
|
|
15
|
+
uses: actions/setup-node@v4
|
|
16
|
+
with:
|
|
17
|
+
node-version: 22
|
|
18
|
+
cache: npm
|
|
19
|
+
|
|
20
|
+
- name: Install
|
|
21
|
+
run: npm ci
|
|
22
|
+
|
|
23
|
+
- name: Test
|
|
24
|
+
run: npm test
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.4
|
|
4
|
+
|
|
5
|
+
- Fix: `embedding.dimensions` is now parsed robustly (number / numeric string / env-var string), so it properly overrides hardcoded model dims (fixes Ollama `nomic-embed-text` dimension mismatch).
|
|
6
|
+
|
|
7
|
+
## 1.0.3
|
|
8
|
+
|
|
9
|
+
- Fix: `memory-pro reembed` no longer crashes (missing `clampInt` helper).
|
|
10
|
+
|
|
11
|
+
## 1.0.2
|
|
12
|
+
|
|
13
|
+
- Fix: pass through `embedding.dimensions` to the OpenAI-compatible `/embeddings` request payload when explicitly configured.
|
|
14
|
+
- Chore: unify plugin version fields (`openclaw.plugin.json` now matches `package.json`).
|
|
15
|
+
|
|
16
|
+
## 1.0.1
|
|
17
|
+
|
|
18
|
+
- Fix: CLI command namespace updated to `memory-pro`.
|
|
19
|
+
|
|
20
|
+
## 1.0.0
|
|
21
|
+
|
|
22
|
+
- Initial npm release.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
# 🧠 memory-lancedb-pro
|
|
3
|
+
# 🧠 memory-lancedb-pro · OpenClaw Plugin
|
|
4
4
|
|
|
5
5
|
**Enhanced Long-Term Memory Plugin for [OpenClaw](https://github.com/openclaw/openclaw)**
|
|
6
6
|
|
|
@@ -326,7 +326,7 @@ This plugin works with **any OpenAI-compatible embedding API**:
|
|
|
326
326
|
| **Jina** (recommended) | `jina-embeddings-v5-text-small` | `https://api.jina.ai/v1` | 1024 |
|
|
327
327
|
| **OpenAI** | `text-embedding-3-small` | `https://api.openai.com/v1` | 1536 |
|
|
328
328
|
| **Google Gemini** | `gemini-embedding-001` | `https://generativelanguage.googleapis.com/v1beta/openai/` | 3072 |
|
|
329
|
-
| **Ollama** (local) | `nomic-embed-text` | `http://localhost:11434/v1` |
|
|
329
|
+
| **Ollama** (local) | `nomic-embed-text` | `http://localhost:11434/v1` | _provider-specific_ (set `embedding.dimensions` to match your Ollama model output) |
|
|
330
330
|
|
|
331
331
|
### Rerank Providers
|
|
332
332
|
|
package/README_CN.md
CHANGED
package/cli.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import type { Command } from "commander";
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
6
7
|
import { loadLanceDB, type MemoryEntry, type MemoryStore } from "./src/store.js";
|
|
7
8
|
import type { MemoryRetriever } from "./src/retriever.js";
|
|
8
9
|
import type { MemoryScopeManager } from "./src/scopes.js";
|
|
@@ -24,6 +25,21 @@ interface CLIContext {
|
|
|
24
25
|
// Utility Functions
|
|
25
26
|
// ============================================================================
|
|
26
27
|
|
|
28
|
+
function getPluginVersion(): string {
|
|
29
|
+
try {
|
|
30
|
+
const pkgUrl = new URL("./package.json", import.meta.url);
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgUrl, "utf8")) as { version?: string };
|
|
32
|
+
return pkg.version || "unknown";
|
|
33
|
+
} catch {
|
|
34
|
+
return "unknown";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function clampInt(value: number, min: number, max: number): number {
|
|
39
|
+
const n = Number.isFinite(value) ? value : min;
|
|
40
|
+
return Math.max(min, Math.min(max, Math.trunc(n)));
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
function formatMemory(memory: any, index?: number): string {
|
|
28
44
|
const prefix = index !== undefined ? `${index + 1}. ` : "";
|
|
29
45
|
const date = new Date(memory.timestamp || memory.createdAt || Date.now()).toISOString().split('T')[0];
|
|
@@ -42,7 +58,15 @@ function formatJson(obj: any): string {
|
|
|
42
58
|
export function registerMemoryCLI(program: Command, context: CLIContext): void {
|
|
43
59
|
const memory = program
|
|
44
60
|
.command("memory-pro")
|
|
45
|
-
.description("Enhanced memory management commands");
|
|
61
|
+
.description("Enhanced memory management commands (LanceDB Pro)");
|
|
62
|
+
|
|
63
|
+
// Version
|
|
64
|
+
memory
|
|
65
|
+
.command("version")
|
|
66
|
+
.description("Print plugin version")
|
|
67
|
+
.action(() => {
|
|
68
|
+
console.log(getPluginVersion());
|
|
69
|
+
});
|
|
46
70
|
|
|
47
71
|
// List memories
|
|
48
72
|
memory
|
package/index.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
8
|
import { join, dirname, basename } from "node:path";
|
|
9
9
|
import { readFile, readdir, writeFile, mkdir } from "node:fs/promises";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
10
11
|
|
|
11
12
|
// Import core components
|
|
12
13
|
import { MemoryStore } from "./src/store.js";
|
|
@@ -83,6 +84,20 @@ function resolveEnvVars(value: string): string {
|
|
|
83
84
|
});
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
function parsePositiveInt(value: unknown): number | undefined {
|
|
88
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
89
|
+
return Math.floor(value);
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === "string") {
|
|
92
|
+
const s = value.trim();
|
|
93
|
+
if (!s) return undefined;
|
|
94
|
+
const resolved = resolveEnvVars(s);
|
|
95
|
+
const n = Number(resolved);
|
|
96
|
+
if (Number.isFinite(n) && n > 0) return Math.floor(n);
|
|
97
|
+
}
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
86
101
|
// ============================================================================
|
|
87
102
|
// Capture & Category Detection (from old plugin)
|
|
88
103
|
// ============================================================================
|
|
@@ -252,6 +267,20 @@ async function findPreviousSessionFile(sessionsDir: string, currentSessionFile?:
|
|
|
252
267
|
} catch {}
|
|
253
268
|
}
|
|
254
269
|
|
|
270
|
+
// ============================================================================
|
|
271
|
+
// Version
|
|
272
|
+
// ============================================================================
|
|
273
|
+
|
|
274
|
+
function getPluginVersion(): string {
|
|
275
|
+
try {
|
|
276
|
+
const pkgUrl = new URL("./package.json", import.meta.url);
|
|
277
|
+
const pkg = JSON.parse(readFileSync(pkgUrl, "utf8")) as { version?: string };
|
|
278
|
+
return pkg.version || "unknown";
|
|
279
|
+
} catch {
|
|
280
|
+
return "unknown";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
255
284
|
// ============================================================================
|
|
256
285
|
// Plugin Definition
|
|
257
286
|
// ============================================================================
|
|
@@ -291,8 +320,10 @@ const memoryLanceDBProPlugin = {
|
|
|
291
320
|
const scopeManager = createScopeManager(config.scopes);
|
|
292
321
|
const migrator = createMigrator(store);
|
|
293
322
|
|
|
323
|
+
const pluginVersion = getPluginVersion();
|
|
324
|
+
|
|
294
325
|
api.logger.info(
|
|
295
|
-
`memory-lancedb-pro: plugin registered (db: ${resolvedDbPath}, model: ${config.embedding.model || "text-embedding-3-small"})`
|
|
326
|
+
`memory-lancedb-pro@${pluginVersion}: plugin registered (db: ${resolvedDbPath}, model: ${config.embedding.model || "text-embedding-3-small"})`
|
|
296
327
|
);
|
|
297
328
|
|
|
298
329
|
// ========================================================================
|
|
@@ -672,7 +703,9 @@ function parsePluginConfig(value: unknown): PluginConfig {
|
|
|
672
703
|
apiKey,
|
|
673
704
|
model: typeof embedding.model === "string" ? embedding.model : "text-embedding-3-small",
|
|
674
705
|
baseURL: typeof embedding.baseURL === "string" ? resolveEnvVars(embedding.baseURL) : undefined,
|
|
675
|
-
|
|
706
|
+
// Accept number, numeric string, or env-var string (e.g. "${EMBED_DIM}").
|
|
707
|
+
// Also accept legacy top-level `dimensions` for convenience.
|
|
708
|
+
dimensions: parsePositiveInt(embedding.dimensions ?? cfg.dimensions),
|
|
676
709
|
taskQuery: typeof embedding.taskQuery === "string" ? embedding.taskQuery : undefined,
|
|
677
710
|
taskPassage: typeof embedding.taskPassage === "string" ? embedding.taskPassage : undefined,
|
|
678
711
|
normalized: typeof embedding.normalized === "boolean" ? embedding.normalized : undefined,
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "memory-lancedb-pro",
|
|
3
3
|
"name": "Memory (LanceDB Pro)",
|
|
4
4
|
"description": "Enhanced LanceDB-backed long-term memory with hybrid retrieval, multi-scope isolation, and management CLI",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "1.0.4",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"configSchema": {
|
|
8
8
|
"type": "object",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memory-lancedb-pro",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "OpenClaw enhanced LanceDB memory plugin with hybrid retrieval (Vector + BM25), cross-encoder rerank, multi-scope isolation, and management CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -32,7 +32,12 @@
|
|
|
32
32
|
"./index.ts"
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"test": "node test/cli-smoke.mjs"
|
|
37
|
+
},
|
|
35
38
|
"devDependencies": {
|
|
39
|
+
"commander": "^14.0.0",
|
|
40
|
+
"jiti": "^2.6.0",
|
|
36
41
|
"typescript": "^5.9.3"
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Non-destructive smoke test for a real OpenClaw environment where the plugin is installed.
|
|
5
|
+
# Intended for release preflight and on-host validation.
|
|
6
|
+
|
|
7
|
+
openclaw memory-pro version
|
|
8
|
+
openclaw memory-pro stats
|
|
9
|
+
openclaw memory-pro list --limit 3
|
|
10
|
+
openclaw memory-pro search "plugin" --limit 3
|
|
11
|
+
|
|
12
|
+
# export/import (dry-run)
|
|
13
|
+
TMP_JSON="/tmp/memory-pro-export.json"
|
|
14
|
+
openclaw memory-pro export --scope global --category decision --output "$TMP_JSON"
|
|
15
|
+
openclaw memory-pro import --dry-run "$TMP_JSON"
|
|
16
|
+
|
|
17
|
+
# delete commands (dry-run/help only)
|
|
18
|
+
openclaw memory-pro delete --help >/dev/null
|
|
19
|
+
openclaw memory-pro delete-bulk --scope global --before 1900-01-01 --dry-run
|
|
20
|
+
|
|
21
|
+
# migrate (read-only)
|
|
22
|
+
openclaw memory-pro migrate check
|
|
23
|
+
|
|
24
|
+
# reembed (dry-run). Adjust source-db path if needed.
|
|
25
|
+
if [[ -d "$HOME/.openclaw/memory/lancedb-pro" ]]; then
|
|
26
|
+
openclaw memory-pro reembed --source-db "$HOME/.openclaw/memory/lancedb-pro" --limit 1 --dry-run
|
|
27
|
+
else
|
|
28
|
+
echo "NOTE: $HOME/.openclaw/memory/lancedb-pro not found; skipping reembed smoke."
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
echo "OK: openclaw smoke suite passed"
|
package/src/embedder.ts
CHANGED
|
@@ -156,6 +156,9 @@ export class Embedder {
|
|
|
156
156
|
private readonly _taskPassage?: string;
|
|
157
157
|
private readonly _normalized?: boolean;
|
|
158
158
|
|
|
159
|
+
/** Optional requested dimensions to pass through to the embedding provider (OpenAI-compatible). */
|
|
160
|
+
private readonly _requestDimensions?: number;
|
|
161
|
+
|
|
159
162
|
constructor(config: EmbeddingConfig) {
|
|
160
163
|
// Resolve environment variables in API key
|
|
161
164
|
const resolvedApiKey = resolveEnvVars(config.apiKey);
|
|
@@ -164,6 +167,7 @@ export class Embedder {
|
|
|
164
167
|
this._taskQuery = config.taskQuery;
|
|
165
168
|
this._taskPassage = config.taskPassage;
|
|
166
169
|
this._normalized = config.normalized;
|
|
170
|
+
this._requestDimensions = config.dimensions;
|
|
167
171
|
|
|
168
172
|
this.client = new OpenAI({
|
|
169
173
|
apiKey: resolvedApiKey,
|
|
@@ -237,6 +241,13 @@ export class Embedder {
|
|
|
237
241
|
if (task) payload.task = task;
|
|
238
242
|
if (this._normalized !== undefined) payload.normalized = this._normalized;
|
|
239
243
|
|
|
244
|
+
// Some OpenAI-compatible providers support requesting a specific vector size.
|
|
245
|
+
// We only pass it through when explicitly configured to avoid breaking providers
|
|
246
|
+
// that reject unknown fields.
|
|
247
|
+
if (this._requestDimensions && this._requestDimensions > 0) {
|
|
248
|
+
payload.dimensions = this._requestDimensions;
|
|
249
|
+
}
|
|
250
|
+
|
|
240
251
|
return payload;
|
|
241
252
|
}
|
|
242
253
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import jitiFactory from "jiti";
|
|
8
|
+
|
|
9
|
+
const jiti = jitiFactory(import.meta.url, { interopDefault: true });
|
|
10
|
+
|
|
11
|
+
async function createSourceDb(sourceDbPath) {
|
|
12
|
+
// Create a minimal LanceDB database with a `memories` table and 1 row.
|
|
13
|
+
const { loadLanceDB } = jiti("../src/store.ts");
|
|
14
|
+
const lancedb = await loadLanceDB();
|
|
15
|
+
const db = await lancedb.connect(sourceDbPath);
|
|
16
|
+
|
|
17
|
+
// Create table if missing.
|
|
18
|
+
// LanceDB JS API supports createTable(name, data).
|
|
19
|
+
const row = {
|
|
20
|
+
id: "test_smoke_1",
|
|
21
|
+
text: "hello from smoke test",
|
|
22
|
+
category: "other",
|
|
23
|
+
scope: "global",
|
|
24
|
+
importance: 0.7,
|
|
25
|
+
timestamp: Date.now(),
|
|
26
|
+
metadata: "{}",
|
|
27
|
+
vector: [0, 0, 0, 0],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
await db.createTable("memories", [row]);
|
|
32
|
+
} catch {
|
|
33
|
+
// If already exists, ignore.
|
|
34
|
+
const table = await db.openTable("memories");
|
|
35
|
+
await table.add([row]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function runCliSmoke() {
|
|
40
|
+
const workDir = mkdtempSync(path.join(tmpdir(), "memory-lancedb-pro-smoke-"));
|
|
41
|
+
const sourceDbPath = path.join(workDir, "source-db");
|
|
42
|
+
|
|
43
|
+
await createSourceDb(sourceDbPath);
|
|
44
|
+
|
|
45
|
+
const { createMemoryCLI } = jiti("../cli.ts");
|
|
46
|
+
|
|
47
|
+
const program = new Command();
|
|
48
|
+
program.exitOverride();
|
|
49
|
+
|
|
50
|
+
const context = {
|
|
51
|
+
// Minimal store interface for reembed dry-run.
|
|
52
|
+
store: { dbPath: path.join(workDir, "target-db") },
|
|
53
|
+
retriever: {},
|
|
54
|
+
scopeManager: {},
|
|
55
|
+
migrator: {},
|
|
56
|
+
// Presence required, but dry-run exits before embeddings.
|
|
57
|
+
embedder: {},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Register commands under `memory-pro`
|
|
61
|
+
createMemoryCLI(context)({ program });
|
|
62
|
+
|
|
63
|
+
// 1) version command should not throw
|
|
64
|
+
await program.parseAsync(["node", "openclaw", "memory-pro", "version"]);
|
|
65
|
+
|
|
66
|
+
// 2) reembed dry-run should not crash (regression test for clampInt)
|
|
67
|
+
await program.parseAsync([
|
|
68
|
+
"node",
|
|
69
|
+
"openclaw",
|
|
70
|
+
"memory-pro",
|
|
71
|
+
"reembed",
|
|
72
|
+
"--source-db",
|
|
73
|
+
sourceDbPath,
|
|
74
|
+
"--limit",
|
|
75
|
+
"1",
|
|
76
|
+
"--batch-size",
|
|
77
|
+
"999",
|
|
78
|
+
"--dry-run",
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
rmSync(workDir, { recursive: true, force: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
runCliSmoke()
|
|
85
|
+
.then(() => {
|
|
86
|
+
console.log("OK: CLI smoke test passed");
|
|
87
|
+
})
|
|
88
|
+
.catch((err) => {
|
|
89
|
+
console.error("FAIL: CLI smoke test failed");
|
|
90
|
+
console.error(err);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
});
|