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
b1265c9c
Commit
b1265c9c
authored
May 25, 2024
by
Jun Siang Cheah
Browse files
Merge remote-tracking branch 'upstream/dev' into feat/backend-web-search
parents
60433856
e9c8341d
Changes
88
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
538 additions
and
830 deletions
+538
-830
src/lib/components/chat/MessageInput.svelte
src/lib/components/chat/MessageInput.svelte
+60
-13
src/lib/components/chat/Messages.svelte
src/lib/components/chat/Messages.svelte
+2
-8
src/lib/components/chat/Messages/CodeBlock.svelte
src/lib/components/chat/Messages/CodeBlock.svelte
+54
-53
src/lib/components/chat/Messages/CompareMessages.svelte
src/lib/components/chat/Messages/CompareMessages.svelte
+0
-3
src/lib/components/chat/Messages/Placeholder.svelte
src/lib/components/chat/Messages/Placeholder.svelte
+36
-37
src/lib/components/chat/Messages/ResponseMessage.svelte
src/lib/components/chat/Messages/ResponseMessage.svelte
+8
-10
src/lib/components/chat/Messages/UserMessage.svelte
src/lib/components/chat/Messages/UserMessage.svelte
+4
-9
src/lib/components/chat/ModelSelector.svelte
src/lib/components/chat/ModelSelector.svelte
+5
-7
src/lib/components/chat/ModelSelector/Selector.svelte
src/lib/components/chat/ModelSelector/Selector.svelte
+43
-18
src/lib/components/chat/Settings/Advanced.svelte
src/lib/components/chat/Settings/Advanced.svelte
+0
-155
src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
...b/components/chat/Settings/Advanced/AdvancedParams.svelte
+177
-97
src/lib/components/chat/Settings/General.svelte
src/lib/components/chat/Settings/General.svelte
+28
-27
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+37
-287
src/lib/components/chat/SettingsModal.svelte
src/lib/components/chat/SettingsModal.svelte
+1
-1
src/lib/components/chat/ShareChatModal.svelte
src/lib/components/chat/ShareChatModal.svelte
+2
-4
src/lib/components/common/Checkbox.svelte
src/lib/components/common/Checkbox.svelte
+1
-0
src/lib/components/common/Tooltip.svelte
src/lib/components/common/Tooltip.svelte
+2
-1
src/lib/components/layout/Navbar.svelte
src/lib/components/layout/Navbar.svelte
+0
-1
src/lib/components/workspace/Models.svelte
src/lib/components/workspace/Models.svelte
+73
-92
src/lib/components/workspace/Playground.svelte
src/lib/components/workspace/Playground.svelte
+5
-7
No files found.
src/lib/components/chat/MessageInput.svelte
View file @
b1265c9c
<script lang="ts">
import { toast } from 'svelte-sonner';
import { onMount, tick, getContext } from 'svelte';
import {
mobile, modelf
ile
s
, settings, showSidebar } from '$lib/stores';
import {
type Model, mob
ile, settings, showSidebar
, models
} from '$lib/stores';
import { blobToFile, calculateSHA256, findWordIndices } from '$lib/utils';
import {
...
...
@@ -27,7 +27,9 @@
export let stopResponse: Function;
export let autoScroll = true;
export let selectedModel = '';
export let atSelectedModel: Model | undefined;
export let selectedModels: [''];
let chatTextAreaElement: HTMLTextAreaElement;
let filesInputElement;
...
...
@@ -55,6 +57,11 @@
let speechRecognition;
let visionCapableModels = [];
$: visionCapableModels = [...(atSelectedModel ? [atSelectedModel] : selectedModels)].filter(
(model) => $models.find((m) => m.id === model)?.info?.meta?.capabilities?.vision ?? true
);
$: if (prompt) {
if (chatTextAreaElement) {
chatTextAreaElement.style.height = '';
...
...
@@ -361,6 +368,10 @@
inputFiles.forEach((file) => {
console.log(file, file.name.split('.').at(-1));
if (['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(file['type'])) {
if (visionCapableModels.length === 0) {
toast.error($i18n.t('Selected model(s) do not support image inputs'));
return;
}
let reader = new FileReader();
reader.onload = (event) => {
files = [
...
...
@@ -432,8 +443,8 @@
<div class="fixed bottom-0 {$showSidebar ? 'left-0 md:left-[260px]' : 'left-0'} right-0">
<div class="w-full">
<div class="
px-2.5 md:px-16
-mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
<div class="flex flex-col max-w-
5xl
w-full">
<div class=" -mb-0.5 mx-auto inset-x-0 bg-transparent flex justify-center">
<div class="flex flex-col max-w-
6xl px-2.5 md:px-6
w-full">
<div class="relative">
{#if autoScroll === false && messages.length > 0}
<div class=" absolute -top-12 left-0 right-0 flex justify-center z-30">
...
...
@@ -497,12 +508,12 @@
bind:chatInputPlaceholder
{messages}
on:select={(e) => {
s
electedModel = e.detail;
atS
electedModel = e.detail;
chatTextAreaElement?.focus();
}}
/>
{#if
s
electedModel !==
''
}
{#if
atS
electedModel !==
undefined
}
<div
class="px-3 py-2.5 text-left w-full flex justify-between items-center absolute bottom-0 left-0 right-0 bg-gradient-to-t from-50% from-white dark:from-gray-900"
>
...
...
@@ -511,21 +522,21 @@
crossorigin="anonymous"
alt="model profile"
class="size-5 max-w-[28px] object-cover rounded-full"
src={$model
file
s.find((model
file
) => model
file.tagName
===
s
electedModel.id)
?.image
U
rl ??
src={$models.find((model) => model
.id
===
atS
electedModel.id)
?.info?.meta
?.
profile_
image
_u
rl ??
($i18n.language === 'dg-DG'
? `/doge.png`
: `${WEBUI_BASE_URL}/static/favicon.png`)}
/>
<div>
Talking to <span class=" font-medium">{
s
electedModel.name}
</span>
Talking to <span class=" font-medium">{
atS
electedModel.name}</span>
</div>
</div>
<div>
<button
class="flex items-center"
on:click={() => {
s
electedModel =
''
;
atS
electedModel =
undefined
;
}}
>
<XMark />
...
...
@@ -538,7 +549,7 @@
</div>
<div class="bg-white dark:bg-gray-900">
<div class="max-w-6xl px-2.5 md:px-
1
6 mx-auto inset-x-0">
<div class="max-w-6xl px-2.5 md:px-6 mx-auto inset-x-0">
<div class=" pb-2">
<input
bind:this={filesInputElement}
...
...
@@ -553,6 +564,12 @@
if (
['image/gif', 'image/webp', 'image/jpeg', 'image/png'].includes(file['type'])
) {
if (visionCapableModels.length === 0) {
toast.error($i18n.t('Selected model(s) do not support image inputs'));
inputFiles = null;
filesInputElement.value = '';
return;
}
let reader = new FileReader();
reader.onload = (event) => {
files = [
...
...
@@ -592,6 +609,7 @@
dir={$settings?.chatDirection ?? 'LTR'}
class=" flex flex-col relative w-full rounded-3xl px-1.5 bg-gray-50 dark:bg-gray-850 dark:text-gray-100"
on:submit|preventDefault={() => {
// check if selectedModels support image input
submitPrompt(prompt, user);
}}
>
...
...
@@ -600,7 +618,36 @@
{#each files as file, fileIdx}
<div class=" relative group">
{#if file.type === 'image'}
<img src={file.url} alt="input" class=" h-16 w-16 rounded-xl object-cover" />
<div class="relative">
<img
src={file.url}
alt="input"
class=" h-16 w-16 rounded-xl object-cover"
/>
{#if atSelectedModel ? visionCapableModels.length === 0 : selectedModels.length !== visionCapableModels.length}
<Tooltip
className=" absolute top-1 left-1"
content={$i18n.t('{{ models }}', {
models: [...(atSelectedModel ? [atSelectedModel] : selectedModels)]
.filter((id) => !visionCapableModels.includes(id))
.join(', ')
})}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="size-4 fill-yellow-300"
>
<path
fill-rule="evenodd"
d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z"
clip-rule="evenodd"
/>
</svg>
</Tooltip>
{/if}
</div>
{:else if file.type === 'doc'}
<div
class="h-16 w-[15rem] flex items-center space-x-3 px-2.5 dark:bg-gray-600 rounded-xl border border-gray-200 dark:border-none"
...
...
@@ -886,7 +933,7 @@
if (e.key === 'Escape') {
console.log('Escape');
s
electedModel =
''
;
atS
electedModel =
undefined
;
}
}}
rows="1"
...
...
src/lib/components/chat/Messages.svelte
View file @
b1265c9c
<script lang="ts">
import { v4 as uuidv4 } from 'uuid';
import { chats, config,
modelfiles,
settings, user as _user, mobile } from '$lib/stores';
import { chats, config, settings, user as _user, mobile } from '$lib/stores';
import { tick, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
...
...
@@ -26,7 +26,6 @@
export let user = $_user;
export let prompt;
export let suggestionPrompts = [];
export let processing = '';
export let bottomPadding = false;
export let autoScroll;
...
...
@@ -34,7 +33,6 @@
export let messages = [];
export let selectedModels;
export let selectedModelfiles = [];
$: if (autoScroll && bottomPadding) {
(async () => {
...
...
@@ -247,9 +245,7 @@
<div class="h-full flex mb-16">
{#if messages.length == 0}
<Placeholder
models={selectedModels}
modelfiles={selectedModelfiles}
{suggestionPrompts}
modelIds={selectedModels}
submitPrompt={async (p) => {
let text = p;
...
...
@@ -316,7 +312,6 @@
{#key message.id}
<ResponseMessage
{message}
modelfiles={selectedModelfiles}
siblings={history.messages[message.parentId]?.childrenIds ?? []}
isLastMessage={messageIdx + 1 === messages.length}
{readOnly}
...
...
@@ -348,7 +343,6 @@
{chatId}
parentMessage={history.messages[message.parentId]}
{messageIdx}
{selectedModelfiles}
{updateChatMessages}
{confirmEditResponseMessage}
{rateMessage}
...
...
src/lib/components/chat/Messages/CodeBlock.svelte
View file @
b1265c9c
...
...
@@ -4,7 +4,7 @@
import hljs from 'highlight.js';
import 'highlight.js/styles/github-dark.min.css';
import { loadPyodide } from 'pyodide';
import { tick } from 'svelte';
import {
onMount,
tick } from 'svelte';
import PyodideWorker from '$lib/workers/pyodide.worker?worker';
export let id = '';
...
...
@@ -12,6 +12,7 @@
export let lang = '';
export let code = '';
let highlightedCode = null;
let executing = false;
let stdout = null;
...
...
@@ -202,11 +203,12 @@ __builtins__.input = input`);
};
};
$: highlightedCode = code ? hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value : '';
$: if (code) {
highlightedCode = hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value || code;
}
</script>
{#if code}
<div class="mb-4" dir="ltr">
<div class="mb-4" dir="ltr">
<div
class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"
>
...
...
@@ -257,5 +259,4 @@ __builtins__.input = input`);
<div class="text-sm">{stdout || stderr || result}</div>
</div>
{/if}
</div>
{/if}
</div>
src/lib/components/chat/Messages/CompareMessages.svelte
View file @
b1265c9c
...
...
@@ -13,8 +13,6 @@
export let parentMessage;
export let selectedModelfiles;
export let updateChatMessages: Function;
export let confirmEditResponseMessage: Function;
export let rateMessage: Function;
...
...
@@ -130,7 +128,6 @@
>
<ResponseMessage
message={groupedMessages[model].messages[groupedMessagesIdx[model]]}
modelfiles={selectedModelfiles}
siblings={groupedMessages[model].messages.map((m) => m.id)}
isLastMessage={true}
{updateChatMessages}
...
...
src/lib/components/chat/Messages/Placeholder.svelte
View file @
b1265c9c
<script lang="ts">
import { WEBUI_BASE_URL } from '$lib/constants';
import {
user
} from '$lib/stores';
import {
config, user, models as _models
} from '$lib/stores';
import { onMount, getContext } from 'svelte';
import { blur, fade } from 'svelte/transition';
...
...
@@ -9,23 +9,20 @@
const i18n = getContext('i18n');
export let modelIds = [];
export let models = [];
export let modelfiles = [];
export let submitPrompt;
export let suggestionPrompts;
let mounted = false;
let modelfile = null;
let selectedModelIdx = 0;
$: modelfile =
models[selectedModelIdx] in modelfiles ? modelfiles[models[selectedModelIdx]] : null;
$: if (models.length > 0) {
$: if (modelIds.length > 0) {
selectedModelIdx = models.length - 1;
}
$: models = modelIds.map((id) => $_models.find((m) => m.id === id));
onMount(() => {
mounted = true;
});
...
...
@@ -41,25 +38,14 @@
selectedModelIdx = modelIdx;
}}
>
{#if model in modelfiles}
<img
crossorigin="anonymous"
src={modelfiles[model]?.imageUrl ?? `${WEBUI_BASE_URL}/static/favicon.png`}
alt="modelfile"
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
draggable="false"
/>
{:else}
<img
crossorigin="anonymous"
src={$i18n.language === 'dg-DG'
? `/doge.png`
: `${WEBUI_BASE_URL}/static/favicon.png`}
src={model?.info?.meta?.profile_image_url ??
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
class=" size-[2.7rem] rounded-full border-[1px] border-gray-200 dark:border-none"
alt="logo"
draggable="false"
/>
{/if}
</button>
{/each}
</div>
...
...
@@ -70,23 +56,32 @@
>
<div>
<div class=" capitalize line-clamp-1" in:fade={{ duration: 200 }}>
{#if model
file
}
{model
file.titl
e}
{#if model
s[selectedModelIdx]?.info
}
{model
s[selectedModelIdx]?.info?.nam
e}
{:else}
{$i18n.t('Hello, {{name}}', { name: $user.name })}
{/if}
</div>
<div in:fade={{ duration: 200, delay: 200 }}>
{#if model
file
}
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400">
{model
file.desc
}
{#if model
s[selectedModelIdx]?.info
}
<div class="mt-0.5 text-base font-normal text-gray-500 dark:text-gray-400
line-clamp-3
">
{model
s[selectedModelIdx]?.info?.meta?.description
}
</div>
{#if model
file
.user}
{#if model
s[selectedModelIdx]?.info?.meta?
.user}
<div class="mt-0.5 text-sm font-normal text-gray-400 dark:text-gray-500">
By <a href="https://openwebui.com/m/{modelfile.user.username}"
>{modelfile.user.name ? modelfile.user.name : `@${modelfile.user.username}`}</a
By
{#if models[selectedModelIdx]?.info?.meta?.user.community}
<a
href="https://openwebui.com/m/{models[selectedModelIdx]?.info?.meta?.user
.username}"
>{models[selectedModelIdx]?.info?.meta?.user.name
? models[selectedModelIdx]?.info?.meta?.user.name
: `@${models[selectedModelIdx]?.info?.meta?.user.username}`}</a
>
{:else}
{models[selectedModelIdx]?.info?.meta?.user.name}
{/if}
</div>
{/if}
{:else}
...
...
@@ -99,7 +94,11 @@
</div>
<div class=" w-full" in:fade={{ duration: 200, delay: 300 }}>
<Suggestions {suggestionPrompts} {submitPrompt} />
<Suggestions
suggestionPrompts={models[selectedModelIdx]?.info?.meta?.suggestion_prompts ??
$config.default_prompt_suggestions}
{submitPrompt}
/>
</div>
</div>
{/key}
src/lib/components/chat/Messages/ResponseMessage.svelte
View file @
b1265c9c
...
...
@@ -14,7 +14,7 @@
const dispatch = createEventDispatcher();
import { config, settings } from '$lib/stores';
import { config,
models,
settings } from '$lib/stores';
import { synthesizeOpenAISpeech } from '$lib/apis/audio';
import { imageGenerations } from '$lib/apis/images';
import {
...
...
@@ -34,7 +34,6 @@
import RateComment from './RateComment.svelte';
import CitationsModal from '$lib/components/chat/Messages/CitationsModal.svelte';
export let modelfiles = [];
export let message;
export let siblings;
...
...
@@ -52,6 +51,9 @@
export let continueGeneration: Function;
export let regenerateResponse: Function;
let model = null;
$: model = $models.find((m) => m.id === message.model);
let edit = false;
let editedContent = '';
let editTextAreaElement: HTMLTextAreaElement;
...
...
@@ -338,17 +340,13 @@
dir={$settings.chatDirection}
>
<ProfileImage
src={model
files[message.model]?.
image
U
rl ??
src={model
?.info?.meta?.profile_
image
_u
rl ??
($i18n.language === 'dg-DG' ? `/doge.png` : `${WEBUI_BASE_URL}/static/favicon.png`)}
/>
<div class="w-full overflow-hidden pl-1">
<Name>
{#if message.model in modelfiles}
{modelfiles[message.model]?.title}
{:else}
{message.model ? ` ${message.model}` : ''}
{/if}
{model?.name ?? message.model}
{#if message.timestamp}
<span
...
...
@@ -541,8 +539,8 @@
{#if token.type === 'code'}
<CodeBlock
id={`${message.id}-${tokenIdx}`}
lang={token.lang}
code={revertSanitizedResponseContent(token.text)}
lang={token
?
.lang
?? ''
}
code={revertSanitizedResponseContent(token
?
.text
?? ''
)}
/>
{:else}
{@html marked.parse(token.raw, {
...
...
src/lib/components/chat/Messages/UserMessage.svelte
View file @
b1265c9c
...
...
@@ -4,7 +4,7 @@
import { tick, createEventDispatcher, getContext } from 'svelte';
import Name from './Name.svelte';
import ProfileImage from './ProfileImage.svelte';
import { model
file
s, settings } from '$lib/stores';
import { models, settings } from '$lib/stores';
import Tooltip from '$lib/components/common/Tooltip.svelte';
import { user as _user } from '$lib/stores';
...
...
@@ -60,8 +60,7 @@
{#if !($settings?.chatBubble ?? true)}
<ProfileImage
src={message.user
? $modelfiles.find((modelfile) => modelfile.tagName === message.user)?.imageUrl ??
'/user.png'
? $models.find((m) => m.id === message.user)?.info?.meta?.profile_image_url ?? '/user.png'
: user?.profile_image_url ?? '/user.png'}
/>
{/if}
...
...
@@ -70,12 +69,8 @@
<div>
<Name>
{#if message.user}
{#if $modelfiles.map((modelfile) => modelfile.tagName).includes(message.user)}
{$modelfiles.find((modelfile) => modelfile.tagName === message.user)?.title}
{:else}
{$i18n.t('You')}
<span class=" text-gray-500 text-sm font-medium">{message?.user ?? ''}</span>
{/if}
{:else if $settings.showUsername || $_user.name !== user.name}
{user.name}
{:else}
...
...
src/lib/components/chat/ModelSelector.svelte
View file @
b1265c9c
...
...
@@ -45,12 +45,10 @@
<div class="mr-1 max-w-full">
<Selector
placeholder={$i18n.t('Select a model')}
items={$models
.filter((model) => model.name !== 'hr')
.map((model) => ({
items={$models.map((model) => ({
value: model.id,
label: model.name,
info
: model
model
: model
}))}
bind:value={selectedModel}
/>
...
...
src/lib/components/chat/ModelSelector/Selector.svelte
View file @
b1265c9c
...
...
@@ -12,7 +12,9 @@
import { user, MODEL_DOWNLOAD_POOL, models, mobile } from '$lib/stores';
import { toast } from 'svelte-sonner';
import { capitalizeFirstLetter, getModels, splitStream } from '$lib/utils';
import { capitalizeFirstLetter, sanitizeResponseContent, splitStream } from '$lib/utils';
import { getModels } from '$lib/apis';
import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n');
...
...
@@ -23,7 +25,12 @@
export let searchEnabled = true;
export let searchPlaceholder = $i18n.t('Search a model');
export let items = [{ value: 'mango', label: 'Mango' }];
export let items: {
label: string;
value: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any;
} = [];
export let className = 'w-[30rem]';
...
...
@@ -239,19 +246,37 @@
}}
>
<div class="flex items-center gap-2">
<div class="flex items-center">
<div class="line-clamp-1">
{item.label}
</div>
{#if item.model.owned_by === 'ollama' && (item.model.ollama?.details?.parameter_size ?? '') !== ''}
<div class="flex ml-1 items-center">
<Tooltip
content={`${
item.model.ollama?.details?.quantization_level
? item.model.ollama?.details?.quantization_level + ' '
: ''
}${
item.model.ollama?.size
? `(${(item.model.ollama?.size / 1024 ** 3).toFixed(1)}GB)`
: ''
}`}
className="self-end"
>
<span class=" text-xs font-medium text-gray-600 dark:text-gray-400"
>{item.
info
?.details?.parameter_size ?? ''}</span
>{item.
model.ollama
?.details?.parameter_size ?? ''}</span
>
</Tooltip>
</div>
{/if}
</div>
<!-- {JSON.stringify(item.info)} -->
{#if item.
info.external
}
<Tooltip content={
item.info?.source ??
'External'}>
<div class="
mr-2
">
{#if item.
model.owned_by === 'openai'
}
<Tooltip content={
`${
'External'}
`}
>
<div class="">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
...
...
@@ -271,15 +296,15 @@
</svg>
</div>
</Tooltip>
{:else}
{/if}
{#if item.model?.info?.meta?.description}
<Tooltip
content={`${
item.info?.details?.quantization_level
? item.info?.details?.quantization_level + ' '
: ''
}${item.info.size ? `(${(item.info.size / 1024 ** 3).toFixed(1)}GB)` : ''}`}
content={`${sanitizeResponseContent(
item.model?.info?.meta?.description
).replaceAll('\n', '<br>')}`}
>
<div class="
mr-2
">
<div class="">
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
...
...
src/lib/components/chat/Settings/Advanced.svelte
deleted
100644 → 0
View file @
60433856
<script lang="ts">
import { createEventDispatcher, onMount, getContext } from 'svelte';
import AdvancedParams from './Advanced/AdvancedParams.svelte';
const i18n = getContext('i18n');
const dispatch = createEventDispatcher();
export let saveSettings: Function;
// Advanced
let requestFormat = '';
let keepAlive = null;
let options = {
// Advanced
seed: 0,
temperature: '',
repeat_penalty: '',
repeat_last_n: '',
mirostat: '',
mirostat_eta: '',
mirostat_tau: '',
top_k: '',
top_p: '',
stop: '',
tfs_z: '',
num_ctx: '',
num_predict: ''
};
const toggleRequestFormat = async () => {
if (requestFormat === '') {
requestFormat = 'json';
} else {
requestFormat = '';
}
saveSettings({ requestFormat: requestFormat !== '' ? requestFormat : undefined });
};
onMount(() => {
let settings = JSON.parse(localStorage.getItem('settings') ?? '{}');
requestFormat = settings.requestFormat ?? '';
keepAlive = settings.keepAlive ?? null;
options.seed = settings.seed ?? 0;
options.temperature = settings.temperature ?? '';
options.repeat_penalty = settings.repeat_penalty ?? '';
options.top_k = settings.top_k ?? '';
options.top_p = settings.top_p ?? '';
options.num_ctx = settings.num_ctx ?? '';
options = { ...options, ...settings.options };
options.stop = (settings?.options?.stop ?? []).join(',');
});
</script>
<div class="flex flex-col h-full justify-between text-sm">
<div class=" space-y-3 pr-1.5 overflow-y-scroll max-h-80">
<div class=" text-sm font-medium">{$i18n.t('Parameters')}</div>
<AdvancedParams bind:options />
<hr class=" dark:border-gray-700" />
<div class=" py-1 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Keep Alive')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
keepAlive = keepAlive === null ? '5m' : null;
}}
>
{#if keepAlive === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
{#if keepAlive !== null}
<div class="flex mt-1 space-x-2">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
bind:value={keepAlive}
/>
</div>
{/if}
</div>
<div>
<div class=" py-1 flex w-full justify-between">
<div class=" self-center text-sm font-medium">{$i18n.t('Request Mode')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
toggleRequestFormat();
}}
>
{#if requestFormat === ''}
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
{:else if requestFormat === 'json'}
<!-- <svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4 self-center"
>
<path
d="M10 2a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 2zM10 15a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 0110 15zM10 7a3 3 0 100 6 3 3 0 000-6zM15.657 5.404a.75.75 0 10-1.06-1.06l-1.061 1.06a.75.75 0 001.06 1.06l1.06-1.06zM6.464 14.596a.75.75 0 10-1.06-1.06l-1.06 1.06a.75.75 0 001.06 1.06l1.06-1.06zM18 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0118 10zM5 10a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 015 10zM14.596 15.657a.75.75 0 001.06-1.06l-1.06-1.061a.75.75 0 10-1.06 1.06l1.06 1.06zM5.404 6.464a.75.75 0 001.06-1.06l-1.06-1.06a.75.75 0 10-1.061 1.06l1.06 1.06z"
/>
</svg> -->
<span class="ml-2 self-center">{$i18n.t('JSON')}</span>
{/if}
</button>
</div>
</div>
</div>
<div class="flex justify-end pt-3 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-lg"
on:click={() => {
saveSettings({
options: {
seed: (options.seed !== 0 ? options.seed : undefined) ?? undefined,
stop: options.stop !== '' ? options.stop.split(',').filter((e) => e) : undefined,
temperature: options.temperature !== '' ? options.temperature : undefined,
repeat_penalty: options.repeat_penalty !== '' ? options.repeat_penalty : undefined,
repeat_last_n: options.repeat_last_n !== '' ? options.repeat_last_n : undefined,
mirostat: options.mirostat !== '' ? options.mirostat : undefined,
mirostat_eta: options.mirostat_eta !== '' ? options.mirostat_eta : undefined,
mirostat_tau: options.mirostat_tau !== '' ? options.mirostat_tau : undefined,
top_k: options.top_k !== '' ? options.top_k : undefined,
top_p: options.top_p !== '' ? options.top_p : undefined,
tfs_z: options.tfs_z !== '' ? options.tfs_z : undefined,
num_ctx: options.num_ctx !== '' ? options.num_ctx : undefined,
num_predict: options.num_predict !== '' ? options.num_predict : undefined
},
keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
});
dispatch('save');
}}
>
{$i18n.t('Save')}
</button>
</div>
</div>
src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
View file @
b1265c9c
<script lang="ts">
import { getContext } from 'svelte';
import { getContext, createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
const i18n = getContext('i18n');
export let
option
s = {
export let
param
s = {
// Advanced
seed: 0,
stop:
''
,
stop:
null
,
temperature: '',
re
peat
_penalty: '',
f
re
quency
_penalty: '',
repeat_last_n: '',
mirostat: '',
mirostat_eta: '',
...
...
@@ -17,40 +19,86 @@
top_p: '',
tfs_z: '',
num_ctx: '',
num_predict: ''
max_tokens: '',
template: null
};
let customFieldName = '';
let customFieldValue = '';
$: if (params) {
dispatch('change', params);
}
</script>
<div class=" space-y-3 text-xs">
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Seed')}</div>
<div class=" flex-1 self-center">
<div class=" space-y-1 text-xs">
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Seed')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.seed = (params?.seed ?? null) === null ? 0 : null;
}}
>
{#if (params?.seed ?? null) === null}
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
{:else}
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
{/if}
</button>
</div>
{#if (params?.seed ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="number"
placeholder="Enter Seed"
bind:value={
option
s.seed}
bind:value={
param
s.seed}
autocomplete="off"
min="0"
/>
</div>
</div>
{/if}
</div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" w-20 text-xs font-medium self-center">{$i18n.t('Stop Sequence')}</div>
<div class=" flex-1 self-center">
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Stop Sequence')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.stop = (params?.stop ?? null) === null ? '' : null;
}}
>
{#if (params?.stop ?? null) === null}
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
{:else}
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
{/if}
</button>
</div>
{#if (params?.stop ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
type="text"
placeholder={$i18n.t('Enter stop sequence')}
bind:value={
option
s.stop}
bind:value={
param
s.stop}
autocomplete="off"
/>
</div>
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
...
...
@@ -61,10 +109,10 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.temperature =
options
.temperature === '' ? 0.8 : '';
param
s.temperature =
(params?
.temperature
?? '')
=== '' ? 0.8 : '';
}}
>
{#if
options
.temperature === ''}
{#if
(params?
.temperature
?? '')
=== ''}
<span class="ml-2 self-center"> {$i18n.t('Default')} </span>
{:else}
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
...
...
@@ -72,7 +120,7 @@
</button>
</div>
{#if
options
.temperature !== ''}
{#if
(params?
.temperature
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -81,13 +129,13 @@
min="0"
max="1"
step="0.05"
bind:value={
option
s.temperature}
bind:value={
param
s.temperature}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.temperature}
bind:value={
param
s.temperature}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -107,18 +155,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.mirostat =
options
.mirostat === '' ? 0 : '';
param
s.mirostat =
(params?
.mirostat
?? '')
=== '' ? 0 : '';
}}
>
{#if
options
.mirostat === ''}
{#if
(params?
.mirostat
?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options
.mirostat !== ''}
{#if
(params?
.mirostat
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -127,13 +175,13 @@
min="0"
max="2"
step="1"
bind:value={
option
s.mirostat}
bind:value={
param
s.mirostat}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.mirostat}
bind:value={
param
s.mirostat}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -153,18 +201,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.mirostat_eta =
options
.mirostat_eta === '' ? 0.1 : '';
param
s.mirostat_eta =
(params?
.mirostat_eta
?? '')
=== '' ? 0.1 : '';
}}
>
{#if
options
.mirostat_eta === ''}
{#if
(params?
.mirostat_eta
?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options
.mirostat_eta !== ''}
{#if
(params?
.mirostat_eta
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -173,13 +221,13 @@
min="0"
max="1"
step="0.05"
bind:value={
option
s.mirostat_eta}
bind:value={
param
s.mirostat_eta}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.mirostat_eta}
bind:value={
param
s.mirostat_eta}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -199,10 +247,10 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.mirostat_tau =
options
.mirostat_tau === '' ? 5.0 : '';
param
s.mirostat_tau =
(params?
.mirostat_tau
?? '')
=== '' ? 5.0 : '';
}}
>
{#if
options
.mirostat_tau === ''}
{#if
(params?
.mirostat_tau
?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...
...
@@ -210,7 +258,7 @@
</button>
</div>
{#if
options
.mirostat_tau !== ''}
{#if
(params?
.mirostat_tau
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -219,13 +267,13 @@
min="0"
max="10"
step="0.5"
bind:value={
option
s.mirostat_tau}
bind:value={
param
s.mirostat_tau}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.mirostat_tau}
bind:value={
param
s.mirostat_tau}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -245,18 +293,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.top_k =
options.top_k
=== '' ? 40 : '';
param
s.top_k =
(params?.top_k ?? '')
=== '' ? 40 : '';
}}
>
{#if
options.top_k
=== ''}
{#if
(params?.top_k ?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options.top_k
!== ''}
{#if
(params?.top_k ?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -265,13 +313,13 @@
min="0"
max="100"
step="0.5"
bind:value={
option
s.top_k}
bind:value={
param
s.top_k}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.top_k}
bind:value={
param
s.top_k}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -291,18 +339,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.top_p =
options.top_p
=== '' ? 0.9 : '';
param
s.top_p =
(params?.top_p ?? '')
=== '' ? 0.9 : '';
}}
>
{#if
options.top_p
=== ''}
{#if
(params?.top_p ?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options.top_p
!== ''}
{#if
(params?.top_p ?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -311,13 +359,13 @@
min="0"
max="1"
step="0.05"
bind:value={
option
s.top_p}
bind:value={
param
s.top_p}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.top_p}
bind:value={
param
s.top_p}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -331,24 +379,24 @@
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('
Repeat
Penalty')}</div>
<div class=" self-center text-xs font-medium">{$i18n.t('
Frequencey
Penalty')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.re
peat
_penalty =
options.repeat
_penalty === '' ? 1.1 : '';
param
s.
f
re
quency
_penalty =
(params?.frequency
_penalty
?? '')
=== '' ? 1.1 : '';
}}
>
{#if
options.repeat
_penalty === ''}
{#if
(params?.frequency
_penalty
?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options.repeat
_penalty !== ''}
{#if
(params?.frequency
_penalty
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -357,13 +405,13 @@
min="0"
max="2"
step="0.05"
bind:value={
option
s.re
peat
_penalty}
bind:value={
param
s.
f
re
quency
_penalty}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.re
peat
_penalty}
bind:value={
param
s.
f
re
quency
_penalty}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -383,18 +431,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.repeat_last_n =
options
.repeat_last_n === '' ? 64 : '';
param
s.repeat_last_n =
(params?
.repeat_last_n
?? '')
=== '' ? 64 : '';
}}
>
{#if
options
.repeat_last_n === ''}
{#if
(params?
.repeat_last_n
?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options
.repeat_last_n !== ''}
{#if
(params?
.repeat_last_n
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -403,13 +451,13 @@
min="-1"
max="128"
step="1"
bind:value={
option
s.repeat_last_n}
bind:value={
param
s.repeat_last_n}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.repeat_last_n}
bind:value={
param
s.repeat_last_n}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
...
...
@@ -429,18 +477,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.tfs_z =
options.tfs_z
=== '' ? 1 : '';
param
s.tfs_z =
(params?.tfs_z ?? '')
=== '' ? 1 : '';
}}
>
{#if
options.tfs_z
=== ''}
{#if
(params?.tfs_z ?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options.tfs_z
!== ''}
{#if
(params?.tfs_z ?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -449,13 +497,13 @@
min="0"
max="2"
step="0.05"
bind:value={
option
s.tfs_z}
bind:value={
param
s.tfs_z}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div>
<input
bind:value={
option
s.tfs_z}
bind:value={
param
s.tfs_z}
type="number"
class=" bg-transparent text-center w-14"
min="0"
...
...
@@ -475,18 +523,18 @@
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
option
s.num_ctx =
options
.num_ctx === '' ? 2048 : '';
param
s.num_ctx =
(params?
.num_ctx
?? '')
=== '' ? 2048 : '';
}}
>
{#if
options
.num_ctx === ''}
{#if
(params?
.num_ctx
?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options
.num_ctx !== ''}
{#if
(params?
.num_ctx
?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -495,13 +543,13 @@
min="-1"
max="10240000"
step="1"
bind:value={
option
s.num_ctx}
bind:value={
param
s.num_ctx}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={
option
s.num_ctx}
bind:value={
param
s.num_ctx}
type="number"
class=" bg-transparent text-center w-14"
min="-1"
...
...
@@ -513,24 +561,24 @@
</div>
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens')}</div>
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens
(num_predict)
')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
options.num_predict = options.num_predict
=== '' ? 128 : '';
params.max_tokens = (params?.max_tokens ?? '')
=== '' ? 128 : '';
}}
>
{#if
options.num_predict
=== ''}
{#if
(params?.max_tokens ?? '')
=== ''}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('
Default
')}</span>
<span class="ml-2 self-center">{$i18n.t('
Custom
')}</span>
{/if}
</button>
</div>
{#if
options.num_predict
!== ''}
{#if
(params?.max_tokens ?? '')
!== ''}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
...
...
@@ -539,13 +587,13 @@
min="-2"
max="16000"
step="1"
bind:value={
options.num_predict
}
bind:value={
params.max_tokens
}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={
options.num_predict
}
bind:value={
params.max_tokens
}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
...
...
@@ -556,4 +604,36 @@
</div>
{/if}
</div>
<div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Template')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.template = (params?.template ?? null) === null ? '' : null;
}}
>
{#if (params?.template ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span>
{/if}
</button>
</div>
{#if (params?.template ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<textarea
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1"
placeholder="Write your model template content here"
rows="4"
bind:value={params.template}
/>
</div>
</div>
{/if}
</div>
</div>
src/lib/components/chat/Settings/General.svelte
View file @
b1265c9c
...
...
@@ -41,21 +41,21 @@
let requestFormat = '';
let keepAlive = null;
let
option
s = {
let
param
s = {
// Advanced
seed: 0,
temperature: '',
re
peat
_penalty: '',
f
re
quency
_penalty: '',
repeat_last_n: '',
mirostat: '',
mirostat_eta: '',
mirostat_tau: '',
top_k: '',
top_p: '',
stop:
''
,
stop:
null
,
tfs_z: '',
num_ctx: '',
num_predict
: ''
max_tokens
: ''
};
const toggleRequestFormat = async () => {
...
...
@@ -80,14 +80,14 @@
requestFormat = settings.requestFormat ?? '';
keepAlive = settings.keepAlive ?? null;
option
s.seed = settings.seed ?? 0;
option
s.temperature = settings.temperature ?? '';
option
s.re
peat
_penalty = settings.re
peat
_penalty ?? '';
option
s.top_k = settings.top_k ?? '';
option
s.top_p = settings.top_p ?? '';
option
s.num_ctx = settings.num_ctx ?? '';
option
s = { ...
option
s, ...settings.
option
s };
option
s.stop =
(
settings?.
option
s?.stop ?? []).join(',');
param
s.seed = settings.seed ?? 0;
param
s.temperature = settings.temperature ?? '';
param
s.
f
re
quency
_penalty = settings.
f
re
quency
_penalty ?? '';
param
s.top_k = settings.top_k ?? '';
param
s.top_p = settings.top_p ?? '';
param
s.num_ctx = settings.num_ctx ?? '';
param
s = { ...
param
s, ...settings.
param
s };
param
s.stop = settings?.
params?.stop ? (settings?.param
s?.stop ?? []).join(',')
: null
;
});
const applyTheme = (_theme: string) => {
...
...
@@ -228,7 +228,7 @@
</div>
{#if showAdvanced}
<AdvancedParams bind:
option
s />
<AdvancedParams bind:
param
s />
<hr class=" dark:border-gray-700" />
<div class=" py-1 w-full justify-between">
...
...
@@ -300,20 +300,21 @@
on:click={() => {
saveSettings({
system: system !== '' ? system : undefined,
options: {
seed: (options.seed !== 0 ? options.seed : undefined) ?? undefined,
stop: options.stop !== '' ? options.stop.split(',').filter((e) => e) : undefined,
temperature: options.temperature !== '' ? options.temperature : undefined,
repeat_penalty: options.repeat_penalty !== '' ? options.repeat_penalty : undefined,
repeat_last_n: options.repeat_last_n !== '' ? options.repeat_last_n : undefined,
mirostat: options.mirostat !== '' ? options.mirostat : undefined,
mirostat_eta: options.mirostat_eta !== '' ? options.mirostat_eta : undefined,
mirostat_tau: options.mirostat_tau !== '' ? options.mirostat_tau : undefined,
top_k: options.top_k !== '' ? options.top_k : undefined,
top_p: options.top_p !== '' ? options.top_p : undefined,
tfs_z: options.tfs_z !== '' ? options.tfs_z : undefined,
num_ctx: options.num_ctx !== '' ? options.num_ctx : undefined,
num_predict: options.num_predict !== '' ? options.num_predict : undefined
params: {
seed: (params.seed !== 0 ? params.seed : undefined) ?? undefined,
stop: params.stop !== null ? params.stop.split(',').filter((e) => e) : undefined,
temperature: params.temperature !== '' ? params.temperature : undefined,
frequency_penalty:
params.frequency_penalty !== '' ? params.frequency_penalty : undefined,
repeat_last_n: params.repeat_last_n !== '' ? params.repeat_last_n : undefined,
mirostat: params.mirostat !== '' ? params.mirostat : undefined,
mirostat_eta: params.mirostat_eta !== '' ? params.mirostat_eta : undefined,
mirostat_tau: params.mirostat_tau !== '' ? params.mirostat_tau : undefined,
top_k: params.top_k !== '' ? params.top_k : undefined,
top_p: params.top_p !== '' ? params.top_p : undefined,
tfs_z: params.tfs_z !== '' ? params.tfs_z : undefined,
num_ctx: params.num_ctx !== '' ? params.num_ctx : undefined,
max_tokens: params.max_tokens !== '' ? params.max_tokens : undefined
},
keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
});
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
b1265c9c
<script lang="ts">
import queue from 'async/queue';
import { toast } from 'svelte-sonner';
import {
...
...
@@ -12,32 +11,19 @@
cancelOllamaRequest,
uploadModel
} from '$lib/apis/ollama';
import { WEBUI_API_BASE_URL, WEBUI_BASE_URL } from '$lib/constants';
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user } from '$lib/stores';
import { WEBUI_NAME, models, MODEL_DOWNLOAD_POOL, user
, config
} from '$lib/stores';
import { splitStream } from '$lib/utils';
import { onMount, getContext } from 'svelte';
import { addLiteLLMModel, deleteLiteLLMModel, getLiteLLMModelInfo } from '$lib/apis/litellm';
import Tooltip from '$lib/components/common/Tooltip.svelte';
const i18n = getContext('i18n');
export let getModels: Function;
let showLiteLLM = false;
let showLiteLLMParams = false;
let modelUploadInputElement: HTMLInputElement;
let liteLLMModelInfo = [];
let liteLLMModel = '';
let liteLLMModelName = '';
let liteLLMAPIBase = '';
let liteLLMAPIKey = '';
let liteLLMRPM = '';
let liteLLMMaxTokens = '';
let deleteLiteLLMModelName = '';
$: liteLLMModelName = liteLLMModel;
// Models
...
...
@@ -439,60 +425,9 @@
}
};
const addLiteLLMModelHandler = async () => {
if (!liteLLMModelInfo.find((info) => info.model_name === liteLLMModelName)) {
const res = await addLiteLLMModel(localStorage.token, {
name: liteLLMModelName,
model: liteLLMModel,
api_base: liteLLMAPIBase,
api_key: liteLLMAPIKey,
rpm: liteLLMRPM,
max_tokens: liteLLMMaxTokens
}).catch((error) => {
toast.error(error);
return null;
});
if (res) {
if (res.message) {
toast.success(res.message);
}
}
} else {
toast.error($i18n.t(`Model {{modelName}} already exists.`, { modelName: liteLLMModelName }));
}
liteLLMModelName = '';
liteLLMModel = '';
liteLLMAPIBase = '';
liteLLMAPIKey = '';
liteLLMRPM = '';
liteLLMMaxTokens = '';
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
models.set(await getModels());
};
const deleteLiteLLMModelHandler = async () => {
const res = await deleteLiteLLMModel(localStorage.token, deleteLiteLLMModelName).catch(
(error) => {
toast.error(error);
return null;
}
);
if (res) {
if (res.message) {
toast.success(res.message);
}
}
deleteLiteLLMModelName = '';
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
models.set(await getModels());
};
onMount(async () => {
await Promise.all([
(async () => {
OLLAMA_URLS = await getOllamaUrls(localStorage.token).catch((error) => {
toast.error(error);
return [];
...
...
@@ -501,9 +436,11 @@
if (OLLAMA_URLS.length > 0) {
selectedOllamaUrlIdx = 0;
}
})(),
(async () => {
ollamaVersion = await getOllamaVersion(localStorage.token).catch((error) => false);
liteLLMModelInfo = await getLiteLLMModelInfo(localStorage.token);
})()
]);
});
</script>
...
...
@@ -587,24 +524,28 @@
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
>
<style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
</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
/>
<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
>
/>
</svg
>
</div>
{:else}
<svg
...
...
@@ -833,24 +774,28 @@
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
><style>
>
<style>
.spinner_ajPY {
transform-origin: center;
animation: spinner_AtaB 0.75s infinite linear;
}
@keyframes spinner_AtaB {
100% {
transform: rotate(360deg);
}
}
</style><path
</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
/>
<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
>
/>
</svg
>
</div>
{:else}
<svg
...
...
@@ -929,203 +874,8 @@
{/if}
</div>
</div>
<hr class=" dark:border-gray-700 my-2" />
{/if}
<div class=" space-y-3">
<div class="mt-2 space-y-3 pr-1.5">
<div>
<div class="mb-2">
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">{$i18n.t('Manage LiteLLM Models')}</div>
<button
class=" text-xs font-medium text-gray-500"
type="button"
on:click={() => {
showLiteLLM = !showLiteLLM;
}}>{showLiteLLM ? $i18n.t('Hide') : $i18n.t('Show')}</button
>
</div>
</div>
{#if showLiteLLM}
<div>
<div class="flex justify-between items-center text-xs">
<div class=" text-sm font-medium">{$i18n.t('Add a model')}</div>
<button
class=" text-xs font-medium text-gray-500"
type="button"
on:click={() => {
showLiteLLMParams = !showLiteLLMParams;
}}
>{showLiteLLMParams
? $i18n.t('Hide Additional Params')
: $i18n.t('Show Additional Params')}</button
>
</div>
</div>
<div class="my-2 space-y-2">
<div class="flex w-full mb-1.5">
<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={$i18n.t('Enter LiteLLM Model (litellm_params.model)')}
bind:value={liteLLMModel}
autocomplete="off"
/>
</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={() => {
addLiteLLMModelHandler();
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M8.75 3.75a.75.75 0 0 0-1.5 0v3.5h-3.5a.75.75 0 0 0 0 1.5h3.5v3.5a.75.75 0 0 0 1.5 0v-3.5h3.5a.75.75 0 0 0 0-1.5h-3.5v-3.5Z"
/>
</svg>
</button>
</div>
{#if showLiteLLMParams}
<div>
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('Model Name')}</div>
<div class="flex w-full">
<div class="flex-1">
<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 Name (model_name)"
bind:value={liteLLMModelName}
autocomplete="off"
/>
</div>
</div>
</div>
<div>
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Base URL')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t(
'Enter LiteLLM API Base URL (litellm_params.api_base)'
)}
bind:value={liteLLMAPIBase}
autocomplete="off"
/>
</div>
</div>
</div>
<div>
<div class=" mb-1.5 text-sm font-medium">{$i18n.t('API Key')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter LiteLLM API Key (litellm_params.api_key)')}
bind:value={liteLLMAPIKey}
autocomplete="off"
/>
</div>
</div>
</div>
<div>
<div class="mb-1.5 text-sm font-medium">{$i18n.t('API RPM')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter LiteLLM API RPM (litellm_params.rpm)')}
bind:value={liteLLMRPM}
autocomplete="off"
/>
</div>
</div>
</div>
<div>
<div class="mb-1.5 text-sm font-medium">{$i18n.t('Max Tokens')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
placeholder={$i18n.t('Enter Max Tokens (litellm_params.max_tokens)')}
bind:value={liteLLMMaxTokens}
type="number"
min="1"
autocomplete="off"
/>
</div>
</div>
</div>
{/if}
</div>
<div class="mb-2 text-xs text-gray-400 dark:text-gray-500">
{$i18n.t('Not sure what to add?')}
<a
class=" text-gray-300 font-medium underline"
href="https://litellm.vercel.app/docs/proxy/configs#quick-start"
target="_blank"
>
{$i18n.t('Click here for help.')}
</a>
</div>
<div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('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={deleteLiteLLMModelName}
placeholder={$i18n.t('Select a model')}
>
{#if !deleteLiteLLMModelName}
<option value="" disabled selected>{$i18n.t('Select a model')}</option>
{/if}
{#each liteLLMModelInfo as model}
<option value={model.model_name} class="bg-gray-100 dark:bg-gray-700"
>{model.model_name}</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={() => {
deleteLiteLLMModelHandler();
}}
>
<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>
{:else}
<div>Ollama Not Detected</div>
{/if}
</div>
</div>
</div>
</div>
</div>
src/lib/components/chat/SettingsModal.svelte
View file @
b1265c9c
...
...
@@ -3,7 +3,7 @@
import { toast } from 'svelte-sonner';
import { models, settings, user } from '$lib/stores';
import { getModels as _getModels } from '$lib/
util
s';
import { getModels as _getModels } from '$lib/
api
s';
import Modal from '../common/Modal.svelte';
import Account from './Settings/Account.svelte';
...
...
src/lib/components/chat/ShareChatModal.svelte
View file @
b1265c9c
<script lang="ts">
import { getContext, onMount } from 'svelte';
import { models } from '$lib/stores';
import { toast } from 'svelte-sonner';
import { deleteSharedChatById, getChatById, shareChatById } from '$lib/apis/chats';
import { modelfiles } from '$lib/stores';
import { copyToClipboard } from '$lib/utils';
import Modal from '../common/Modal.svelte';
...
...
@@ -43,9 +43,7 @@
tab.postMessage(
JSON.stringify({
chat: _chat,
modelfiles: $modelfiles.filter((modelfile) =>
_chat.models.includes(modelfile.tagName)
)
models: $models.filter((m) => _chat.models.includes(m.id))
}),
'*'
);
...
...
src/lib/components/common/Checkbox.svelte
View file @
b1265c9c
...
...
@@ -29,6 +29,7 @@
dispatch('change', _state);
}
}}
type="button"
>
<div class="top-0 left-0 absolute w-full flex justify-center">
{#if _state === 'checked'}
...
...
src/lib/components/common/Tooltip.svelte
View file @
b1265c9c
...
...
@@ -5,6 +5,7 @@
export let placement = 'top';
export let content = `I'm a tooltip!`;
export let touch = true;
export let className = 'flex';
let tooltipElement;
let tooltipInstance;
...
...
@@ -29,6 +30,6 @@
});
</script>
<div bind:this={tooltipElement} aria-label={content} class=
"flex"
>
<div bind:this={tooltipElement} aria-label={content} class=
{className}
>
<slot />
</div>
src/lib/components/layout/Navbar.svelte
View file @
b1265c9c
...
...
@@ -6,7 +6,6 @@
WEBUI_NAME,
chatId,
mobile,
modelfiles,
settings,
showArchivedChats,
showSettings,
...
...
src/lib/components/workspace/Model
file
s.svelte
→
src/lib/components/workspace/Models.svelte
View file @
b1265c9c
...
...
@@ -5,67 +5,82 @@
import { onMount, getContext } from 'svelte';
import { WEBUI_NAME, modelfiles, settings, user } from '$lib/stores';
import { createModel, deleteModel } from '$lib/apis/ollama';
import {
createNewModelfile,
deleteModelfileByTagName,
getModelfiles
} from '$lib/apis/modelfiles';
import { WEBUI_NAME, modelfiles, models, settings, user } from '$lib/stores';
import { addNewModel, deleteModelById, getModelInfos } from '$lib/apis/models';
import { deleteModel } from '$lib/apis/ollama';
import { goto } from '$app/navigation';
import { getModels } from '$lib/apis';
const i18n = getContext('i18n');
let localModelfiles = [];
let importFiles;
let modelfilesImportInputElement: HTMLInputElement;
const deleteModelHandler = async (tagName) => {
let success = null;
success = await deleteModel(localStorage.token, tagName).catch((err) => {
toast.error(err);
let importFiles;
let modelsImportInputElement: HTMLInputElement;
const deleteModelHandler = async (model) => {
console.log(model.info);
if (!model?.info) {
toast.error(
$i18n.t('{{ owner }}: You cannot delete a base model', {
owner: model.owned_by.toUpperCase()
})
);
return null;
});
}
const res = await deleteModelById(localStorage.token, model.id);
if (
succes
s) {
toast.success($i18n.t(`Deleted {{
tagN
ame}}`, {
tagName
}));
if (
re
s) {
toast.success($i18n.t(`Deleted {{
n
ame}}`, {
name: model.id
}));
}
return success
;
await models.set(await getModels(localStorage.token))
;
};
const deleteModelfile = async (tagName) => {
await deleteModelHandler(tagName);
await deleteModelfileByTagName(localStorage.token, tagName);
await modelfiles.set(await getModelfiles(localStorage.token));
const cloneModelHandler = async (model) => {
if ((model?.info?.base_model_id ?? null) === null) {
toast.error($i18n.t('You cannot clone a base model'));
return;
} else {
sessionStorage.model = JSON.stringify({
...model,
id: `${model.id}-clone`,
name: `${model.name} (Clone)`
});
goto('/workspace/models/create');
}
};
const shareModel
fi
le = async (model
file
) => {
const shareModel
Hand
le
r
= async (model) => {
toast.success($i18n.t('Redirecting you to OpenWebUI Community'));
const url = 'https://openwebui.com';
const tab = await window.open(`${url}/model
file
s/create`, '_blank');
const tab = await window.open(`${url}/models/create`, '_blank');
window.addEventListener(
'message',
(event) => {
if (event.origin !== url) return;
if (event.data === 'loaded') {
tab.postMessage(JSON.stringify(model
file
), '*');
tab.postMessage(JSON.stringify(model), '*');
}
},
false
);
};
const
save
Model
file
s = async (model
file
s) => {
let blob = new Blob([JSON.stringify(model
file
s)], {
const
download
Models = async (models) => {
let blob = new Blob([JSON.stringify(models)], {
type: 'application/json'
});
saveAs(blob, `model
file
s-export-${Date.now()}.json`);
saveAs(blob, `models-export-${Date.now()}.json`);
};
onMount(() => {
// Legacy code to sync localModelfiles with models
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
if (localModelfiles) {
...
...
@@ -76,13 +91,13 @@
<svelte:head>
<title>
{$i18n.t('Model
file
s')} | {$WEBUI_NAME}
{$i18n.t('Models')} | {$WEBUI_NAME}
</title>
</svelte:head>
<div class=" text-lg font-semibold mb-3">{$i18n.t('Model
file
s')}</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/model
file
s/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="w-full h-10 flex justify-center rounded-full bg-transparent dark:bg-gray-700 border border-dashed border-gray-200"
...
...
@@ -98,26 +113,26 @@
</div>
<div class=" self-center">
<div class=" font-bold">{$i18n.t('Create a model
file
')}</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>
</a>
<hr class=" dark:border-gray-850" />
<div class=" my-2 mb-5">
{#each $model
file
s as model
file
}
{#each $models as model}
<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"
>
<a
class=" flex flex-1 space-x-4 cursor-pointer w-full"
href={`/?models=${encodeURIComponent(model
file.tagName
)}`}
href={`/?models=${encodeURIComponent(model
.id
)}`}
>
<div class=" self-center w-10">
<div class=" rounded-full bg-stone-700">
<img
src={modelfile
.
image
U
rl ?? '/
user
.png'}
src={model
?.info?.meta?.pro
file
_
image
_u
rl ?? '/
favicon
.png'}
alt="modelfile profile"
class=" rounded-full w-full h-auto object-cover"
/>
...
...
@@ -125,9 +140,9 @@
</div>
<div class=" flex-1 self-center">
<div class=" font-bold
capitalize">{modelfile.titl
e}</div>
<div class=" font-bold
line-clamp-1">{model.nam
e}</div>
<div class=" text-sm overflow-hidden text-ellipsis line-clamp-1">
{model
file.desc
}
{
!!
model
?.info?.meta?.description ? model?.info?.meta?.description : model.id
}
</div>
</div>
</a>
...
...
@@ -135,7 +150,7 @@
<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"
type="button"
href={`/workspace/model
file
s/edit?
tag
=${encodeURIComponent(model
file.tagName
)}`}
href={`/workspace/models/edit?
id
=${encodeURIComponent(model
.id
)}`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
...
...
@@ -157,9 +172,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"
type="button"
on:click={() => {
// console.log(modelfile);
sessionStorage.modelfile = JSON.stringify(modelfile);
goto('/workspace/modelfiles/create');
cloneModelHandler(model);
}}
>
<svg
...
...
@@ -182,7 +195,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"
type="button"
on:click={() => {
shareModel
fi
le(model
file
);
shareModel
Hand
le
r
(model);
}}
>
<svg
...
...
@@ -205,7 +218,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"
type="button"
on:click={() => {
deleteModel
fi
le(model
file.tagName
);
deleteModel
Hand
le
r
(model);
}}
>
<svg
...
...
@@ -231,8 +244,8 @@
<div class=" flex justify-end w-full mb-3">
<div class="flex space-x-1">
<input
id="model
file
s-import-input"
bind:this={model
file
sImportInputElement}
id="models-import-input"
bind:this={modelsImportInputElement}
bind:files={importFiles}
type="file"
accept=".json"
...
...
@@ -242,16 +255,18 @@
let reader = new FileReader();
reader.onload = async (event) => {
let savedModel
file
s = JSON.parse(event.target.result);
console.log(savedModel
file
s);
let savedModels = JSON.parse(event.target.result);
console.log(savedModels);
for (const modelfile of savedModelfiles) {
await createNewModelfile(localStorage.token, modelfile).catch((error) => {
for (const model of savedModels) {
if (model?.info ?? false) {
await addNewModel(localStorage.token, model.info).catch((error) => {
return null;
});
}
}
await model
file
s.set(await getModel
file
s(localStorage.token));
await models.set(await getModels(localStorage.token));
};
reader.readAsText(importFiles[0]);
...
...
@@ -261,10 +276,10 @@
<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"
on:click={() => {
model
file
sImportInputElement.click();
modelsImportInputElement.click();
}}
>
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Model
file
s')}</div>
<div class=" self-center mr-2 font-medium">{$i18n.t('Import Models')}</div>
<div class=" self-center">
<svg
...
...
@@ -285,10 +300,10 @@
<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"
on:click={async () => {
save
Model
file
s($model
file
s);
download
Models($models);
}}
>
<div class=" self-center mr-2 font-medium">{$i18n.t('Export Model
file
s')}</div>
<div class=" self-center mr-2 font-medium">{$i18n.t('Export Models')}</div>
<div class=" self-center">
<svg
...
...
@@ -314,47 +329,13 @@
</div>
<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 createNewModelfile(localStorage.token, modelfile).catch((error) => {
return null;
});
}
saveModelfiles(localModelfiles);
localStorage.removeItem('modelfiles');
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
await modelfiles.set(await getModelfiles(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
class="self-center w-fit text-sm p-1.5 border dark:border-gray-600 rounded-xl flex"
on:click={async () => {
save
Model
file
s(localModelfiles);
download
Models(localModelfiles);
localStorage.removeItem('modelfiles');
localModelfiles = JSON.parse(localStorage.getItem('modelfiles') ?? '[]');
await modelfiles.set(await getModelfiles(localStorage.token));
}}
>
<div class=" self-center">
...
...
@@ -402,7 +383,7 @@
</div>
<div class=" self-center">
<div class=" font-bold">{$i18n.t('Discover a model
file
')}</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>
</a>
...
...
src/lib/components/workspace/Playground.svelte
View file @
b1265c9c
...
...
@@ -321,12 +321,10 @@
<div class="max-w-full">
<Selector
placeholder={$i18n.t('Select a model')}
items={$models
.filter((model) => model.name !== 'hr')
.map((model) => ({
items={$models.map((model) => ({
value: model.id,
label: model.name,
info
: model
model
: model
}))}
bind:value={selectedModelId}
/>
...
...
Prev
1
2
3
4
5
Next
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