diff --git a/src/types/index.ts b/src/types/index.ts index c41d9bb..d1dfb7d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -9,13 +9,20 @@ export const LMStudioLoadedInstanceSchema = z.looseObject({ }), }) +/** + * Reasoning levels LM Studio is known to publish. Kept as documentation only: + * the schema accepts any string so a level added by a newer LM Studio build + * cannot fail validation for the whole response. + */ +export const KNOWN_REASONING_LEVELS = ["off", "on", "low", "medium", "high"] as const + /** Capabilities reported for a native v1 LLM record. */ export const LMStudioCapabilitiesSchema = z.looseObject({ vision: z.boolean(), trained_for_tool_use: z.boolean(), reasoning: z.looseObject({ - allowed_options: z.array(z.enum(["off", "on", "low", "medium", "high"])), - default: z.enum(["off", "on", "low", "medium", "high"]), + allowed_options: z.array(z.string()), + default: z.string(), }).optional(), }) diff --git a/test/plugin.test.ts b/test/plugin.test.ts index 4aa1446..584fea4 100644 --- a/test/plugin.test.ts +++ b/test/plugin.test.ts @@ -98,6 +98,26 @@ describe("LM Studio native API v1", () => { ) }) + it("keeps discovering when a model reports a reasoning level this release predates", async () => { + const fetcher = vi.fn(async () => modelsResponse([ + model({ + capabilities: { + vision: false, + trained_for_tool_use: true, + reasoning: { allowed_options: ["off", "low", "medium", "xhigh", "on"], default: "xhigh" }, + }, + }), + model({ key: "publisher/second", display_name: "Second" }), + ])) + + const response = await discoverModels("http://127.0.0.1:1234", { + fetch: fetcher as typeof fetch, + }) + + expect(response.models).toHaveLength(2) + expect(response.models[0]?.capabilities?.reasoning?.default).toBe("xhigh") + }) + it("rejects HTTP-200 error bodies instead of treating status as endpoint support", async () => { const fetcher = vi.fn(async () => new Response(JSON.stringify({ error: "Unexpected endpoint" }), { status: 200 }))