kongbrain 0.4.4 → 0.5.0
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/CHANGELOG.md +47 -0
- package/README.github.md +53 -3
- package/README.md +29 -3
- package/README.npm.md +29 -3
- package/SKILL.md +1 -1
- package/bin/kongbrain-reembed.ts +143 -0
- package/openclaw.plugin.json +37 -7
- package/package.json +4 -1
- package/src/causal.ts +4 -1
- package/src/cognitive-bootstrap.ts +1 -0
- package/src/concept-extract.ts +4 -2
- package/src/config.ts +56 -10
- package/src/embeddings-openai.ts +232 -0
- package/src/embeddings.ts +48 -6
- package/src/identity.ts +2 -0
- package/src/index.ts +54 -5
- package/src/memory-daemon.ts +1 -1
- package/src/migrate-reembed.ts +305 -0
- package/src/reflection.ts +10 -4
- package/src/schema.surql +29 -0
- package/src/skills.ts +14 -5
- package/src/supersedes.ts +2 -1
- package/src/surreal.ts +77 -19
- package/src/workspace-migrate.ts +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to KongBrain are documented here.
|
|
4
4
|
|
|
5
|
+
## [0.5.0] - 2026-04-25
|
|
6
|
+
|
|
7
|
+
Configurable embedding providers. Closes #1.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
- **Configurable embedding provider**: New `embedding.provider` config field. Options: `local` (BGE-M3 via node-llama-cpp, default and unchanged) or `openai-compat` (any OpenAI-compatible `/v1/embeddings` endpoint — OpenAI, Azure OpenAI, Together, Anyscale, vLLM, LM Studio, Ollama, DeepInfra, Fireworks).
|
|
11
|
+
- **OpenAI-compatible provider**: `fetch`-based, no SDK dependency. Batches inputs at 96/request, retries 429 + 5xx with exponential backoff and `Retry-After` honoring, hard-fails on 401/403/404 and `insufficient_quota` with clear error messages, verifies returned dimensionality matches config.
|
|
12
|
+
- **Per-row provider tagging**: Every vector-bearing table (`turn`, `concept`, `memory`, `artifact`, `identity_chunk`, `skill`, `reflection`, `monologue`) gets an `embedding_provider` column. Searches filter by the active provider so vectors from different models (different vector spaces) never mix in HNSW results.
|
|
13
|
+
- **Re-embed migration tool**: `npx kongbrain-reembed --from <provider-id> [--dry-run] [--tables …] [--batch …]`. Resumable on interruption (the WHERE filter naturally excludes processed rows). Reports per-table progress and estimated cost.
|
|
14
|
+
- **Startup mismatch warning**: Logs a clear notice (with row counts and migration command) when the configured provider does not match what is in the database.
|
|
15
|
+
- **Provider env overrides**: `KONGBRAIN_EMBED_PROVIDER` flips provider without editing config; `OPENAI_BASE_URL` overrides endpoint (matches the official OpenAI SDK convention); `embedding.openaiCompat.apiKeyEnv` names the env var holding the secret so keys never appear in config files.
|
|
16
|
+
- **Plugin manifest**: `openclaw.plugin.json` extended with `provider` / `dimensions` / `openaiCompat` schema and uiHints with inline help text.
|
|
17
|
+
|
|
18
|
+
### Infrastructure
|
|
19
|
+
- **Idempotent schema migration + backfill**: All schema additions use `IF NOT EXISTS`. On first startup with this version, existing rows are tagged with `local-bge-m3`. Runs cleanly on every subsequent startup as a no-op.
|
|
20
|
+
|
|
21
|
+
### Tests
|
|
22
|
+
- 439 → 469 tests. New: 17 OpenAI provider unit tests (success, batching, dim mismatch, retry, hard-fail), 4 config tests (provider, env overrides, fallback), 8 migration tests (full migrate, table filter, dry-run, blank-text, multi-batch, refusal, no-op, format), 4 backfill upgrade-path integration tests, plus 6 gated live tests (real OpenAI / real DB / real reembed) that skip in CI.
|
|
23
|
+
|
|
24
|
+
### Documentation
|
|
25
|
+
- README and README.npm updated with embedding-provider comparison table, switching instructions for OpenAI / Ollama / vLLM, and migration command.
|
|
26
|
+
|
|
27
|
+
### Upgrade notes
|
|
28
|
+
- **No action required for existing local BGE-M3 deployments.** The schema migration adds the new column and tags all existing rows as `local-bge-m3`. Search continues to work identically.
|
|
29
|
+
- **To switch providers**: set `embedding.provider: "openai-compat"` and `OPENAI_API_KEY`. On restart you will see a warning about rows in the old vector space. Run `npx kongbrain-reembed --from local-bge-m3 --dry-run` to estimate cost (~$0.04 per ~3,400 turns on text-embedding-3-small), then drop `--dry-run` to migrate. Resumable if interrupted.
|
|
30
|
+
|
|
31
|
+
## [0.4.4] - 2026-04-04
|
|
32
|
+
|
|
33
|
+
### Performance
|
|
34
|
+
- **WMR rebalance**: Cosine-dominant scoring weights, dampen access count feedback loop that was reinforcing already-popular memories.
|
|
35
|
+
- **Tag-boosted concept retrieval**: Surface topically relevant concepts even when embedding similarity alone misses them.
|
|
36
|
+
|
|
37
|
+
### Bug Fixes
|
|
38
|
+
- **Empty LLM extraction responses**: `outputFormat` injected via pi-ai's `onPayload` hook caused Anthropic API to return 0 content blocks. Removed structured output from pi-ai code path; daemon's JSON parsing cascade handles free-text reliably.
|
|
39
|
+
- **`SELECT WHERE id IN $ids` binding**: Same silent no-op as `bumpAccessCounts` — SurrealDB string arrays don't resolve to record references. Fixed in `getSessionRetrievedMemories` and ACAN `fetchTrainingData`.
|
|
40
|
+
- **ACAN NaN/Infinity validation**: `loadWeights` now rejects corrupted weights (null, NaN, Infinity in bias, W_final, or spot-checked W_q/W_k rows).
|
|
41
|
+
- **Lazy daemon start**: If gateway restarts mid-session, `afterTurn` now starts the daemon on demand instead of silently skipping extraction.
|
|
42
|
+
- **`getOrCreateSession` in afterTurn**: Resumed sessions after gateway restart no longer return null.
|
|
43
|
+
- **Model object unwrapping**: `defaults.model` can be `{primary: "provider/model"}` — unwrap and split provider/model format.
|
|
44
|
+
|
|
45
|
+
### Infrastructure
|
|
46
|
+
- **CI pipeline**: GitHub Actions with SurrealDB service container, Node 22, 439 tests (unit + integration).
|
|
47
|
+
- **PR checks**: Type checking + unit tests on all pull requests.
|
|
48
|
+
|
|
49
|
+
### Tests
|
|
50
|
+
- 415 → 439 tests. New: ACAN NaN/Infinity validation (7), score stability/performance (3), `SELECT IN` integration test, additional integration coverage.
|
|
51
|
+
|
|
5
52
|
## [0.4.2] - 2026-04-03
|
|
6
53
|
|
|
7
54
|
### Performance
|
package/README.github.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
[](https://nodejs.org)
|
|
12
12
|
[](https://surrealdb.com)
|
|
13
13
|
[](https://github.com/openclaw/openclaw)
|
|
14
|
-
[](https://vitest.dev)
|
|
15
15
|
|
|
16
16
|
**A graph-backed cognitive engine for [OpenClaw](https://github.com/openclaw/openclaw).**
|
|
17
17
|
|
|
@@ -121,7 +121,9 @@ openclaw tui
|
|
|
121
121
|
|
|
122
122
|
That's it. KongBrain uses whatever LLM provider and model you already have configured in OpenClaw (Anthropic, OpenAI, Google, Ollama, whatever). No separate API keys needed for the brain itself.
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
By default KongBrain runs the BGE-M3 embedding model locally via `node-llama-cpp` — the GGUF (~420MB) auto-downloads from [Hugging Face](https://huggingface.co/BAAI/bge-m3) on first startup. For high-traffic deployments the local model can become a bottleneck on serial embedding calls; in that case switch to any OpenAI-compatible API (real OpenAI, Azure OpenAI, Together, vLLM, LM Studio, Ollama) by changing one config field. See [Embedding Providers](#embedding-providers) below.
|
|
125
|
+
|
|
126
|
+
All database tables and indexes are created automatically on first run. No manual setup required.
|
|
125
127
|
|
|
126
128
|
<details>
|
|
127
129
|
<summary><strong>Configuration Options</strong></summary>
|
|
@@ -135,8 +137,12 @@ All options have sensible defaults. Override via plugin config or environment va
|
|
|
135
137
|
| `surreal.pass` | `SURREAL_PASS` | (required) |
|
|
136
138
|
| `surreal.ns` | `SURREAL_NS` | `kong` |
|
|
137
139
|
| `surreal.db` | `SURREAL_DB` | `memory` |
|
|
138
|
-
| `embedding.
|
|
140
|
+
| `embedding.provider` | `KONGBRAIN_EMBED_PROVIDER` | `local` (or `openai-compat`) |
|
|
139
141
|
| `embedding.dimensions` | - | `1024` |
|
|
142
|
+
| `embedding.modelPath` | `EMBED_MODEL_PATH` | Auto-downloaded BGE-M3 Q4_K_M |
|
|
143
|
+
| `embedding.openaiCompat.model` | - | `text-embedding-3-small` |
|
|
144
|
+
| `embedding.openaiCompat.baseURL` | `OPENAI_BASE_URL` | `https://api.openai.com/v1` |
|
|
145
|
+
| `embedding.openaiCompat.apiKeyEnv` | - | `OPENAI_API_KEY` |
|
|
140
146
|
|
|
141
147
|
Full config example:
|
|
142
148
|
|
|
@@ -166,6 +172,50 @@ Full config example:
|
|
|
166
172
|
|
|
167
173
|
</details>
|
|
168
174
|
|
|
175
|
+
### Embedding Providers
|
|
176
|
+
|
|
177
|
+
| | `local` (default) | `openai-compat` |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| **Inference** | BGE-M3 GGUF via node-llama-cpp, in-process | HTTP POST to `/v1/embeddings` |
|
|
180
|
+
| **Cost** | Zero (~420MB model on disk, CPU inference) | Per-token API charges |
|
|
181
|
+
| **Throughput** | Serial; bottlenecks under high turn volume | High parallelism, batched at 96 inputs/request |
|
|
182
|
+
| **Network** | None required | Required |
|
|
183
|
+
| **Compatible servers** | n/a | OpenAI, Azure OpenAI, Together, Anyscale, vLLM, LM Studio, Ollama, DeepInfra, Fireworks |
|
|
184
|
+
|
|
185
|
+
Switching providers is safe by design. Every embedding written to the database is tagged with the provider that produced it (`embedding_provider` column). At search time, KongBrain only compares vectors that were produced by the active provider — vectors from a different provider live in a different vector space and would corrupt similarity scores if mixed.
|
|
186
|
+
|
|
187
|
+
When you switch from `local` to `openai-compat`, pre-existing rows stay in the database but become invisible to recall until re-embedded. Run the included migration tool to bring them into the new vector space:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Estimate cost first (no writes)
|
|
191
|
+
npx kongbrain-reembed --from local-bge-m3 --dry-run
|
|
192
|
+
|
|
193
|
+
# Then run for real (resumable on interruption)
|
|
194
|
+
npx kongbrain-reembed --from local-bge-m3
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Cost is small: text-embedding-3-small is $0.02 per 1M tokens; a typical 3,400-turn database costs ~$0.04 to re-embed.
|
|
198
|
+
|
|
199
|
+
#### Switching to OpenAI
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
export KONGBRAIN_EMBED_PROVIDER=openai-compat
|
|
203
|
+
export OPENAI_API_KEY=sk-...
|
|
204
|
+
# Restart the plugin. Migration tool handles existing data.
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
#### Switching to a local OpenAI-compatible server (Ollama, vLLM, LM Studio)
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# Ollama example — runs entirely locally, no API key needed
|
|
211
|
+
ollama pull nomic-embed-text
|
|
212
|
+
export OPENAI_BASE_URL=http://localhost:11434/v1
|
|
213
|
+
export OPENAI_API_KEY=ollama # any non-empty string; Ollama doesn't validate
|
|
214
|
+
export KONGBRAIN_EMBED_PROVIDER=openai-compat
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Set `embedding.dimensions` to match the server's native output dim. KongBrain verifies the returned vector size on every response and fails loudly if the server ignores the `dimensions` parameter — preventing silently corrupted indexes.
|
|
218
|
+
|
|
169
219
|
---
|
|
170
220
|
|
|
171
221
|
## Architecture
|
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://nodejs.org)
|
|
8
8
|
[](https://surrealdb.com)
|
|
9
9
|
[](https://github.com/openclaw/openclaw)
|
|
10
|
-
[](https://vitest.dev)
|
|
11
11
|
|
|
12
12
|
**A graph-backed cognitive engine for [OpenClaw](https://github.com/openclaw/openclaw).**
|
|
13
13
|
|
|
@@ -111,7 +111,9 @@ openclaw tui
|
|
|
111
111
|
|
|
112
112
|
That's it. KongBrain uses whatever LLM provider and model you already have configured in OpenClaw (Anthropic, OpenAI, Google, Ollama, whatever). No separate API keys needed for the brain itself.
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
By default KongBrain runs the BGE-M3 embedding model locally via `node-llama-cpp` — the GGUF (~420MB) auto-downloads from [Hugging Face](https://huggingface.co/BAAI/bge-m3) on first startup. For high-traffic deployments the local model can become a bottleneck on serial embedding calls; in that case switch to any OpenAI-compatible API (real OpenAI, Azure OpenAI, Together, vLLM, LM Studio, Ollama) by changing one config field.
|
|
115
|
+
|
|
116
|
+
All database tables and indexes are created automatically on first run. No manual setup required.
|
|
115
117
|
|
|
116
118
|
<details>
|
|
117
119
|
<summary><strong>Configuration Options</strong></summary>
|
|
@@ -125,8 +127,12 @@ All options have sensible defaults. Override via plugin config or environment va
|
|
|
125
127
|
| `surreal.pass` | `SURREAL_PASS` | (required) |
|
|
126
128
|
| `surreal.ns` | `SURREAL_NS` | `kong` |
|
|
127
129
|
| `surreal.db` | `SURREAL_DB` | `memory` |
|
|
128
|
-
| `embedding.
|
|
130
|
+
| `embedding.provider` | `KONGBRAIN_EMBED_PROVIDER` | `local` (or `openai-compat`) |
|
|
129
131
|
| `embedding.dimensions` | - | `1024` |
|
|
132
|
+
| `embedding.modelPath` | `EMBED_MODEL_PATH` | Auto-downloaded BGE-M3 Q4_K_M |
|
|
133
|
+
| `embedding.openaiCompat.model` | - | `text-embedding-3-small` |
|
|
134
|
+
| `embedding.openaiCompat.baseURL` | `OPENAI_BASE_URL` | `https://api.openai.com/v1` |
|
|
135
|
+
| `embedding.openaiCompat.apiKeyEnv` | - | `OPENAI_API_KEY` |
|
|
130
136
|
|
|
131
137
|
Full config example:
|
|
132
138
|
|
|
@@ -156,6 +162,26 @@ Full config example:
|
|
|
156
162
|
|
|
157
163
|
</details>
|
|
158
164
|
|
|
165
|
+
### Embedding Providers
|
|
166
|
+
|
|
167
|
+
| | `local` (default) | `openai-compat` |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| **Inference** | BGE-M3 GGUF via node-llama-cpp, in-process | HTTP POST to `/v1/embeddings` |
|
|
170
|
+
| **Cost** | Zero | Per-token API charges |
|
|
171
|
+
| **Throughput** | Serial; bottlenecks under high turn volume | High parallelism, batched at 96 inputs/request |
|
|
172
|
+
| **Compatible servers** | n/a | OpenAI, Azure OpenAI, Together, Anyscale, vLLM, LM Studio, Ollama, DeepInfra, Fireworks |
|
|
173
|
+
|
|
174
|
+
Every embedding is tagged with the provider that produced it. At search time, KongBrain only compares vectors from the active provider — vectors from a different provider live in a different vector space.
|
|
175
|
+
|
|
176
|
+
When you switch providers, run the included migration tool to re-embed pre-existing rows:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx kongbrain-reembed --from local-bge-m3 --dry-run # estimate cost
|
|
180
|
+
npx kongbrain-reembed --from local-bge-m3 # run for real (resumable)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
text-embedding-3-small costs ~$0.04 to re-embed a typical 3,400-turn database.
|
|
184
|
+
|
|
159
185
|
---
|
|
160
186
|
|
|
161
187
|
## Architecture
|
package/README.npm.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
[](https://nodejs.org)
|
|
8
8
|
[](https://surrealdb.com)
|
|
9
9
|
[](https://github.com/openclaw/openclaw)
|
|
10
|
-
[](https://vitest.dev)
|
|
11
11
|
|
|
12
12
|
**A graph-backed cognitive engine for [OpenClaw](https://github.com/openclaw/openclaw).**
|
|
13
13
|
|
|
@@ -111,7 +111,9 @@ openclaw tui
|
|
|
111
111
|
|
|
112
112
|
That's it. KongBrain uses whatever LLM provider and model you already have configured in OpenClaw (Anthropic, OpenAI, Google, Ollama, whatever). No separate API keys needed for the brain itself.
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
By default KongBrain runs the BGE-M3 embedding model locally via `node-llama-cpp` — the GGUF (~420MB) auto-downloads from [Hugging Face](https://huggingface.co/BAAI/bge-m3) on first startup. For high-traffic deployments the local model can become a bottleneck on serial embedding calls; in that case switch to any OpenAI-compatible API (real OpenAI, Azure OpenAI, Together, vLLM, LM Studio, Ollama) by changing one config field.
|
|
115
|
+
|
|
116
|
+
All database tables and indexes are created automatically on first run. No manual setup required.
|
|
115
117
|
|
|
116
118
|
<details>
|
|
117
119
|
<summary><strong>Configuration Options</strong></summary>
|
|
@@ -125,8 +127,12 @@ All options have sensible defaults. Override via plugin config or environment va
|
|
|
125
127
|
| `surreal.pass` | `SURREAL_PASS` | (required) |
|
|
126
128
|
| `surreal.ns` | `SURREAL_NS` | `kong` |
|
|
127
129
|
| `surreal.db` | `SURREAL_DB` | `memory` |
|
|
128
|
-
| `embedding.
|
|
130
|
+
| `embedding.provider` | `KONGBRAIN_EMBED_PROVIDER` | `local` (or `openai-compat`) |
|
|
129
131
|
| `embedding.dimensions` | - | `1024` |
|
|
132
|
+
| `embedding.modelPath` | `EMBED_MODEL_PATH` | Auto-downloaded BGE-M3 Q4_K_M |
|
|
133
|
+
| `embedding.openaiCompat.model` | - | `text-embedding-3-small` |
|
|
134
|
+
| `embedding.openaiCompat.baseURL` | `OPENAI_BASE_URL` | `https://api.openai.com/v1` |
|
|
135
|
+
| `embedding.openaiCompat.apiKeyEnv` | - | `OPENAI_API_KEY` |
|
|
130
136
|
|
|
131
137
|
Full config example:
|
|
132
138
|
|
|
@@ -156,6 +162,26 @@ Full config example:
|
|
|
156
162
|
|
|
157
163
|
</details>
|
|
158
164
|
|
|
165
|
+
### Embedding Providers
|
|
166
|
+
|
|
167
|
+
| | `local` (default) | `openai-compat` |
|
|
168
|
+
|---|---|---|
|
|
169
|
+
| **Inference** | BGE-M3 GGUF via node-llama-cpp, in-process | HTTP POST to `/v1/embeddings` |
|
|
170
|
+
| **Cost** | Zero | Per-token API charges |
|
|
171
|
+
| **Throughput** | Serial; bottlenecks under high turn volume | High parallelism, batched at 96 inputs/request |
|
|
172
|
+
| **Compatible servers** | n/a | OpenAI, Azure OpenAI, Together, Anyscale, vLLM, LM Studio, Ollama, DeepInfra, Fireworks |
|
|
173
|
+
|
|
174
|
+
Every embedding is tagged with the provider that produced it. At search time, KongBrain only compares vectors from the active provider — vectors from a different provider live in a different vector space.
|
|
175
|
+
|
|
176
|
+
When you switch providers, run the included migration tool to re-embed pre-existing rows:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx kongbrain-reembed --from local-bge-m3 --dry-run # estimate cost
|
|
180
|
+
npx kongbrain-reembed --from local-bge-m3 # run for real (resumable)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
text-embedding-3-small costs ~$0.04 to re-embed a typical 3,400-turn database.
|
|
184
|
+
|
|
159
185
|
---
|
|
160
186
|
|
|
161
187
|
## Architecture
|
package/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: kongbrain
|
|
3
3
|
description: Graph-backed persistent memory engine for OpenClaw. Replaces the default context window with SurrealDB + vector embeddings that learn across sessions.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
homepage: https://github.com/42U/kongbrain
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* kongbrain-reembed — re-embed migration CLI.
|
|
4
|
+
*
|
|
5
|
+
* Reads connection settings from the same env vars the plugin uses, plus
|
|
6
|
+
* KONGBRAIN_EMBED_PROVIDER / OPENAI_BASE_URL / etc. for the target
|
|
7
|
+
* provider. Migrates rows tagged with --from to the active provider's
|
|
8
|
+
* vector space.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* kongbrain-reembed --from local-bge-m3 [--dry-run] [--tables turn,memory] [--batch 256]
|
|
12
|
+
*
|
|
13
|
+
* Resumable: each batch flips embedding_provider so processed rows leave
|
|
14
|
+
* the FROM filter. Restarting after a crash continues from where it
|
|
15
|
+
* stopped.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { parsePluginConfig } from "../src/config.js";
|
|
19
|
+
import { createEmbeddingService } from "../src/embeddings.js";
|
|
20
|
+
import { SurrealStore } from "../src/surreal.js";
|
|
21
|
+
import {
|
|
22
|
+
formatResult,
|
|
23
|
+
reembedAll,
|
|
24
|
+
VECTOR_TABLES,
|
|
25
|
+
type VectorTable,
|
|
26
|
+
} from "../src/migrate-reembed.js";
|
|
27
|
+
|
|
28
|
+
interface CliFlags {
|
|
29
|
+
from: string | null;
|
|
30
|
+
dryRun: boolean;
|
|
31
|
+
tables: VectorTable[] | null;
|
|
32
|
+
batch: number;
|
|
33
|
+
help: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseArgs(argv: string[]): CliFlags {
|
|
37
|
+
const flags: CliFlags = {
|
|
38
|
+
from: null,
|
|
39
|
+
dryRun: false,
|
|
40
|
+
tables: null,
|
|
41
|
+
batch: 256,
|
|
42
|
+
help: false,
|
|
43
|
+
};
|
|
44
|
+
for (let i = 0; i < argv.length; i++) {
|
|
45
|
+
const a = argv[i];
|
|
46
|
+
if (a === "--help" || a === "-h") flags.help = true;
|
|
47
|
+
else if (a === "--dry-run") flags.dryRun = true;
|
|
48
|
+
else if (a === "--from") flags.from = argv[++i] ?? null;
|
|
49
|
+
else if (a === "--batch") flags.batch = Number(argv[++i] ?? "256");
|
|
50
|
+
else if (a === "--tables") {
|
|
51
|
+
const list = (argv[++i] ?? "").split(",").map(s => s.trim()).filter(Boolean);
|
|
52
|
+
const valid: VectorTable[] = [];
|
|
53
|
+
for (const t of list) {
|
|
54
|
+
if ((VECTOR_TABLES as readonly string[]).includes(t)) valid.push(t as VectorTable);
|
|
55
|
+
else throw new Error(`Unknown table: ${t}. Valid: ${VECTOR_TABLES.join(", ")}`);
|
|
56
|
+
}
|
|
57
|
+
flags.tables = valid;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return flags;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const HELP = `kongbrain-reembed — migrate embeddings between providers
|
|
64
|
+
|
|
65
|
+
Required:
|
|
66
|
+
--from <provider-id> Provider tag to migrate FROM (e.g. local-bge-m3)
|
|
67
|
+
|
|
68
|
+
Optional:
|
|
69
|
+
--dry-run Count rows + estimate cost without writing
|
|
70
|
+
--tables turn,memory Only migrate these tables (default: all 8)
|
|
71
|
+
--batch <n> Rows per batch (default: 256)
|
|
72
|
+
--help Show this message
|
|
73
|
+
|
|
74
|
+
The TARGET provider is whatever the active EmbeddingService produces, set
|
|
75
|
+
via plugin config + env vars (KONGBRAIN_EMBED_PROVIDER, OPENAI_BASE_URL,
|
|
76
|
+
the API key env var named in embedding.openaiCompat.apiKeyEnv).
|
|
77
|
+
|
|
78
|
+
Resumability: each batch flips the embedding_provider tag, so re-running
|
|
79
|
+
after an interruption picks up from where the last successful batch left
|
|
80
|
+
off — no checkpoint file needed.
|
|
81
|
+
|
|
82
|
+
Example: migrate from local BGE-M3 to OpenAI text-embedding-3-small at 1024d:
|
|
83
|
+
export KONGBRAIN_EMBED_PROVIDER=openai-compat
|
|
84
|
+
export OPENAI_API_KEY=sk-...
|
|
85
|
+
npx kongbrain-reembed --from local-bge-m3 --dry-run # check the size
|
|
86
|
+
npx kongbrain-reembed --from local-bge-m3 # run for real
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
async function main(): Promise<number> {
|
|
90
|
+
const flags = parseArgs(process.argv.slice(2));
|
|
91
|
+
if (flags.help) {
|
|
92
|
+
console.log(HELP);
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
if (!flags.from) {
|
|
96
|
+
console.error("Missing required --from <provider-id>. See --help.");
|
|
97
|
+
return 2;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const config = parsePluginConfig();
|
|
101
|
+
const store = new SurrealStore(config.surreal);
|
|
102
|
+
const embeddings = createEmbeddingService(config.embedding);
|
|
103
|
+
|
|
104
|
+
console.log(`Source provider: ${flags.from}`);
|
|
105
|
+
console.log(`Target provider: ${embeddings.providerId}`);
|
|
106
|
+
console.log(`SurrealDB: ${config.surreal.url}`);
|
|
107
|
+
console.log(`Mode: ${flags.dryRun ? "DRY RUN" : "WRITE"}`);
|
|
108
|
+
console.log("");
|
|
109
|
+
|
|
110
|
+
await store.initialize();
|
|
111
|
+
store.setActiveProvider(embeddings.providerId);
|
|
112
|
+
if (!flags.dryRun) {
|
|
113
|
+
await embeddings.initialize();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const result = await reembedAll(store, embeddings, {
|
|
118
|
+
fromProvider: flags.from,
|
|
119
|
+
tables: flags.tables ?? undefined,
|
|
120
|
+
batchSize: flags.batch,
|
|
121
|
+
dryRun: flags.dryRun,
|
|
122
|
+
onProgress: ev => {
|
|
123
|
+
process.stdout.write(
|
|
124
|
+
`[${ev.table}] ${ev.tableProcessed}/${ev.tableTotal}\r`,
|
|
125
|
+
);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
process.stdout.write("\n");
|
|
129
|
+
console.log(formatResult(result, embeddings.providerId));
|
|
130
|
+
return 0;
|
|
131
|
+
} finally {
|
|
132
|
+
await embeddings.dispose().catch(() => {});
|
|
133
|
+
await store.close().catch(() => {});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
main().then(
|
|
138
|
+
code => process.exit(code),
|
|
139
|
+
err => {
|
|
140
|
+
console.error("kongbrain-reembed failed:", err?.message ?? err);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
},
|
|
143
|
+
);
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "kongbrain",
|
|
3
3
|
"name": "KongBrain",
|
|
4
|
-
"description": "Graph-backed cognitive context engine with SurrealDB + BGE-M3",
|
|
4
|
+
"description": "Graph-backed cognitive context engine with SurrealDB + pluggable embeddings (local BGE-M3 or OpenAI-compatible)",
|
|
5
5
|
"kind": "context-engine",
|
|
6
6
|
"requires": {
|
|
7
7
|
"bins": ["surreal"],
|
|
@@ -30,16 +30,36 @@
|
|
|
30
30
|
"label": "SurrealDB Database",
|
|
31
31
|
"placeholder": "memory"
|
|
32
32
|
},
|
|
33
|
-
"embedding.
|
|
34
|
-
"label": "Embedding
|
|
35
|
-
"placeholder": "
|
|
36
|
-
"help": "
|
|
37
|
-
"advanced": true
|
|
33
|
+
"embedding.provider": {
|
|
34
|
+
"label": "Embedding Provider",
|
|
35
|
+
"placeholder": "local",
|
|
36
|
+
"help": "Either 'local' (BGE-M3 via node-llama-cpp) or 'openai-compat' (any OpenAI-compatible /v1/embeddings endpoint: OpenAI, Azure, Together, vLLM, Ollama, etc.)"
|
|
38
37
|
},
|
|
39
38
|
"embedding.dimensions": {
|
|
40
39
|
"label": "Embedding Dimensions",
|
|
41
40
|
"placeholder": "1024",
|
|
41
|
+
"help": "Output vector dimensionality. Must match across providers if you intend to swap them. text-embedding-3-* honors arbitrary values; non-OpenAI compat servers may return their native dim regardless."
|
|
42
|
+
},
|
|
43
|
+
"embedding.modelPath": {
|
|
44
|
+
"label": "Local Model Path",
|
|
45
|
+
"placeholder": "~/.node-llama-cpp/models/bge-m3-q4_k_m.gguf",
|
|
46
|
+
"help": "Path to BGE-M3 GGUF file. Only used when provider is 'local'.",
|
|
42
47
|
"advanced": true
|
|
48
|
+
},
|
|
49
|
+
"embedding.openaiCompat.model": {
|
|
50
|
+
"label": "OpenAI-compat Model",
|
|
51
|
+
"placeholder": "text-embedding-3-small",
|
|
52
|
+
"help": "Model name passed in the embeddings request body. Only used when provider is 'openai-compat'."
|
|
53
|
+
},
|
|
54
|
+
"embedding.openaiCompat.baseURL": {
|
|
55
|
+
"label": "OpenAI-compat Base URL",
|
|
56
|
+
"placeholder": "https://api.openai.com/v1",
|
|
57
|
+
"help": "Endpoint base. The OPENAI_BASE_URL env var overrides this if set. Only used when provider is 'openai-compat'."
|
|
58
|
+
},
|
|
59
|
+
"embedding.openaiCompat.apiKeyEnv": {
|
|
60
|
+
"label": "API Key Env Var",
|
|
61
|
+
"placeholder": "OPENAI_API_KEY",
|
|
62
|
+
"help": "Name of the env var holding the API key (the secret stays out of config files). Only used when provider is 'openai-compat'."
|
|
43
63
|
}
|
|
44
64
|
},
|
|
45
65
|
"configSchema": {
|
|
@@ -61,8 +81,18 @@
|
|
|
61
81
|
"type": "object",
|
|
62
82
|
"additionalProperties": false,
|
|
63
83
|
"properties": {
|
|
84
|
+
"provider": { "type": "string", "enum": ["local", "openai-compat"] },
|
|
85
|
+
"dimensions": { "type": "number" },
|
|
64
86
|
"modelPath": { "type": "string" },
|
|
65
|
-
"
|
|
87
|
+
"openaiCompat": {
|
|
88
|
+
"type": "object",
|
|
89
|
+
"additionalProperties": false,
|
|
90
|
+
"properties": {
|
|
91
|
+
"model": { "type": "string" },
|
|
92
|
+
"baseURL": { "type": "string" },
|
|
93
|
+
"apiKeyEnv": { "type": "string" }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
66
96
|
}
|
|
67
97
|
}
|
|
68
98
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kongbrain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Graph-backed persistent memory engine for OpenClaw. Replaces the default context window with SurrealDB + vector embeddings that learn across sessions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,9 @@
|
|
|
24
24
|
"knowledge-graph",
|
|
25
25
|
"llm"
|
|
26
26
|
],
|
|
27
|
+
"bin": {
|
|
28
|
+
"kongbrain-reembed": "./bin/kongbrain-reembed.ts"
|
|
29
|
+
},
|
|
27
30
|
"openclaw": {
|
|
28
31
|
"extensions": [
|
|
29
32
|
"./src/index.ts"
|
package/src/causal.ts
CHANGED
|
@@ -127,9 +127,12 @@ export async function queryCausalContext(
|
|
|
127
127
|
const seen = new Set<string>(validIds);
|
|
128
128
|
let frontier = validIds;
|
|
129
129
|
const results: VectorSearchResult[] = [];
|
|
130
|
-
|
|
130
|
+
// Score only against rows in the active provider's vector space; rows from
|
|
131
|
+
// other providers still appear via graph traversal but score 0.
|
|
132
|
+
const bindings = { vec: queryVec, provider: store.getActiveProvider() };
|
|
131
133
|
|
|
132
134
|
const scoreExpr = `, IF embedding != NONE AND array::len(embedding) > 0
|
|
135
|
+
AND embedding_provider = $provider
|
|
133
136
|
THEN vector::similarity::cosine(embedding, $vec)
|
|
134
137
|
ELSE 0 END AS score`;
|
|
135
138
|
|
package/src/concept-extract.ts
CHANGED
|
@@ -108,9 +108,10 @@ export async function linkToRelevantConcepts(
|
|
|
108
108
|
`SELECT id, vector::similarity::cosine(embedding, $vec) AS score
|
|
109
109
|
FROM concept
|
|
110
110
|
WHERE embedding != NONE AND array::len(embedding) > 0
|
|
111
|
+
AND embedding_provider = $provider
|
|
111
112
|
ORDER BY score DESC
|
|
112
113
|
LIMIT $lim`,
|
|
113
|
-
{ vec, lim: limit },
|
|
114
|
+
{ vec, lim: limit, provider: embeddings.providerId },
|
|
114
115
|
);
|
|
115
116
|
for (const m of matches) {
|
|
116
117
|
if (m.score < threshold) break;
|
|
@@ -175,9 +176,10 @@ export async function linkConceptHierarchy(
|
|
|
175
176
|
FROM concept
|
|
176
177
|
WHERE id != $cid
|
|
177
178
|
AND embedding != NONE AND array::len(embedding) > 0
|
|
179
|
+
AND embedding_provider = $provider
|
|
178
180
|
ORDER BY score DESC
|
|
179
181
|
LIMIT 3`,
|
|
180
|
-
{ vec: conceptEmb, cid: conceptId },
|
|
182
|
+
{ vec: conceptEmb, cid: conceptId, provider: embeddings.providerId },
|
|
181
183
|
);
|
|
182
184
|
for (const s of similar) {
|
|
183
185
|
if (s.score < 0.75) break;
|
package/src/config.ts
CHANGED
|
@@ -10,9 +10,26 @@ export interface SurrealConfig {
|
|
|
10
10
|
db: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
export type EmbeddingProvider = "local" | "openai-compat";
|
|
14
|
+
|
|
15
|
+
export interface OpenAICompatEmbeddingConfig {
|
|
16
|
+
/** Model name passed in the embeddings request body (e.g. "text-embedding-3-small"). */
|
|
17
|
+
model: string;
|
|
18
|
+
/** Endpoint base URL. Default: "https://api.openai.com/v1". */
|
|
19
|
+
baseURL: string;
|
|
20
|
+
/** Name of the env var holding the API key. Default: "OPENAI_API_KEY". */
|
|
21
|
+
apiKeyEnv: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
13
24
|
export interface EmbeddingConfig {
|
|
14
|
-
|
|
25
|
+
/** Which provider to use. Default "local" (BGE-M3 via node-llama-cpp). */
|
|
26
|
+
provider: EmbeddingProvider;
|
|
27
|
+
/** Vector dimensionality the active provider should produce. */
|
|
15
28
|
dimensions: number;
|
|
29
|
+
/** Path to the local GGUF model — only consulted when provider === "local". */
|
|
30
|
+
modelPath: string;
|
|
31
|
+
/** OpenAI-compatible provider settings — only consulted when provider === "openai-compat". */
|
|
32
|
+
openaiCompat: OpenAICompatEmbeddingConfig;
|
|
16
33
|
}
|
|
17
34
|
|
|
18
35
|
export interface ThresholdConfig {
|
|
@@ -34,6 +51,43 @@ export interface KongBrainConfig {
|
|
|
34
51
|
thresholds: ThresholdConfig;
|
|
35
52
|
}
|
|
36
53
|
|
|
54
|
+
function parseEmbeddingConfig(raw: Record<string, unknown>): EmbeddingConfig {
|
|
55
|
+
const openaiCompatRaw = (raw.openaiCompat ?? {}) as Record<string, unknown>;
|
|
56
|
+
|
|
57
|
+
// Provider precedence: env var > plugin config > default "local"
|
|
58
|
+
const rawProvider =
|
|
59
|
+
process.env.KONGBRAIN_EMBED_PROVIDER ??
|
|
60
|
+
(typeof raw.provider === "string" ? raw.provider : null);
|
|
61
|
+
const provider: EmbeddingProvider =
|
|
62
|
+
rawProvider === "openai-compat" ? "openai-compat" : "local";
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
provider,
|
|
66
|
+
dimensions: typeof raw.dimensions === "number" ? raw.dimensions : 1024,
|
|
67
|
+
modelPath:
|
|
68
|
+
process.env.EMBED_MODEL_PATH ??
|
|
69
|
+
(typeof raw.modelPath === "string"
|
|
70
|
+
? raw.modelPath
|
|
71
|
+
: join(homedir(), ".node-llama-cpp", "models", "bge-m3-q4_k_m.gguf")),
|
|
72
|
+
openaiCompat: {
|
|
73
|
+
model:
|
|
74
|
+
typeof openaiCompatRaw.model === "string"
|
|
75
|
+
? openaiCompatRaw.model
|
|
76
|
+
: "text-embedding-3-small",
|
|
77
|
+
// baseURL: env wins (matches the official openai SDK convention)
|
|
78
|
+
baseURL:
|
|
79
|
+
process.env.OPENAI_BASE_URL ??
|
|
80
|
+
(typeof openaiCompatRaw.baseURL === "string"
|
|
81
|
+
? openaiCompatRaw.baseURL
|
|
82
|
+
: "https://api.openai.com/v1"),
|
|
83
|
+
apiKeyEnv:
|
|
84
|
+
typeof openaiCompatRaw.apiKeyEnv === "string"
|
|
85
|
+
? openaiCompatRaw.apiKeyEnv
|
|
86
|
+
: "OPENAI_API_KEY",
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
37
91
|
/**
|
|
38
92
|
* Parse plugin config from openclaw.plugin.json configSchema values,
|
|
39
93
|
* with env var overrides and sensible defaults.
|
|
@@ -66,15 +120,7 @@ export function parsePluginConfig(raw?: Record<string, unknown>): KongBrainConfi
|
|
|
66
120
|
ns: (typeof surreal.ns === "string" ? surreal.ns : null) ?? process.env.SURREAL_NS ?? "kong",
|
|
67
121
|
db: (typeof surreal.db === "string" ? surreal.db : null) ?? process.env.SURREAL_DB ?? "memory",
|
|
68
122
|
},
|
|
69
|
-
embedding:
|
|
70
|
-
modelPath:
|
|
71
|
-
process.env.EMBED_MODEL_PATH ??
|
|
72
|
-
(typeof embedding.modelPath === "string"
|
|
73
|
-
? embedding.modelPath
|
|
74
|
-
: join(homedir(), ".node-llama-cpp", "models", "bge-m3-q4_k_m.gguf")),
|
|
75
|
-
dimensions:
|
|
76
|
-
typeof embedding.dimensions === "number" ? embedding.dimensions : 1024,
|
|
77
|
-
},
|
|
123
|
+
embedding: parseEmbeddingConfig(embedding),
|
|
78
124
|
thresholds: {
|
|
79
125
|
daemonTokenThreshold:
|
|
80
126
|
typeof thresholds.daemonTokenThreshold === "number" ? thresholds.daemonTokenThreshold : 4000,
|