From fe28097817ff78fdc5d9d6762bd98bdfa9bdd3f0 Mon Sep 17 00:00:00 2001 From: silentoplayz Date: Sun, 28 Sep 2025 18:49:42 -0400 Subject: [PATCH] feat: refactor model import to a single backend endpoint This refactors the model import functionality to improve performance and user experience by centralizing the logic on the backend. Previously, the frontend would parse an imported JSON file and send an individual API request for each model, which was slow and inefficient. This change introduces a new backend endpoint, `/api/v1/models/import`, that accepts a list of model objects. The frontend now reads the selected JSON file, parses it, and sends the entire payload to the backend in a single request. The backend then processes this list, creating or updating models as necessary. This commit also includes the following fixes: - Handles cases where the imported JSON contains models without `meta` or `params` fields by providing default empty values. --- backend/open_webui/routers/models.py | 10 ++++--- src/lib/apis/models/index.ts | 8 ++--- .../components/admin/Settings/Models.svelte | 30 ++++++++++++------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/backend/open_webui/routers/models.py b/backend/open_webui/routers/models.py index 2ced14de4e..5c5a2dcd90 100644 --- a/backend/open_webui/routers/models.py +++ b/backend/open_webui/routers/models.py @@ -22,8 +22,6 @@ from fastapi import ( Request, status, Response, - UploadFile, - File, ) from fastapi.responses import FileResponse, StreamingResponse @@ -112,12 +110,16 @@ async def export_models(user=Depends(get_admin_user)): ############################ +class ModelsImportForm(BaseModel): + models: list[dict] + + @router.post("/import", response_model=bool) async def import_models( - user: str = Depends(get_admin_user), file: UploadFile = File(...) + user: str = Depends(get_admin_user), form_data: ModelsImportForm = (...) ): try: - data = json.loads(await file.read()) + data = form_data.models if isinstance(data, list): for model_data in data: # Here, you can add logic to validate model_data if needed diff --git a/src/lib/apis/models/index.ts b/src/lib/apis/models/index.ts index 63ca95f200..d324fa9173 100644 --- a/src/lib/apis/models/index.ts +++ b/src/lib/apis/models/index.ts @@ -31,18 +31,16 @@ export const getModels = async (token: string = '') => { return res; }; -export const importModels = async (token: string, file: File) => { +export const importModels = async (token: string, models: object[]) => { let error = null; - const formData = new FormData(); - formData.append('file', file); - const res = await fetch(`${WEBUI_API_BASE_URL}/models/import`, { method: 'POST', headers: { + 'Content-Type': 'application/json', authorization: `Bearer ${token}` }, - body: formData + body: JSON.stringify({ models: models }) }) .then(async (res) => { if (!res.ok) throw await res.json(); diff --git a/src/lib/components/admin/Settings/Models.svelte b/src/lib/components/admin/Settings/Models.svelte index 211d60efa5..1f0e33d1dd 100644 --- a/src/lib/components/admin/Settings/Models.svelte +++ b/src/lib/components/admin/Settings/Models.svelte @@ -465,18 +465,28 @@ let modelsImportInProgress = false; type="file" accept=".json" hidden - on:change={async () => { + on:change={() => { if (importFiles.length > 0) { - modelsImportInProgress = true; - const res = await importModels(localStorage.token, importFiles[0]); - modelsImportInProgress = false; + const reader = new FileReader(); + reader.onload = async (event) => { + try { + const models = JSON.parse(String(event.target.result)); + modelsImportInProgress = true; + const res = await importModels(localStorage.token, models); + modelsImportInProgress = false; - if (res) { - toast.success($i18n.t('Models imported successfully')); - await init(); - } else { - toast.error($i18n.t('Failed to import models')); - } + if (res) { + toast.success($i18n.t('Models imported successfully')); + await init(); + } else { + toast.error($i18n.t('Failed to import models')); + } + } catch (e) { + toast.error($i18n.t('Invalid JSON file')); + console.error(e); + } + }; + reader.readAsText(importFiles[0]); } }} />