kongbrain 0.5.0 → 0.5.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/.kongcode-handoff.json +8 -0
- package/README.github.md +3 -1
- package/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/config.ts +9 -1
- package/src/context-engine.ts +4 -2
- package/src/index.ts +52 -20
- package/src/model-resolution.ts +98 -0
- package/src/schema-loader.ts +21 -3
- package/src/schema.surql +8 -8
- package/src/surreal.ts +10 -2
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sessionId": "7a2dcd89-1468-49c3-a1be-24dac5a05662",
|
|
3
|
+
"timestamp": "2026-05-02T21:39:49.691Z",
|
|
4
|
+
"userTurnCount": 1,
|
|
5
|
+
"lastUserText": "Drain the KongCode pending_work queue. Loop: call mcp__plugin_kongcode_kongcode__fetch_pending_work to claim the next item, analyze the data per the work-type instructions, then call mcp__plugin_kongcode_kongcode__commit_work_results with your output. Repeat until fetch_pending_work returns empty. Be efficient: minimize per-item analysis. This is auto-drain, not user-facing — produce no narration, just process items.",
|
|
6
|
+
"lastAssistantText": "",
|
|
7
|
+
"unextractedTokens": 0
|
|
8
|
+
}
|
package/README.github.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
+
[](https://voidorigin.com)
|
|
8
|
+
|
|
7
9
|
[](https://www.npmjs.com/package/kongbrain)
|
|
8
10
|
[](https://clawhub.ai/packages/kongbrain)
|
|
9
11
|
[](https://github.com/42U/kongbrain)
|
|
@@ -498,6 +500,6 @@ The lobster doesn't accept contributions. The ape does.
|
|
|
498
500
|
|
|
499
501
|
<div align="center">
|
|
500
502
|
|
|
501
|
-
MIT License | Built by [42U](https://github.com/42U)
|
|
503
|
+
MIT License | Built by [42U](https://github.com/42U) | [VoidOrigin](https://voidorigin.com)
|
|
502
504
|
|
|
503
505
|
</div>
|
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.5.
|
|
4
|
+
version: 0.5.1
|
|
5
5
|
homepage: https://github.com/42U/kongbrain
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kongbrain",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
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",
|
package/src/config.ts
CHANGED
|
@@ -51,6 +51,14 @@ export interface KongBrainConfig {
|
|
|
51
51
|
thresholds: ThresholdConfig;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
const DEFAULT_EMBEDDING_DIMENSIONS = 1024;
|
|
55
|
+
|
|
56
|
+
function parsePositiveInteger(value: unknown, fallback: number): number {
|
|
57
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
58
|
+
? value
|
|
59
|
+
: fallback;
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
function parseEmbeddingConfig(raw: Record<string, unknown>): EmbeddingConfig {
|
|
55
63
|
const openaiCompatRaw = (raw.openaiCompat ?? {}) as Record<string, unknown>;
|
|
56
64
|
|
|
@@ -63,7 +71,7 @@ function parseEmbeddingConfig(raw: Record<string, unknown>): EmbeddingConfig {
|
|
|
63
71
|
|
|
64
72
|
return {
|
|
65
73
|
provider,
|
|
66
|
-
dimensions:
|
|
74
|
+
dimensions: parsePositiveInteger(raw.dimensions, DEFAULT_EMBEDDING_DIMENSIONS),
|
|
67
75
|
modelPath:
|
|
68
76
|
process.env.EMBED_MODEL_PATH ??
|
|
69
77
|
(typeof raw.modelPath === "string"
|
package/src/context-engine.ts
CHANGED
|
@@ -57,7 +57,7 @@ export class KongBrainContextEngine implements ContextEngine {
|
|
|
57
57
|
readonly info: ContextEngineInfo = {
|
|
58
58
|
id: "kongbrain",
|
|
59
59
|
name: "KongBrain",
|
|
60
|
-
version: "0.
|
|
60
|
+
version: "0.5.1",
|
|
61
61
|
ownsCompaction: true,
|
|
62
62
|
};
|
|
63
63
|
|
|
@@ -76,7 +76,9 @@ export class KongBrainContextEngine implements ContextEngine {
|
|
|
76
76
|
// Run schema once per process (idempotent but expensive on every bootstrap)
|
|
77
77
|
if (!this.state.schemaApplied) {
|
|
78
78
|
try {
|
|
79
|
-
const schemaSql = loadSchema(
|
|
79
|
+
const schemaSql = loadSchema({
|
|
80
|
+
embeddingDimensions: this.state.config.embedding.dimensions,
|
|
81
|
+
});
|
|
80
82
|
await store.queryExec(schemaSql);
|
|
81
83
|
this.state.schemaApplied = true;
|
|
82
84
|
} catch (e) {
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { parsePluginConfig } from "./config.js";
|
|
|
12
12
|
import { SurrealStore } from "./surreal.js";
|
|
13
13
|
import { createEmbeddingService } from "./embeddings.js";
|
|
14
14
|
import { GlobalPluginState, type CompleteFn } from "./state.js";
|
|
15
|
+
import { resolveModelRef } from "./model-resolution.js";
|
|
15
16
|
import { KongBrainContextEngine } from "./context-engine.js";
|
|
16
17
|
import { createRecallToolDef } from "./tools/recall.js";
|
|
17
18
|
import { createCoreMemoryToolDef } from "./tools/core-memory.js";
|
|
@@ -333,6 +334,38 @@ async function checkEmbeddingProviderMismatch(
|
|
|
333
334
|
}
|
|
334
335
|
}
|
|
335
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Detect vectors whose dimensionality doesn't match the configured dimension.
|
|
339
|
+
* DEFINE INDEX IF NOT EXISTS is non-destructive — changing embedding.dimensions
|
|
340
|
+
* after initial setup leaves the HNSW index at the old dimension while new
|
|
341
|
+
* vectors are written at the new size. Warn so the user knows to rebuild.
|
|
342
|
+
*/
|
|
343
|
+
async function checkEmbeddingDimensionMismatch(
|
|
344
|
+
store: SurrealStore,
|
|
345
|
+
configuredDimensions: number,
|
|
346
|
+
logger: { warn: (msg: string) => void },
|
|
347
|
+
): Promise<void> {
|
|
348
|
+
if (!store.isAvailable()) return;
|
|
349
|
+
const tables = ["concept", "memory", "turn", "artifact", "identity_chunk", "skill", "reflection", "monologue"];
|
|
350
|
+
for (const t of tables) {
|
|
351
|
+
try {
|
|
352
|
+
const rows = await store.queryFirst<{ len: number }>(
|
|
353
|
+
`SELECT array::len(embedding) AS len FROM ${t} WHERE embedding != NONE AND array::len(embedding) > 0 LIMIT 1`,
|
|
354
|
+
);
|
|
355
|
+
if (rows.length > 0 && rows[0].len !== configuredDimensions) {
|
|
356
|
+
logger.warn(
|
|
357
|
+
`Embedding dimension mismatch: existing vectors are ${rows[0].len}-dimensional but embedding.dimensions is configured as ${configuredDimensions}. ` +
|
|
358
|
+
`HNSW indexes created at the old dimension are not updated by DEFINE INDEX IF NOT EXISTS. ` +
|
|
359
|
+
`To fix: drop and recreate the vector indexes, then re-embed affected rows.`,
|
|
360
|
+
);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
} catch (e) {
|
|
364
|
+
swallow.warn(`factory:dimensionMismatchCheck:${t}`, e);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
336
369
|
export default definePluginEntry({
|
|
337
370
|
id: "kongbrain",
|
|
338
371
|
name: "KongBrain",
|
|
@@ -349,7 +382,9 @@ export default definePluginEntry({
|
|
|
349
382
|
// ensure a single instance survives across module reloads.
|
|
350
383
|
let globalState = getGlobalState();
|
|
351
384
|
if (!globalState) {
|
|
352
|
-
const store = new SurrealStore(config.surreal
|
|
385
|
+
const store = new SurrealStore(config.surreal, {
|
|
386
|
+
embeddingDimensions: config.embedding.dimensions,
|
|
387
|
+
});
|
|
353
388
|
const embeddings = createEmbeddingService(config.embedding);
|
|
354
389
|
// Tag every embedding write and filter every embedding search by this
|
|
355
390
|
// provider id, so vectors from different models (different vector
|
|
@@ -383,27 +418,22 @@ export default definePluginEntry({
|
|
|
383
418
|
}
|
|
384
419
|
piAi = await import(piAiPath);
|
|
385
420
|
}
|
|
386
|
-
// Fall back to calling pi-ai directly (runtime.complete not in OpenClaw 2026.3.24)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
//
|
|
390
|
-
const
|
|
391
|
-
|
|
392
|
-
:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
resolvedProvider = modelId.slice(0, idx);
|
|
399
|
-
modelId = modelId.slice(idx + 1);
|
|
400
|
-
}
|
|
401
|
-
const model = piAi!.getModel(resolvedProvider, modelId);
|
|
421
|
+
// Fall back to calling pi-ai directly (runtime.complete not in OpenClaw 2026.3.24).
|
|
422
|
+
// Fully-qualified OpenClaw refs (provider/model) are authoritative:
|
|
423
|
+
// stale runtime default providers must not override e.g.
|
|
424
|
+
// "openrouter/google/gemini-3-flash-preview".
|
|
425
|
+
const cfg = apiRef.runtime.config.loadConfig();
|
|
426
|
+
const resolved = resolveModelRef({
|
|
427
|
+
explicitProvider: params.provider,
|
|
428
|
+
explicitModel: params.model,
|
|
429
|
+
config: cfg,
|
|
430
|
+
runtimeDefaults: apiRef.runtime.agent.defaults,
|
|
431
|
+
});
|
|
432
|
+
const model = piAi!.getModel(resolved.provider, resolved.modelId);
|
|
402
433
|
if (!model) {
|
|
403
|
-
throw new Error(`Model "${modelId}" not found for provider "${provider}"`);
|
|
434
|
+
throw new Error(`Model "${resolved.modelId}" not found for provider "${resolved.provider}"`);
|
|
404
435
|
}
|
|
405
436
|
// Resolve auth via OpenClaw's runtime (handles profiles, env vars, etc.)
|
|
406
|
-
const cfg = apiRef.runtime.config.loadConfig();
|
|
407
437
|
const auth = await apiRef.runtime.modelAuth.getApiKeyForModel({ model, cfg });
|
|
408
438
|
// Build context
|
|
409
439
|
const now = Date.now();
|
|
@@ -417,7 +447,7 @@ export default definePluginEntry({
|
|
|
417
447
|
);
|
|
418
448
|
const context = { systemPrompt: params.system, messages };
|
|
419
449
|
// Pass apiKey directly in options so the provider can use it
|
|
420
|
-
log.info(`complete(): provider=${
|
|
450
|
+
log.info(`complete(): provider=${resolved.provider} model=${resolved.modelId} msgs=${params.messages.length}`);
|
|
421
451
|
// NOTE: outputFormat (structured output) is intentionally NOT passed to pi-ai.
|
|
422
452
|
// pi-ai's SimpleStreamOptions doesn't support it, and injecting it via onPayload
|
|
423
453
|
// causes the Anthropic API to return empty responses. The daemon's JSON parsing
|
|
@@ -471,6 +501,8 @@ export default definePluginEntry({
|
|
|
471
501
|
// re-embedded with the active provider.
|
|
472
502
|
checkEmbeddingProviderMismatch(store, embeddings.providerId, logger)
|
|
473
503
|
.catch(e => swallow.warn("factory:providerMismatchCheck", e));
|
|
504
|
+
checkEmbeddingDimensionMismatch(store, config.embedding.dimensions, logger)
|
|
505
|
+
.catch(e => swallow.warn("factory:dimensionMismatchCheck", e));
|
|
474
506
|
}
|
|
475
507
|
} catch (e) {
|
|
476
508
|
logger.warn(`Embeddings init failed — running in degraded mode: ${e}`);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export interface ResolveModelRefInput {
|
|
2
|
+
explicitProvider?: unknown;
|
|
3
|
+
explicitModel?: unknown;
|
|
4
|
+
config?: unknown;
|
|
5
|
+
runtimeDefaults?: unknown;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface ResolvedModelRef {
|
|
9
|
+
provider: string;
|
|
10
|
+
modelId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
14
|
+
return typeof value === "object" && value !== null
|
|
15
|
+
? value as Record<string, unknown>
|
|
16
|
+
: null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function nonEmptyString(value: unknown): string | undefined {
|
|
20
|
+
return typeof value === "string" && value.trim().length > 0
|
|
21
|
+
? value.trim()
|
|
22
|
+
: undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function unwrapModelRef(value: unknown): string | undefined {
|
|
26
|
+
const direct = nonEmptyString(value);
|
|
27
|
+
if (direct) return direct;
|
|
28
|
+
|
|
29
|
+
const record = asRecord(value);
|
|
30
|
+
if (!record) return undefined;
|
|
31
|
+
|
|
32
|
+
return unwrapModelRef(record.primary) ?? unwrapModelRef(record.id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function defaultModelFromConfig(config: unknown): string | undefined {
|
|
36
|
+
const cfg = asRecord(config);
|
|
37
|
+
const agents = asRecord(cfg?.agents);
|
|
38
|
+
const defaults = asRecord(agents?.defaults);
|
|
39
|
+
return unwrapModelRef(defaults?.model);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function defaultModelFromRuntime(runtimeDefaults: unknown): string | undefined {
|
|
43
|
+
const defaults = asRecord(runtimeDefaults);
|
|
44
|
+
return unwrapModelRef(defaults?.model);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function providerFromRuntime(runtimeDefaults: unknown): string | undefined {
|
|
48
|
+
const defaults = asRecord(runtimeDefaults);
|
|
49
|
+
return nonEmptyString(defaults?.provider);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function splitQualifiedModelRef(modelRef: string): { provider: string; modelId: string } | null {
|
|
53
|
+
const slash = modelRef.indexOf("/");
|
|
54
|
+
if (slash <= 0 || slash === modelRef.length - 1) return null;
|
|
55
|
+
return {
|
|
56
|
+
provider: modelRef.slice(0, slash),
|
|
57
|
+
modelId: modelRef.slice(slash + 1),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function chooseModelRef(input: ResolveModelRefInput): { modelRef: string; explicit: boolean } | null {
|
|
62
|
+
const explicit = unwrapModelRef(input.explicitModel);
|
|
63
|
+
if (explicit) return { modelRef: explicit, explicit: true };
|
|
64
|
+
|
|
65
|
+
const configDefault = defaultModelFromConfig(input.config);
|
|
66
|
+
if (configDefault) return { modelRef: configDefault, explicit: false };
|
|
67
|
+
|
|
68
|
+
const runtimeDefault = defaultModelFromRuntime(input.runtimeDefaults);
|
|
69
|
+
if (runtimeDefault) return { modelRef: runtimeDefault, explicit: false };
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function resolveModelRef(input: ResolveModelRefInput): ResolvedModelRef {
|
|
75
|
+
const chosen = chooseModelRef(input);
|
|
76
|
+
|
|
77
|
+
if (!chosen) {
|
|
78
|
+
throw new Error("No LLM model configured for KongBrain internal completion");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const explicitProvider = nonEmptyString(input.explicitProvider);
|
|
82
|
+
if (chosen.explicit && explicitProvider) {
|
|
83
|
+
return { provider: explicitProvider, modelId: chosen.modelRef };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const qualified = splitQualifiedModelRef(chosen.modelRef);
|
|
87
|
+
if (qualified) return qualified;
|
|
88
|
+
|
|
89
|
+
const provider =
|
|
90
|
+
explicitProvider ??
|
|
91
|
+
providerFromRuntime(input.runtimeDefaults);
|
|
92
|
+
|
|
93
|
+
if (!provider) {
|
|
94
|
+
throw new Error(`No LLM provider configured for model "${chosen.modelRef}"`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return { provider, modelId: chosen.modelRef };
|
|
98
|
+
}
|
package/src/schema-loader.ts
CHANGED
|
@@ -12,12 +12,30 @@ import { fileURLToPath } from "node:url";
|
|
|
12
12
|
|
|
13
13
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const DEFAULT_EMBEDDING_DIMENSIONS = 1024;
|
|
16
|
+
const DIMENSION_PLACEHOLDER = "__KONGBRAIN_EMBEDDING_DIMENSIONS__";
|
|
17
|
+
|
|
18
|
+
export interface LoadSchemaOptions {
|
|
19
|
+
embeddingDimensions?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeDimensions(value: unknown): number {
|
|
23
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
24
|
+
? value
|
|
25
|
+
: DEFAULT_EMBEDDING_DIMENSIONS;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function loadSchema(options: LoadSchemaOptions = {}): string {
|
|
16
29
|
const primary = join(__dirname, "schema.surql");
|
|
30
|
+
let schema: string;
|
|
17
31
|
try {
|
|
18
|
-
|
|
32
|
+
schema = readFileSync(primary, "utf-8");
|
|
19
33
|
} catch {
|
|
20
34
|
// Dev fallback: compiled output lives in dist/, schema source in src/
|
|
21
|
-
|
|
35
|
+
schema = readFileSync(join(__dirname, "..", "src", "schema.surql"), "utf-8");
|
|
22
36
|
}
|
|
37
|
+
return schema.replaceAll(
|
|
38
|
+
DIMENSION_PLACEHOLDER,
|
|
39
|
+
String(normalizeDimensions(options.embeddingDimensions)),
|
|
40
|
+
);
|
|
23
41
|
}
|
package/src/schema.surql
CHANGED
|
@@ -42,7 +42,7 @@ DEFINE FIELD IF NOT EXISTS embedding ON artifact TYPE option<array<float>>;
|
|
|
42
42
|
DEFINE FIELD IF NOT EXISTS embedding_provider ON artifact TYPE option<string>;
|
|
43
43
|
DEFINE FIELD IF NOT EXISTS tags ON artifact TYPE option<array>;
|
|
44
44
|
DEFINE FIELD IF NOT EXISTS created_at ON artifact TYPE datetime DEFAULT time::now();
|
|
45
|
-
DEFINE INDEX IF NOT EXISTS artifact_vec_idx ON artifact FIELDS embedding HNSW DIMENSION
|
|
45
|
+
DEFINE INDEX IF NOT EXISTS artifact_vec_idx ON artifact FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
46
46
|
DEFINE INDEX IF NOT EXISTS artifact_type_idx ON artifact FIELDS type;
|
|
47
47
|
DEFINE INDEX IF NOT EXISTS artifact_emb_provider_idx ON artifact FIELDS embedding_provider;
|
|
48
48
|
|
|
@@ -62,7 +62,7 @@ DEFINE FIELD IF NOT EXISTS tags ON concept TYPE option<array>;
|
|
|
62
62
|
DEFINE FIELD IF NOT EXISTS source ON concept TYPE option<string>;
|
|
63
63
|
DEFINE FIELD IF NOT EXISTS created_at ON concept TYPE datetime DEFAULT time::now();
|
|
64
64
|
DEFINE FIELD IF NOT EXISTS last_accessed ON concept TYPE option<datetime>;
|
|
65
|
-
DEFINE INDEX IF NOT EXISTS concept_vec_idx ON concept FIELDS embedding HNSW DIMENSION
|
|
65
|
+
DEFINE INDEX IF NOT EXISTS concept_vec_idx ON concept FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
66
66
|
DEFINE INDEX IF NOT EXISTS concept_emb_provider_idx ON concept FIELDS embedding_provider;
|
|
67
67
|
|
|
68
68
|
-- ============================================================
|
|
@@ -82,7 +82,7 @@ DEFINE FIELD IF NOT EXISTS token_count ON turn TYPE option<int>;
|
|
|
82
82
|
DEFINE FIELD IF NOT EXISTS tool_name ON turn TYPE option<string>;
|
|
83
83
|
DEFINE FIELD IF NOT EXISTS model ON turn TYPE option<string>;
|
|
84
84
|
DEFINE FIELD IF NOT EXISTS usage ON turn TYPE option<object>;
|
|
85
|
-
DEFINE INDEX IF NOT EXISTS turn_vec_idx ON turn FIELDS embedding HNSW DIMENSION
|
|
85
|
+
DEFINE INDEX IF NOT EXISTS turn_vec_idx ON turn FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
86
86
|
DEFINE INDEX IF NOT EXISTS turn_session_idx ON turn FIELDS session_id;
|
|
87
87
|
DEFINE INDEX IF NOT EXISTS turn_tool_name_idx ON turn FIELDS tool_name;
|
|
88
88
|
DEFINE INDEX IF NOT EXISTS turn_emb_provider_idx ON turn FIELDS embedding_provider;
|
|
@@ -96,7 +96,7 @@ DEFINE FIELD IF NOT EXISTS text ON identity_chunk TYPE string;
|
|
|
96
96
|
DEFINE FIELD IF NOT EXISTS embedding ON identity_chunk TYPE option<array<float>>;
|
|
97
97
|
DEFINE FIELD IF NOT EXISTS embedding_provider ON identity_chunk TYPE option<string>;
|
|
98
98
|
DEFINE FIELD IF NOT EXISTS importance ON identity_chunk TYPE float DEFAULT 0.5;
|
|
99
|
-
DEFINE INDEX IF NOT EXISTS identity_vec_idx ON identity_chunk FIELDS embedding HNSW DIMENSION
|
|
99
|
+
DEFINE INDEX IF NOT EXISTS identity_vec_idx ON identity_chunk FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
100
100
|
DEFINE INDEX IF NOT EXISTS identity_emb_provider_idx ON identity_chunk FIELDS embedding_provider;
|
|
101
101
|
|
|
102
102
|
-- Sessions (lightweight, links to task for 5-pillar integration)
|
|
@@ -125,7 +125,7 @@ DEFINE FIELD IF NOT EXISTS last_accessed ON memory TYPE option<datetime>;
|
|
|
125
125
|
DEFINE FIELD IF NOT EXISTS status ON memory TYPE option<string> DEFAULT "active";
|
|
126
126
|
DEFINE FIELD IF NOT EXISTS resolved_at ON memory TYPE option<datetime>;
|
|
127
127
|
DEFINE FIELD IF NOT EXISTS resolved_by ON memory TYPE option<string>;
|
|
128
|
-
DEFINE INDEX IF NOT EXISTS memory_vec_idx ON memory FIELDS embedding HNSW DIMENSION
|
|
128
|
+
DEFINE INDEX IF NOT EXISTS memory_vec_idx ON memory FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
129
129
|
DEFINE INDEX IF NOT EXISTS memory_category_idx ON memory FIELDS category;
|
|
130
130
|
DEFINE INDEX IF NOT EXISTS memory_emb_provider_idx ON memory FIELDS embedding_provider;
|
|
131
131
|
|
|
@@ -276,7 +276,7 @@ DEFINE FIELD IF NOT EXISTS failure_count ON skill TYPE int DEFAULT 0;
|
|
|
276
276
|
DEFINE FIELD IF NOT EXISTS avg_duration_ms ON skill TYPE float DEFAULT 0.0;
|
|
277
277
|
DEFINE FIELD IF NOT EXISTS last_used ON skill TYPE option<datetime>;
|
|
278
278
|
DEFINE FIELD IF NOT EXISTS created_at ON skill TYPE datetime DEFAULT time::now();
|
|
279
|
-
DEFINE INDEX IF NOT EXISTS skill_vec_idx ON skill FIELDS embedding HNSW DIMENSION
|
|
279
|
+
DEFINE INDEX IF NOT EXISTS skill_vec_idx ON skill FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
280
280
|
DEFINE INDEX IF NOT EXISTS skill_active_idx ON skill FIELDS active;
|
|
281
281
|
DEFINE INDEX IF NOT EXISTS skill_emb_provider_idx ON skill FIELDS embedding_provider;
|
|
282
282
|
|
|
@@ -296,7 +296,7 @@ DEFINE FIELD IF NOT EXISTS severity ON reflection TYPE string DEFAULT "minor"; -
|
|
|
296
296
|
DEFINE FIELD IF NOT EXISTS importance ON reflection TYPE float DEFAULT 7.0;
|
|
297
297
|
DEFINE FIELD IF NOT EXISTS access_count ON reflection TYPE int DEFAULT 0;
|
|
298
298
|
DEFINE FIELD IF NOT EXISTS created_at ON reflection TYPE datetime DEFAULT time::now();
|
|
299
|
-
DEFINE INDEX IF NOT EXISTS reflection_vec_idx ON reflection FIELDS embedding HNSW DIMENSION
|
|
299
|
+
DEFINE INDEX IF NOT EXISTS reflection_vec_idx ON reflection FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
300
300
|
DEFINE INDEX IF NOT EXISTS reflection_emb_provider_idx ON reflection FIELDS embedding_provider;
|
|
301
301
|
|
|
302
302
|
DEFINE TABLE IF NOT EXISTS reflects_on TYPE RELATION IN reflection OUT session;
|
|
@@ -347,7 +347,7 @@ DEFINE FIELD IF NOT EXISTS embedding_provider ON monologue TYPE option<string>;
|
|
|
347
347
|
DEFINE FIELD IF NOT EXISTS timestamp ON monologue TYPE datetime DEFAULT time::now();
|
|
348
348
|
DEFINE INDEX IF NOT EXISTS monologue_session ON monologue FIELDS session_id;
|
|
349
349
|
DEFINE INDEX IF NOT EXISTS monologue_category ON monologue FIELDS category;
|
|
350
|
-
DEFINE INDEX IF NOT EXISTS monologue_vec_idx ON monologue FIELDS embedding HNSW DIMENSION
|
|
350
|
+
DEFINE INDEX IF NOT EXISTS monologue_vec_idx ON monologue FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
|
|
351
351
|
DEFINE INDEX IF NOT EXISTS monologue_emb_provider_idx ON monologue FIELDS embedding_provider;
|
|
352
352
|
|
|
353
353
|
-- Fibonacci resurfacing: proactive memory that fades over time
|
package/src/surreal.ts
CHANGED
|
@@ -47,6 +47,10 @@ export interface UtilityCacheEntry {
|
|
|
47
47
|
retrieval_count: number;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface SurrealStoreOptions {
|
|
51
|
+
embeddingDimensions?: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
50
54
|
const RECORD_ID_RE = /^[a-zA-Z_][a-zA-Z0-9_]*:[a-zA-Z0-9_]+$/;
|
|
51
55
|
|
|
52
56
|
function assertRecordId(id: string): void {
|
|
@@ -132,9 +136,11 @@ export class SurrealStore {
|
|
|
132
136
|
* deployments keep working if the wire-up step is ever skipped.
|
|
133
137
|
*/
|
|
134
138
|
private activeProvider: string = "local-bge-m3";
|
|
139
|
+
private schemaOptions: SurrealStoreOptions;
|
|
135
140
|
|
|
136
|
-
constructor(config: SurrealConfig) {
|
|
141
|
+
constructor(config: SurrealConfig, options: SurrealStoreOptions = {}) {
|
|
137
142
|
this.config = config;
|
|
143
|
+
this.schemaOptions = options;
|
|
138
144
|
this.db = new Surreal();
|
|
139
145
|
}
|
|
140
146
|
|
|
@@ -216,7 +222,9 @@ export class SurrealStore {
|
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
private async runSchema(): Promise<void> {
|
|
219
|
-
const schema = loadSchema(
|
|
225
|
+
const schema = loadSchema({
|
|
226
|
+
embeddingDimensions: this.schemaOptions.embeddingDimensions,
|
|
227
|
+
});
|
|
220
228
|
await this.db.query(schema);
|
|
221
229
|
}
|
|
222
230
|
|