浏览代码

feat: unified /models endpoint

Timothy J. Baek 11 月之前
父节点
当前提交
110ed67468

+ 0 - 8
backend/apps/litellm/main.py

@@ -242,8 +242,6 @@ async def get_models(user=Depends(get_current_user)):
                         )
                         )
                     )
                     )
 
 
-            for model in data["data"]:
-                add_custom_info_to_model(model)
             return data
             return data
         except Exception as e:
         except Exception as e:
 
 
@@ -284,12 +282,6 @@ async def get_models(user=Depends(get_current_user)):
         }
         }
 
 
 
 
-def add_custom_info_to_model(model: dict):
-    model["custom_info"] = next(
-        (item for item in app.state.MODEL_CONFIG if item.id == model["id"]), None
-    )
-
-
 @app.get("/model/info")
 @app.get("/model/info")
 async def get_model_list(user=Depends(get_admin_user)):
 async def get_model_list(user=Depends(get_admin_user)):
     return {"data": app.state.CONFIG["model_list"]}
     return {"data": app.state.CONFIG["model_list"]}

+ 0 - 11
backend/apps/ollama/main.py

@@ -67,8 +67,6 @@ app.state.config = AppConfig()
 
 
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
-app.state.MODEL_CONFIG = Models.get_all_models()
-
 
 
 app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
 app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
 app.state.config.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
 app.state.config.OLLAMA_BASE_URLS = OLLAMA_BASE_URLS
@@ -192,21 +190,12 @@ async def get_all_models():
 
 
     else:
     else:
         models = {"models": []}
         models = {"models": []}
-        
-    for model in models["models"]:
-        add_custom_info_to_model(model)
 
 
     app.state.MODELS = {model["model"]: model for model in models["models"]}
     app.state.MODELS = {model["model"]: model for model in models["models"]}
 
 
     return models
     return models
 
 
 
 
