From 876e72671a0b82b579a9783eb86f452a4a026a52 Mon Sep 17 00:00:00 2001 From: ginnoir Date: Wed, 8 Jul 2026 16:15:19 -0500 Subject: [PATCH] fix(agent): validate assistant model requests --- src/lib/llm/models.ts | 9 ++++---- tests/unit/llm-models.test.ts | 41 ++++++++++++++++++++++++++++++++++- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/lib/llm/models.ts b/src/lib/llm/models.ts index fbbf396..5ad6fca 100644 --- a/src/lib/llm/models.ts +++ b/src/lib/llm/models.ts @@ -41,9 +41,8 @@ export function normalizeLlmModelsPayload(payload: unknown): LlmModelOption[] { if (typeof row !== "object" || row === null || !("id" in row)) continue; const id = (row as { id?: unknown }).id; if (typeof id !== "string") continue; - const trimmed = id.trim(); - if (!isValidLlmModelId(trimmed)) continue; - ids.add(trimmed); + if (!isValidLlmModelId(id)) continue; + ids.add(id); } return [...ids].sort((a, b) => a.localeCompare(b)).map((id) => ({ id, label: id })); @@ -101,8 +100,8 @@ export function resolveAssistantModel(options: { ? options.fallbackModel : (options.models[0]?.id ?? options.fallbackModel); - if (options.requestedModel) { - if (!available.has(options.requestedModel)) { + if (options.requestedModel !== null && options.requestedModel !== undefined) { + if (!isValidLlmModelId(options.requestedModel) || !available.has(options.requestedModel)) { return { ok: false, model: fallback, error: "Invalid assistant model" }; } return { ok: true, model: options.requestedModel }; diff --git a/tests/unit/llm-models.test.ts b/tests/unit/llm-models.test.ts index 8fdde69..2bc2879 100644 --- a/tests/unit/llm-models.test.ts +++ b/tests/unit/llm-models.test.ts @@ -29,7 +29,13 @@ describe("normalizeLlmModelsPayload", () => { it("ignores invalid or empty model rows", () => { const models = normalizeLlmModelsPayload({ - data: [{ id: "" }, { id: " " }, { id: "bad model" }, { object: "model" }], + data: [ + { id: "" }, + { id: " " }, + { id: "bad model" }, + { id: " llama3.2 " }, + { object: "model" }, + ], }); assert.deepEqual(models, []); @@ -111,6 +117,39 @@ describe("resolveAssistantModel", () => { }); it("rejects invalid requested models", () => { + const resolved = resolveAssistantModel({ + requestedModel: "bad model", + savedModel: null, + fallbackModel: "llama3.2", + models: [ + { id: "llama3.2", label: "llama3.2" }, + { id: "bad model", label: "bad model" }, + ], + }); + + assert.deepEqual(resolved, { + ok: false, + model: "llama3.2", + error: "Invalid assistant model", + }); + }); + + it("rejects empty requested models", () => { + const resolved = resolveAssistantModel({ + requestedModel: "", + savedModel: null, + fallbackModel: "llama3.2", + models: [{ id: "llama3.2", label: "llama3.2" }], + }); + + assert.deepEqual(resolved, { + ok: false, + model: "llama3.2", + error: "Invalid assistant model", + }); + }); + + it("rejects unavailable requested models", () => { const resolved = resolveAssistantModel({ requestedModel: "missing", savedModel: null,