fix(memory): reject empty lancedb embedding config

This commit is contained in:
Peter Steinberger
2026-04-28 02:01:19 +01:00
parent f8a15a06f2
commit 2d0cc1ee22
4 changed files with 44 additions and 6 deletions

View File

@@ -61,7 +61,7 @@ describe("memory-lancedb config", () => {
expect(parsed.embedding.provider).toBe("openai");
});
it("rejects empty embedding placeholders in the manifest schema", () => {
it("rejects empty embedding config in the manifest schema and runtime parser", () => {
const manifestResult = validateJsonSchemaValue({
schema: manifest.configSchema,
cacheKey: "memory-lancedb.manifest.empty-embedding",
@@ -71,6 +71,17 @@ describe("memory-lancedb config", () => {
});
expect(manifestResult.ok).toBe(false);
if (!manifestResult.ok) {
expect(manifestResult.errors.map((error) => error.text)).toContain(
"embedding: must NOT have fewer than 1 properties",
);
}
expect(() => {
memoryConfigSchema.parse({
embedding: {},
});
}).toThrow("embedding config must include at least one setting");
});
it("rejects empty embedding providers", () => {

View File

@@ -58,6 +58,7 @@ const EMBEDDING_DIMENSIONS: Record<string, number> = {
"text-embedding-3-small": 1536,
"text-embedding-3-large": 3072,
};
const EMBEDDING_CONFIG_KEYS = ["provider", "apiKey", "model", "baseUrl", "dimensions"] as const;
function assertAllowedKeys(value: Record<string, unknown>, allowed: string[], label: string) {
const unknown = Object.keys(value).filter((key) => !allowed.includes(key));
@@ -118,11 +119,10 @@ export const memoryConfigSchema = {
if (!embedding || typeof embedding !== "object" || Array.isArray(embedding)) {
throw new Error("embedding config required");
}
assertAllowedKeys(
embedding,
["provider", "apiKey", "model", "baseUrl", "dimensions"],
"embedding config",
);
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 model = resolveEmbeddingModel(embedding);
const provider = typeof embedding.provider === "string" ? embedding.provider.trim() : "openai";

View File

@@ -73,6 +73,7 @@
"type": "object",
"minProperties": 1,
"additionalProperties": false,
"minProperties": 1,
"properties": {
"apiKey": {
"type": "string"

View File

@@ -517,6 +517,32 @@ describe("plugins cli install", () => {
expect(runtimeLogs.some((line) => line.includes("requires configuration first"))).toBe(true);
});
it("enables config-gated bundled installs when provider-backed config is explicit", async () => {
const cfg = {
plugins: {
entries: {
"memory-lancedb": {
config: {
embedding: {
provider: "openai",
model: "text-embedding-3-small",
},
},
},
},
},
} as OpenClawConfig;
const enabledCfg = createEnabledPluginConfig("memory-lancedb");
loadConfig.mockReturnValue(cfg);
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
await runPluginsCommand(["plugins", "install", "memory-lancedb"]);
expect(enablePluginInConfig).toHaveBeenCalled();
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
expect(runtimeLogs.some((line) => line.includes("requires configuration first"))).toBe(false);
});
it("passes force through as overwrite mode for ClawHub installs", async () => {
const cfg = {
plugins: {