Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
chenpangpang
open-webui
Commits
fbdac832
"git@developer.sourcefind.cn:modelzoo/resnet50_tensorflow.git" did not exist on "83f81f51a9fbaa1c02e42cb960fe707233754e9d"
Commit
fbdac832
authored
Mar 05, 2024
by
Timothy J. Baek
Browse files
feat: multiple ollama model management
parent
8181d98e
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
348 additions
and
303 deletions
+348
-303
backend/apps/ollama/main.py
backend/apps/ollama/main.py
+6
-2
src/lib/apis/ollama/index.ts
src/lib/apis/ollama/index.ts
+17
-14
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+325
-287
No files found.
backend/apps/ollama/main.py
View file @
fbdac832
...
@@ -207,9 +207,12 @@ async def pull_model(
...
@@ -207,9 +207,12 @@ async def pull_model(
form_data
:
ModelNameForm
,
url_idx
:
int
=
0
,
user
=
Depends
(
get_admin_user
)
form_data
:
ModelNameForm
,
url_idx
:
int
=
0
,
user
=
Depends
(
get_admin_user
)
):
):
url
=
app
.
state
.
OLLAMA_BASE_URLS
[
url_idx
]
url
=
app
.
state
.
OLLAMA_BASE_URLS
[
url_idx
]
print
(
url
)
r
=
None
r
=
None
def
get_request
(
url
):
def
get_request
():
nonlocal
url
nonlocal
r
nonlocal
r
try
:
try
:
...
@@ -235,7 +238,7 @@ async def pull_model(
...
@@ -235,7 +238,7 @@ async def pull_model(
raise
e
raise
e
try
:
try
:
return
await
run_in_threadpool
(
get_request
(
url
)
)
return
await
run_in_threadpool
(
get_request
)
except
Exception
as
e
:
except
Exception
as
e
:
print
(
e
)
print
(
e
)
error_detail
=
"Open WebUI: Server Connection Error"
error_detail
=
"Open WebUI: Server Connection Error"
...
@@ -454,6 +457,7 @@ async def delete_model(
...
@@ -454,6 +457,7 @@ async def delete_model(
)
)
url
=
app
.
state
.
OLLAMA_BASE_URLS
[
url_idx
]
url
=
app
.
state
.
OLLAMA_BASE_URLS
[
url_idx
]
print
(
url
)
try
:
try
:
r
=
requests
.
request
(
r
=
requests
.
request
(
...
...
src/lib/apis/ollama/index.ts
View file @
fbdac832
...
@@ -318,10 +318,12 @@ export const createModel = async (token: string, tagName: string, content: strin
...
@@ -318,10 +318,12 @@ export const createModel = async (token: string, tagName: string, content: strin
return
res
;
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
;
let
error
=
null
;
const
res
=
await
fetch
(
`
${
OLLAMA_API_BASE_URL
}
/api/delete`
,
{
const
res
=
await
fetch
(
`
${
OLLAMA_API_BASE_URL
}
/api/delete
${
urlIdx
!==
null
?
`/
${
urlIdx
}
`
:
''
}
`
,
{
method
:
'
DELETE
'
,
method
:
'
DELETE
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
@@ -331,7 +333,8 @@ export const deleteModel = async (token: string, tagName: string) => {
...
@@ -331,7 +333,8 @@ export const deleteModel = async (token: string, tagName: string) => {
body
:
JSON
.
stringify
({
body
:
JSON
.
stringify
({
name
:
tagName
name
:
tagName
})
})
})
}
)
.
then
(
async
(
res
)
=>
{
.
then
(
async
(
res
)
=>
{
if
(
!
res
.
ok
)
throw
await
res
.
json
();
if
(
!
res
.
ok
)
throw
await
res
.
json
();
return
res
.
json
();
return
res
.
json
();
...
@@ -358,10 +361,10 @@ export const deleteModel = async (token: string, tagName: string) => {
...
@@ -358,10 +361,10 @@ export const deleteModel = async (token: string, tagName: string) => {
return
res
;
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
;
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
'
,
method
:
'
POST
'
,
headers
:
{
headers
:
{
Accept
:
'
application/json
'
,
Accept
:
'
application/json
'
,
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
fbdac832
...
@@ -2,7 +2,13 @@
...
@@ -2,7 +2,13 @@
import queue from 'async/queue';
import queue from 'async/queue';
import { toast } from 'svelte-sonner';
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_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
import { WEBUI_NAME, models, user } from '$lib/stores';
import { WEBUI_NAME, models, user } from '$lib/stores';
import { splitStream } from '$lib/utils';
import { splitStream } from '$lib/utils';
...
@@ -27,6 +33,9 @@
...
@@ -27,6 +33,9 @@
$: liteLLMModelName = liteLLMModel;
$: liteLLMModelName = liteLLMModel;
// Models
// Models
let OLLAMA_URLS = [];
let selectedOllamaUrlIdx: string | null = null;
let showExperimentalOllama = false;
let showExperimentalOllama = false;
let ollamaVersion = '';
let ollamaVersion = '';
const MAX_PARALLEL_DOWNLOADS = 3;
const MAX_PARALLEL_DOWNLOADS = 3;
...
@@ -236,9 +245,11 @@
...
@@ -236,9 +245,11 @@
};
};
const deleteModelHandler = async () => {
const deleteModelHandler = async () => {
const res = await deleteModel(localStorage.token, deleteModelTag).catch((error) => {
const res = await deleteModel(localStorage.token, deleteModelTag, selectedOllamaUrlIdx).catch(
(error) => {
toast.error(error);
toast.error(error);
});
}
);
if (res) {
if (res) {
toast.success(`Deleted ${deleteModelTag}`);
toast.success(`Deleted ${deleteModelTag}`);
...
@@ -249,10 +260,12 @@
...
@@ -249,10 +260,12 @@
};
};
const pullModelHandlerProcessor = async (opts: { modelName: string; callback: Function }) => {
const pullModelHandlerProcessor = async (opts: { modelName: string; callback: Function }) => {
const res = await pullModel(localStorage.token, opts.modelName).catch((error) => {
const res = await pullModel(localStorage.token, opts.modelName, selectedOllamaUrlIdx).catch(
(error) => {
opts.callback({ success: false, error, modelName: opts.modelName });
opts.callback({ success: false, error, modelName: opts.modelName });
return null;
return null;
});
}
);
if (res) {
if (res) {
const reader = res.body
const reader = res.body
...
@@ -358,6 +371,15 @@
...
@@ -358,6 +371,15 @@
};
};
onMount(async () => {
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);
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
});
});
...
@@ -367,20 +389,35 @@
...
@@ -367,20 +389,35 @@
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[23rem]">
<div class=" space-y-3 pr-1.5 overflow-y-scroll h-[23rem]">
{#if ollamaVersion}
{#if ollamaVersion}
<div class="space-y-2 pr-1.5">
<div class="space-y-2 pr-1.5">
<div>
<div class="text-sm font-medium">Manage Ollama Models</div>
<div class=" mb-2 text-sm font-medium">Manage Ollama Models</div>
{#if OLLAMA_URLS.length > 1}
<div class="flex-1 pb-1">
<select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={selectedOllamaUrlIdx}
placeholder="Select an Ollama instance"
>
{#each OLLAMA_URLS as url, idx}
<option value={idx} class="bg-gray-100 dark:bg-gray-700">{url}</option>
{/each}
</select>
</div>
{/if}
<div class="space-y-2">
<div>
<div class=" mb-2 text-sm font-medium">Pull a model from Ollama.com</div>
<div class=" mb-2 text-sm font-medium">Pull a model from Ollama.com</div>
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1 mr-2">
<div class="flex-1 mr-2">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter model tag (e.g. mistral:7b)"
placeholder="Enter model tag (e.g. mistral:7b)"
bind:value={modelTag}
bind:value={modelTag}
/>
/>
</div>
</div>
<button
<button
class="px-
3
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
class="px-
2.5
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded
-lg
transition"
on:click={() => {
on:click={() => {
pullModelHandler();
pullModelHandler();
}}
}}
...
@@ -463,7 +500,7 @@
...
@@ -463,7 +500,7 @@
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1 mr-2">
<div class="flex-1 mr-2">
<select
<select
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteModelTag}
bind:value={deleteModelTag}
placeholder="Select a model"
placeholder="Select a model"
>
>
...
@@ -478,7 +515,7 @@
...
@@ -478,7 +515,7 @@
</select>
</select>
</div>
</div>
<button
<button
class="px-
3
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
class="px-
2.5
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded
-lg
transition"
on:click={() => {
on:click={() => {
deleteModelHandler();
deleteModelHandler();
}}
}}
...
@@ -499,7 +536,7 @@
...
@@ -499,7 +536,7 @@
</div>
</div>
</div>
</div>
<div>
<div
class="pt-1"
>
<div class="flex justify-between items-center text-xs">
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Experimental</div>
<div class=" text-sm font-medium">Experimental</div>
<button
<button
...
@@ -507,7 +544,7 @@
...
@@ -507,7 +544,7 @@
type="button"
type="button"
on:click={() => {
on:click={() => {
showExperimentalOllama = !showExperimentalOllama;
showExperimentalOllama = !showExperimentalOllama;
}}>{showExperimentalOllama ? '
Show
' : '
Hide
'}</button
}}>{showExperimentalOllama ? '
Hide
' : '
Show
'}</button
>
>
</div>
</div>
</div>
</div>
...
@@ -559,7 +596,7 @@
...
@@ -559,7 +596,7 @@
<button
<button
type="button"
type="button"
class="w-full rounded text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850"
class="w-full rounded
-lg
text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850"
on:click={modelUploadInputElement.click}
on:click={modelUploadInputElement.click}
>
>
{#if modelInputFile && modelInputFile.length > 0}
{#if modelInputFile && modelInputFile.length > 0}
...
@@ -572,7 +609,7 @@
...
@@ -572,7 +609,7 @@
{:else}
{:else}
<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
<input
<input
class="w-full rounded text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !==
class="w-full rounded
-lg
text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !==
''
''
? 'mr-2'
? 'mr-2'
: ''}"
: ''}"
...
@@ -676,6 +713,7 @@
...
@@ -676,6 +713,7 @@
</form>
</form>
{/if}
{/if}
</div>
</div>
</div>
<hr class=" dark:border-gray-700 my-2" />
<hr class=" dark:border-gray-700 my-2" />
{/if}
{/if}
...
@@ -692,7 +730,7 @@
...
@@ -692,7 +730,7 @@
type="button"
type="button"
on:click={() => {
on:click={() => {
showLiteLLMParams = !showLiteLLMParams;
showLiteLLMParams = !showLiteLLMParams;
}}>{showLiteLLMParams ? '
Advanced' : 'Default
'}</button
}}>{showLiteLLMParams ? '
Hide Additional Params' : 'Show Additional Params
'}</button
>
>
</div>
</div>
</div>
</div>
...
@@ -701,7 +739,7 @@
...
@@ -701,7 +739,7 @@
<div class="flex w-full mb-1.5">
<div class="flex w-full mb-1.5">
<div class="flex-1 mr-2">
<div class="flex-1 mr-2">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM Model (litellm_params.model)"
placeholder="Enter LiteLLM Model (litellm_params.model)"
bind:value={liteLLMModel}
bind:value={liteLLMModel}
autocomplete="off"
autocomplete="off"
...
@@ -709,7 +747,7 @@
...
@@ -709,7 +747,7 @@
</div>
</div>
<button
<button
class="px-
3
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
class="px-
2.5
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded
-lg
transition"
on:click={() => {
on:click={() => {
addLiteLLMModelHandler();
addLiteLLMModelHandler();
}}
}}
...
@@ -733,7 +771,7 @@
...
@@ -733,7 +771,7 @@
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1">
<div class="flex-1">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter Model Name (model_name)"
placeholder="Enter Model Name (model_name)"
bind:value={liteLLMModelName}
bind:value={liteLLMModelName}
autocomplete="off"
autocomplete="off"
...
@@ -747,7 +785,7 @@
...
@@ -747,7 +785,7 @@
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1">
<div class="flex-1">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API Base URL (litellm_params.api_base)"
placeholder="Enter LiteLLM API Base URL (litellm_params.api_base)"
bind:value={liteLLMAPIBase}
bind:value={liteLLMAPIBase}
autocomplete="off"
autocomplete="off"
...
@@ -761,7 +799,7 @@
...
@@ -761,7 +799,7 @@
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1">
<div class="flex-1">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API Key (litellm_params.api_key)"
placeholder="Enter LiteLLM API Key (litellm_params.api_key)"
bind:value={liteLLMAPIKey}
bind:value={liteLLMAPIKey}
autocomplete="off"
autocomplete="off"
...
@@ -775,7 +813,7 @@
...
@@ -775,7 +813,7 @@
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1">
<div class="flex-1">
<input
<input
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Enter LiteLLM API RPM (litellm_params.rpm)"
placeholder="Enter LiteLLM API RPM (litellm_params.rpm)"
bind:value={liteLLMRPM}
bind:value={liteLLMRPM}
autocomplete="off"
autocomplete="off"
...
@@ -802,7 +840,7 @@
...
@@ -802,7 +840,7 @@
<div class="flex w-full">
<div class="flex w-full">
<div class="flex-1 mr-2">
<div class="flex-1 mr-2">
<select
<select
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
class="w-full rounded
-lg
py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteLiteLLMModelId}
bind:value={deleteLiteLLMModelId}
placeholder="Select a model"
placeholder="Select a model"
>
>
...
@@ -817,7 +855,7 @@
...
@@ -817,7 +855,7 @@
</select>
</select>
</div>
</div>
<button
<button
class="px-
3
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded transition"
class="px-
2.5
bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-gray-100 rounded
-lg
transition"
on:click={() => {
on:click={() => {
deleteLiteLLMModelHandler();
deleteLiteLLMModelHandler();
}}
}}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment