Bladeren bron

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.
silentoplayz 1 week geleden
bovenliggende
commit
fe28097817

+ 6 - 4
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

+ 3 - 5
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();

+ 21 - 11
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;
-
-								if (res) {
-									toast.success($i18n.t('Models imported successfully'));
-									await init();
-								} else {
-									toast.error($i18n.t('Failed to import models'));
-								}
+								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'));
+										}
+									} catch (e) {
+										toast.error($i18n.t('Invalid JSON file'));
+										console.error(e);
+									}
+								};
+								reader.readAsText(importFiles[0]);
 							}
 						}}
 					/>