"vscode:/vscode.git/clone" did not exist on "6b1bc4ccaf9d9d82d668442be44fc50105bac072"
Commit 4ff17acc authored by Jun Siang Cheah's avatar Jun Siang Cheah
Browse files

Merge remote-tracking branch 'upstream/dev' into feat/oauth

parents f49d814d 9928114c
...@@ -5,21 +5,25 @@ ...@@ -5,21 +5,25 @@
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let admin = false;
export let params = { export let params = {
// Advanced // Advanced
seed: 0, seed: null,
stop: null, stop: null,
temperature: '', temperature: null,
frequency_penalty: '', frequency_penalty: null,
repeat_last_n: '', repeat_last_n: null,
mirostat: '', mirostat: null,
mirostat_eta: '', mirostat_eta: null,
mirostat_tau: '', mirostat_tau: null,
top_k: '', top_k: null,
top_p: '', top_p: null,
tfs_z: '', tfs_z: null,
num_ctx: '', num_ctx: null,
max_tokens: '', num_batch: null,
num_keep: null,
max_tokens: null,
use_mmap: null, use_mmap: null,
use_mlock: null, use_mlock: null,
num_thread: null, num_thread: null,
...@@ -112,10 +116,10 @@ ...@@ -112,10 +116,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.temperature = (params?.temperature ?? '') === '' ? 0.8 : ''; params.temperature = (params?.temperature ?? null) === null ? 0.8 : null;
}} }}
> >
{#if (params?.temperature ?? '') === ''} {#if (params?.temperature ?? null) === null}
<span class="ml-2 self-center"> {$i18n.t('Default')} </span> <span class="ml-2 self-center"> {$i18n.t('Default')} </span>
{:else} {:else}
<span class="ml-2 self-center"> {$i18n.t('Custom')} </span> <span class="ml-2 self-center"> {$i18n.t('Custom')} </span>
...@@ -123,7 +127,7 @@ ...@@ -123,7 +127,7 @@
</button> </button>
</div> </div>
{#if (params?.temperature ?? '') !== ''} {#if (params?.temperature ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -143,7 +147,7 @@ ...@@ -143,7 +147,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="1" max="1"
step="0.05" step="any"
/> />
</div> </div>
</div> </div>
...@@ -158,10 +162,10 @@ ...@@ -158,10 +162,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.mirostat = (params?.mirostat ?? '') === '' ? 0 : ''; params.mirostat = (params?.mirostat ?? null) === null ? 0 : null;
}} }}
> >
{#if (params?.mirostat ?? '') === ''} {#if (params?.mirostat ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -169,7 +173,7 @@ ...@@ -169,7 +173,7 @@
</button> </button>
</div> </div>
{#if (params?.mirostat ?? '') !== ''} {#if (params?.mirostat ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -204,10 +208,10 @@ ...@@ -204,10 +208,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.mirostat_eta = (params?.mirostat_eta ?? '') === '' ? 0.1 : ''; params.mirostat_eta = (params?.mirostat_eta ?? null) === null ? 0.1 : null;
}} }}
> >
{#if (params?.mirostat_eta ?? '') === ''} {#if (params?.mirostat_eta ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -215,7 +219,7 @@ ...@@ -215,7 +219,7 @@
</button> </button>
</div> </div>
{#if (params?.mirostat_eta ?? '') !== ''} {#if (params?.mirostat_eta ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -235,7 +239,7 @@ ...@@ -235,7 +239,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="1" max="1"
step="0.05" step="any"
/> />
</div> </div>
</div> </div>
...@@ -250,10 +254,10 @@ ...@@ -250,10 +254,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.mirostat_tau = (params?.mirostat_tau ?? '') === '' ? 5.0 : ''; params.mirostat_tau = (params?.mirostat_tau ?? null) === null ? 5.0 : null;
}} }}
> >
{#if (params?.mirostat_tau ?? '') === ''} {#if (params?.mirostat_tau ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -261,7 +265,7 @@ ...@@ -261,7 +265,7 @@
</button> </button>
</div> </div>
{#if (params?.mirostat_tau ?? '') !== ''} {#if (params?.mirostat_tau ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -281,7 +285,7 @@ ...@@ -281,7 +285,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="10" max="10"
step="0.5" step="any"
/> />
</div> </div>
</div> </div>
...@@ -296,10 +300,10 @@ ...@@ -296,10 +300,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.top_k = (params?.top_k ?? '') === '' ? 40 : ''; params.top_k = (params?.top_k ?? null) === null ? 40 : null;
}} }}
> >
{#if (params?.top_k ?? '') === ''} {#if (params?.top_k ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -307,7 +311,7 @@ ...@@ -307,7 +311,7 @@
</button> </button>
</div> </div>
{#if (params?.top_k ?? '') !== ''} {#if (params?.top_k ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -327,7 +331,7 @@ ...@@ -327,7 +331,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="100" max="100"
step="0.5" step="any"
/> />
</div> </div>
</div> </div>
...@@ -342,10 +346,10 @@ ...@@ -342,10 +346,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.top_p = (params?.top_p ?? '') === '' ? 0.9 : ''; params.top_p = (params?.top_p ?? null) === null ? 0.9 : null;
}} }}
> >
{#if (params?.top_p ?? '') === ''} {#if (params?.top_p ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -353,7 +357,7 @@ ...@@ -353,7 +357,7 @@
</button> </button>
</div> </div>
{#if (params?.top_p ?? '') !== ''} {#if (params?.top_p ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -373,7 +377,7 @@ ...@@ -373,7 +377,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="1" max="1"
step="0.05" step="any"
/> />
</div> </div>
</div> </div>
...@@ -388,10 +392,10 @@ ...@@ -388,10 +392,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.frequency_penalty = (params?.frequency_penalty ?? '') === '' ? 1.1 : ''; params.frequency_penalty = (params?.frequency_penalty ?? null) === null ? 1.1 : null;
}} }}
> >
{#if (params?.frequency_penalty ?? '') === ''} {#if (params?.frequency_penalty ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -399,7 +403,7 @@ ...@@ -399,7 +403,7 @@
</button> </button>
</div> </div>
{#if (params?.frequency_penalty ?? '') !== ''} {#if (params?.frequency_penalty ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -419,7 +423,7 @@ ...@@ -419,7 +423,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="2" max="2"
step="0.05" step="any"
/> />
</div> </div>
</div> </div>
...@@ -434,10 +438,10 @@ ...@@ -434,10 +438,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.repeat_last_n = (params?.repeat_last_n ?? '') === '' ? 64 : ''; params.repeat_last_n = (params?.repeat_last_n ?? null) === null ? 64 : null;
}} }}
> >
{#if (params?.repeat_last_n ?? '') === ''} {#if (params?.repeat_last_n ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -445,7 +449,7 @@ ...@@ -445,7 +449,7 @@
</button> </button>
</div> </div>
{#if (params?.repeat_last_n ?? '') !== ''} {#if (params?.repeat_last_n ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -480,10 +484,10 @@ ...@@ -480,10 +484,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.tfs_z = (params?.tfs_z ?? '') === '' ? 1 : ''; params.tfs_z = (params?.tfs_z ?? null) === null ? 1 : null;
}} }}
> >
{#if (params?.tfs_z ?? '') === ''} {#if (params?.tfs_z ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -491,7 +495,7 @@ ...@@ -491,7 +495,7 @@
</button> </button>
</div> </div>
{#if (params?.tfs_z ?? '') !== ''} {#if (params?.tfs_z ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -511,7 +515,7 @@ ...@@ -511,7 +515,7 @@
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="0" min="0"
max="2" max="2"
step="0.05" step="any"
/> />
</div> </div>
</div> </div>
...@@ -526,10 +530,10 @@ ...@@ -526,10 +530,10 @@
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.num_ctx = (params?.num_ctx ?? '') === '' ? 2048 : ''; params.num_ctx = (params?.num_ctx ?? null) === null ? 2048 : null;
}} }}
> >
{#if (params?.num_ctx ?? '') === ''} {#if (params?.num_ctx ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -537,7 +541,7 @@ ...@@ -537,7 +541,7 @@
</button> </button>
</div> </div>
{#if (params?.num_ctx ?? '') !== ''} {#if (params?.num_ctx ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
...@@ -556,7 +560,7 @@ ...@@ -556,7 +560,7 @@
type="number" type="number"
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="-1" min="-1"
step="10" step="1"
/> />
</div> </div>
</div> </div>
...@@ -565,16 +569,16 @@ ...@@ -565,16 +569,16 @@
<div class=" py-0.5 w-full justify-between"> <div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens (num_predict)')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Batch Size (num_batch)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.max_tokens = (params?.max_tokens ?? '') === '' ? 128 : ''; params.num_batch = (params?.num_batch ?? null) === null ? 512 : null;
}} }}
> >
{#if (params?.max_tokens ?? '') === ''} {#if (params?.num_batch ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -582,27 +586,26 @@ ...@@ -582,27 +586,26 @@
</button> </button>
</div> </div>
{#if (params?.max_tokens ?? '') !== ''} {#if (params?.num_batch ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
id="steps-range" id="steps-range"
type="range" type="range"
min="-2" min="256"
max="16000" max="8192"
step="1" step="256"
bind:value={params.max_tokens} bind:value={params.num_batch}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/> />
</div> </div>
<div class=""> <div class="">
<input <input
bind:value={params.max_tokens} bind:value={params.num_batch}
type="number" type="number"
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="-2" min="256"
max="16000" step="256"
step="1"
/> />
</div> </div>
</div> </div>
...@@ -611,56 +614,18 @@ ...@@ -611,56 +614,18 @@
<div class=" py-0.5 w-full justify-between"> <div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('use_mmap (Ollama)')}</div> <div class=" self-center text-xs font-medium">
{$i18n.t('Tokens To Keep On Context Refresh (num_keep)')}
<button </div>
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.use_mmap = (params?.use_mmap ?? null) === null ? true : null;
}}
>
{#if (params?.use_mmap ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{/if}
</button>
</div>
</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('use_mlock (Ollama)')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.use_mlock = (params?.use_mlock ?? null) === null ? true : null;
}}
>
{#if (params?.use_mlock ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{/if}
</button>
</div>
</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('num_thread (Ollama)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.num_thread = (params?.num_thread ?? null) === null ? 2 : null; params.num_keep = (params?.num_keep ?? null) === null ? 24 : null;
}} }}
> >
{#if (params?.num_thread ?? null) === null} {#if (params?.num_keep ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -668,26 +633,25 @@ ...@@ -668,26 +633,25 @@
</button> </button>
</div> </div>
{#if (params?.num_thread ?? null) !== null} {#if (params?.num_keep ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<input <input
id="steps-range" id="steps-range"
type="range" type="range"
min="1" min="-1"
max="256" max="10240000"
step="1" step="1"
bind:value={params.num_thread} bind:value={params.num_keep}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700" class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/> />
</div> </div>
<div class=""> <div class="">
<input <input
bind:value={params.num_thread} bind:value={params.num_keep}
type="number" type="number"
class=" bg-transparent text-center w-14" class=" bg-transparent text-center w-14"
min="1" min="-1"
max="256"
step="1" step="1"
/> />
</div> </div>
...@@ -697,16 +661,16 @@ ...@@ -697,16 +661,16 @@
<div class=" py-0.5 w-full justify-between"> <div class=" py-0.5 w-full justify-between">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Template')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Max Tokens (num_predict)')}</div>
<button <button
class="p-1 px-3 text-xs flex rounded transition" class="p-1 px-3 text-xs flex rounded transition"
type="button" type="button"
on:click={() => { on:click={() => {
params.template = (params?.template ?? null) === null ? '' : null; params.max_tokens = (params?.max_tokens ?? null) === null ? 128 : null;
}} }}
> >
{#if (params?.template ?? null) === null} {#if (params?.max_tokens ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span> <span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else} {:else}
<span class="ml-2 self-center">{$i18n.t('Custom')}</span> <span class="ml-2 self-center">{$i18n.t('Custom')}</span>
...@@ -714,17 +678,151 @@ ...@@ -714,17 +678,151 @@
</button> </button>
</div> </div>
{#if (params?.template ?? null) !== null} {#if (params?.max_tokens ?? null) !== null}
<div class="flex mt-0.5 space-x-2"> <div class="flex mt-0.5 space-x-2">
<div class=" flex-1"> <div class=" flex-1">
<textarea <input
class="px-3 py-1.5 text-sm w-full bg-transparent border dark:border-gray-600 outline-none rounded-lg -mb-1" id="steps-range"
placeholder="Write your model template content here" type="range"
rows="4" min="-2"
bind:value={params.template} max="16000"
step="1"
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={params.max_tokens}
type="number"
class=" bg-transparent text-center w-14"
min="-2"
max="16000"
step="1"
/> />
</div> </div>
</div> </div>
{/if} {/if}
</div> </div>
{#if admin}
<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('use_mmap (Ollama)')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.use_mmap = (params?.use_mmap ?? null) === null ? true : null;
}}
>
{#if (params?.use_mmap ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{/if}
</button>
</div>
</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('use_mlock (Ollama)')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.use_mlock = (params?.use_mlock ?? null) === null ? true : null;
}}
>
{#if (params?.use_mlock ?? null) === null}
<span class="ml-2 self-center">{$i18n.t('Default')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{/if}
</button>
</div>
</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('num_thread (Ollama)')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
params.num_thread = (params?.num_thread ?? null) === null ? 2 : null;
}}
>
{#if (params?.num_thread ?? 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?.num_thread ?? null) !== null}
<div class="flex mt-0.5 space-x-2">
<div class=" flex-1">
<input
id="steps-range"
type="range"
min="1"
max="256"
step="1"
bind:value={params.num_thread}
class="w-full h-2 rounded-lg appearance-none cursor-pointer dark:bg-gray-700"
/>
</div>
<div class="">
<input
bind:value={params.num_thread}
type="number"
class=" bg-transparent text-center w-14"
min="1"
max="256"
step="1"
/>
</div>
</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> -->
{/if}
</div> </div>
<script lang="ts"> <script lang="ts">
import { getAudioConfig, updateAudioConfig } from '$lib/apis/audio'; import { user, settings, config } from '$lib/stores';
import { user, settings } from '$lib/stores';
import { createEventDispatcher, onMount, getContext } from 'svelte'; import { createEventDispatcher, onMount, getContext } from 'svelte';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
import Switch from '$lib/components/common/Switch.svelte'; import Switch from '$lib/components/common/Switch.svelte';
...@@ -11,26 +10,15 @@ ...@@ -11,26 +10,15 @@
export let saveSettings: Function; export let saveSettings: Function;
// Audio // Audio
let OpenAIUrl = '';
let OpenAIKey = '';
let OpenAISpeaker = '';
let STTEngines = ['', 'openai'];
let STTEngine = '';
let conversationMode = false; let conversationMode = false;
let speechAutoSend = false; let speechAutoSend = false;
let responseAutoPlayback = false; let responseAutoPlayback = false;
let nonLocalVoices = false; let nonLocalVoices = false;
let TTSEngines = ['', 'openai']; let STTEngine = '';
let TTSEngine = '';
let voices = []; let voices = [];
let speaker = ''; let voice = '';
let models = [];
let model = '';
const getOpenAIVoices = () => { const getOpenAIVoices = () => {
voices = [ voices = [
...@@ -43,10 +31,6 @@ ...@@ -43,10 +31,6 @@
]; ];
}; };
const getOpenAIVoicesModel = () => {
models = [{ name: 'tts-1' }, { name: 'tts-1-hd' }];
};
const getWebAPIVoices = () => { const getWebAPIVoices = () => {
const getVoicesLoop = setInterval(async () => { const getVoicesLoop = setInterval(async () => {
voices = await speechSynthesis.getVoices(); voices = await speechSynthesis.getVoices();
...@@ -58,21 +42,6 @@ ...@@ -58,21 +42,6 @@
}, 100); }, 100);
}; };
const toggleConversationMode = async () => {
conversationMode = !conversationMode;
if (conversationMode) {
responseAutoPlayback = true;
speechAutoSend = true;
}
saveSettings({
conversationMode: conversationMode,
responseAutoPlayback: responseAutoPlayback,
speechAutoSend: speechAutoSend
});
};
const toggleResponseAutoPlayback = async () => { const toggleResponseAutoPlayback = async () => {
responseAutoPlayback = !responseAutoPlayback; responseAutoPlayback = !responseAutoPlayback;
saveSettings({ responseAutoPlayback: responseAutoPlayback }); saveSettings({ responseAutoPlayback: responseAutoPlayback });
...@@ -83,76 +52,35 @@ ...@@ -83,76 +52,35 @@
saveSettings({ speechAutoSend: speechAutoSend }); saveSettings({ speechAutoSend: speechAutoSend });
}; };
const updateConfigHandler = async () => {
if (TTSEngine === 'openai') {
const res = await updateAudioConfig(localStorage.token, {
url: OpenAIUrl,
key: OpenAIKey,
model: model,
speaker: OpenAISpeaker
});
if (res) {
OpenAIUrl = res.OPENAI_API_BASE_URL;
OpenAIKey = res.OPENAI_API_KEY;
model = res.OPENAI_API_MODEL;
OpenAISpeaker = res.OPENAI_API_VOICE;
}
}
};
onMount(async () => { onMount(async () => {
conversationMode = $settings.conversationMode ?? false; conversationMode = $settings.conversationMode ?? false;
speechAutoSend = $settings.speechAutoSend ?? false; speechAutoSend = $settings.speechAutoSend ?? false;
responseAutoPlayback = $settings.responseAutoPlayback ?? false; responseAutoPlayback = $settings.responseAutoPlayback ?? false;
STTEngine = $settings?.audio?.STTEngine ?? ''; STTEngine = $settings?.audio?.stt?.engine ?? '';
TTSEngine = $settings?.audio?.TTSEngine ?? ''; voice = $settings?.audio?.tts?.voice ?? $config.audio.tts.voice ?? '';
nonLocalVoices = $settings.audio?.nonLocalVoices ?? false; nonLocalVoices = $settings.audio?.tts?.nonLocalVoices ?? false;
speaker = $settings?.audio?.speaker ?? '';
model = $settings?.audio?.model ?? '';
if (TTSEngine === 'openai') { if ($config.audio.tts.engine === 'openai') {
getOpenAIVoices(); getOpenAIVoices();
getOpenAIVoicesModel();
} else { } else {
getWebAPIVoices(); getWebAPIVoices();
} }
if ($user.role === 'admin') {
const res = await getAudioConfig(localStorage.token);
if (res) {
OpenAIUrl = res.OPENAI_API_BASE_URL;
OpenAIKey = res.OPENAI_API_KEY;
model = res.OPENAI_API_MODEL;
OpenAISpeaker = res.OPENAI_API_VOICE;
if (TTSEngine === 'openai') {
speaker = OpenAISpeaker;
}
}
}
}); });
</script> </script>
<form <form
class="flex flex-col h-full justify-between space-y-3 text-sm" class="flex flex-col h-full justify-between space-y-3 text-sm"
on:submit|preventDefault={async () => { on:submit|preventDefault={async () => {
if ($user.role === 'admin') {
await updateConfigHandler();
}
saveSettings({ saveSettings({
audio: { audio: {
STTEngine: STTEngine !== '' ? STTEngine : undefined, stt: {
TTSEngine: TTSEngine !== '' ? TTSEngine : undefined, engine: STTEngine !== '' ? STTEngine : undefined
speaker: },
(TTSEngine === 'openai' ? OpenAISpeaker : speaker) !== '' tts: {
? TTSEngine === 'openai' voice: voice !== '' ? voice : undefined,
? OpenAISpeaker nonLocalVoices: $config.audio.tts.engine === '' ? nonLocalVoices : undefined
: speaker }
: undefined,
model: model !== '' ? model : undefined,
nonLocalVoices: nonLocalVoices
} }
}); });
dispatch('save'); dispatch('save');
...@@ -162,53 +90,25 @@ ...@@ -162,53 +90,25 @@
<div> <div>
<div class=" mb-1 text-sm font-medium">{$i18n.t('STT Settings')}</div> <div class=" mb-1 text-sm font-medium">{$i18n.t('STT Settings')}</div>
<div class=" py-0.5 flex w-full justify-between"> {#if $config.audio.stt.engine !== 'web'}
<div class=" self-center text-xs font-medium">{$i18n.t('Speech-to-Text Engine')}</div> <div class=" py-0.5 flex w-full justify-between">
<div class="flex items-center relative"> <div class=" self-center text-xs font-medium">{$i18n.t('Speech-to-Text Engine')}</div>
<select <div class="flex items-center relative">
class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right" <select
bind:value={STTEngine} class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
placeholder="Select a mode" bind:value={STTEngine}
on:change={(e) => { placeholder="Select an engine"
if (e.target.value !== '') { >
navigator.mediaDevices.getUserMedia({ audio: true }).catch(function (err) { <option value="">{$i18n.t('Default')}</option>
toast.error( <option value="web">{$i18n.t('Web API')}</option>
$i18n.t(`Permission denied when accessing microphone: {{error}}`, { </select>
error: err </div>
})
);
STTEngine = '';
});
}
}}
>
<option value="">{$i18n.t('Default (Web API)')}</option>
<option value="whisper-local">{$i18n.t('Whisper (Local)')}</option>
</select>
</div> </div>
</div> {/if}
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Conversation Mode')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
toggleConversationMode();
}}
type="button"
>
{#if conversationMode === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if}
</button>
</div>
<div class=" py-0.5 flex w-full justify-between"> <div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium"> <div class=" self-center text-xs font-medium">
{$i18n.t('Auto-send input after 3 sec.')} {$i18n.t('Instant Auto-Send After Voice Transcription')}
</div> </div>
<button <button
...@@ -230,50 +130,6 @@ ...@@ -230,50 +130,6 @@
<div> <div>
<div class=" mb-1 text-sm font-medium">{$i18n.t('TTS Settings')}</div> <div class=" mb-1 text-sm font-medium">{$i18n.t('TTS Settings')}</div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Text-to-Speech Engine')}</div>
<div class="flex items-center relative">
<select
class=" dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
bind:value={TTSEngine}
placeholder="Select a mode"
on:change={(e) => {
if (e.target.value === 'openai') {
getOpenAIVoices();
OpenAISpeaker = 'alloy';
model = 'tts-1';
} else {
getWebAPIVoices();
speaker = '';
}
}}
>
<option value="">{$i18n.t('Default (Web API)')}</option>
<option value="openai">{$i18n.t('Open AI')}</option>
</select>
</div>
</div>
{#if $user.role === 'admin'}
{#if TTSEngine === 'openai'}
<div class="mt-1 flex gap-2 mb-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('API Base URL')}
bind:value={OpenAIUrl}
required
/>
<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('API Key')}
bind:value={OpenAIKey}
required
/>
</div>
{/if}
{/if}
<div class=" py-0.5 flex w-full justify-between"> <div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Auto-playback response')}</div> <div class=" self-center text-xs font-medium">{$i18n.t('Auto-playback response')}</div>
...@@ -293,23 +149,23 @@ ...@@ -293,23 +149,23 @@
</div> </div>
</div> </div>
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-850" />
{#if TTSEngine === ''} {#if $config.audio.tts.engine === ''}
<div> <div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
<div class="flex w-full"> <div class="flex w-full">
<div class="flex-1"> <div class="flex-1">
<select <select
class="w-full rounded-lg 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={speaker} bind:value={voice}
> >
<option value="" selected={speaker !== ''}>{$i18n.t('Default')}</option> <option value="" selected={voice !== ''}>{$i18n.t('Default')}</option>
{#each voices.filter((v) => nonLocalVoices || v.localService === true) as voice} {#each voices.filter((v) => nonLocalVoices || v.localService === true) as _voice}
<option <option
value={voice.name} value={_voice.name}
class="bg-gray-100 dark:bg-gray-700" class="bg-gray-100 dark:bg-gray-700"
selected={speaker === voice.name}>{voice.name}</option selected={voice === _voice.name}>{_voice.name}</option
> >
{/each} {/each}
</select> </select>
...@@ -325,7 +181,7 @@ ...@@ -325,7 +181,7 @@
</div> </div>
</div> </div>
</div> </div>
{:else if TTSEngine === 'openai'} {:else if $config.audio.tts.engine === 'openai'}
<div> <div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div> <div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Voice')}</div>
<div class="flex w-full"> <div class="flex w-full">
...@@ -333,7 +189,7 @@ ...@@ -333,7 +189,7 @@
<input <input
list="voice-list" list="voice-list"
class="w-full rounded-lg 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={OpenAISpeaker} bind:value={voice}
placeholder="Select a voice" placeholder="Select a voice"
/> />
...@@ -345,25 +201,6 @@ ...@@ -345,25 +201,6 @@
</div> </div>
</div> </div>
</div> </div>
<div>
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Set Model')}</div>
<div class="flex w-full">
<div class="flex-1">
<input
list="model-list"
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={model}
placeholder="Select a model"
/>
<datalist id="model-list">
{#each models as model}
<option value={model.name} />
{/each}
</datalist>
</div>
</div>
</div>
{/if} {/if}
</div> </div>
......
...@@ -161,7 +161,7 @@ ...@@ -161,7 +161,7 @@
</div> </div>
</div> </div>
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-850" />
<div class="flex flex-col"> <div class="flex flex-col">
<input <input
...@@ -218,7 +218,7 @@ ...@@ -218,7 +218,7 @@
</button> </button>
</div> </div>
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-850" />
<div class="flex flex-col"> <div class="flex flex-col">
{#if showArchiveConfirm} {#if showArchiveConfirm}
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import { getLanguages } from '$lib/i18n'; import { getLanguages } from '$lib/i18n';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
import { models, settings, theme } from '$lib/stores'; import { models, settings, theme, user } from '$lib/stores';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -43,19 +43,21 @@ ...@@ -43,19 +43,21 @@
let params = { let params = {
// Advanced // Advanced
seed: 0, seed: null,
temperature: '', temperature: null,
frequency_penalty: '', frequency_penalty: null,
repeat_last_n: '', repeat_last_n: null,
mirostat: '', mirostat: null,
mirostat_eta: '', mirostat_eta: null,
mirostat_tau: '', mirostat_tau: null,
top_k: '', top_k: null,
top_p: '', top_p: null,
stop: null, stop: null,
tfs_z: '', tfs_z: null,
num_ctx: '', num_ctx: null,
max_tokens: '' num_batch: null,
num_keep: null,
max_tokens: null
}; };
const toggleRequestFormat = async () => { const toggleRequestFormat = async () => {
...@@ -79,12 +81,6 @@ ...@@ -79,12 +81,6 @@
requestFormat = $settings.requestFormat ?? ''; requestFormat = $settings.requestFormat ?? '';
keepAlive = $settings.keepAlive ?? null; keepAlive = $settings.keepAlive ?? null;
params.seed = $settings.seed ?? 0;
params.temperature = $settings.temperature ?? '';
params.frequency_penalty = $settings.frequency_penalty ?? '';
params.top_k = $settings.top_k ?? '';
params.top_p = $settings.top_p ?? '';
params.num_ctx = $settings.num_ctx ?? '';
params = { ...params, ...$settings.params }; params = { ...params, ...$settings.params };
params.stop = $settings?.params?.stop ? ($settings?.params?.stop ?? []).join(',') : null; params.stop = $settings?.params?.stop ? ($settings?.params?.stop ?? []).join(',') : null;
}); });
...@@ -146,6 +142,7 @@ ...@@ -146,6 +142,7 @@
<option value="dark">🌑 {$i18n.t('Dark')}</option> <option value="dark">🌑 {$i18n.t('Dark')}</option>
<option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option> <option value="oled-dark">🌃 {$i18n.t('OLED Dark')}</option>
<option value="light">☀️ {$i18n.t('Light')}</option> <option value="light">☀️ {$i18n.t('Light')}</option>
<option value="her">🌷 Her</option>
<!-- <option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option> <!-- <option value="rose-pine dark">🪻 {$i18n.t('Rosé Pine')}</option>
<option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option> --> <option value="rose-pine-dawn light">🌷 {$i18n.t('Rosé Pine Dawn')}</option> -->
</select> </select>
...@@ -203,7 +200,7 @@ ...@@ -203,7 +200,7 @@
</div> </div>
</div> </div>
<hr class=" dark:border-gray-700 my-3" /> <hr class=" dark:border-gray-850 my-3" />
<div> <div>
<div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div> <div class=" my-2.5 text-sm font-medium">{$i18n.t('System Prompt')}</div>
...@@ -227,8 +224,8 @@ ...@@ -227,8 +224,8 @@
</div> </div>
{#if showAdvanced} {#if showAdvanced}
<AdvancedParams bind:params /> <AdvancedParams admin={$user?.role === 'admin'} bind:params />
<hr class=" dark:border-gray-700" /> <hr class=" dark:border-gray-850" />
<div class=" py-1 w-full justify-between"> <div class=" py-1 w-full justify-between">
<div class="flex w-full justify-between"> <div class="flex w-full justify-between">
...@@ -300,20 +297,25 @@ ...@@ -300,20 +297,25 @@
saveSettings({ saveSettings({
system: system !== '' ? system : undefined, system: system !== '' ? system : undefined,
params: { params: {
seed: (params.seed !== 0 ? params.seed : undefined) ?? undefined, seed: (params.seed !== null ? params.seed : undefined) ?? undefined,
stop: params.stop ? params.stop.split(',').filter((e) => e) : undefined, stop: params.stop ? params.stop.split(',').filter((e) => e) : undefined,
temperature: params.temperature !== '' ? params.temperature : undefined, temperature: params.temperature !== null ? params.temperature : undefined,
frequency_penalty: frequency_penalty:
params.frequency_penalty !== '' ? params.frequency_penalty : undefined, params.frequency_penalty !== null ? params.frequency_penalty : undefined,
repeat_last_n: params.repeat_last_n !== '' ? params.repeat_last_n : undefined, repeat_last_n: params.repeat_last_n !== null ? params.repeat_last_n : undefined,
mirostat: params.mirostat !== '' ? params.mirostat : undefined, mirostat: params.mirostat !== null ? params.mirostat : undefined,
mirostat_eta: params.mirostat_eta !== '' ? params.mirostat_eta : undefined, mirostat_eta: params.mirostat_eta !== null ? params.mirostat_eta : undefined,
mirostat_tau: params.mirostat_tau !== '' ? params.mirostat_tau : undefined, mirostat_tau: params.mirostat_tau !== null ? params.mirostat_tau : undefined,
top_k: params.top_k !== '' ? params.top_k : undefined, top_k: params.top_k !== null ? params.top_k : undefined,
top_p: params.top_p !== '' ? params.top_p : undefined, top_p: params.top_p !== null ? params.top_p : undefined,
tfs_z: params.tfs_z !== '' ? params.tfs_z : undefined, tfs_z: params.tfs_z !== null ? params.tfs_z : undefined,
num_ctx: params.num_ctx !== '' ? params.num_ctx : undefined, num_ctx: params.num_ctx !== null ? params.num_ctx : undefined,
max_tokens: params.max_tokens !== '' ? params.max_tokens : undefined num_batch: params.num_batch !== null ? params.num_batch : undefined,
num_keep: params.num_keep !== null ? params.num_keep : undefined,
max_tokens: params.max_tokens !== null ? params.max_tokens : undefined,
use_mmap: params.use_mmap !== null ? params.use_mmap : undefined,
use_mlock: params.use_mlock !== null ? params.use_mlock : undefined,
num_thread: params.num_thread !== null ? params.num_thread : undefined
}, },
keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined keepAlive: keepAlive ? (isNaN(keepAlive) ? keepAlive : parseInt(keepAlive)) : undefined
}); });
......
...@@ -14,19 +14,18 @@ ...@@ -14,19 +14,18 @@
// Addons // Addons
let titleAutoGenerate = true; let titleAutoGenerate = true;
let responseAutoCopy = false; let responseAutoCopy = false;
let titleAutoGenerateModel = '';
let titleAutoGenerateModelExternal = '';
let widescreenMode = false; let widescreenMode = false;
let titleGenerationPrompt = '';
let splitLargeChunks = false; let splitLargeChunks = false;
// Interface // Interface
let defaultModelId = ''; let defaultModelId = '';
let promptSuggestions = [];
let showUsername = false; let showUsername = false;
let chatBubble = true; let chatBubble = true;
let chatDirection: 'LTR' | 'RTL' = 'LTR'; let chatDirection: 'LTR' | 'RTL' = 'LTR';
let showEmojiInCall = false;
const toggleSplitLargeChunks = async () => { const toggleSplitLargeChunks = async () => {
splitLargeChunks = !splitLargeChunks; splitLargeChunks = !splitLargeChunks;
saveSettings({ splitLargeChunks: splitLargeChunks }); saveSettings({ splitLargeChunks: splitLargeChunks });
...@@ -47,6 +46,11 @@ ...@@ -47,6 +46,11 @@
saveSettings({ showUsername: showUsername }); saveSettings({ showUsername: showUsername });
}; };
const toggleEmojiInCall = async () => {
showEmojiInCall = !showEmojiInCall;
saveSettings({ showEmojiInCall: showEmojiInCall });
};
const toggleTitleAutoGenerate = async () => { const toggleTitleAutoGenerate = async () => {
titleAutoGenerate = !titleAutoGenerate; titleAutoGenerate = !titleAutoGenerate;
saveSettings({ saveSettings({
...@@ -85,36 +89,19 @@ ...@@ -85,36 +89,19 @@
}; };
const updateInterfaceHandler = async () => { const updateInterfaceHandler = async () => {
if ($user.role === 'admin') {
promptSuggestions = await setDefaultPromptSuggestions(localStorage.token, promptSuggestions);
await config.set(await getBackendConfig());
}
saveSettings({ saveSettings({
title: {
...$settings.title,
model: titleAutoGenerateModel !== '' ? titleAutoGenerateModel : undefined,
modelExternal:
titleAutoGenerateModelExternal !== '' ? titleAutoGenerateModelExternal : undefined,
prompt: titleGenerationPrompt ? titleGenerationPrompt : undefined
},
models: [defaultModelId] models: [defaultModelId]
}); });
}; };
onMount(async () => { onMount(async () => {
if ($user.role === 'admin') {
promptSuggestions = $config?.default_prompt_suggestions;
}
titleAutoGenerate = $settings?.title?.auto ?? true; titleAutoGenerate = $settings?.title?.auto ?? true;
titleAutoGenerateModel = $settings?.title?.model ?? '';
titleAutoGenerateModelExternal = $settings?.title?.modelExternal ?? '';
titleGenerationPrompt =
$settings?.title?.prompt ??
`Create a concise, 3-5 word phrase as a header for the following query, strictly adhering to the 3-5 word limit and avoiding the use of the word 'title': {{prompt}}`;
responseAutoCopy = $settings.responseAutoCopy ?? false; responseAutoCopy = $settings.responseAutoCopy ?? false;
showUsername = $settings.showUsername ?? false; showUsername = $settings.showUsername ?? false;
showEmojiInCall = $settings.showEmojiInCall ?? false;
chatBubble = $settings.chatBubble ?? true; chatBubble = $settings.chatBubble ?? true;
widescreenMode = $settings.widescreenMode ?? false; widescreenMode = $settings.widescreenMode ?? false;
splitLargeChunks = $settings.splitLargeChunks ?? false; splitLargeChunks = $settings.splitLargeChunks ?? false;
...@@ -217,6 +204,26 @@ ...@@ -217,6 +204,26 @@
</div> </div>
</div> </div>
<div>
<div class=" py-0.5 flex w-full justify-between">
<div class=" self-center text-xs font-medium">{$i18n.t('Display Emoji in Call')}</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
on:click={() => {
toggleEmojiInCall();
}}
type="button"
>
{#if showEmojiInCall === true}
<span class="ml-2 self-center">{$i18n.t('On')}</span>
{:else}
<span class="ml-2 self-center">{$i18n.t('Off')}</span>
{/if}
</button>
</div>
</div>
{#if !$settings.chatBubble} {#if !$settings.chatBubble}
<div> <div>
<div class=" py-0.5 flex w-full justify-between"> <div class=" py-0.5 flex w-full justify-between">
...@@ -304,162 +311,6 @@ ...@@ -304,162 +311,6 @@
</select> </select>
</div> </div>
</div> </div>
<hr class=" dark:border-gray-850" />
<div>
<div class=" mb-2.5 text-sm font-medium flex">
<div class=" mr-1">{$i18n.t('Set Task Model')}</div>
<Tooltip
content={$i18n.t(
'A task model is used when performing tasks such as generating titles for chats and web search queries'
)}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-5 h-5"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m11.25 11.25.041-.02a.75.75 0 0 1 1.063.852l-.708 2.836a.75.75 0 0 0 1.063.853l.041-.021M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9-3.75h.008v.008H12V8.25Z"
/>
</svg>
</Tooltip>
</div>
<div class="flex w-full gap-2 pr-2">
<div class="flex-1">
<div class=" text-xs mb-1">Local Models</div>
<select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={titleAutoGenerateModel}
placeholder={$i18n.t('Select a model')}
>
<option value="" selected>{$i18n.t('Current Model')}</option>
{#each $models.filter((m) => m.owned_by === 'ollama') as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
{model.name}
</option>
{/each}
</select>
</div>
<div class="flex-1">
<div class=" text-xs mb-1">External Models</div>
<select
class="w-full rounded-lg py-2 px-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none"
bind:value={titleAutoGenerateModelExternal}
placeholder={$i18n.t('Select a model')}
>
<option value="" selected>{$i18n.t('Current Model')}</option>
{#each $models as model}
<option value={model.id} class="bg-gray-100 dark:bg-gray-700">
{model.name}
</option>
{/each}
</select>
</div>
</div>
<div class="mt-3 mr-2">
<div class=" mb-2.5 text-sm font-medium">{$i18n.t('Title Generation Prompt')}</div>
<textarea
bind:value={titleGenerationPrompt}
class="w-full rounded-lg p-4 text-sm dark:text-gray-300 dark:bg-gray-850 outline-none resize-none"
rows="3"
/>
</div>
</div>
{#if $user.role === 'admin'}
<hr class=" dark:border-gray-700" />
<div class=" space-y-3 pr-1.5">
<div class="flex w-full justify-between mb-2">
<div class=" self-center text-sm font-semibold">
{$i18n.t('Default Prompt Suggestions')}
</div>
<button
class="p-1 px-3 text-xs flex rounded transition"
type="button"
on:click={() => {
if (promptSuggestions.length === 0 || promptSuggestions.at(-1).content !== '') {
promptSuggestions = [...promptSuggestions, { content: '', title: ['', ''] }];
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z"
/>
</svg>
</button>
</div>
<div class="flex flex-col space-y-1">
{#each promptSuggestions as prompt, promptIdx}
<div class=" flex border dark:border-gray-600 rounded-lg">
<div class="flex flex-col flex-1">
<div class="flex border-b dark:border-gray-600 w-full">
<input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-600"
placeholder={$i18n.t('Title (e.g. Tell me a fun fact)')}
bind:value={prompt.title[0]}
/>
<input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-600"
placeholder={$i18n.t('Subtitle (e.g. about the Roman Empire)')}
bind:value={prompt.title[1]}
/>
</div>
<input
class="px-3 py-1.5 text-xs w-full bg-transparent outline-none border-r dark:border-gray-600"
placeholder={$i18n.t('Prompt (e.g. Tell me a fun fact about the Roman Empire)')}
bind:value={prompt.content}
/>
</div>
<button
class="px-2"
type="button"
on:click={() => {
promptSuggestions.splice(promptIdx, 1);
promptSuggestions = promptSuggestions;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
{/each}
</div>
{#if promptSuggestions.length > 0}
<div class="text-xs text-left w-full mt-2">
{$i18n.t('Adjusting these settings will apply changes universally to all users.')}
</div>
{/if}
</div>
{/if}
</div> </div>
<div class="flex justify-end text-sm font-medium"> <div class="flex justify-end text-sm font-medium">
......
...@@ -541,7 +541,7 @@ ...@@ -541,7 +541,7 @@
]); ]);
} else { } else {
ollamaEnabled = false; ollamaEnabled = false;
toast.error('Ollama API is disabled'); toast.error($i18n.t('Ollama API is disabled'));
} }
}); });
</script> </script>
...@@ -1063,7 +1063,7 @@ ...@@ -1063,7 +1063,7 @@
</div> </div>
{/if} {/if}
{:else if ollamaEnabled === false} {:else if ollamaEnabled === false}
<div>Ollama API is disabled</div> <div>{$i18n.t('Ollama API is disabled')}</div>
{:else} {:else}
<div class="flex h-full justify-center"> <div class="flex h-full justify-center">
<div class="my-auto"> <div class="my-auto">
......
...@@ -35,7 +35,9 @@ ...@@ -35,7 +35,9 @@
<div> <div>
<div class="flex items-center justify-between mb-1"> <div class="flex items-center justify-between mb-1">
<Tooltip <Tooltip
content="This is an experimental feature, it may not function as expected and is subject to change at any time." content={$i18n.t(
'This is an experimental feature, it may not function as expected and is subject to change at any time.'
)}
> >
<div class="text-sm font-medium"> <div class="text-sm font-medium">
{$i18n.t('Memory')} {$i18n.t('Memory')}
...@@ -57,8 +59,9 @@ ...@@ -57,8 +59,9 @@
<div class="text-xs text-gray-600 dark:text-gray-400"> <div class="text-xs text-gray-600 dark:text-gray-400">
<div> <div>
You can personalize your interactions with LLMs by adding memories through the 'Manage' {$i18n.t(
button below, making them more helpful and tailored to you. "You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you."
)}
</div> </div>
<!-- <div class="mt-3"> <!-- <div class="mt-3">
...@@ -79,7 +82,7 @@ ...@@ -79,7 +82,7 @@
showManageModal = true; showManageModal = true;
}} }}
> >
Manage {$i18n.t('Manage')}
</button> </button>
</div> </div>
</div> </div>
......
...@@ -2,13 +2,12 @@ ...@@ -2,13 +2,12 @@
import { createEventDispatcher, getContext } from 'svelte'; import { createEventDispatcher, getContext } from 'svelte';
import Modal from '$lib/components/common/Modal.svelte'; import Modal from '$lib/components/common/Modal.svelte';
import { addNewMemory } from '$lib/apis/memories'; import { addNewMemory, updateMemoryById } from '$lib/apis/memories';
import { toast } from 'svelte-sonner'; import { toast } from 'svelte-sonner';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let show; export let show;
const i18n = getContext('i18n'); const i18n = getContext('i18n');
let loading = false; let loading = false;
...@@ -38,7 +37,9 @@ ...@@ -38,7 +37,9 @@
<Modal bind:show size="sm"> <Modal bind:show size="sm">
<div> <div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2"> <div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center">{$i18n.t('Add Memory')}</div> <div class=" text-lg font-medium self-center">
{$i18n.t('Add Memory')}
</div>
<button <button
class="self-center" class="self-center"
on:click={() => { on:click={() => {
...@@ -75,7 +76,7 @@ ...@@ -75,7 +76,7 @@
/> />
<div class="text-xs text-gray-500"> <div class="text-xs text-gray-500">
ⓘ Refer to yourself as "User" (e.g., "User is learning Spanish") {$i18n.t('Refer to yourself as "User" (e.g., "User is learning Spanish")')}
</div> </div>
</div> </div>
......
<script>
import { createEventDispatcher, getContext } from 'svelte';
import { toast } from 'svelte-sonner';
import { updateMemoryById } from '$lib/apis/memories';
import Modal from '$lib/components/common/Modal.svelte';
const dispatch = createEventDispatcher();
export let show;
export let memory = {};
const i18n = getContext('i18n');
let loading = false;
let content = '';
$: if (show) {
setContent();
}
const setContent = () => {
content = memory.content;
};
const submitHandler = async () => {
loading = true;
const res = await updateMemoryById(localStorage.token, memory.id, content).catch((error) => {
toast.error(error);
return null;
});
if (res) {
console.log(res);
toast.success('Memory updated successfully');
dispatch('save');
show = false;
}
loading = false;
};
</script>
<Modal bind:show size="sm">
<div>
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-2">
<div class=" text-lg font-medium self-center">
{$i18n.t('Edit Memory')}
</div>
<button
class="self-center"
on:click={() => {
show = false;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="w-5 h-5"
>
<path
d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z"
/>
</svg>
</button>
</div>
<div class="flex flex-col md:flex-row w-full px-5 pb-4 md:space-x-4 dark:text-gray-200">
<div class=" flex flex-col w-full sm:flex-row sm:justify-center sm:space-x-6">
<form
class="flex flex-col w-full"
on:submit|preventDefault={() => {
submitHandler();
}}
>
<div class="">
<textarea
bind:value={content}
class=" bg-transparent w-full text-sm resize-none rounded-xl p-3 outline outline-1 outline-gray-100 dark:outline-gray-800"
rows="3"
placeholder={$i18n.t('Enter a detail about yourself for your LLMs to recall')}
/>
<div class="text-xs text-gray-500">
ⓘ {$i18n.t('Refer to yourself as "User" (e.g., "User is learning Spanish")')}
</div>
</div>
<div class="flex justify-end pt-1 text-sm font-medium">
<button
class=" px-4 py-2 bg-emerald-700 hover:bg-emerald-800 text-gray-100 transition rounded-3xl flex flex-row space-x-1 items-center {loading
? ' cursor-not-allowed'
: ''}"
type="submit"
disabled={loading}
>
{$i18n.t('Update')}
{#if loading}
<div class="ml-2 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>
{/if}
</button>
</div>
</form>
</div>
</div>
</div>
</Modal>
...@@ -10,18 +10,24 @@ ...@@ -10,18 +10,24 @@
import { deleteMemoriesByUserId, deleteMemoryById, getMemories } from '$lib/apis/memories'; import { deleteMemoriesByUserId, deleteMemoryById, getMemories } from '$lib/apis/memories';
import Tooltip from '$lib/components/common/Tooltip.svelte'; import Tooltip from '$lib/components/common/Tooltip.svelte';
import { error } from '@sveltejs/kit'; import { error } from '@sveltejs/kit';
import EditMemoryModal from './EditMemoryModal.svelte';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
export let show = false; export let show = false;
let memories = []; let memories = [];
let loading = true;
let showAddMemoryModal = false; let showAddMemoryModal = false;
let showEditMemoryModal = false;
$: if (show) { let selectedMemory = null;
$: if (show && memories.length === 0 && loading) {
(async () => { (async () => {
memories = await getMemories(localStorage.token); memories = await getMemories(localStorage.token);
loading = false;
})(); })();
} }
</script> </script>
...@@ -62,7 +68,9 @@ ...@@ -62,7 +68,9 @@
> >
<tr> <tr>
<th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th> <th scope="col" class="px-3 py-2"> {$i18n.t('Name')} </th>
<th scope="col" class="px-3 py-2 hidden md:flex"> {$i18n.t('Created At')} </th> <th scope="col" class="px-3 py-2 hidden md:flex">
{$i18n.t('Last Modified')}
</th>
<th scope="col" class="px-3 py-2 text-right" /> <th scope="col" class="px-3 py-2 text-right" />
</tr> </tr>
</thead> </thead>
...@@ -76,11 +84,38 @@ ...@@ -76,11 +84,38 @@
</td> </td>
<td class=" px-3 py-1 hidden md:flex h-[2.5rem]"> <td class=" px-3 py-1 hidden md:flex h-[2.5rem]">
<div class="my-auto whitespace-nowrap"> <div class="my-auto whitespace-nowrap">
{dayjs(memory.created_at * 1000).format($i18n.t('MMMM DD, YYYY'))} {dayjs(memory.updated_at * 1000).format(
$i18n.t('MMMM DD, YYYY hh:mm:ss A')
)}
</div> </div>
</td> </td>
<td class="px-3 py-1"> <td class="px-3 py-1">
<div class="flex justify-end w-full"> <div class="flex justify-end w-full">
<Tooltip content="Edit">
<button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
on:click={() => {
selectedMemory = memory;
showEditMemoryModal = true;
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4 s-FoVA_WMOgxUD"
><path
stroke-linecap="round"
stroke-linejoin="round"
d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
class="s-FoVA_WMOgxUD"
/></svg
>
</button>
</Tooltip>
<Tooltip content="Delete"> <Tooltip content="Delete">
<button <button
class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl" class="self-center w-fit text-sm px-2 py-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
...@@ -136,7 +171,7 @@ ...@@ -136,7 +171,7 @@
class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl" class=" px-3.5 py-1.5 font-medium hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-gray-300 dark:outline-gray-800 rounded-3xl"
on:click={() => { on:click={() => {
showAddMemoryModal = true; showAddMemoryModal = true;
}}>Add memory</button }}>{$i18n.t('Add Memory')}</button
> >
<button <button
class=" px-3.5 py-1.5 font-medium text-red-500 hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-red-300 dark:outline-red-800 rounded-3xl" class=" px-3.5 py-1.5 font-medium text-red-500 hover:bg-black/5 dark:hover:bg-white/5 outline outline-1 outline-red-300 dark:outline-red-800 rounded-3xl"
...@@ -150,7 +185,7 @@ ...@@ -150,7 +185,7 @@
toast.success('Memory cleared successfully'); toast.success('Memory cleared successfully');
memories = []; memories = [];
} }
}}>Clear memory</button }}>{$i18n.t('Clear memory')}</button
> >
</div> </div>
</div> </div>
...@@ -163,3 +198,11 @@ ...@@ -163,3 +198,11 @@
memories = await getMemories(localStorage.token); memories = await getMemories(localStorage.token);
}} }}
/> />
<EditMemoryModal
bind:show={showEditMemoryModal}
memory={selectedMemory}
on:save={async () => {
memories = await getMemories(localStorage.token);
}}
/>
...@@ -8,16 +8,14 @@ ...@@ -8,16 +8,14 @@
import Modal from '../common/Modal.svelte'; import Modal from '../common/Modal.svelte';
import Account from './Settings/Account.svelte'; import Account from './Settings/Account.svelte';
import About from './Settings/About.svelte'; import About from './Settings/About.svelte';
import Models from './Settings/Models.svelte';
import General from './Settings/General.svelte'; import General from './Settings/General.svelte';
import Interface from './Settings/Interface.svelte'; import Interface from './Settings/Interface.svelte';
import Audio from './Settings/Audio.svelte'; import Audio from './Settings/Audio.svelte';
import Chats from './Settings/Chats.svelte'; import Chats from './Settings/Chats.svelte';
import Connections from './Settings/Connections.svelte';
import Images from './Settings/Images.svelte';
import User from '../icons/User.svelte'; import User from '../icons/User.svelte';
import Personalization from './Settings/Personalization.svelte'; import Personalization from './Settings/Personalization.svelte';
import { updateUserSettings } from '$lib/apis/users'; import { updateUserSettings } from '$lib/apis/users';
import { goto } from '$app/navigation';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
...@@ -90,55 +88,32 @@ ...@@ -90,55 +88,32 @@
<div class=" self-center">{$i18n.t('General')}</div> <div class=" self-center">{$i18n.t('General')}</div>
</button> </button>
{#if $user?.role === 'admin'} {#if $user.role === 'admin'}
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'connections'
? 'bg-gray-200 dark:bg-gray-700'
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
on:click={() => {
selectedTab = 'connections';
}}
>
<div class=" self-center mr-2">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
fill="currentColor"
class="w-4 h-4"
>
<path
d="M1 9.5A3.5 3.5 0 0 0 4.5 13H12a3 3 0 0 0 .917-5.857 2.503 2.503 0 0 0-3.198-3.019 3.5 3.5 0 0 0-6.628 2.171A3.5 3.5 0 0 0 1 9.5Z"
/>
</svg>
</div>
<div class=" self-center">{$i18n.t('Connections')}</div>
</button>
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'models' 'admin'
? 'bg-gray-200 dark:bg-gray-700' ? 'bg-gray-200 dark:bg-gray-700'
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}" : ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
on:click={() => { on:click={async () => {
selectedTab = 'models'; await goto('/admin/settings');
show = false;
}} }}
> >
<div class=" self-center mr-2"> <div class=" self-center mr-2">
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20" viewBox="0 0 24 24"
fill="currentColor" fill="currentColor"
class="w-4 h-4" class="size-4"
> >
<path <path
fill-rule="evenodd" fill-rule="evenodd"
d="M10 1c3.866 0 7 1.79 7 4s-3.134 4-7 4-7-1.79-7-4 3.134-4 7-4zm5.694 8.13c.464-.264.91-.583 1.306-.952V10c0 2.21-3.134 4-7 4s-7-1.79-7-4V8.178c.396.37.842.688 1.306.953C5.838 10.006 7.854 10.5 10 10.5s4.162-.494 5.694-1.37zM3 13.179V15c0 2.21 3.134 4 7 4s7-1.79 7-4v-1.822c-.396.37-.842.688-1.306.953-1.532.875-3.548 1.369-5.694 1.369s-4.162-.494-5.694-1.37A7.009 7.009 0 013 13.179z" d="M4.5 3.75a3 3 0 0 0-3 3v10.5a3 3 0 0 0 3 3h15a3 3 0 0 0 3-3V6.75a3 3 0 0 0-3-3h-15Zm4.125 3a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Zm-3.873 8.703a4.126 4.126 0 0 1 7.746 0 .75.75 0 0 1-.351.92 7.47 7.47 0 0 1-3.522.877 7.47 7.47 0 0 1-3.522-.877.75.75 0 0 1-.351-.92ZM15 8.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15ZM14.25 12a.75.75 0 0 1 .75-.75h3.75a.75.75 0 0 1 0 1.5H15a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 0-1.5H15Z"
clip-rule="evenodd" clip-rule="evenodd"
/> />
</svg> </svg>
</div> </div>
<div class=" self-center">{$i18n.t('Models')}</div> <div class=" self-center">{$i18n.t('Admin Settings')}</div>
</button> </button>
{/if} {/if}
...@@ -210,34 +185,6 @@ ...@@ -210,34 +185,6 @@
<div class=" self-center">{$i18n.t('Audio')}</div> <div class=" self-center">{$i18n.t('Audio')}</div>
</button> </button>
{#if $user.role === 'admin'}
<button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'images'
? 'bg-gray-200 dark:bg-gray-700'
: ' hover:bg-gray-300 dark:hover:bg-gray-800'}"
on:click={() => {
selectedTab = 'images';
}}
>
<div class=" self-center mr-2">
<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="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm10.5 5.707a.5.5 0 0 0-.146-.353l-1-1a.5.5 0 0 0-.708 0L9.354 9.646a.5.5 0 0 1-.708 0L6.354 7.354a.5.5 0 0 0-.708 0l-2 2a.5.5 0 0 0-.146.353V12a.5.5 0 0 0 .5.5h8a.5.5 0 0 0 .5-.5V9.707ZM12 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"
clip-rule="evenodd"
/>
</svg>
</div>
<div class=" self-center">{$i18n.t('Images')}</div>
</button>
{/if}
<button <button
class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab === class="px-2.5 py-2.5 min-w-fit rounded-lg flex-1 md:flex-none flex text-right transition {selectedTab ===
'chats' 'chats'
...@@ -325,15 +272,6 @@ ...@@ -325,15 +272,6 @@
toast.success($i18n.t('Settings saved successfully!')); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'models'}
<Models {getModels} />
{:else if selectedTab === 'connections'}
<Connections
{getModels}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'interface'} {:else if selectedTab === 'interface'}
<Interface <Interface
{saveSettings} {saveSettings}
...@@ -355,13 +293,6 @@ ...@@ -355,13 +293,6 @@
toast.success($i18n.t('Settings saved successfully!')); toast.success($i18n.t('Settings saved successfully!'));
}} }}
/> />
{:else if selectedTab === 'images'}
<Images
{saveSettings}
on:save={() => {
toast.success($i18n.t('Settings saved successfully!'));
}}
/>
{:else if selectedTab === 'chats'} {:else if selectedTab === 'chats'}
<Chats {saveSettings} /> <Chats {saveSettings} />
{:else if selectedTab === 'account'} {:else if selectedTab === 'account'}
......
<script lang="ts">
import { basicSetup, EditorView } from 'codemirror';
import { keymap, placeholder } from '@codemirror/view';
import { Compartment, EditorState } from '@codemirror/state';
import { acceptCompletion } from '@codemirror/autocomplete';
import { indentWithTab } from '@codemirror/commands';
import { indentUnit } from '@codemirror/language';
import { python } from '@codemirror/lang-python';
import { oneDark } from '@codemirror/theme-one-dark';
import { onMount, createEventDispatcher } from 'svelte';
import { formatPythonCode } from '$lib/apis/utils';
import { toast } from 'svelte-sonner';
const dispatch = createEventDispatcher();
export let boilerplate = '';
export let value = '';
let codeEditor;
let isDarkMode = false;
let editorTheme = new Compartment();
export const formatPythonCodeHandler = async () => {
if (codeEditor) {
const res = await formatPythonCode(value).catch((error) => {
toast.error(error);
return null;
});
if (res && res.code) {
const formattedCode = res.code;
codeEditor.dispatch({
changes: [{ from: 0, to: codeEditor.state.doc.length, insert: formattedCode }]
});
toast.success('Code formatted successfully');
return true;
}
return false;
}
return false;
};
let extensions = [
basicSetup,
keymap.of([{ key: 'Tab', run: acceptCompletion }, indentWithTab]),
python(),
indentUnit.of(' '),
placeholder('Enter your code here...'),
EditorView.updateListener.of((e) => {
if (e.docChanged) {
value = e.state.doc.toString();
}
}),
editorTheme.of([])
];
onMount(() => {
console.log(value);
if (value === '') {
value = boilerplate;
}
// Check if html class has dark mode
isDarkMode = document.documentElement.classList.contains('dark');
// python code editor, highlight python code
codeEditor = new EditorView({
state: EditorState.create({
doc: value,
extensions: extensions
}),
parent: document.getElementById('code-textarea')
});
if (isDarkMode) {
codeEditor.dispatch({
effects: editorTheme.reconfigure(oneDark)
});
}
// listen to html class changes this should fire only when dark mode is toggled
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const _isDarkMode = document.documentElement.classList.contains('dark');
if (_isDarkMode !== isDarkMode) {
isDarkMode = _isDarkMode;
if (_isDarkMode) {
codeEditor.dispatch({
effects: editorTheme.reconfigure(oneDark)
});
} else {
codeEditor.dispatch({
effects: editorTheme.reconfigure()
});
}
}
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
const keydownHandler = async (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
dispatch('save');
}
// Format code when Ctrl + Shift + F is pressed
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'f') {
e.preventDefault();
await formatPythonCodeHandler();
}
};
document.addEventListener('keydown', keydownHandler);
return () => {
observer.disconnect();
document.removeEventListener('keydown', keydownHandler);
};
});
</script>
<div id="code-textarea" class="h-full w-full" />
<script lang="ts">
import { onMount, createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import { flyAndScale } from '$lib/utils/transitions';
const dispatch = createEventDispatcher();
export let title = 'Confirm your action';
export let message = 'This action cannot be undone. Do you wish to continue?';
export let cancelLabel = 'Cancel';
export let confirmLabel = 'Confirm';
export let show = false;
let modalElement = null;
let mounted = false;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
console.log('Escape');
show = false;
}
};
onMount(() => {
mounted = true;
});
$: if (mounted) {
if (show) {
window.addEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'hidden';
} else {
window.removeEventListener('keydown', handleKeyDown);
document.body.style.overflow = 'unset';
}
}
</script>
{#if show}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div
bind:this={modalElement}
class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full min-h-screen h-screen flex justify-center z-[9999] overflow-hidden overscroll-contain"
in:fade={{ duration: 10 }}
on:mousedown={() => {
show = false;
}}
>
<div
class=" m-auto rounded-2xl max-w-full w-[32rem] mx-2 bg-gray-50 dark:bg-gray-950 shadow-3xl border border-gray-850"
in:flyAndScale
on:mousedown={(e) => {
e.stopPropagation();
}}
>
<div class="px-[1.75rem] py-6">
<div class=" text-lg font-semibold dark:text-gray-200 mb-2.5">{title}</div>
<slot>
<div class=" text-sm text-gray-500">
{message}
</div>
</slot>
<div class="mt-6 flex justify-between gap-1.5">
<button
class="bg-gray-100 hover:bg-gray-200 text-gray-800 dark:bg-gray-850 dark:hover:bg-gray-800 dark:text-white font-medium w-full py-2.5 rounded-lg transition"
on:click={() => {
show = false;
}}
type="button"
>
{cancelLabel}
</button>
<button
class="bg-gray-900 hover:bg-gray-850 text-gray-100 dark:bg-gray-100 dark:hover:bg-white dark:text-gray-800 font-medium w-full py-2.5 rounded-lg transition"
on:click={() => {
show = false;
dispatch('confirm');
}}
type="button"
>
{confirmLabel}
</button>
</div>
</div>
</div>
</div>
{/if}
<style>
.modal-content {
animation: scaleUp 0.1s ease-out forwards;
}
@keyframes scaleUp {
from {
transform: scale(0.985);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
</style>
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
<ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" /> <ChevronDown className="absolute end-2 top-1/2 -translate-y-[45%] size-3.5" strokeWidth="2.5" />
</Select.Trigger> </Select.Trigger>
<Select.Content <Select.Content
class="w-full rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-700/50 outline-none" class="w-full rounded-lg bg-white dark:bg-gray-900 dark:text-white shadow-lg border border-gray-300/30 dark:border-gray-850/50 outline-none"
transition={flyAndScale} transition={flyAndScale}
sideOffset={4} sideOffset={4}
> >
......
<script lang="ts"> <script lang="ts">
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { marked } from 'marked';
import tippy from 'tippy.js'; import tippy from 'tippy.js';
export let placement = 'top'; export let placement = 'top';
......
...@@ -95,7 +95,7 @@ ...@@ -95,7 +95,7 @@
)} )}
</div> </div>
<hr class=" dark:border-gray-700 my-3" /> <hr class=" dark:border-gray-850 my-3" />
{/if} {/if}
<div> <div>
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
export let saveHandler: Function; export let saveHandler: Function;
let webConfig = null; let webConfig = null;
let webSearchEngines = ['searxng', 'google_pse', 'brave', 'serpstack', 'serper']; let webSearchEngines = ['searxng', 'google_pse', 'brave', 'serpstack', 'serper', 'serply'];
let youtubeLanguage = 'en'; let youtubeLanguage = 'en';
let youtubeTranslation = null; let youtubeTranslation = null;
...@@ -68,10 +68,10 @@ ...@@ -68,10 +68,10 @@
<select <select
class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right" class="dark:bg-gray-900 w-fit pr-8 rounded px-2 p-1 text-xs bg-transparent outline-none text-right"
bind:value={webConfig.search.engine} bind:value={webConfig.search.engine}
placeholder="Select a engine" placeholder={$i18n.t('Select a engine')}
required required
> >
<option disabled selected value="">Select a engine</option> <option disabled selected value="">{$i18n.t('Select a engine')}</option>
{#each webSearchEngines as engine} {#each webSearchEngines as engine}
<option value={engine}>{engine}</option> <option value={engine}>{engine}</option>
{/each} {/each}
......
<script lang="ts">
export let className = 'w-4 h-4';
export let strokeWidth = '0';
</script>
<svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
fill-rule="evenodd"
d="M12 5a7 7 0 0 0-7 7v1.17c.313-.11.65-.17 1-.17h2a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H6a3 3 0 0 1-3-3v-6a9 9 0 0 1 18 0v6a3 3 0 0 1-3 3h-2a1 1 0 0 1-1-1v-6a1 1 0 0 1 1-1h2c.35 0 .687.06 1 .17V12a7 7 0 0 0-7-7Z"
clip-rule="evenodd"
/>
</svg>
<script lang="ts">
export let className = 'size-4';
export let strokeWidth = '2';
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width={strokeWidth}
stroke="currentColor"
class={className}
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"
/>
</svg>
<script lang="ts">
export let className = 'size-4';
</script>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class={className}>
<path
fill-rule="evenodd"
d="M12 6.75a5.25 5.25 0 0 1 6.775-5.025.75.75 0 0 1 .313 1.248l-3.32 3.319c.063.475.276.934.641 1.299.365.365.824.578 1.3.64l3.318-3.319a.75.75 0 0 1 1.248.313 5.25 5.25 0 0 1-5.472 6.756c-1.018-.086-1.87.1-2.309.634L7.344 21.3A3.298 3.298 0 1 1 2.7 16.657l8.684-7.151c.533-.44.72-1.291.634-2.309A5.342 5.342 0 0 1 12 6.75ZM4.117 19.125a.75.75 0 0 1 .75-.75h.008a.75.75 0 0 1 .75.75v.008a.75.75 0 0 1-.75.75h-.008a.75.75 0 0 1-.75-.75v-.008Z"
clip-rule="evenodd"
/>
</svg>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment