From 557509ef8431f5dedd978892fa6e5261d3e685e7 Mon Sep 17 00:00:00 2001 From: tombii Date: Wed, 1 Apr 2026 23:01:30 +0200 Subject: [PATCH 1/2] fix(model-sync): skip replace when auto-sync returns empty model list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent auto-sync from wiping manually-imported models when the upstream /models endpoint fails, times out, or returns an empty list. Added `allowEmpty` option (default false) to replaceCustomModels — callers that intentionally clear all models (DELETE ?all=true) pass `allowEmpty: true`. Co-Authored-By: Claude Sonnet 4.6 --- src/app/api/provider-models/route.ts | 2 +- src/lib/db/models.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/app/api/provider-models/route.ts b/src/app/api/provider-models/route.ts index c230d8fc..5fbfcd3b 100644 --- a/src/app/api/provider-models/route.ts +++ b/src/app/api/provider-models/route.ts @@ -258,7 +258,7 @@ export async function DELETE(request) { // DELETE /api/provider-models?provider=&all=true — clear all models const all = searchParams.get("all"); if (all === "true") { - await replaceCustomModels(provider, []); + await replaceCustomModels(provider, [], { allowEmpty: true }); return Response.json({ cleared: true }); } diff --git a/src/lib/db/models.ts b/src/lib/db/models.ts index 671fbc14..9c1538e1 100644 --- a/src/lib/db/models.ts +++ b/src/lib/db/models.ts @@ -383,7 +383,8 @@ export async function replaceCustomModels( source?: string; apiFormat?: string; supportedEndpoints?: string[]; - }> + }>, + { allowEmpty = false }: { allowEmpty?: boolean } = {} ) { const db = getDbInstance(); const existing = await getCustomModels(providerId); @@ -420,6 +421,12 @@ export async function replaceCustomModels( }); if (merged.length === 0) { + // Guard: skip destructive clear when the caller hasn't explicitly opted in. + // This prevents auto-sync from wiping manually-imported models when the + // upstream /models endpoint fails, times out, or returns an empty list. + if (!allowEmpty) { + return Array.isArray(existing) ? existing : []; + } db.prepare("DELETE FROM key_value WHERE namespace = 'customModels' AND key = ?").run( providerId ); From c214c6c120180fd0ec40c10a4659d9a37293d922 Mon Sep 17 00:00:00 2001 From: tombii Date: Wed, 1 Apr 2026 23:18:30 +0200 Subject: [PATCH 2/2] refactor(model-sync): move empty-list guard to early return Co-Authored-By: Claude Sonnet 4.6 --- src/lib/db/models.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib/db/models.ts b/src/lib/db/models.ts index 9c1538e1..74234a7f 100644 --- a/src/lib/db/models.ts +++ b/src/lib/db/models.ts @@ -386,6 +386,14 @@ export async function replaceCustomModels( }>, { allowEmpty = false }: { allowEmpty?: boolean } = {} ) { + // Guard: skip destructive clear when the caller hasn't explicitly opted in. + // This prevents auto-sync from wiping manually-imported models when the + // upstream /models endpoint fails, times out, or returns an empty list. + if (models.length === 0 && !allowEmpty) { + const existing = await getCustomModels(providerId); + return Array.isArray(existing) ? existing : []; + } + const db = getDbInstance(); const existing = await getCustomModels(providerId); const existingMap = new Map(); @@ -421,12 +429,6 @@ export async function replaceCustomModels( }); if (merged.length === 0) { - // Guard: skip destructive clear when the caller hasn't explicitly opted in. - // This prevents auto-sync from wiping manually-imported models when the - // upstream /models endpoint fails, times out, or returns an empty list. - if (!allowEmpty) { - return Array.isArray(existing) ? existing : []; - } db.prepare("DELETE FROM key_value WHERE namespace = 'customModels' AND key = ?").run( providerId );