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:OpenDAS/lightx2v.git" did not exist on "a1ebc651ab830a381e8960029145b557990342d6"
Commit
fbdac832
authored
Mar 05, 2024
by
Timothy J. Baek
Browse files
feat: multiple ollama model management
parent
8181d98e
Changes
3
Hide 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,20 +318,23 @@ export const createModel = async (token: string, tagName: string, content: strin
...
@@ -318,20 +318,23 @@ 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
(
method
:
'
DELETE
'
,
`
${
OLLAMA_API_BASE_URL
}
/api/delete
${
urlIdx
!==
null
?
`/
${
urlIdx
}
`
:
''
}
`
,
headers
:
{
{
Accept
:
'
application/json
'
,
method
:
'
DELETE
'
,
'
Content-Type
'
:
'
application/json
'
,
headers
:
{
Authorization
:
`Bearer
${
token
}
`
Accept
:
'
application/json
'
,
},
'
Content-Type
'
:
'
application/json
'
,
body
:
JSON
.
stringify
({
Authorization
:
`Bearer
${
token
}
`
name
:
tagName
},
})
body
:
JSON
.
stringify
({
})
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(
toast.error(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(
opts.callback({ success: false, error, modelName: opts.modelName });
(error) => {
return null;
opts.callback({ success: false, error, modelName: opts.modelName });
});
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,52 +389,137 @@
...
@@ -367,52 +389,137 @@
<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=" mb-2 text-sm font-medium">Pull a model from Ollama.com</div>
<div class="flex-1 pb-1">
<div class="flex w-full">
<select
<div class="flex-1 mr-2">
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
<input
bind:value={selectedOllamaUrlIdx}
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder="Select an Ollama instance"
placeholder="Enter model tag (e.g. mistral:7b)"
bind:value={modelTag}
/>
</div>
<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"
on:click={() => {
pullModelHandler();
}}
disabled={modelTransferring}
>
>
{#if modelTransferring}
{#each OLLAMA_URLS as url, idx}
<div class="self-center">
<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="flex w-full">
<div class="flex-1 mr-2">
<input
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)"
bind:value={modelTag}
/>
</div>
<button
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={() => {
pullModelHandler();
}}
disabled={modelTransferring}
>
{#if modelTransferring}
<div class="self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
</div>
{:else}
<svg
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
><style>
viewBox="0 0 16 16"
.spinner_ajPY {
fill="currentColor"
transform-origin: center;
class="w-4 h-4"
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
>
<path
d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
/>
<path
d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
/>
</svg>
{/if}
</button>
</div>
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
To access the available model names for downloading, <a
class=" text-gray-500 dark:text-gray-300 font-medium underline"
href="https://ollama.com/library"
target="_blank">click here.</a
>
</div>
{#if Object.keys(modelDownloadStatus).length > 0}
{#each Object.keys(modelDownloadStatus) as model}
<div class="flex flex-col">
<div class="font-medium mb-1">{model}</div>
<div class="">
<div
class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
style="width: {Math.max(15, modelDownloadStatus[model].pullProgress ?? 0)}%"
>
{modelDownloadStatus[model].pullProgress ?? 0}%
</div>
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
{modelDownloadStatus[model].digest}
</div>
</div>
</div>
</div>
{:else}
{/each}
{/if}
</div>
<div>
<div class=" mb-2 text-sm font-medium">Delete a model</div>
<div class="flex w-full">
<div class="flex-1 mr-2">
<select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteModelTag}
placeholder="Select a model"
>
{#if !deleteModelTag}
<option value="" disabled selected>Select a model</option>
{/if}
{#each $models.filter((m) => m.size != null) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
>
{/each}
</select>
</div>
<button
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={() => {
deleteModelHandler();
}}
>
<svg
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
viewBox="0 0 16 16"
...
@@ -420,261 +527,192 @@
...
@@ -420,261 +527,192 @@
class="w-4 h-4"
class="w-4 h-4"
>
>
<path
<path
d="M8.75 2.75a.75.75 0 0 0-1.5 0v5.69L5.03 6.22a.75.75 0 0 0-1.06 1.06l3.5 3.5a.75.75 0 0 0 1.06 0l3.5-3.5a.75.75 0 0 0-1.06-1.06L8.75 8.44V2.75Z"
fill-rule="evenodd"
/>
d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
<path
clip-rule="evenodd"
d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
/>
/>
</svg>
</svg>
{/if}
</button>
</button>
</div>
<div class="mt-2 mb-1 text-xs text-gray-400 dark:text-gray-500">
To access the available model names for downloading, <a
class=" text-gray-500 dark:text-gray-300 font-medium underline"
href="https://ollama.com/library"
target="_blank">click here.</a
>
</div>
{#if Object.keys(modelDownloadStatus).length > 0}
{#each Object.keys(modelDownloadStatus) as model}
<div class="flex flex-col">
<div class="font-medium mb-1">{model}</div>
<div class="">
<div
class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
style="width: {Math.max(15, modelDownloadStatus[model].pullProgress ?? 0)}%"
>
{modelDownloadStatus[model].pullProgress ?? 0}%
</div>
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
{modelDownloadStatus[model].digest}
</div>
</div>
</div>
{/each}
{/if}
</div>
<div>
<div class=" mb-2 text-sm font-medium">Delete a model</div>
<div class="flex w-full">
<div class="flex-1 mr-2">
<select
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={deleteModelTag}
placeholder="Select a model"
>
{#if !deleteModelTag}
<option value="" disabled selected>Select a model</option>
{/if}
{#each $models.filter((m) => m.size != null) as model}
<option value={model.name} class="bg-gray-100 dark:bg-gray-700"
>{model.name + ' (' + (model.size / 1024 ** 3).toFixed(1) + ' GB)'}</option
>
{/each}
</select>
</div>
</div>
<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"
on:click={() => {
deleteModelHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
fill-rule="evenodd"
d="M5 3.25V4H2.75a.75.75 0 0 0 0 1.5h.3l.815 8.15A1.5 1.5 0 0 0 5.357 15h5.285a1.5 1.5 0 0 0 1.493-1.35l.815-8.15h.3a.75.75 0 0 0 0-1.5H11v-.75A2.25 2.25 0 0 0 8.75 1h-1.5A2.25 2.25 0 0 0 5 3.25Zm2.25-.75a.75.75 0 0 0-.75.75V4h3v-.75a.75.75 0 0 0-.75-.75h-1.5ZM6.05 6a.75.75 0 0 1 .787.713l.275 5.5a.75.75 0 0 1-1.498.075l-.275-5.5A.75.75 0 0 1 6.05 6Zm3.9 0a.75.75 0 0 1 .712.787l-.275 5.5a.75.75 0 0 1-1.498-.075l.275-5.5a.75.75 0 0 1 .786-.711Z"
clip-rule="evenodd"
/>
</svg>
</button>
</div>
</div>
</div>
<div>
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Experimental</div>
<button
class=" text-xs font-medium text-gray-500"
type="button"
on:click={() => {
showExperimentalOllama = !showExperimentalOllama;
}}>{showExperimentalOllama ? 'Show' : 'Hide'}</button
>
</div>
</div>
{#if showExperimentalOllama}
<form
on:submit|preventDefault={() => {
uploadModelHandler();
}}
>
<div class=" mb-2 flex w-full justify-between">
<div class=" text-sm font-medium">Upload a GGUF model</div>
<div class="pt-1">
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">Experimental</div>
<button
<button
class="p-1 px-3 text-xs flex rounded transition"
class=" text-xs font-medium text-gray-500"
on:click={() => {
if (modelUploadMode === 'file') {
modelUploadMode = 'url';
} else {
modelUploadMode = 'file';
}
}}
type="button"
type="button"
on:click={() => {
showExperimentalOllama = !showExperimentalOllama;
}}>{showExperimentalOllama ? 'Hide' : 'Show'}</button
>
>
{#if modelUploadMode === 'file'}
<span class="ml-2 self-center">File Mode</span>
{:else}
<span class="ml-2 self-center">URL Mode</span>
{/if}
</button>
</div>
</div>
</div>
<div class="flex w-full mb-1.5">
{#if showExperimentalOllama}
<div class="flex flex-col w-full">
<form
{#if modelUploadMode === 'file'}
on:submit|preventDefault={() => {
<div class="flex-1 {modelInputFile && modelInputFile.length > 0 ? 'mr-2' : ''}">
uploadModelHandler();
<input
}}
id="model-upload-input"
>
bind:this={modelUploadInputElement}
<div class=" mb-2 flex w-full justify-between">
type="file"
<div class=" text-sm font-medium">Upload a GGUF model</div>
bind:files={modelInputFile}
on:change={() => {
console.log(modelInputFile);
}}
accept=".gguf"
required
hidden
/>
<button
type="button"
class="w-full rounded text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850"
on:click={modelUploadInputElement.click}
>
{#if modelInputFile && modelInputFile.length > 0}
{modelInputFile[0].name}
{:else}
Click here to select
{/if}
</button>
</div>
{:else}
<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
<input
class="w-full rounded text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !==
''
? 'mr-2'
: ''}"
type="url"
required
bind:value={modelFileUrl}
placeholder="Type Hugging Face Resolve (Download) URL"
/>
</div>
{/if}
</div>
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
<button
<button
class="px-3 text-gray-100 bg-emerald-600 hover:bg-emerald-700 disabled:bg-gray-700 disabled:cursor-not-allowed rounded transition"
class="p-1 px-3 text-xs flex rounded transition"
type="submit"
on:click={() => {
disabled={modelTransferring}
if (modelUploadMode === 'file') {
modelUploadMode = 'url';
} else {
modelUploadMode = 'file';
}
}}
type="button"
>
>
{#if modelTransferring}
{#if modelUploadMode === 'file'}
<div class="self-center">
<span class="ml-2 self-center">File Mode</span>
<svg
{:else}
class=" w-4 h-4"
<span class="ml-2 self-center">URL Mode</span>
viewBox="0 0 24 24"
{/if}
fill="currentColor"
</button>
xmlns="http://www.w3.org/2000/svg"
</div>
><style>
.spinner_ajPY {
<div class="flex w-full mb-1.5">
transform-origin: center;
<div class="flex flex-col w-full">
animation: spinner_AtaB 0.75s infinite linear;
{#if modelUploadMode === 'file'}
}
<div class="flex-1 {modelInputFile && modelInputFile.length > 0 ? 'mr-2' : ''}">
@keyframes spinner_AtaB {
<input
100% {
id="model-upload-input"
transform: rotate(360deg);
bind:this={modelUploadInputElement}
}
type="file"
}
bind:files={modelInputFile}
</style><path
on:change={() => {
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
console.log(modelInputFile);
opacity=".25"
}}
/><path
accept=".gguf"
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
required
class="spinner_ajPY"
hidden
/></svg
/>
<button
type="button"
class="w-full rounded-lg text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850"
on:click={modelUploadInputElement.click}
>
>
{#if modelInputFile && modelInputFile.length > 0}
{modelInputFile[0].name}
{:else}
Click here to select
{/if}
</button>
</div>
</div>
{:else}
{:else}
<svg
<div class="flex-1 {modelFileUrl !== '' ? 'mr-2' : ''}">
xmlns="http://www.w3.org/2000/svg"
<input
viewBox="0 0 16 16"
class="w-full rounded-lg text-left py-2 px-4 dark:text-gray-300 dark:bg-gray-850 outline-none {modelFileUrl !==
fill="currentColor"
''
class="w-4 h-4"
? 'mr-2'
>
: ''}"
<path
type="url"
d="M7.25 10.25a.75.75 0 0 0 1.5 0V4.56l2.22 2.22a.75.75 0 1 0 1.06-1.06l-3.5-3.5a.75.75 0 0 0-1.06 0l-3.5 3.5a.75.75 0 0 0 1.06 1.06l2.22-2.22v5.69Z"
required
/>
bind:value={modelFileUrl}
<path
placeholder="Type Hugging Face Resolve (Download) URL"
d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
/>
/>
</
svg
>
</
div
>
{/if}
{/if}
</button>
</div>
{/if}
</div>
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
<div>
<button
class="px-3 text-gray-100 bg-emerald-600 hover:bg-emerald-700 disabled:bg-gray-700 disabled:cursor-not-allowed rounded transition"
type="submit"
disabled={modelTransferring}
>
{#if modelTransferring}
<div class="self-center">
<svg
class=" w-4 h-4"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25"
/><path
d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z"
class="spinner_ajPY"
/></svg
>
</div>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M7.25 10.25a.75.75 0 0 0 1.5 0V4.56l2.22 2.22a.75.75 0 1 0 1.06-1.06l-3.5-3.5a.75.75 0 0 0-1.06 0l-3.5 3.5a.75.75 0 0 0 1.06 1.06l2.22-2.22v5.69Z"
/>
<path
d="M3.5 9.75a.75.75 0 0 0-1.5 0v1.5A2.75 2.75 0 0 0 4.75 14h6.5A2.75 2.75 0 0 0 14 11.25v-1.5a.75.75 0 0 0-1.5 0v1.5c0 .69-.56 1.25-1.25 1.25h-6.5c-.69 0-1.25-.56-1.25-1.25v-1.5Z"
/>
</svg>
{/if}
</button>
{/if}
</div>
{#if (modelUploadMode === 'file' && modelInputFile && modelInputFile.length > 0) || (modelUploadMode === 'url' && modelFileUrl !== '')}
<div>
<div>
<div class=" my-2.5 text-sm font-medium">Modelfile Content</div>
<div>
<textarea
<div class=" my-2.5 text-sm font-medium">Modelfile Content</div>
bind:value={modelFileContent}
<textarea
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
bind:value={modelFileContent}
rows="6"
class="w-full rounded py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-800 outline-none resize-none"
/>
rows="6"
/>
</div>
</div>
</div>
{/if}
<div class=" mt-1 text-xs text-gray-400 dark:text-gray-500">
To access the GGUF models available for downloading, <a
class=" text-gray-500 dark:text-gray-300 font-medium underline"
href="https://huggingface.co/models?search=gguf"
target="_blank">click here.</a
>
</div>
</div>
{/if}
<div class=" mt-1 text-xs text-gray-400 dark:text-gray-500">
To access the GGUF models available for downloading, <a
class=" text-gray-500 dark:text-gray-300 font-medium underline"
href="https://huggingface.co/models?search=gguf"
target="_blank">click here.</a
>
</div>
{#if uploadProgress !== null}
{#if uploadProgress !== null}
<div class="mt-2">
<div class="mt-2">
<div class=" mb-2 text-xs">Upload Progress</div>
<div class=" mb-2 text-xs">Upload Progress</div>
<div class="w-full rounded-full dark:bg-gray-800">
<div class="w-full rounded-full dark:bg-gray-800">
<div
<div
class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
class="dark:bg-gray-600 bg-gray-500 text-xs font-medium text-gray-100 text-center p-0.5 leading-none rounded-full"
style="width: {Math.max(15, uploadProgress ?? 0)}%"
style="width: {Math.max(15, uploadProgress ?? 0)}%"
>
>
{uploadProgress ?? 0}%
{uploadProgress ?? 0}%
</div>
</div>
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
{modelFileDigest}
</div>
</div>
</div>
</div>
<div class="mt-1 text-xs dark:text-gray-500" style="font-size: 0.5rem;">
{/if}
{modelFileDigest}
</form>
</div>
{/if}
</div>
</div>
{/if}
</form>
{/if}
</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