From 9be4fd0a0ea094e4d99fa08652f6d82231df68eb Mon Sep 17 00:00:00 2001 From: Ravi Tharuma <25951435+RaviTharuma@users.noreply.github.com> Date: Fri, 28 Aug 2026 03:14:45 +0200 Subject: [PATCH] fix(models): hide unsupported Gemini Live discovery (#11710) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Obrigado! Validado em lote combinado (8 PRs, release/v3.8.51): - Follow-up correto de #10590/#10591 — omite modelos Gemini Live-only da descoberta dinâmica, preserva chat para modelos híbridos. - Teste focado atualizado: `tests/unit/gemini-models-parser.test.ts` — 6/6 verde. - Gates estáticos do lote OK (typecheck, lint sem regressão, complexity, cognitive, file-size, changelog-integrity). --- changelog.d/fixes/10591-gemini-live-catalog.md | 1 + src/lib/providerModels/geminiModelsParser.ts | 18 ++++++++++++++---- tests/unit/gemini-models-parser.test.ts | 15 +++++++++++---- 3 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 changelog.d/fixes/10591-gemini-live-catalog.md diff --git a/changelog.d/fixes/10591-gemini-live-catalog.md b/changelog.d/fixes/10591-gemini-live-catalog.md new file mode 100644 index 0000000000..7663591649 --- /dev/null +++ b/changelog.d/fixes/10591-gemini-live-catalog.md @@ -0,0 +1 @@ +- Stop advertising Gemini Live-only models as supported audio endpoints until OmniRoute proxies the bidirectional Live protocol. diff --git a/src/lib/providerModels/geminiModelsParser.ts b/src/lib/providerModels/geminiModelsParser.ts index e4fd1bfdd7..9d4aa68de8 100644 --- a/src/lib/providerModels/geminiModelsParser.ts +++ b/src/lib/providerModels/geminiModelsParser.ts @@ -6,7 +6,7 @@ * - predict → "images" (Imagen image generation) * - predictLongRunning → "videos" (Veo video generation) * - embedContent → "embeddings" - * - bidiGenerateContent → "audio" (Live real-time audio) + * - bidiGenerateContent → ignored (Gemini Live is not proxied) * * Model-id heuristics refine the long-running bucket because Google exposes both * Imagen and Veo via long-running methods on the same endpoint: @@ -19,7 +19,7 @@ * * This is shared by the `gemini` discovery config and the `vertex` / * `vertex-partner` (incl. Vertex AI Express key) discovery branches, so every - * model the account can access — chat, image, video, audio and embeddings — + * supported model the account can access — chat, image, video and embeddings — * surfaces dynamically instead of being limited to the small static registry. */ const METHOD_TO_ENDPOINT: Record = { @@ -27,7 +27,6 @@ const METHOD_TO_ENDPOINT: Record = { embedContent: "embeddings", predict: "images", predictLongRunning: "videos", - bidiGenerateContent: "audio", generateAnswer: "chat", }; @@ -37,6 +36,7 @@ const IGNORED_METHODS = new Set([ "createCachedContent", "batchGenerateContent", "asyncBatchEmbedContent", + "bidiGenerateContent", ]); const RETIRED_GEMINI_MODEL_IDS = new Set(["gemini-3.5-flash"]); @@ -78,6 +78,13 @@ export function parseGeminiModelsList(data: any): GeminiDiscoveryModel[] { endpoints.add("images"); } + if ( + endpoints.size === 0 && + methods.length > 0 && + methods.every((method) => IGNORED_METHODS.has(method)) + ) { + return null; + } if (endpoints.size === 0) endpoints.add("chat"); return { @@ -91,5 +98,8 @@ export function parseGeminiModelsList(data: any): GeminiDiscoveryModel[] { ...(m.thinking === true ? { supportsThinking: true } : {}), } as GeminiDiscoveryModel; }) - .filter((model: GeminiDiscoveryModel) => !RETIRED_GEMINI_MODEL_IDS.has(model.id)); + .filter( + (model: GeminiDiscoveryModel | null): model is GeminiDiscoveryModel => + Boolean(model) && !RETIRED_GEMINI_MODEL_IDS.has(model.id) + ); } diff --git a/tests/unit/gemini-models-parser.test.ts b/tests/unit/gemini-models-parser.test.ts index 8c9da3dac4..f9463d61b5 100644 --- a/tests/unit/gemini-models-parser.test.ts +++ b/tests/unit/gemini-models-parser.test.ts @@ -72,14 +72,21 @@ test("parseGeminiModelsList maps generateContent image models to the chat endpoi assert.deepEqual(proImage!.supportedEndpoints, ["chat"]); }); -test("parseGeminiModelsList maps embedContent and bidiGenerateContent", () => { +test("parseGeminiModelsList maps embeddings without advertising unsupported Gemini Live", () => { const models = parseGeminiModelsList(SAMPLE); assert.deepEqual(models.find((m) => m.id === "text-embedding-004")!.supportedEndpoints, [ "embeddings", ]); - assert.deepEqual(models.find((m) => m.id === "gemini-live-2.5-flash")!.supportedEndpoints, [ - "audio", - ]); + assert.equal(models.some((m) => m.id === "gemini-live-2.5-flash"), false); + const [hybrid] = parseGeminiModelsList({ + models: [ + { + name: "models/gemini-live-hybrid", + supportedGenerationMethods: ["generateContent", "bidiGenerateContent"], + }, + ], + }); + assert.deepEqual(hybrid.supportedEndpoints, ["chat"]); }); test("parseGeminiModelsList maps Veo predictLongRunning models to the videos endpoint", () => {