From 0d97fa6fa2f89ccfc78277deb2b490664873dfa3 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Thu, 4 Jun 2026 19:04:34 -0300 Subject: [PATCH] fix(providers): empty refresh must not resurface just-cleared synced models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regression from #3148 (synced models authoritative) × #3120: on `?refresh=true` with no remote models, persistDiscoveredModels([]) clears THIS connection's synced cache, but the handler's top-level providerSyncedModels snapshot was stale and leaked the cleared models back into the local-catalog fallback. Re-read the provider's synced models in the empty-discovery branch so the response reflects the cleared state (union across remaining connections, or the static catalog when none remain). Fixes the pre-existing failing test 'provider models route clears cached discovery when a refresh returns no remote models' (the regression guard). --- src/app/api/providers/[id]/models/route.ts | 50 ++++++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/app/api/providers/[id]/models/route.ts b/src/app/api/providers/[id]/models/route.ts index 83d7a77f312..f2d4bcb2dce 100755 --- a/src/app/api/providers/[id]/models/route.ts +++ b/src/app/api/providers/[id]/models/route.ts @@ -923,10 +923,52 @@ export async function GET( }); } - const fallback = buildLocalCatalogResponse( - "No remote models discovered — using local catalog" - ); - if (fallback) return fallback; + // Empty discovery just cleared THIS connection's synced cache (via + // persistDiscoveredModels([])). `providerSyncedModels` was read at the top + // of the handler and is now stale, so it must not leak the just-cleared + // models back into the response (#3148 made synced authoritative for the + // normal path; here we re-read the current state instead). Re-derive the + // local catalog from the provider's remaining synced models (union across + // its other connections) or the static catalog when none remain. + let freshSynced: Awaited> = []; + try { + freshSynced = await getSyncedAvailableModels(provider); + } catch { + /* DB unavailable — fall through to static catalog */ + } + const freshRegistry = freshSynced.length + ? freshSynced.map((m) => ({ + id: m.id, + name: m.name || m.id, + ...(m.apiFormat ? { apiFormat: m.apiFormat } : {}), + ...(m.supportedEndpoints ? { supportedEndpoints: m.supportedEndpoints } : {}), + })) + : getModelsByProviderId(provider) || []; + const freshSpecialty = freshSynced.length ? [] : getStaticModelsForProvider(provider) || []; + const freshLocal = mergeLocalCatalogModels(freshRegistry, freshSpecialty).map((model) => ({ + id: model.id, + name: model.name || model.id, + ...((model as Record).apiFormat + ? { apiFormat: (model as Record).apiFormat as string | undefined } + : {}), + ...((model as Record).supportedEndpoints + ? { + supportedEndpoints: (model as Record).supportedEndpoints as + | string[] + | undefined, + } + : {}), + ...(freshRegistry.length > 0 ? { owned_by: provider } : {}), + })); + if (freshLocal.length > 0) { + return buildResponse({ + provider, + connectionId, + models: freshLocal, + source: "local_catalog", + warning: "No remote models discovered — using local catalog", + }); + } return buildResponse({ provider,