From 125fb81fa39f6edcd3ac4fc95924618348a402ff Mon Sep 17 00:00:00 2001 From: Chris Staley Date: Thu, 2 Apr 2026 11:41:28 -0600 Subject: [PATCH] feat(dashboard): Gemini Available Models reads from API sync, hide Custom Models --- .../dashboard/providers/[id]/page.tsx | 23 +++++++++++-- src/app/api/synced-available-models/route.ts | 33 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/app/api/synced-available-models/route.ts diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index e941a11b93..6b5ab3a4a4 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -835,6 +835,7 @@ export default function ProviderDetailPage() { customModels: CompatModelRow[]; modelCompatOverrides: Array; }>({ customModels: [], modelCompatOverrides: [] }); + const [syncedAvailableModels, setSyncedAvailableModels] = useState([]); const [compatSavingModelId, setCompatSavingModelId] = useState(null); const [applyingCodexAuthId, setApplyingCodexAuthId] = useState(null); const [exportingCodexAuthId, setExportingCodexAuthId] = useState(null); @@ -873,7 +874,11 @@ export default function ProviderDetailPage() { (OAUTH_PROVIDERS as any)[providerId] || (APIKEY_PROVIDERS as any)[providerId]; const isOAuth = !!(FREE_PROVIDERS as any)[providerId] || !!(OAUTH_PROVIDERS as any)[providerId]; - const models = getModelsByProviderId(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 + ? syncedAvailableModels + : registryModels; const providerAlias = getProviderAlias(providerId); const isManagedAvailableModelsProvider = isCompatible || providerId === "openrouter"; const isSearchProvider = providerId.endsWith("-search"); @@ -906,6 +911,20 @@ export default function ProviderDetailPage() { customModels: data.models || [], modelCompatOverrides: data.modelCompatOverrides || [], }); + // Fetch synced available models for Gemini + if (providerId === "gemini") { + try { + const syncRes = await fetch("/api/synced-available-models?provider=gemini", { + cache: "no-store", + }); + if (syncRes.ok) { + const syncData = await syncRes.json(); + setSyncedAvailableModels(syncData.models || []); + } + } catch { + // Non-critical + } + } } catch (e) { console.error("fetchProviderModelMeta", e); } @@ -2432,7 +2451,7 @@ export default function ProviderDetailPage() { {renderModelsSection()} {/* Custom Models — available for providers without managed available-model metadata */} - {!isManagedAvailableModelsProvider && ( + {!isManagedAvailableModelsProvider && providerId !== "gemini" && ( + * List synced available models for a provider (or all providers). + */ +export async function GET(request: Request) { + try { + if (!(await isAuthenticated(request))) { + return Response.json( + { error: { message: "Authentication required", type: "invalid_api_key" } }, + { status: 401 } + ); + } + + const { searchParams } = new URL(request.url); + const provider = searchParams.get("provider"); + + if (provider) { + const models = await getSyncedAvailableModels(provider); + return Response.json({ models }); + } + + const allModels = await getAllSyncedAvailableModels(); + return Response.json(allModels); + } catch { + return Response.json( + { error: { message: "Failed to fetch synced available models", type: "server_error" } }, + { status: 500 } + ); + } +}