openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
232 lines (231 loc) • 8.51 kB
JavaScript
import { g as parseFiniteNumber } from "./number-coercion-CJQ8TR--.js";
import "./number-runtime-DBLVDypr.js";
import fs from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
//#region extensions/memory-lancedb/config.ts
const MEMORY_CATEGORIES = [
"preference",
"fact",
"decision",
"entity",
"other"
];
const DEFAULT_MODEL = "text-embedding-3-small";
const DEFAULT_CAPTURE_MAX_CHARS = 500;
const DEFAULT_RECALL_MAX_CHARS = 1e3;
const LEGACY_STATE_DIRS = [];
function resolveDefaultDbPath() {
const home = homedir();
const preferred = join(home, ".openclaw", "memory", "lancedb");
try {
if (fs.existsSync(preferred)) return preferred;
} catch {}
for (const legacy of LEGACY_STATE_DIRS) {
const candidate = join(home, legacy, "memory", "lancedb");
try {
if (fs.existsSync(candidate)) return candidate;
} catch {}
}
return preferred;
}
const DEFAULT_DB_PATH = resolveDefaultDbPath();
const EMBEDDING_DIMENSIONS = {
"text-embedding-3-small": 1536,
"text-embedding-3-large": 3072
};
const EMBEDDING_CONFIG_KEYS = [
"provider",
"apiKey",
"model",
"baseUrl",
"dimensions"
];
function assertAllowedKeys(value, allowed, label) {
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
if (unknown.length === 0) return;
throw new Error(`${label} has unknown keys: ${unknown.join(", ")}`);
}
function vectorDimsForModel(model) {
const dims = EMBEDDING_DIMENSIONS[model];
if (!dims) throw new Error(`Unsupported embedding model: ${model}`);
return dims;
}
function resolveEnvVars(value) {
return value.replace(/\$\{([^}]+)\}/g, (_, envVar) => {
const envValue = process.env[envVar];
if (!envValue) throw new Error(`Environment variable ${envVar} is not set`);
return envValue;
});
}
function resolveEmbeddingModel(embedding, dimensions) {
const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL;
if (dimensions === void 0) vectorDimsForModel(model);
return model;
}
function resolveFiniteIntegerConfig(value) {
if (typeof value !== "number") return;
const parsed = parseFiniteNumber(value);
return parsed === void 0 ? void 0 : Math.floor(parsed);
}
function resolveBoundedIntegerConfig(params) {
const resolved = resolveFiniteIntegerConfig(params.value) ?? params.fallback;
if (resolved < params.min || resolved > params.max) throw new Error(`${params.label} must be between ${params.min} and ${params.max}`);
return resolved;
}
function resolveEmbeddingDimensions(embedding) {
if (embedding.dimensions === void 0) return;
const dimensions = typeof embedding.dimensions === "number" ? parseFiniteNumber(embedding.dimensions) : void 0;
if (dimensions === void 0 || !Number.isInteger(dimensions) || dimensions < 1) throw new Error("embedding.dimensions must be a positive integer");
return dimensions;
}
const memoryConfigSchema = {
parse(value) {
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("memory config required");
const cfg = value;
assertAllowedKeys(cfg, [
"embedding",
"dreaming",
"dbPath",
"autoCapture",
"autoRecall",
"captureMaxChars",
"customTriggers",
"recallMaxChars",
"storageOptions"
], "memory config");
const embedding = cfg.embedding;
if (!embedding || typeof embedding !== "object" || Array.isArray(embedding)) throw new Error("embedding config required");
assertAllowedKeys(embedding, [...EMBEDDING_CONFIG_KEYS], "embedding config");
if (Object.keys(embedding).length === 0) throw new Error("embedding config must include at least one setting");
const dimensions = resolveEmbeddingDimensions(embedding);
const model = resolveEmbeddingModel(embedding, dimensions);
const provider = typeof embedding.provider === "string" ? embedding.provider.trim() : "openai";
if (!provider) throw new Error("embedding.provider must not be empty");
const captureMaxChars = resolveBoundedIntegerConfig({
value: cfg.captureMaxChars,
fallback: 500,
min: 100,
max: 1e4,
label: "captureMaxChars"
});
const recallMaxChars = resolveBoundedIntegerConfig({
value: cfg.recallMaxChars,
fallback: DEFAULT_RECALL_MAX_CHARS,
min: 100,
max: 1e4,
label: "recallMaxChars"
});
let customTriggers;
if (cfg.customTriggers !== void 0) {
if (!Array.isArray(cfg.customTriggers)) throw new Error("customTriggers must be an array of strings");
customTriggers = cfg.customTriggers.map((trigger, index) => {
if (typeof trigger !== "string") throw new Error(`customTriggers.${index} must be a string`);
const normalized = trigger.trim();
if (!normalized) throw new Error(`customTriggers.${index} must not be empty`);
if (normalized.length > 100) throw new Error(`customTriggers.${index} must be at most 100 characters`);
return normalized;
});
if (customTriggers.length > 50) throw new Error("customTriggers must include at most 50 entries");
}
const dreaming = cfg.dreaming === void 0 ? void 0 : cfg.dreaming && typeof cfg.dreaming === "object" && !Array.isArray(cfg.dreaming) ? cfg.dreaming : (() => {
throw new Error("dreaming config must be an object");
})();
let storageOptions;
const storageOpts = cfg.storageOptions;
if (storageOpts !== void 0 && storageOpts !== null) {
if (!storageOpts || typeof storageOpts !== "object" || Array.isArray(storageOpts)) throw new Error("storageOptions must be an object");
storageOptions = {};
for (const [key, valueLocal] of Object.entries(storageOpts)) {
if (typeof valueLocal !== "string") throw new Error(`storageOptions.${key} must be a string`);
storageOptions[key] = resolveEnvVars(valueLocal);
}
}
return {
embedding: {
provider,
model,
apiKey: typeof embedding.apiKey === "string" ? resolveEnvVars(embedding.apiKey) : void 0,
baseUrl: typeof embedding.baseUrl === "string" ? resolveEnvVars(embedding.baseUrl) : void 0,
dimensions
},
dreaming,
dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH,
autoCapture: cfg.autoCapture === true,
autoRecall: cfg.autoRecall !== false,
captureMaxChars,
...customTriggers ? { customTriggers } : {},
recallMaxChars,
...storageOptions ? { storageOptions } : {}
};
},
uiHints: {
"embedding.provider": {
label: "Embedding Provider",
placeholder: "openai",
help: "Memory embedding provider adapter to use (for example openai, github-copilot, ollama)"
},
"embedding.apiKey": {
label: "OpenAI API Key",
sensitive: true,
placeholder: "sk-proj-...",
help: "Optional API key override for OpenAI-compatible embeddings; omit to use configured provider auth"
},
"embedding.baseUrl": {
label: "Base URL",
placeholder: "https://api.openai.com/v1",
help: "Optional provider or OpenAI-compatible embedding endpoint base URL",
advanced: true
},
"embedding.dimensions": {
label: "Dimensions",
placeholder: "1536",
help: "Vector dimensions for custom models (required for non-standard models)",
advanced: true
},
"embedding.model": {
label: "Embedding Model",
placeholder: DEFAULT_MODEL,
help: "OpenAI embedding model to use"
},
dbPath: {
label: "Database Path",
placeholder: "~/.openclaw/memory/lancedb",
advanced: true,
help: "Local filesystem path or cloud storage URI (s3://, gs://) for LanceDB database"
},
autoCapture: {
label: "Auto-Capture",
help: "Automatically capture important information from conversations"
},
autoRecall: {
label: "Auto-Recall",
help: "Automatically inject relevant memories into context"
},
captureMaxChars: {
label: "Capture Max Chars",
help: "Maximum message length eligible for auto-capture",
advanced: true,
placeholder: String(500)
},
customTriggers: {
label: "Custom Triggers",
help: "Literal phrases that should make auto-capture consider a message memory-worthy",
advanced: true
},
recallMaxChars: {
label: "Recall Query Max Chars",
help: "Maximum prompt/query length embedded for memory recall. Lower for small local embedding models.",
advanced: true,
placeholder: String(DEFAULT_RECALL_MAX_CHARS)
},
storageOptions: {
label: "Storage Options",
sensitive: true,
advanced: true,
help: "Storage configuration options (access_key, secret_key, endpoint, etc.); supports ${ENV_VAR} values"
}
}
};
//#endregion
export { vectorDimsForModel as a, memoryConfigSchema as i, DEFAULT_RECALL_MAX_CHARS as n, MEMORY_CATEGORIES as r, DEFAULT_CAPTURE_MAX_CHARS as t };