Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/server/management/model-routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,9 @@ export async function handleModelRoutes(ctx: ManagementContext): Promise<Respons
}

if (url.pathname === "/api/custom-models" && req.method === "POST") {
let body: { provider?: unknown; modelId?: unknown; displayName?: unknown; contextWindow?: unknown; inputModalities?: unknown };
let body: unknown;
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
if (!isPlainRecord(body)) return jsonResponse({ error: "invalid JSON body" }, 400);
const provider = typeof body.provider === "string" ? body.provider.trim() : "";
const modelId = typeof body.modelId === "string" ? body.modelId.trim() : "";
if (!provider || !modelId) return jsonResponse({ error: "provider and modelId are required" }, 400);
Expand Down Expand Up @@ -357,8 +358,9 @@ export async function handleModelRoutes(ctx: ManagementContext): Promise<Respons
if (customPutMatch && req.method === "PUT") {
let id: string;
try { id = decodeURIComponent(customPutMatch[1]); } catch { return jsonResponse({ error: "invalid id encoding" }, 400); }
let body: { displayName?: unknown; contextWindow?: unknown; inputModalities?: unknown; modelId?: unknown };
let body: unknown;
try { body = await readManagementJsonBody(req); } catch (error) { rethrowManagementBodyTooLarge(error); return jsonResponse({ error: "invalid JSON body" }, 400); }
if (!isPlainRecord(body)) return jsonResponse({ error: "invalid JSON body" }, 400);
const list = config.customModels ?? [];
const idx = list.findIndex(cm => cm.id === id);
if (idx === -1) return jsonResponse({ error: "not found" }, 404);
Expand Down
14 changes: 14 additions & 0 deletions tests/catalog-input-modality-enum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,20 @@ describe("custom-model API rejects out-of-enum input modalities", () => {
expect(persistCalls).toBe(0);
});

test("custom-model writes reject non-object JSON bodies", async () => {
for (const body of [null, [], "model"]) {
persistCalls = 0;
const posted = await callCustomModels("POST", body);
expect(posted?.status).toBe(400);
expect(await posted!.json()).toEqual({ error: "invalid JSON body" });

const put = await callCustomModels("PUT", body, "/api/custom-models/existing-uuid");
expect(put?.status).toBe(400);
expect(await put!.json()).toEqual({ error: "invalid JSON body" });
expect(persistCalls).toBe(0);
}
});

test("an accepted modality set still passes", async () => {
persistCalls = 0;
const res = await callCustomModels("POST", {
Expand Down
Loading