-def add_custom_info_to_model(model: dict):
-    model["custom_info"] = next(
-        (item for item in app.state.MODEL_CONFIG if item.id == model["model"]), None
-    )
-
-
 @app.get("/api/tags")
 @app.get("/api/tags")
 @app.get("/api/tags/{url_idx}")
 @app.get("/api/tags/{url_idx}")
 async def get_ollama_tags(
 async def get_ollama_tags(

+ 7 - 12
backend/apps/openai/main.py

@@ -52,8 +52,6 @@ app.state.config = AppConfig()
 
 
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
-app.state.MODEL_CONFIG = Models.get_all_models()
-
 
 
 app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
 app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
 app.state.config.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS
 app.state.config.OPENAI_API_BASE_URLS = OPENAI_API_BASE_URLS
@@ -207,7 +205,13 @@ def merge_models_lists(model_lists):
         if models is not None and "error" not in models:
         if models is not None and "error" not in models:
             merged_list.extend(
             merged_list.extend(
                 [
                 [
-                    {**model, "urlIdx": idx}
+                    {
+                        **model,
+                        "name": model["id"],
+                        "owned_by": "openai",
+                        "openai": model,
+                        "urlIdx": idx,
+                    }
                     for model in models
                     for model in models
                     if "api.openai.com"
                     if "api.openai.com"
                     not in app.state.config.OPENAI_API_BASE_URLS[idx]
                     not in app.state.config.OPENAI_API_BASE_URLS[idx]
@@ -250,21 +254,12 @@ async def get_all_models():
             )
             )
         }
         }
 
 
-        for model in models["data"]:
-            add_custom_info_to_model(model)
-
         log.info(f"models: {models}")
         log.info(f"models: {models}")
         app.state.MODELS = {model["id"]: model for model in models["data"]}
         app.state.MODELS = {model["id"]: model for model in models["data"]}
 
 
     return models
     return models
 
 
 
 
-def add_custom_info_to_model(model: dict):
-    model["custom_info"] = next(
-        (item for item in app.state.MODEL_CONFIG if item.id == model["id"]), None
-    )
-
-
 @app.get("/models")
 @app.get("/models")
 @app.get("/models/{url_idx}")
 @app.get("/models/{url_idx}")
 async def get_models(url_idx: Optional[int] = None, user=Depends(get_current_user)):
 async def get_models(url_idx: Optional[int] = None, user=Depends(get_current_user)):

+ 78 - 5
backend/main.py

@@ -19,8 +19,8 @@ from starlette.exceptions import HTTPException as StarletteHTTPException
 from starlette.middleware.base import BaseHTTPMiddleware
 from starlette.middleware.base import BaseHTTPMiddleware
 from starlette.responses import StreamingResponse, Response
 from starlette.responses import StreamingResponse, Response
 
 
-from apps.ollama.main import app as ollama_app
-from apps.openai.main import app as openai_app
+from apps.ollama.main import app as ollama_app, get_all_models as get_ollama_models
+from apps.openai.main import app as openai_app, get_all_models as get_openai_models
 
 
 from apps.litellm.main import (
 from apps.litellm.main import (
     app as litellm_app,
     app as litellm_app,
@@ -39,7 +39,7 @@ from pydantic import BaseModel
 from typing import List, Optional
 from typing import List, Optional
 
 
 from apps.web.models.models import Models, ModelModel
 from apps.web.models.models import Models, ModelModel
-from utils.utils import get_admin_user
+from utils.utils import get_admin_user, get_verified_user
 from apps.rag.utils import rag_messages
 from apps.rag.utils import rag_messages
 
 
 from config import (
 from config import (
@@ -53,6 +53,8 @@ from config import (
     FRONTEND_BUILD_DIR,
     FRONTEND_BUILD_DIR,
     CACHE_DIR,
     CACHE_DIR,
     STATIC_DIR,
     STATIC_DIR,
+    ENABLE_OPENAI_API,
+    ENABLE_OLLAMA_API,
     ENABLE_LITELLM,
     ENABLE_LITELLM,
     ENABLE_MODEL_FILTER,
     ENABLE_MODEL_FILTER,
     MODEL_FILTER_LIST,
     MODEL_FILTER_LIST,
@@ -110,10 +112,13 @@ app = FastAPI(
 )
 )
 
 
 app.state.config = AppConfig()
 app.state.config = AppConfig()
+
+app.state.config.ENABLE_OPENAI_API = ENABLE_OPENAI_API
+app.state.config.ENABLE_OLLAMA_API = ENABLE_OLLAMA_API
+
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.ENABLE_MODEL_FILTER = ENABLE_MODEL_FILTER
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
 app.state.config.MODEL_FILTER_LIST = MODEL_FILTER_LIST
 
 
-app.state.MODEL_CONFIG = Models.get_all_models()
 
 
 app.state.config.WEBHOOK_URL = WEBHOOK_URL
 app.state.config.WEBHOOK_URL = WEBHOOK_URL
 
 
@@ -249,9 +254,11 @@ async def update_embedding_function(request: Request, call_next):
     return response
     return response
 
 
 
 
+# TODO: Deprecate LiteLLM
 app.mount("/litellm/api", litellm_app)
 app.mount("/litellm/api", litellm_app)
+
 app.mount("/ollama", ollama_app)
 app.mount("/ollama", ollama_app)
-app.mount("/openai/api", openai_app)
+app.mount("/openai", openai_app)
 
 
 app.mount("/images/api/v1", images_app)
 app.mount("/images/api/v1", images_app)
 app.mount("/audio/api/v1", audio_app)
 app.mount("/audio/api/v1", audio_app)
@@ -262,6 +269,72 @@ app.mount("/api/v1", webui_app)
 webui_app.state.EMBEDDING_FUNCTION = rag_app.state.EMBEDDING_FUNCTION
 webui_app.state.EMBEDDING_FUNCTION = rag_app.state.EMBEDDING_FUNCTION
 
 
 
 
+@app.get("/api/models")
+async def get_models(user=Depends(get_verified_user)):
+    openai_models = []
+    ollama_models = []
+
+    if app.state.config.ENABLE_OPENAI_API:
+        openai_models = await get_openai_models()
+        openai_app.state.MODELS = openai_models
+
+        openai_models = openai_models["data"]
+
+    if app.state.config.ENABLE_OLLAMA_API:
+        ollama_models = await get_ollama_models()
+        ollama_app.state.MODELS = ollama_models
+
+        print(ollama_models)
+
+        ollama_models = [
+            {
+                "id": model["model"],
+                "name": model["name"],
+                "object": "model",
+                "created": int(time.time()),
+                "owned_by": "ollama",
+                "ollama": model,
+            }
+            for model in ollama_models["models"]
+        ]
+
+    print("openai", openai_models)
+    print("ollama", ollama_models)
+
+    models = openai_models + ollama_models
+    custom_models = Models.get_all_models()
+
+    for custom_model in custom_models:
+        if custom_model.base_model_id == None:
+            for model in models:
+                if custom_model.id == model["id"]:
+                    model["name"] = custom_model.name
+                    model["info"] = custom_model.model_dump()
+        else:
+            models.append(
+                {
+                    "id": custom_model.id,
+                    "name": custom_model.name,
+                    "object": "model",
+                    "created": custom_model.created_at,
+                    "owned_by": "user",
+                    "info": custom_model.model_dump(),
+                }
+            )
+
+    if app.state.config.ENABLE_MODEL_FILTER:
+        if user.role == "user":
+            models = list(
+                filter(
+                    lambda model: model["id"] in app.state.config.MODEL_FILTER_LIST,
+                    models,
+                )
+            )
+            return {"data": models}
+
+    return {"data": models}
+
+
 @app.get("/api/config")
 @app.get("/api/config")
 async def get_app_config():
 async def get_app_config():
     # Checking and Handling the Absence of 'ui' in CONFIG_DATA
     # Checking and Handling the Absence of 'ui' in CONFIG_DATA

+ 28 - 0
src/lib/apis/index.ts

@@ -1,5 +1,33 @@
 import { WEBUI_BASE_URL } from '$lib/constants';
 import { WEBUI_BASE_URL } from '$lib/constants';
 
 
+export const getModels = async (token: string = '') => {
+	let error = null;
+
+	const res = await fetch(`${WEBUI_BASE_URL}/api/models`, {
+		method: 'GET',
+		headers: {
+			Accept: 'application/json',
+			'Content-Type': 'application/json',
+			...(token && { authorization: `Bearer ${token}` })
+		}
+	})
+		.then(async (res) => {
+			if (!res.ok) throw await res.json();
+			return res.json();
+		})
+		.catch((err) => {
+			console.log(err);
+			error = err;
+			return null;
+		});
+
+	if (error) {
+		throw error;
+	}
+
+	return res?.data ?? [];
+};
+
 export const getBackendConfig = async () => {
 export const getBackendConfig = async () => {
 	let error = null;
 	let error = null;
 
 

+ 3 - 5
src/lib/components/chat/MessageInput/Models.svelte

@@ -21,10 +21,8 @@
 	let filteredModels = [];
 	let filteredModels = [];
 
 
 	$: filteredModels = $models
 	$: filteredModels = $models
-		.filter((p) =>
-			(p.custom_info?.name ?? p.name).includes(prompt.split(' ')?.at(0)?.substring(1) ?? '')
-		)
-		.sort((a, b) => (a.custom_info?.name ?? a.name).localeCompare(b.custom_info?.name ?? b.name));
+		.filter((p) => p.name.includes(prompt.split(' ')?.at(0)?.substring(1) ?? ''))
+		.sort((a, b) => a.name.localeCompare(b.name));
 
 
 	$: if (prompt) {
 	$: if (prompt) {
 		selectedIdx = 0;
 		selectedIdx = 0;
@@ -158,7 +156,7 @@
 								on:focus={() => {}}
 								on:focus={() => {}}
 							>
 							>
 								<div class=" font-medium text-black line-clamp-1">
 								<div class=" font-medium text-black line-clamp-1">
-									{model.custom_info?.name ?? model.name}
+									{model.name}
 								</div>
 								</div>
 
 
 								<!-- <div class=" text-xs text-gray-600 line-clamp-1">
 								<!-- <div class=" text-xs text-gray-600 line-clamp-1">

+ 34 - 79
src/lib/components/workspace/Modelfiles.svelte → src/lib/components/workspace/Models.svelte

@@ -5,63 +5,53 @@
 
 
 	import { onMount, getContext } from 'svelte';
 	import { onMount, getContext } from 'svelte';
 
 
-	import { WEBUI_NAME, modelfiles, settings, user } from '$lib/stores';
-	import { createModel, deleteModel } from '$lib/apis/ollama';
+	import { WEBUI_NAME, modelfiles, models, settings, user } from '$lib/stores';
 	import { addNewModel, deleteModelById, getModels } from '$lib/apis/models';
 	import { addNewModel, deleteModelById, getModels } from '$lib/apis/models';
 	import { goto } from '$app/navigation';
 	import { goto } from '$app/navigation';
+	import { getAllModels } from '$lib/utils';
 
 
 	const i18n = getContext('i18n');
 	const i18n = getContext('i18n');
 
 
 	let localModelfiles = [];
 	let localModelfiles = [];
 	let importFiles;
 	let importFiles;
 	let modelfilesImportInputElement: HTMLInputElement;
 	let modelfilesImportInputElement: HTMLInputElement;
-	const deleteModelHandler = async (tagName) => {
-		let success = null;
 
 
-		success = await deleteModel(localStorage.token, tagName).catch((err) => {
-			toast.error(err);
-			return null;
-		});
+	const deleteModelHandler = async (id) => {
+		const res = await deleteModelById(localStorage.token, id);
 
 
-		if (success) {
-			toast.success($i18n.t(`Deleted {{tagName}}`, { tagName }));
+		if (res) {
+			toast.success($i18n.t(`Deleted {{tagName}}`, { id }));
 		}
 		}
-
-		return success;
-	};
-
-	const deleteModelfile = async (tagName) => {
-		await deleteModelHandler(tagName);
-		await deleteModelById(localStorage.token, tagName);
-		await modelfiles.set(await getModels(localStorage.token));
+		await models.set(await getAllModels(localStorage.token));
 	};
 	};
 
 
-	const shareModelfile = async (modelfile) => {
+	const shareModelHandler = async (model) => {
 		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
 		toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
 
 
 		const url = 'https://openwebui.com';
 		const url = 'https://openwebui.com';
 
 
-		const tab = await window.open(`${url}/modelfiles/create`, '_blank');
+		const tab = await window.open(`${url}/models/create`, '_blank');
 		window.addEventListener(
 		window.addEventListener(
 			'message',
 			'message',
 			(event) => {
 			(event) => {
 				if (event.origin !== url) return;
 				if (event.origin !== url) return;
 				if (event.data === 'loaded') {
 				if (event.data === 'loaded') {
-					tab.postMessage(JSON.stringify(modelfile), '*');
+					tab.postMessage(JSON.stringify(model), '*');
 				}
 				}
 			},
 			},
 			false
 			false
 		);
 		);
 	};
 	};
 
 
-	const saveModelfiles = async (modelfiles) => {
-		let blob = new Blob([JSON.stringify(modelfiles)], {
+	const downloadModels = async (models) => {
+		let blob = new Blob([JSON.stringify(models)], {
 			type: 'application/json'
 			type: 'application/json'
 		});
 		});
-		saveAs(blob, `modelfiles-export-${Date.now()}.json`);
+		saveAs(blob, `models-export-${Date.now()}.json`);
 	};
 	};
 
 
 	onMount(() => {
 	onMount(() => {
+		// Legacy code to sync localModelfiles with models
 		localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
 		localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
 
 
 		if (localModelfiles) {
 		if (localModelfiles) {
@@ -72,13 +62,13 @@
 
 
 <svelte:head>
 <svelte:head>
 	<title>
 	<title>
-		{$i18n.t('Modelfiles')} | {$WEBUI_NAME}
+		{$i18n.t('Models')} | {$WEBUI_NAME}
 	</title>
 	</title>
 </svelte:head>
 </svelte:head>
 
 
-<div class=" text-lg font-semibold mb-3">{$i18n.t('Modelfiles')}</div>
+<div class=" text-lg font-semibold mb-3">{$i18n.t('Models')}</div>
 
 
-<a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/modelfiles/create">
+<a class=" flex space-x-4 cursor-pointer w-full mb-2 px-3 py-2" href="/workspace/models/create">
 	<div class=" self-center w-10">
 	<div class=" self-center w-10">
 		<div
 		<div
 			class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
 			class="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
@@ -94,26 +84,26 @@
 	</div>
 	</div>
 
 
 	<div class=" self-center">
 	<div class=" self-center">
-		<div class=" font-bold">{$i18n.t('Create a modelfile')}</div>
-		<div class=" text-sm">{$i18n.t('Customize Ollama models for a specific purpose')}</div>
+		<div class=" font-bold">{$i18n.t('Create a model')}</div>
+		<div class=" text-sm">{$i18n.t('Customize models for a specific purpose')}</div>
 	</div>
 	</div>
 </a>
 </a>
 
 
 <hr class=" dark:border-gray-850" />
 <hr class=" dark:border-gray-850" />
 
 
 <div class=" my-2 mb-5">
 <div class=" my-2 mb-5">
-	{#each $modelfiles as modelfile}
+	{#each $models as model}
 		<div
 		<div
 			class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
 			class=" flex space-x-4 cursor-pointer w-full px-3 py-2 dark:hover:bg-white/5 hover:bg-black/5 rounded-xl"
 		>
 		>
 			<a
 			<a
 				class=" flex flex-1 space-x-4 cursor-pointer w-full"
 				class=" flex flex-1 space-x-4 cursor-pointer w-full"
-				href={`/?models=${encodeURIComponent(modelfile.tagName)}`}
+				href={`/?models=${encodeURIComponent(model.id)}`}
 			>
 			>
 				<div class=" self-center w-10">
 				<div class=" self-center w-10">
 					<div class=" rounded-full bg-stone-700">
 					<div class=" rounded-full bg-stone-700">
 						<img
 						<img
-							src={modelfile.imageUrl ?? '/user.png'}
+							src={model?.meta?.profile_image_url ?? '/favicon.png'}
 							alt="modelfile profile"
 							alt="modelfile profile"
 							class=" rounded-full w-full h-auto object-cover"
 							class=" rounded-full w-full h-auto object-cover"
 						/>
 						/>
@@ -121,9 +111,9 @@
 				</div>
 				</div>
 
 
 				<div class=" flex-1 self-center">
 				<div class=" flex-1 self-center">
-					<div class=" font-bold capitalize">{modelfile.title}</div>
+					<div class=" font-bold capitalize">{model.name}</div>
 					<div class=" text-sm overflow-hidden text-ellipsis line-clamp-1">
 					<div class=" text-sm overflow-hidden text-ellipsis line-clamp-1">
-						{modelfile.desc}
+						{model?.meta?.description ?? 'No description'}
 					</div>
 					</div>
 				</div>
 				</div>
 			</a>
 			</a>
@@ -131,7 +121,7 @@
 				<a
 				<a
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					type="button"
 					type="button"
-					href={`/workspace/modelfiles/edit?tag=${encodeURIComponent(modelfile.tagName)}`}
+					href={`/workspace/models/edit?tag=${encodeURIComponent(model.id)}`}
 				>
 				>
 					<svg
 					<svg
 						xmlns="http://www.w3.org/2000/svg"
 						xmlns="http://www.w3.org/2000/svg"
@@ -153,9 +143,8 @@
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					type="button"
 					type="button"
 					on:click={() => {
 					on:click={() => {
-						// console.log(modelfile);
-						sessionStorage.modelfile = JSON.stringify(modelfile);
-						goto('/workspace/modelfiles/create');
+						sessionStorage.model = JSON.stringify(model);
+						goto('/workspace/models/create');
 					}}
 					}}
 				>
 				>
 					<svg
 					<svg
@@ -178,7 +167,7 @@
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					type="button"
 					type="button"
 					on:click={() => {
 					on:click={() => {
-						shareModelfile(modelfile);
+						shareModelHandler(model);
 					}}
 					}}
 				>
 				>
 					<svg
 					<svg
@@ -201,7 +190,7 @@
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
 					type="button"
 					type="button"
 					on:click={() => {
 					on:click={() => {
-						deleteModelfile(modelfile.tagName);
+						deleteModelHandler(model.id);
 					}}
 					}}
 				>
 				>
 					<svg
 					<svg
@@ -260,7 +249,7 @@
 				modelfilesImportInputElement.click();
 				modelfilesImportInputElement.click();
 			}}
 			}}
 		>
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Modelfiles')}</div>
+			<div class=" self-center mr-2 font-medium">{$i18n.t('Import Models')}</div>
 
 
 			<div class=" self-center">
 			<div class=" self-center">
 				<svg
 				<svg
@@ -281,10 +270,10 @@
 		<button
 		<button
 			class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
 			class="flex text-xs items-center space-x-1 px-3 py-1.5 rounded-xl bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700 dark:text-gray-200 transition"
 			on:click={async () => {
 			on:click={async () => {
-				saveModelfiles($modelfiles);
+				downloadModels($models);
 			}}
 			}}
 		>
 		>
-			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Modelfiles')}</div>
+			<div class=" self-center mr-2 font-medium">{$i18n.t('Export Models')}</div>
 
 
 			<div class=" self-center">
 			<div class=" self-center">
 				<svg
 				<svg
@@ -310,47 +299,13 @@
 			</div>
 			</div>
 
 
 			<div class="flex space-x-1">
 			<div class="flex space-x-1">
-				<button
-					class="self-center w-fit text-sm px-3 py-1 border dark:border-gray-600 rounded-xl flex"
-					on:click={async () => {
-						for (const modelfile of localModelfiles) {
-							await addNewModel(localStorage.token, modelfile).catch((error) => {
-								return null;
-							});
-						}
-
-						saveModelfiles(localModelfiles);
-						localStorage.removeItem('modelfiles');
-						localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
-						await modelfiles.set(await getModels(localStorage.token));
-					}}
-				>
-					<div class=" self-center mr-2 font-medium">{$i18n.t('Sync All')}</div>
-
-					<div class=" self-center">
-						<svg
-							xmlns="http://www.w3.org/2000/svg"
-							viewBox="0 0 16 16"
-							fill="currentColor"
-							class="w-3.5 h-3.5"
-						>
-							<path
-								fill-rule="evenodd"
-								d="M13.836 2.477a.75.75 0 0 1 .75.75v3.182a.75.75 0 0 1-.75.75h-3.182a.75.75 0 0 1 0-1.5h1.37l-.84-.841a4.5 4.5 0 0 0-7.08.932.75.75 0 0 1-1.3-.75 6 6 0 0 1 9.44-1.242l.842.84V3.227a.75.75 0 0 1 .75-.75Zm-.911 7.5A.75.75 0 0 1 13.199 11a6 6 0 0 1-9.44 1.241l-.84-.84v1.371a.75.75 0 0 1-1.5 0V9.591a.75.75 0 0 1 .75-.75H5.35a.75.75 0 0 1 0 1.5H3.98l.841.841a4.5 4.5 0 0 0 7.08-.932.75.75 0 0 1 1.025-.273Z"
-								clip-rule="evenodd"
-							/>
-						</svg>
-					</div>
-				</button>
-
 				<button
 				<button
 					class="self-center w-fit text-sm p-1.5 border dark:border-gray-600 rounded-xl flex"
 					class="self-center w-fit text-sm p-1.5 border dark:border-gray-600 rounded-xl flex"
 					on:click={async () => {
 					on:click={async () => {
-						saveModelfiles(localModelfiles);
+						downloadModels(localModelfiles);
 
 
 						localStorage.removeItem('modelfiles');
 						localStorage.removeItem('modelfiles');
 						localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
 						localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
-						await modelfiles.set(await getModels(localStorage.token));
 					}}
 					}}
 				>
 				>
 					<div class=" self-center">
 					<div class=" self-center">
@@ -398,7 +353,7 @@
 		</div>
 		</div>
 
 
 		<div class=" self-center">
 		<div class=" self-center">
-			<div class=" font-bold">{$i18n.t('Discover a modelfile')}</div>
+			<div class=" font-bold">{$i18n.t('Discover a model')}</div>
 			<div class=" text-sm">{$i18n.t('Discover, download, and explore model presets')}</div>
 			<div class=" text-sm">{$i18n.t('Discover, download, and explore model presets')}</div>
 		</div>
 		</div>
 	</a>
 	</a>

+ 1 - 1
src/lib/constants.ts

@@ -8,7 +8,7 @@ export const WEBUI_API_BASE_URL = `${WEBUI_BASE_URL}/api/v1`;
 
 
 export const LITELLM_API_BASE_URL = `${WEBUI_BASE_URL}/litellm/api`;
 export const LITELLM_API_BASE_URL = `${WEBUI_BASE_URL}/litellm/api`;
 export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama`;
 export const OLLAMA_API_BASE_URL = `${WEBUI_BASE_URL}/ollama`;
-export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai/api`;
+export const OPENAI_API_BASE_URL = `${WEBUI_BASE_URL}/openai`;
 export const AUDIO_API_BASE_URL = `${WEBUI_BASE_URL}/audio/api/v1`;
 export const AUDIO_API_BASE_URL = `${WEBUI_BASE_URL}/audio/api/v1`;
 export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
 export const IMAGES_API_BASE_URL = `${WEBUI_BASE_URL}/images/api/v1`;
 export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;
 export const RAG_API_BASE_URL = `${WEBUI_BASE_URL}/rag/api/v1`;

+ 7 - 17
src/lib/utils/index.ts

@@ -1,27 +1,17 @@
 import { v4 as uuidv4 } from 'uuid';
 import { v4 as uuidv4 } from 'uuid';
 import sha256 from 'js-sha256';
 import sha256 from 'js-sha256';
-import { getOllamaModels } from '$lib/apis/ollama';
-import { getOpenAIModels } from '$lib/apis/openai';
-import { getLiteLLMModels } from '$lib/apis/litellm';
+
+import { getModels } from '$lib/apis';
 
 
 export const getAllModels = async (token: string) => {
 export const getAllModels = async (token: string) => {
-	let models = await Promise.all([
-		getOllamaModels(token).catch((error) => {
-			console.log(error);
-			return null;
-		}),
-		getOpenAIModels(token).catch((error) => {
-			console.log(error);
-			return null;
-		}),
-		getLiteLLMModels(token).catch((error) => {
-			console.log(error);
-			return null;
-		})
-	]);
+	let models = await getModels(token).catch((error) => {
+		console.log(error);
+		return null;
+	});
 
 
 	models = models.filter((models) => models).reduce((a, e, i, arr) => a.concat(e), []);
 	models = models.filter((models) => models).reduce((a, e, i, arr) => a.concat(e), []);
 
 
+	console.log(models);
 	return models;
 	return models;
 };
 };
 
 

+ 0 - 24
src/routes/(app)/+layout.svelte

@@ -9,7 +9,6 @@
 
 
 	import { getAllModels as _getAllModels } from '$lib/utils';
 	import { getAllModels as _getAllModels } from '$lib/utils';
 	import { getOllamaVersion } from '$lib/apis/ollama';
 	import { getOllamaVersion } from '$lib/apis/ollama';
-	import { getModels } from '$lib/apis/models';
 	import { getPrompts } from '$lib/apis/prompts';
 	import { getPrompts } from '$lib/apis/prompts';
 
 
 	import { getDocs } from '$lib/apis/documents';
 	import { getDocs } from '$lib/apis/documents';
@@ -50,21 +49,6 @@
 		return _getAllModels(localStorage.token);
 		return _getAllModels(localStorage.token);
 	};
 	};
 
 
-	const setOllamaVersion = async (version: string = '') => {
-		if (version === '') {
-			version = await getOllamaVersion(localStorage.token).catch((error) => {
-				return '';
-			});
-		}
-
-		ollamaVersion = version;
-
-		console.log(ollamaVersion);
-		if (compareVersion(REQUIRED_OLLAMA_VERSION, ollamaVersion)) {
-			toast.error(`Ollama Version: ${ollamaVersion !== '' ? ollamaVersion : 'Not Detected'}`);
-		}
-	};
-
 	onMount(async () => {
 	onMount(async () => {
 		if ($user === undefined) {
 		if ($user === undefined) {
 			await goto('/auth');
 			await goto('/auth');
@@ -93,9 +77,6 @@
 				(async () => {
 				(async () => {
 					models.set(await getAllModels());
 					models.set(await getAllModels());
 				})(),
 				})(),
-				(async () => {
-					modelfiles.set(await getModels(localStorage.token));
-				})(),
 				(async () => {
 				(async () => {
 					prompts.set(await getPrompts(localStorage.token));
 					prompts.set(await getPrompts(localStorage.token));
 				})(),
 				})(),
@@ -107,11 +88,6 @@
 				})()
 				})()
 			]);
 			]);
 
 
-			modelfiles.subscribe(async () => {
-				// should fetch models
-				models.set(await getAllModels());
-			});
-
 			document.addEventListener('keydown', function (event) {
 			document.addEventListener('keydown', function (event) {
 				const isCtrlPressed = event.ctrlKey || event.metaKey; // metaKey is for Cmd key on Mac
 				const isCtrlPressed = event.ctrlKey || event.metaKey; // metaKey is for Cmd key on Mac
 				// Check if the Shift key is pressed
 				// Check if the Shift key is pressed

+ 2 - 2
src/routes/(app)/workspace/+layout.svelte

@@ -39,10 +39,10 @@
 			class="flex scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-xl bg-transparent/10 p-1"
 			class="flex scrollbar-none overflow-x-auto w-fit text-center text-sm font-medium rounded-xl bg-transparent/10 p-1"
 		>
 		>
 			<a
 			<a
-				class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/modelfiles')
+				class="min-w-fit rounded-lg p-1.5 px-3 {$page.url.pathname.includes('/workspace/models')
 					? 'bg-gray-50 dark:bg-gray-850'
 					? 'bg-gray-50 dark:bg-gray-850'
 					: ''} transition"
 					: ''} transition"
-				href="/workspace/modelfiles">{$i18n.t('Modelfiles')}</a
+				href="/workspace/models">{$i18n.t('Models')}</a
 			>
 			>
 
 
 			<a
 			<a

+ 1 - 1
src/routes/(app)/workspace/+page.svelte

@@ -3,6 +3,6 @@
 	import { onMount } from 'svelte';
 	import { onMount } from 'svelte';
 
 
 	onMount(() => {
 	onMount(() => {
-		goto('/workspace/modelfiles');
+		goto('/workspace/models');
 	});
 	});
 </script>
 </script>

+ 0 - 5
src/routes/(app)/workspace/modelfiles/+page.svelte

@@ -1,5 +0,0 @@
-<script>
-	import Modelfiles from '$lib/components/workspace/Modelfiles.svelte';
-</script>
-
-<Modelfiles />

+ 5 - 0
src/routes/(app)/workspace/models/+page.svelte

@@ -0,0 +1,5 @@
+<script>
+	import Models from '$lib/components/workspace/Models.svelte';
+</script>
+
+<Models />

+ 0 - 0
src/routes/(app)/workspace/modelfiles/create/+page.svelte → src/routes/(app)/workspace/models/create/+page.svelte


+ 0 - 0
src/routes/(app)/workspace/modelfiles/edit/+page.svelte → src/routes/(app)/workspace/models/edit/+page.svelte