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": {