From 0c27e521c4516f728e61cc9c06d6bbf0e68b56cb Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 4 Aug 2026 03:24:53 +0000 Subject: [PATCH] fix(config): preserved LM Studio native modalities Keep the richer LM Studio /api/v0/models input metadata ahead of the thin OpenAI-compatible row while retaining row-first behavior for generic openai-models-list discovery. Add a regression covering a text-only /v1/models row paired with a native VLM record. --- .../src/config/model-discovery.ts | 4 +-- .../coding-agent/test/model-discovery.test.ts | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/coding-agent/src/config/model-discovery.ts b/packages/coding-agent/src/config/model-discovery.ts index 0e98ad377..ba54b12ca 100644 --- a/packages/coding-agent/src/config/model-discovery.ts +++ b/packages/coding-agent/src/config/model-discovery.ts @@ -826,8 +826,8 @@ export async function discoverOpenAIModelsList( baseUrl, reasoning: reference?.reasoning ?? false, thinking: inheritReferenceThinking(undefined, reference, providerConfig.provider), - input: extractOpenAIModelsListInputCapabilities(item) ?? - nativeMetadataForModel?.input ?? + input: nativeMetadataForModel?.input ?? + extractOpenAIModelsListInputCapabilities(item) ?? reference?.input ?? ["text"], ...(providerConfig.discovery.type === "lm-studio" ? { imageInputDecoder: "stb" as const } : {}), // Proxy/gateway pricing is provider-specific and rarely matches diff --git a/packages/coding-agent/test/model-discovery.test.ts b/packages/coding-agent/test/model-discovery.test.ts index 15f1d2f19..0c98a8c00 100644 --- a/packages/coding-agent/test/model-discovery.test.ts +++ b/packages/coding-agent/test/model-discovery.test.ts @@ -2212,6 +2212,38 @@ providers: expect(registry.find("openai-test", "medium")?.input).toEqual(["text"]); }); + test("lm-studio discovery keeps native VLM modalities over a thin OpenAI row", async () => { + writeRawModelsJson({ + "lm-studio-test": { + baseUrl: "http://127.0.0.1:9995", + api: "openai-completions", + auth: "none", + discovery: { type: "lm-studio" }, + }, + }); + const fetchMock: FetchImpl = async input => { + const url = String(input); + if (url === "http://127.0.0.1:9995/v1/models") { + return new Response(JSON.stringify({ data: [{ id: "local-vlm", object: "model", input: ["text"] }] }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + if (url === "http://127.0.0.1:9995/api/v0/models") { + return new Response( + JSON.stringify({ + data: [{ id: "local-vlm", type: "vlm", capabilities: ["vision"], state: "loaded" }], + }), + { status: 200, headers: { "Content-Type": "application/json" } }, + ); + } + throw new Error(`Unexpected URL: ${url}`); + }; + const registry = new ModelRegistry(authStorage, modelsJsonPath, { fetch: fetchMock }); + await registry.refresh(); + expect(registry.find("lm-studio-test", "local-vlm")?.input).toEqual(["text", "image"]); + }); + test("proxy discovery honors API-reported context_length and endpoint routing", async () => { writeRawModelsJson({ "proxy-test": {