|
@@ -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>
|