feat(memory-lancedb): Custom OpenAI BaseURL & Dimensions Support (#17874)

* feat(memory-lancedb): add custom baseUrl and dimensions support

* fix(memory-lancedb): strict model typing and safe dimension resolution

* style: fix formatting in memory-lancedb config

* fix(memory-lancedb): sync manifest schema with new embedding options

---------

Co-authored-by: OpenClaw Bot <bot@openclaw.ai>
This commit is contained in:
Rishabh Jain
2026-02-27 10:56:09 -05:00
committed by GitHub
parent 62fa65ec85
commit 6675aacb5e
3 changed files with 47 additions and 8 deletions

View File

@@ -5,8 +5,10 @@ import { join } from "node:path";
export type MemoryConfig = { export type MemoryConfig = {
embedding: { embedding: {
provider: "openai"; provider: "openai";
model?: string; model: string;
apiKey: string; apiKey: string;
baseUrl?: string;
dimensions?: number;
}; };
dbPath?: string; dbPath?: string;
autoCapture?: boolean; autoCapture?: boolean;
@@ -81,7 +83,9 @@ function resolveEnvVars(value: string): string {
function resolveEmbeddingModel(embedding: Record<string, unknown>): string { function resolveEmbeddingModel(embedding: Record<string, unknown>): string {
const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL; const model = typeof embedding.model === "string" ? embedding.model : DEFAULT_MODEL;
vectorDimsForModel(model); if (typeof embedding.dimensions !== "number") {
vectorDimsForModel(model);
}
return model; return model;
} }
@@ -101,7 +105,7 @@ export const memoryConfigSchema = {
if (!embedding || typeof embedding.apiKey !== "string") { if (!embedding || typeof embedding.apiKey !== "string") {
throw new Error("embedding.apiKey is required"); throw new Error("embedding.apiKey is required");
} }
assertAllowedKeys(embedding, ["apiKey", "model"], "embedding config"); assertAllowedKeys(embedding, ["apiKey", "model", "baseUrl", "dimensions"], "embedding config");
const model = resolveEmbeddingModel(embedding); const model = resolveEmbeddingModel(embedding);
@@ -119,6 +123,9 @@ export const memoryConfigSchema = {
provider: "openai", provider: "openai",
model, model,
apiKey: resolveEnvVars(embedding.apiKey), apiKey: resolveEnvVars(embedding.apiKey),
baseUrl:
typeof embedding.baseUrl === "string" ? resolveEnvVars(embedding.baseUrl) : undefined,
dimensions: typeof embedding.dimensions === "number" ? embedding.dimensions : undefined,
}, },
dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH, dbPath: typeof cfg.dbPath === "string" ? cfg.dbPath : DEFAULT_DB_PATH,
autoCapture: cfg.autoCapture === true, autoCapture: cfg.autoCapture === true,
@@ -133,6 +140,18 @@ export const memoryConfigSchema = {
placeholder: "sk-proj-...", placeholder: "sk-proj-...",
help: "API key for OpenAI embeddings (or use ${OPENAI_API_KEY})", help: "API key for OpenAI embeddings (or use ${OPENAI_API_KEY})",
}, },
"embedding.baseUrl": {
label: "Base URL",
placeholder: "https://api.openai.com/v1",
help: "Base URL for compatible providers (e.g. http://localhost:11434/v1)",
advanced: true,
},
"embedding.dimensions": {
label: "Dimensions",
placeholder: "1536",
help: "Vector dimensions for custom models (required for non-standard models)",
advanced: true,
},
"embedding.model": { "embedding.model": {
label: "Embedding Model", label: "Embedding Model",
placeholder: DEFAULT_MODEL, placeholder: DEFAULT_MODEL,

View File

@@ -166,8 +166,9 @@ class Embeddings {
constructor( constructor(
apiKey: string, apiKey: string,
private model: string, private model: string,
baseUrl?: string,
) { ) {
this.client = new OpenAI({ apiKey }); this.client = new OpenAI({ apiKey, baseURL: baseUrl });
} }
async embed(text: string): Promise<number[]> { async embed(text: string): Promise<number[]> {
@@ -293,9 +294,11 @@ const memoryPlugin = {
register(api: OpenClawPluginApi) { register(api: OpenClawPluginApi) {
const cfg = memoryConfigSchema.parse(api.pluginConfig); const cfg = memoryConfigSchema.parse(api.pluginConfig);
const resolvedDbPath = api.resolvePath(cfg.dbPath!); const resolvedDbPath = api.resolvePath(cfg.dbPath!);
const vectorDim = vectorDimsForModel(cfg.embedding.model ?? "text-embedding-3-small"); const { model, dimensions, apiKey, baseUrl } = cfg.embedding;
const vectorDim = dimensions ?? vectorDimsForModel(model);
const db = new MemoryDB(resolvedDbPath, vectorDim); const db = new MemoryDB(resolvedDbPath, vectorDim);
const embeddings = new Embeddings(cfg.embedding.apiKey, cfg.embedding.model!); const embeddings = new Embeddings(apiKey, model, baseUrl);
api.logger.info(`memory-lancedb: plugin registered (db: ${resolvedDbPath}, lazy init)`); api.logger.info(`memory-lancedb: plugin registered (db: ${resolvedDbPath}, lazy init)`);

View File

@@ -13,6 +13,18 @@
"placeholder": "text-embedding-3-small", "placeholder": "text-embedding-3-small",
"help": "OpenAI embedding model to use" "help": "OpenAI embedding model to use"
}, },
"embedding.baseUrl": {
"label": "Base URL",
"placeholder": "https://api.openai.com/v1",
"help": "Base URL for compatible providers (e.g. http://localhost:11434/v1)",
"advanced": true
},
"embedding.dimensions": {
"label": "Dimensions",
"placeholder": "1536",
"help": "Vector dimensions for custom models (required for non-standard models)",
"advanced": true
},
"dbPath": { "dbPath": {
"label": "Database Path", "label": "Database Path",
"placeholder": "~/.openclaw/memory/lancedb", "placeholder": "~/.openclaw/memory/lancedb",
@@ -45,8 +57,13 @@
"type": "string" "type": "string"
}, },
"model": { "model": {
"type": "string", "type": "string"
"enum": ["text-embedding-3-small", "text-embedding-3-large"] },
"baseUrl": {
"type": "string"
},
"dimensions": {
"type": "number"
} }
}, },
"required": ["apiKey"] "required": ["apiKey"]