feat(memory): support custom embeddings (baseUrl, dimensions) for Qwen compatibility

This commit is contained in:
lluviaoscuradeldoce-design 2026-01-29 18:01:57 -05:00
parent c3f38398e4
commit a8d20cf5ca
2 changed files with 14 additions and 6 deletions

View File

@ -7,6 +7,8 @@ export type MemoryConfig = {
provider: "openai"; provider: "openai";
model?: string; model?: string;
apiKey: string; apiKey: string;
baseUrl?: string;
dimensions?: number;
}; };
dbPath?: string; dbPath?: string;
autoCapture?: boolean; autoCapture?: boolean;
@ -54,7 +56,10 @@ 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); // Only validate model if dimensions not explicitly provided
if (typeof embedding.dimensions !== "number") {
vectorDimsForModel(model);
}
return model; return model;
} }
@ -70,7 +75,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);
@ -79,6 +84,8 @@ export const memoryConfigSchema = {
provider: "openai", provider: "openai",
model, model,
apiKey: resolveEnvVars(embedding.apiKey), apiKey: resolveEnvVars(embedding.apiKey),
baseUrl: typeof embedding.baseUrl === "string" ? 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 !== false, autoCapture: cfg.autoCapture !== false,

View File

@ -52,7 +52,7 @@ class MemoryDB {
constructor( constructor(
private readonly dbPath: string, private readonly dbPath: string,
private readonly vectorDim: number, private readonly vectorDim: number,
) {} ) { }
private async ensureInitialized(): Promise<void> { private async ensureInitialized(): Promise<void> {
if (this.table) return; if (this.table) return;
@ -156,8 +156,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 });
} }
async embed(text: string): Promise<number[]> { async embed(text: string): Promise<number[]> {
@ -223,9 +224,9 @@ const memoryPlugin = {
register(api: MoltbotPluginApi) { register(api: MoltbotPluginApi) {
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 vectorDim = cfg.embedding.dimensions ?? vectorDimsForModel(cfg.embedding.model ?? "text-embedding-3-small");
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(cfg.embedding.apiKey, cfg.embedding.model!, cfg.embedding.baseUrl);
api.logger.info( api.logger.info(
`memory-lancedb: plugin registered (db: ${resolvedDbPath}, lazy init)`, `memory-lancedb: plugin registered (db: ${resolvedDbPath}, lazy init)`,