From fbdac832bb06cd2ae2f38cb4e5ad3775cac057c2 Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Tue, 5 Mar 2024 02:12:55 -0800 Subject: [PATCH] feat: multiple ollama model management --- backend/apps/ollama/main.py | 8 +- src/lib/apis/ollama/index.ts | 31 +- .../components/chat/Settings/Models.svelte | 636 ++++++++++-------- 3 files changed, 360 insertions(+), 315 deletions(-) diff --git a/backend/apps/ollama/main.py b/backend/apps/ollama/main.py index 988d4bf4..05fcb6dd 100644 --- a/backend/apps/ollama/main.py +++ b/backend/apps/ollama/main.py @@ -207,9 +207,12 @@ async def pull_model( form_data: ModelNameForm, url_idx: int = 0, user=Depends(get_admin_user) ): url = app.state.OLLAMA_BASE_URLS[url_idx] + print(url) + r = None - def get_request(url): + def get_request(): + nonlocal url nonlocal r try: @@ -235,7 +238,7 @@ async def pull_model( raise e try: - return await run_in_threadpool(get_request(url)) + return await run_in_threadpool(get_request) except Exception as e: print(e) error_detail = "Open WebUI: Server Connection Error" @@ -454,6 +457,7 @@ async def delete_model( ) url = app.state.OLLAMA_BASE_URLS[url_idx] + print(url) try: r = requests.request( diff --git a/src/lib/apis/ollama/index.ts b/src/lib/apis/ollama/index.ts index 5887b11f..2047fede 100644 --- a/src/lib/apis/ollama/index.ts +++ b/src/lib/apis/ollama/index.ts @@ -318,20 +318,23 @@ export const createModel = async (token: string, tagName: string, content: strin return res; }; -export const deleteModel = async (token: string, tagName: string) => { +export const deleteModel = async (token: string, tagName: string, urlIdx: string | null = null) => { let error = null; - const res = await fetch(`${OLLAMA_API_BASE_URL}/api/delete`, { - method: 'DELETE', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` - }, - body: JSON.stringify({ - name: tagName - }) - }) + const res = await fetch( + `${OLLAMA_API_BASE_URL}/api/delete${urlIdx !== null ? `/${urlIdx}` : ''}`, + { + method: 'DELETE', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + body: JSON.stringify({ + name: tagName + }) + } + ) .then(async (res) => { if (!res.ok) throw await res.json(); return res.json(); @@ -358,10 +361,10 @@ export const deleteModel = async (token: string, tagName: string) => { return res; }; -export const pullModel = async (token: string, tagName: string) => { +export const pullModel = async (token: string, tagName: string, urlIdx: string | null = null) => { let error = null; - const res = await fetch(`${OLLAMA_API_BASE_URL}/api/pull`, { + const res = await fetch(`${OLLAMA_API_BASE_URL}/api/pull${urlIdx !== null ? `/${urlIdx}` : ''}`, { method: 'POST', headers: { Accept: 'application/json', diff --git a/src/lib/components/chat/Settings/Models.svelte b/src/lib/components/chat/Settings/Models.svelte index 3225f699..f6b70892 100644 --- a/src/lib/components/chat/Settings/Models.svelte +++ b/src/lib/components/chat/Settings/Models.svelte @@ -2,7 +2,13 @@ import queue from 'async/queue'; import { toast } from 'svelte-sonner'; - import { createModel, deleteModel, getOllamaVersion, pullModel } from '$lib/apis/ollama'; + import { + createModel, + deleteModel, + getOllamaUrls, + getOllamaVersion, + pullModel + } from '$lib/apis/ollama'; import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants'; import { WEBUI_NAME, models, user } from '$lib/stores'; import { splitStream } from '$lib/utils'; @@ -27,6 +33,9 @@ $: liteLLMModelName = liteLLMModel; // Models + + let OLLAMA_URLS = []; + let selectedOllamaUrlIdx: string | null = null; let showExperimentalOllama = false; let ollamaVersion = ''; const MAX_PARALLEL_DOWNLOADS = 3; @@ -236,9 +245,11 @@ }; const deleteModelHandler = async () => { - const res = await deleteModel(localStorage.token, deleteModelTag).catch((error) => { - toast.error(error); - }); + const res = await deleteModel(localStorage.token, deleteModelTag, selectedOllamaUrlIdx).catch( + (error) => { + toast.error(error); + } + ); if (res) { toast.success(`Deleted ${deleteModelTag}`); @@ -249,10 +260,12 @@ }; const pullModelHandlerProcessor = async (opts: { modelName: string; callback: Function }) => { - const res = await pullModel(localStorage.token, opts.modelName).catch((error) => { - opts.callback({ success: false, error, modelName: opts.modelName }); - return null; - }); + const res = await pullModel(localStorage.token, opts.modelName, selectedOllamaUrlIdx).catch( + (error) => { + opts.callback({ success: false, error, modelName: opts.modelName }); + return null; + } + ); if (res) { const reader = res.body @@ -358,6 +371,15 @@ }; onMount(async () => { + OLLAMA_URLS = await getOllamaUrls(localStorage.token).catch((error) => { + toast.error(error); + return []; + }); + + if (OLLAMA_URLS.length > 1) { + selectedOllamaUrlIdx = 0; + } + ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false); liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token); }); @@ -367,52 +389,137 @@
{#if ollamaVersion}
-
-
Manage Ollama Models
+
Manage Ollama Models
-
Pull a model from Ollama.com
-
-
- -
- +
+ +
+ To access the available model names for downloading, click here. +
+ + {#if Object.keys(modelDownloadStatus).length > 0} + {#each Object.keys(modelDownloadStatus) as model} +
+
{model}
+
+
+ {modelDownloadStatus[model].pullProgress ?? 0}% +
+
+ {modelDownloadStatus[model].digest} +
+
- {:else} + {/each} + {/if} +
+ +
+
Delete a model
+
+
+ +
+ -
- -
- To access the available model names for downloading, click here. -
- - {#if Object.keys(modelDownloadStatus).length > 0} - {#each Object.keys(modelDownloadStatus) as model} -
-
{model}
-
-
- {modelDownloadStatus[model].pullProgress ?? 0}% -
-
- {modelDownloadStatus[model].digest} -
-
-
- {/each} - {/if} -
- -
-
Delete a model
-
-
- -
- -
-
- -
-
-
Experimental
- -
-
- - {#if showExperimentalOllama} -
{ - uploadModelHandler(); - }} - > -
-
Upload a GGUF model
- -
+
-
-
- {#if modelUploadMode === 'file'} -
- { - console.log(modelInputFile); - }} - accept=".gguf" - required - hidden - /> +
+
+
Experimental
+ +
+
- -
- {:else} -
- -
+ {#if showExperimentalOllama} + { + uploadModelHandler(); + }} + > +
+
Upload a GGUF model
+ + +
+ +
+
+ {#if modelUploadMode === 'file'} +
+ { + console.log(modelInputFile); + }} + accept=".gguf" + required + hidden + /> + + +
+ {:else} +
+ +
+ {/if} +
+ + {#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} + {/if}
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} - - {/if} -
- - {#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')} -
-
Modelfile Content
-