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
4ff17acc
"vscode:/vscode.git/clone" did not exist on "6b1bc4ccaf9d9d82d668442be44fc50105bac072"
Commit
4ff17acc
authored
Jun 16, 2024
by
Jun Siang Cheah
Browse files
Merge remote-tracking branch 'upstream/dev' into feat/oauth
parents
f49d814d
9928114c
Changes
168
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
836 additions
and
638 deletions
+836
-638
src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
...b/components/chat/Settings/Advanced/AdvancedParams.svelte
+219
-121
src/lib/components/chat/Settings/Audio.svelte
src/lib/components/chat/Settings/Audio.svelte
+38
-201
src/lib/components/chat/Settings/Chats.svelte
src/lib/components/chat/Settings/Chats.svelte
+2
-2
src/lib/components/chat/Settings/General.svelte
src/lib/components/chat/Settings/General.svelte
+36
-34
src/lib/components/chat/Settings/Interface.svelte
src/lib/components/chat/Settings/Interface.svelte
+32
-181
src/lib/components/chat/Settings/Models.svelte
src/lib/components/chat/Settings/Models.svelte
+2
-2
src/lib/components/chat/Settings/Personalization.svelte
src/lib/components/chat/Settings/Personalization.svelte
+7
-4
src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte
...nents/chat/Settings/Personalization/AddMemoryModal.svelte
+5
-4
src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte
...ents/chat/Settings/Personalization/EditMemoryModal.svelte
+136
-0
src/lib/components/chat/Settings/Personalization/ManageModal.svelte
...mponents/chat/Settings/Personalization/ManageModal.svelte
+48
-5
src/lib/components/chat/SettingsModal.svelte
src/lib/components/chat/SettingsModal.svelte
+10
-79
src/lib/components/common/CodeEditor.svelte
src/lib/components/common/CodeEditor.svelte
+135
-0
src/lib/components/common/ConfirmDialog.svelte
src/lib/components/common/ConfirmDialog.svelte
+109
-0
src/lib/components/common/Selector.svelte
src/lib/components/common/Selector.svelte
+1
-1
src/lib/components/common/Tooltip.svelte
src/lib/components/common/Tooltip.svelte
+2
-0
src/lib/components/documents/Settings/QueryParams.svelte
src/lib/components/documents/Settings/QueryParams.svelte
+1
-1
src/lib/components/documents/Settings/WebParams.svelte
src/lib/components/documents/Settings/WebParams.svelte
+3
-3
src/lib/components/icons/Headphone.svelte
src/lib/components/icons/Headphone.svelte
+20
-0
src/lib/components/icons/MagnifyingGlass.svelte
src/lib/components/icons/MagnifyingGlass.svelte
+19
-0
src/lib/components/icons/WrenchSolid.svelte
src/lib/components/icons/WrenchSolid.svelte
+11
-0
No files found.
src/lib/components/chat/Settings/Advanced/AdvancedParams.svelte
View file @
4ff17acc
...
@@ -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="1
0
"
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 ?? ''
) ===
''
? 12
8
:
''
;
params.
num_batch
= (params?.
num_batch ?? null
) ===
null
?
5
12 :
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="2
56
"
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 ? 2
4
: 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>
src/lib/components/chat/Settings/Audio.svelte
View file @
4ff17acc
<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
(
TTSE
ngine
===
'openai'
)
{
if (
$config.audio.tts.e
ngine === '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-
s
end
input after 3 sec.
'
)}
{$i18n.t('
Instant
Auto-
S
end
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-
70
0"
/>
<hr class=" dark:border-gray-
85
0" />
{#
if
TTSE
ngine
===
''
}
{#if
$config.audio.tts.e
ngine === ''}
<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
TTSE
ngine
===
'openai'
}
{:else if
$config.audio.tts.e
ngine === '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>
...
...
src/lib/components/chat/Settings/Chats.svelte
View file @
4ff17acc
...
@@ -161,7 +161,7 @@
...
@@ -161,7 +161,7 @@
</div>
</div>
</div>
</div>
<hr class=" dark:border-gray-
70
0" />
<hr class=" dark:border-gray-
85
0" />
<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-
70
0" />
<hr class=" dark:border-gray-
85
0" />
<div class="flex flex-col">
<div class="flex flex-col">
{#if showArchiveConfirm}
{#if showArchiveConfirm}
...
...
src/lib/components/chat/Settings/General.svelte
View file @
4ff17acc
...
@@ -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-
70
0 my-3" />
<hr class=" dark:border-gray-
85
0 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-
70
0" />
<hr class=" dark:border-gray-
85
0" />
<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
});
});
...
...
src/lib/components/chat/Settings/Interface.svelte
View file @
4ff17acc
...
@@ -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">
...
...
src/lib/components/chat/Settings/Models.svelte
View file @
4ff17acc
...
@@ -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">
...
...
src/lib/components/chat/Settings/Personalization.svelte
View file @
4ff17acc
...
@@ -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>
...
...
src/lib/components/chat/Settings/Personalization/AddMemoryModal.svelte
View file @
4ff17acc
...
@@ -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>
...
...
src/lib/components/chat/Settings/Personalization/EditMemoryModal.svelte
0 → 100644
View file @
4ff17acc
<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>
src/lib/components/chat/Settings/Personalization/ManageModal.svelte
View file @
4ff17acc
...
@@ -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
m
emory</button
}}>
{$i18n.t('
Add
M
emory
')}
</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);
}}
/>
src/lib/components/chat/SettingsModal.svelte
View file @
4ff17acc
...
@@ -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 2
0
2
0
"
viewBox="0 0 2
4
2
4
"
fill="currentColor"
fill="currentColor"
class="
w-4 h
-4"
class="
size
-4"
>
>
<path
<path
fill-rule="evenodd"
fill-rule="evenodd"
d="M
10 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="M
4.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.1
26
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('
Model
s')}</div>
<div class=" self-center">{$i18n.t('
Admin Setting
s')}</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'}
...
...
src/lib/components/common/CodeEditor.svelte
0 → 100644
View file @
4ff17acc
<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" />
src/lib/components/common/ConfirmDialog.svelte
0 → 100644
View file @
4ff17acc
<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>
src/lib/components/common/Selector.svelte
View file @
4ff17acc
...
@@ -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-
70
0/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-
85
0/50 outline-none"
transition={flyAndScale}
transition={flyAndScale}
sideOffset={4}
sideOffset={4}
>
>
...
...
src/lib/components/common/Tooltip.svelte
View file @
4ff17acc
<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';
...
...
src/lib/components/documents/Settings/QueryParams.svelte
View file @
4ff17acc
...
@@ -95,7 +95,7 @@
...
@@ -95,7 +95,7 @@
)}
)}
</div>
</div>
<hr class=" dark:border-gray-
70
0 my-3" />
<hr class=" dark:border-gray-
85
0 my-3" />
{/if}
{/if}
<div>
<div>
...
...
src/lib/components/documents/Settings/WebParams.svelte
View file @
4ff17acc
...
@@ -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}
...
...
src/lib/components/icons/Headphone.svelte
0 → 100644
View file @
4ff17acc
<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>
src/lib/components/icons/MagnifyingGlass.svelte
0 → 100644
View file @
4ff17acc
<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>
src/lib/components/icons/WrenchSolid.svelte
0 → 100644
View file @
4ff17acc
<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>
Prev
1
2
3
4
5
6
7
8
9
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