From b4e674aeb0d0ea2efe2346eaf57f313c944cbace Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Thu, 2 Apr 2026 12:12:57 -0600 Subject: [PATCH] =?UTF-8?q?fix(gemini):=20no=20registry=20fallback=20?= =?UTF-8?q?=E2=80=94=20show=200=20models=20when=20no=20API=20keys=20exist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed hardcoded registry fallback for Gemini in dashboard, catalog, and v1beta endpoints. Without synced models (no API keys), Gemini shows nothing. Hardcoded entries are always removed from v1beta regardless of sync result. --- .../dashboard/providers/[id]/page.tsx | 4 +- src/app/api/v1/models/catalog.ts | 34 ++++++++-------- src/app/api/v1beta/models/route.ts | 39 +++++++++---------- 3 files changed, 36 insertions(+), 41 deletions(-) diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index 6b5ab3a4a4..ce09f91b3a 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -875,8 +875,8 @@ export default function ProviderDetailPage() { (APIKEY_PROVIDERS as any)[providerId]; const isOAuth = !!(FREE_PROVIDERS as any)[providerId] || !!(OAUTH_PROVIDERS as any)[providerId]; const registryModels = getModelsByProviderId(providerId); - // For Gemini: use synced API models if available, otherwise fall back to registry - const models = providerId === "gemini" && syncedAvailableModels.length > 0 + // For Gemini: always use synced API models (empty if no keys added yet) + const models = providerId === "gemini" ? syncedAvailableModels : registryModels; const providerAlias = getProviderAlias(providerId); diff --git a/src/app/api/v1/models/catalog.ts b/src/app/api/v1/models/catalog.ts index 3875efcb74..3eea6ea2c0 100644 --- a/src/app/api/v1/models/catalog.ts +++ b/src/app/api/v1/models/catalog.ts @@ -255,30 +255,28 @@ export async function getUnifiedModelsResponse( const registryEntry = REGISTRY[alias] || REGISTRY[canonicalProviderId]; const defaultContextLength = registryEntry?.defaultContextLength; - // Skip hardcoded Gemini models if synced models are available + // For Gemini: use synced API models exclusively (no hardcoded fallback) if (alias === "gemini") { try { const syncedModels = await getSyncedAvailableModels("gemini"); - if (syncedModels.length > 0) { - for (const sm of syncedModels) { - const aliasId = `gemini/${sm.id}`; - if (getModelIsHidden("gemini", sm.id)) continue; - models.push({ - id: aliasId, - object: "model", - created: timestamp, - owned_by: "gemini", - permission: [], - root: sm.id, - parent: null, - ...(sm.inputTokenLimit ? { context_length: sm.inputTokenLimit } : {}), - }); - } - continue; // Skip hardcoded models for this provider + for (const sm of syncedModels) { + const aliasId = `gemini/${sm.id}`; + if (getModelIsHidden("gemini", sm.id)) continue; + models.push({ + id: aliasId, + object: "model", + created: timestamp, + owned_by: "gemini", + permission: [], + root: sm.id, + parent: null, + ...(sm.inputTokenLimit ? { context_length: sm.inputTokenLimit } : {}), + }); } } catch { - // Fall through to hardcoded models + // No synced models — show nothing for Gemini } + continue; // Always skip hardcoded models for Gemini } for (const model of providerModels) { diff --git a/src/app/api/v1beta/models/route.ts b/src/app/api/v1beta/models/route.ts index cb36613e50..9680a40bc6 100644 --- a/src/app/api/v1beta/models/route.ts +++ b/src/app/api/v1beta/models/route.ts @@ -37,31 +37,28 @@ export async function GET() { } } - // Gemini: replace hardcoded entries with synced models when available + // Gemini: always replace hardcoded entries with synced models (no fallback) + // Always remove hardcoded gemini entries — even if sync returns empty + for (let i = models.length - 1; i >= 0; i--) { + if (typeof (models[i] as any).name === "string" && (models[i] as any).name.startsWith("models/gemini/")) { + models.splice(i, 1); + } + } try { const syncedGeminiModels = await getSyncedAvailableModels("gemini"); - if (syncedGeminiModels.length > 0) { - // Remove all hardcoded gemini entries (non-consecutive, so filter instead of splice) - for (let i = models.length - 1; i >= 0; i--) { - if (typeof (models[i] as any).name === "string" && (models[i] as any).name.startsWith("models/gemini/")) { - models.splice(i, 1); - } - } - // Add synced models - for (const m of syncedGeminiModels) { - models.push({ - name: `models/gemini/${m.id}`, - displayName: m.name || m.id, - ...(typeof m.description === "string" ? { description: m.description } : {}), - supportedGenerationMethods: ["generateContent"], - inputTokenLimit: typeof m.inputTokenLimit === "number" ? m.inputTokenLimit : 128000, - outputTokenLimit: typeof m.outputTokenLimit === "number" ? m.outputTokenLimit : 8192, - ...(m.supportsThinking === true ? { thinking: true } : {}), - }); - } + for (const m of syncedGeminiModels) { + models.push({ + name: `models/gemini/${m.id}`, + displayName: m.name || m.id, + ...(typeof m.description === "string" ? { description: m.description } : {}), + supportedGenerationMethods: ["generateContent"], + inputTokenLimit: typeof m.inputTokenLimit === "number" ? m.inputTokenLimit : 128000, + outputTokenLimit: typeof m.outputTokenLimit === "number" ? m.outputTokenLimit : 8192, + ...(m.supportsThinking === true ? { thinking: true } : {}), + }); } } catch { - // Non-critical + // No synced models — Gemini shows nothing } // Custom models (use stored metadata from provider APIs)