diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx index 4724289e0e..a20e14d2e1 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.tsx +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.tsx @@ -1542,6 +1542,29 @@ export default function ProviderDetailPage() { } }; + const [clearingModels, setClearingModels] = useState(false); + const handleClearAllModels = async () => { + if (clearingModels) return; + if (!confirm(t("clearAllModelsConfirm"))) return; + setClearingModels(true); + try { + const res = await fetch( + `/api/provider-models?provider=${encodeURIComponent(providerStorageAlias)}&all=true`, + { method: "DELETE" } + ); + if (res.ok) { + await fetchProviderModelMeta(); + notify.success(t("clearAllModelsSuccess")); + } else { + notify.error(t("clearAllModelsFailed")); + } + } catch { + notify.error(t("clearAllModelsFailed")); + } finally { + setClearingModels(false); + } + }; + const customMap = useMemo(() => buildCompatMap(modelMeta.customModels), [modelMeta.customModels]); const overrideMap = useMemo( () => buildCompatMap(modelMeta.modelCompatOverrides), @@ -1650,10 +1673,25 @@ export default function ProviderDetailPage() { ); + const clearAllButton = modelMeta.customModels.length > 0 && ( + + ); + if (isCompatible) { return (
-
{autoSyncToggle}
+
+ {autoSyncToggle} + {clearAllButton} +
{autoSyncToggle} + {clearAllButton} {!canImportModels && ( {t("addConnectionToImport")} )} diff --git a/src/app/api/provider-models/route.ts b/src/app/api/provider-models/route.ts index e919e0bf7d..c230d8fcca 100644 --- a/src/app/api/provider-models/route.ts +++ b/src/app/api/provider-models/route.ts @@ -3,6 +3,7 @@ import { getAllCustomModels, addCustomModel, removeCustomModel, + replaceCustomModels, updateCustomModel, getModelCompatOverrides, mergeModelCompatOverride, @@ -242,11 +243,30 @@ export async function DELETE(request) { const provider = searchParams.get("provider"); const modelId = searchParams.get("model"); - if (!provider || !modelId) { + if (!provider) { return Response.json( { error: { - message: "provider and model query params are required", + message: "provider query param is required", + type: "validation_error", + }, + }, + { status: 400 } + ); + } + + // DELETE /api/provider-models?provider=&all=true — clear all models + const all = searchParams.get("all"); + if (all === "true") { + await replaceCustomModels(provider, []); + return Response.json({ cleared: true }); + } + + if (!modelId) { + return Response.json( + { + error: { + message: "model query param is required (or use all=true)", type: "validation_error", }, }, diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 02083438af..73cd3287b4 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -1383,6 +1383,10 @@ "autoSyncEnabled": "Auto-sync enabled — models will refresh periodically", "autoSyncDisabled": "Auto-sync disabled", "autoSyncToggleFailed": "Failed to toggle auto-sync", + "clearAllModels": "Clear All Models", + "clearAllModelsConfirm": "Are you sure you want to remove all models for this provider? This cannot be undone.", + "clearAllModelsSuccess": "All models cleared", + "clearAllModelsFailed": "Failed to clear models", "addConnectionToImport": "Add a connection to enable importing.", "noModelsConfigured": "No models configured", "connectionCount": "{count} connection(s)", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 01d64dc86c..2072515c01 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -1383,6 +1383,10 @@ "autoSyncEnabled": "已启用自动同步 — 模型列表将定期刷新", "autoSyncDisabled": "已禁用自动同步", "autoSyncToggleFailed": "切换自动同步失败", + "clearAllModels": "清除所有模型", + "clearAllModelsConfirm": "确定要删除此提供商的所有模型吗?此操作不可撤销。", + "clearAllModelsSuccess": "已清除所有模型", + "clearAllModelsFailed": "清除模型失败", "addConnectionToImport": "添加连接以启用导入。", "noModelsConfigured": "尚未配置模型", "connectionCount": "{count} 连接",