mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-02 02:12:25 +00:00
refac
This commit is contained in:
parent
ad9da98168
commit
7cbaabe02f
111 changed files with 70942 additions and 2334 deletions
|
|
@ -26,7 +26,7 @@ export default {
|
|||
namespaceSeparator: false,
|
||||
output: 'src/lib/i18n/locales/$LOCALE/$NAMESPACE.json',
|
||||
pluralSeparator: '_',
|
||||
input: 'src/**/*.{js,svelte}',
|
||||
input: 'src/**/*.{js,ts,svelte}',
|
||||
sort: true,
|
||||
verbose: true,
|
||||
failOnWarnings: false,
|
||||
|
|
|
|||
|
|
@ -244,7 +244,9 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Audio')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.audio.title')}
|
||||
</h2>
|
||||
{#if $config?.features?.slim === true}
|
||||
<p class="mb-4 text-xs text-gray-500">
|
||||
{$i18n.t(
|
||||
|
|
@ -254,10 +256,10 @@
|
|||
{/if}
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<AdminSettingSection title={$i18n.t('Speech-to-Text')} first>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.audio.sections.speechToText.title')} first>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Speech-to-Text Engine')}
|
||||
description={$i18n.t('Choose the transcription provider used for audio input.')}
|
||||
label={$i18n.t('settings.admin.audio.speechToTextEngine.label')}
|
||||
description={$i18n.t('settings.admin.audio.speechToTextEngine.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={STT_ENGINE} placeholder={$i18n.t('Select an engine')}>
|
||||
<option value="" disabled={$config?.features?.slim === true}
|
||||
|
|
@ -273,8 +275,8 @@
|
|||
|
||||
{#if STT_ENGINE !== 'web'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Supported MIME Types')}
|
||||
description={$i18n.t('Comma-separated audio or video MIME types accepted for upload.')}
|
||||
label={$i18n.t('settings.admin.audio.supportedMimeTypes.label')}
|
||||
description={$i18n.t('settings.admin.audio.supportedMimeTypes.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -286,26 +288,26 @@
|
|||
|
||||
{#if STT_ENGINE === 'openai'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('API Base URL')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttOpenaiApiBaseUrl.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.audio.sttOpenaiApiBaseUrl.label')}
|
||||
bind:value={STT_OPENAI_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttOpenaiApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.sttOpenaiApiKey.label')}
|
||||
bind:value={STT_OPENAI_API_KEY}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
</div>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Request Format')}
|
||||
description={$i18n.t('Select how audio is sent to the OpenAI-compatible endpoint.')}
|
||||
label={$i18n.t('settings.admin.audio.requestFormat.label')}
|
||||
description={$i18n.t('settings.admin.audio.requestFormat.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={STT_OPENAI_API_REQUEST_FORMAT}>
|
||||
<option value="multipart">{$i18n.t('Multipart Upload')}</option>
|
||||
|
|
@ -313,7 +315,7 @@
|
|||
</SettingsSelect>
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField label={$i18n.t('STT Model')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttModel.label')}>
|
||||
<input
|
||||
list="stt-openai-model-list"
|
||||
class={inputClass}
|
||||
|
|
@ -325,17 +327,17 @@
|
|||
</datalist>
|
||||
</AdminSettingField>
|
||||
{:else if STT_ENGINE === 'deepgram'}
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttDeepgramApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.sttDeepgramApiKey.label')}
|
||||
bind:value={STT_DEEPGRAM_API_KEY}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('STT Model')}
|
||||
description={$i18n.t('Leave model field empty to use the default model.')}
|
||||
label={$i18n.t('settings.admin.audio.sttModel.label')}
|
||||
description={$i18n.t('settings.admin.audio.sttModel.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -349,38 +351,38 @@
|
|||
</div>
|
||||
</AdminSettingField>
|
||||
{:else if STT_ENGINE === 'azure'}
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttAzureApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.sttAzureApiKey.label')}
|
||||
bind:value={STT_AZURE_API_KEY}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('Azure Region')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttAzureRegion.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={STT_AZURE_REGION}
|
||||
placeholder={$i18n.t('e.g., westus (leave blank for eastus)')}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('Language Locales')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.languageLocales.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={STT_AZURE_LOCALES}
|
||||
placeholder={$i18n.t('e.g., en-US,ja-JP (leave blank for auto-detect)')}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('Endpoint URL')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttAzureBaseUrl.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={STT_AZURE_BASE_URL}
|
||||
placeholder={$i18n.t('(leave blank for to use commercial endpoint)')}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('Max Speakers')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.maxSpeakers.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={STT_AZURE_MAX_SPEAKERS}
|
||||
|
|
@ -390,26 +392,26 @@
|
|||
</div>
|
||||
{:else if STT_ENGINE === 'mistral'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('API Base URL')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttMistralApiBaseUrl.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.audio.sttMistralApiBaseUrl.label')}
|
||||
bind:value={STT_MISTRAL_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.sttMistralApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.sttMistralApiKey.label')}
|
||||
bind:value={STT_MISTRAL_API_KEY}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
</div>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('STT Model')}
|
||||
description={$i18n.t('Leave empty to use the default model (voxtral-mini-latest).')}
|
||||
label={$i18n.t('settings.admin.audio.mistralSttModel.label')}
|
||||
description={$i18n.t('settings.admin.audio.mistralSttModel.description')}
|
||||
>
|
||||
<input class={inputClass} bind:value={STT_MODEL} placeholder="voxtral-mini-latest" />
|
||||
<div class="mt-1 {linkedHelpClass}">
|
||||
|
|
@ -420,10 +422,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Use Chat Completions API')}
|
||||
description={$i18n.t(
|
||||
'Use /v1/chat/completions endpoint instead of /v1/audio/transcriptions for potentially better accuracy.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.audio.useChatCompletionsApi.label')}
|
||||
description={$i18n.t('settings.admin.audio.useChatCompletionsApi.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={STT_MISTRAL_USE_CHAT_COMPLETIONS} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -433,8 +433,8 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingField
|
||||
label={$i18n.t('STT Model')}
|
||||
description={$i18n.t('Open WebUI uses faster-whisper internally.')}
|
||||
label={$i18n.t('settings.admin.audio.sttWhisperModel.label')}
|
||||
description={$i18n.t('settings.admin.audio.sttWhisperModel.description')}
|
||||
>
|
||||
<div class="flex w-full gap-2">
|
||||
<input
|
||||
|
|
@ -449,7 +449,7 @@
|
|||
sttModelUpdateHandler();
|
||||
}}
|
||||
disabled={STT_WHISPER_MODEL_LOADING}
|
||||
aria-label={$i18n.t('Update model')}
|
||||
aria-label={$i18n.t('settings.admin.audio.updateModel.label')}
|
||||
>
|
||||
{#if STT_WHISPER_MODEL_LOADING}
|
||||
<Spinner />
|
||||
|
|
@ -481,10 +481,10 @@
|
|||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Text-to-Speech')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.audio.sections.textToSpeech.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Text-to-Speech Engine')}
|
||||
description={$i18n.t('Choose the speech provider used for assistant audio output.')}
|
||||
label={$i18n.t('settings.admin.audio.textToSpeechEngine.label')}
|
||||
description={$i18n.t('settings.admin.audio.textToSpeechEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={TTS_ENGINE}
|
||||
|
|
@ -521,50 +521,50 @@
|
|||
|
||||
{#if TTS_ENGINE === 'openai'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('API Base URL')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsOpenaiApiBaseUrl.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.audio.ttsOpenaiApiBaseUrl.label')}
|
||||
bind:value={TTS_OPENAI_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsOpenaiApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.ttsOpenaiApiKey.label')}
|
||||
bind:value={TTS_OPENAI_API_KEY}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
</div>
|
||||
{:else if TTS_ENGINE === 'elevenlabs'}
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.ttsApiKey.label')}
|
||||
bind:value={TTS_API_KEY}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
{:else if TTS_ENGINE === 'azure'}
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.ttsApiKey.label')}
|
||||
bind:value={TTS_API_KEY}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('Azure Region')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsAzureSpeechRegion.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={TTS_AZURE_SPEECH_REGION}
|
||||
placeholder={$i18n.t('e.g., westus (leave blank for eastus)')}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('Endpoint URL')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsAzureSpeechBaseUrl.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={TTS_AZURE_SPEECH_BASE_URL}
|
||||
|
|
@ -574,18 +574,18 @@
|
|||
</div>
|
||||
{:else if TTS_ENGINE === 'mistral'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('API Base URL')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsMistralApiBaseUrl.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.audio.ttsMistralApiBaseUrl.label')}
|
||||
bind:value={TTS_MISTRAL_API_BASE_URL}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsMistralApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.audio.ttsMistralApiKey.label')}
|
||||
bind:value={TTS_MISTRAL_API_KEY}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
|
@ -593,7 +593,7 @@
|
|||
{/if}
|
||||
|
||||
{#if TTS_ENGINE === ''}
|
||||
<AdminSettingField label={$i18n.t('TTS Voice')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsVoice.label')}>
|
||||
<SettingsSelect bind:value={TTS_VOICE} className="w-full">
|
||||
<option value="" selected={TTS_VOICE !== ''}>{$i18n.t('Default')}</option>
|
||||
{#each voices as voice}
|
||||
|
|
@ -610,8 +610,8 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingField
|
||||
label={$i18n.t('TTS Model')}
|
||||
description={$i18n.t('Open WebUI uses SpeechT5 and CMU Arctic speaker embeddings.')}
|
||||
label={$i18n.t('settings.admin.audio.ttsModel.label')}
|
||||
description={$i18n.t('settings.admin.audio.ttsModel.description')}
|
||||
>
|
||||
<input
|
||||
list="tts-transformers-model-list"
|
||||
|
|
@ -635,7 +635,7 @@
|
|||
</AdminSettingField>
|
||||
{:else if TTS_ENGINE === 'openai'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('TTS Voice')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsVoice.label')}>
|
||||
<TTSVoiceInput
|
||||
bind:value={TTS_VOICE}
|
||||
voices={providerVoices}
|
||||
|
|
@ -643,7 +643,7 @@
|
|||
className={inputClass}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('TTS Model')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsModel.label')}>
|
||||
<input
|
||||
list="tts-model-list"
|
||||
class={inputClass}
|
||||
|
|
@ -653,10 +653,8 @@
|
|||
</AdminSettingField>
|
||||
</div>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Additional Parameters')}
|
||||
description={$i18n.t(
|
||||
'Enter additional OpenAI-compatible TTS request parameters as JSON.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.audio.additionalParameters.label')}
|
||||
description={$i18n.t('settings.admin.audio.additionalParameters.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -666,7 +664,7 @@
|
|||
</AdminSettingField>
|
||||
{:else if TTS_ENGINE === 'elevenlabs' || TTS_ENGINE === 'mistral'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('TTS Voice')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsVoice.label')}>
|
||||
<TTSVoiceInput
|
||||
bind:value={TTS_VOICE}
|
||||
voices={providerVoices}
|
||||
|
|
@ -674,7 +672,7 @@
|
|||
className={inputClass}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('TTS Model')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsModel.label')}>
|
||||
<input
|
||||
list="tts-model-list"
|
||||
class={inputClass}
|
||||
|
|
@ -685,7 +683,7 @@
|
|||
</div>
|
||||
{:else if TTS_ENGINE === 'azure'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('TTS Voice')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.ttsVoice.label')}>
|
||||
<TTSVoiceInput
|
||||
bind:value={TTS_VOICE}
|
||||
voices={providerVoices}
|
||||
|
|
@ -693,7 +691,7 @@
|
|||
className={inputClass}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField label={$i18n.t('Output format')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.audio.outputFormat.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
bind:value={TTS_AZURE_SPEECH_OUTPUT_FORMAT}
|
||||
|
|
@ -718,10 +716,8 @@
|
|||
</datalist>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Response Splitting')}
|
||||
description={$i18n.t(
|
||||
"Control how message text is split for TTS requests. 'Punctuation' splits into sentences, 'paragraphs' splits into paragraphs, and 'none' keeps the message as a single string."
|
||||
)}
|
||||
label={$i18n.t('settings.admin.audio.responseSplitting.label')}
|
||||
description={$i18n.t('settings.admin.audio.responseSplitting.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
aria-label={$i18n.t('Select how to split message text for TTS requests')}
|
||||
|
|
|
|||
|
|
@ -131,15 +131,18 @@
|
|||
|
||||
<form class="flex h-full flex-col justify-between text-sm" on:submit|preventDefault={submitHandler}>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('Authentication')}
|
||||
{$i18n.t('settings.admin.authentication.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if adminConfig !== null}
|
||||
<AdminSettingSection title={$i18n.t('User Access')} first>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.authentication.sections.userAccess.title')}
|
||||
first
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Default User Role')}
|
||||
description={$i18n.t('Role assigned to new users when they create an account.')}
|
||||
label={$i18n.t('settings.admin.authentication.defaultUserRole.label')}
|
||||
description={$i18n.t('settings.admin.authentication.defaultUserRole.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={adminConfig.DEFAULT_USER_ROLE}
|
||||
|
|
@ -152,8 +155,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Default Group')}
|
||||
description={$i18n.t('Group assigned to new users by default.')}
|
||||
label={$i18n.t('settings.admin.authentication.defaultGroup.label')}
|
||||
description={$i18n.t('settings.admin.authentication.defaultGroup.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={adminConfig.DEFAULT_GROUP_ID}
|
||||
|
|
@ -167,26 +170,24 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Login Form')}
|
||||
description={$i18n.t(
|
||||
'Show email and password sign-in on the login page. Set up SSO or LDAP before disabling. Disabling also blocks local sign-ups; password API access is unchanged.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.authentication.loginForm.label')}
|
||||
description={$i18n.t('settings.admin.authentication.loginForm.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_LOGIN_FORM} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('New Sign Ups')}
|
||||
description={$i18n.t('Allow new users to create accounts.')}
|
||||
label={$i18n.t('settings.admin.authentication.newSignUps.label')}
|
||||
description={$i18n.t('settings.admin.authentication.newSignUps.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_SIGNUP} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('API Keys')}
|
||||
description={$i18n.t('Allow users to create API keys for programmatic access.')}
|
||||
label={$i18n.t('settings.admin.authentication.apiKeys.label')}
|
||||
description={$i18n.t('settings.admin.authentication.apiKeys.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_API_KEYS} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -194,8 +195,10 @@
|
|||
|
||||
{#if adminConfig?.ENABLE_API_KEYS}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('API Key Endpoint Restrictions')}
|
||||
description={$i18n.t('Limit API keys to configured endpoints.')}
|
||||
label={$i18n.t('settings.admin.authentication.apiKeyEndpointRestrictions.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.authentication.apiKeyEndpointRestrictions.description'
|
||||
)}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -206,8 +209,8 @@
|
|||
|
||||
{#if adminConfig?.ENABLE_API_KEYS_ENDPOINT_RESTRICTIONS}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Allowed Endpoints')}
|
||||
description={$i18n.t('Comma-separated API paths that API keys can access.')}
|
||||
label={$i18n.t('settings.admin.authentication.allowedEndpoints.label')}
|
||||
description={$i18n.t('settings.admin.authentication.allowedEndpoints.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -227,10 +230,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('JWT Expiration')}
|
||||
description={$i18n.t(
|
||||
"Valid time units: 's', 'm', 'h', 'd', 'w' or '-1' for no expiration."
|
||||
)}
|
||||
label={$i18n.t('settings.admin.authentication.jwtExpiration.label')}
|
||||
description={$i18n.t('settings.admin.authentication.jwtExpiration.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -251,10 +252,12 @@
|
|||
</AdminSettingField>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Pending Accounts')}>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.authentication.sections.pendingAccounts.title')}
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Admin Details')}
|
||||
description={$i18n.t('Show admin contact details while an account waits for approval.')}
|
||||
label={$i18n.t('settings.admin.authentication.adminDetails.label')}
|
||||
description={$i18n.t('settings.admin.authentication.adminDetails.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.SHOW_ADMIN_DETAILS} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -262,8 +265,8 @@
|
|||
|
||||
{#if adminConfig.SHOW_ADMIN_DETAILS}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Admin Contact Email')}
|
||||
description={$i18n.t('Email shown in the pending account overlay.')}
|
||||
label={$i18n.t('settings.admin.authentication.adminContactEmail.label')}
|
||||
description={$i18n.t('settings.admin.authentication.adminContactEmail.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -275,8 +278,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Pending User Overlay Title')}
|
||||
description={$i18n.t('Custom title shown while an account waits for approval.')}
|
||||
label={$i18n.t('settings.admin.authentication.pendingUserOverlayTitle.label')}
|
||||
description={$i18n.t('settings.admin.authentication.pendingUserOverlayTitle.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -288,8 +291,10 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Pending User Overlay Content')}
|
||||
description={$i18n.t('Custom message shown while an account waits for approval.')}
|
||||
label={$i18n.t('settings.admin.authentication.pendingUserOverlayContent.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.authentication.pendingUserOverlayContent.description'
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -302,10 +307,10 @@
|
|||
</AdminSettingSection>
|
||||
{/if}
|
||||
|
||||
<AdminSettingSection title={$i18n.t('LDAP')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.authentication.sections.ldap.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('LDAP')}
|
||||
description={$i18n.t('Allow users to authenticate with an LDAP directory.')}
|
||||
label={$i18n.t('settings.admin.authentication.ldap.label')}
|
||||
description={$i18n.t('settings.admin.authentication.ldap.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={ENABLE_LDAP} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -314,8 +319,8 @@
|
|||
{#if ENABLE_LDAP}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Label')}
|
||||
description={$i18n.t('Display name for this LDAP connection.')}
|
||||
label={$i18n.t('settings.admin.authentication.label.label')}
|
||||
description={$i18n.t('settings.admin.authentication.label.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -328,8 +333,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Host')}
|
||||
description={$i18n.t('LDAP server hostname or IP address.')}
|
||||
label={$i18n.t('settings.admin.authentication.host.label')}
|
||||
description={$i18n.t('settings.admin.authentication.host.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -339,7 +344,10 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('Port')} description={$i18n.t('LDAP server port.')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.authentication.port.label')}
|
||||
description={$i18n.t('settings.admin.authentication.port.description')}
|
||||
>
|
||||
<Tooltip
|
||||
placement="top-start"
|
||||
content={$i18n.t('Default to 389 or 636 if TLS is enabled')}
|
||||
|
|
@ -357,8 +365,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Application DN')}
|
||||
description={$i18n.t('Bind DN used for directory search.')}
|
||||
label={$i18n.t('settings.admin.authentication.applicationDn.label')}
|
||||
description={$i18n.t('settings.admin.authentication.applicationDn.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t('The Application Account DN you bind with for search')}
|
||||
|
|
@ -373,8 +381,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Application DN Password')}
|
||||
description={$i18n.t('Password for the bind DN.')}
|
||||
label={$i18n.t('settings.admin.authentication.applicationDnPassword.label')}
|
||||
description={$i18n.t('settings.admin.authentication.applicationDnPassword.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -387,8 +395,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Attribute for Mail')}
|
||||
description={$i18n.t('LDAP attribute used as the user email address.')}
|
||||
label={$i18n.t('settings.admin.authentication.attributeForMail.label')}
|
||||
description={$i18n.t('settings.admin.authentication.attributeForMail.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
|
|
@ -406,8 +414,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Attribute for Username')}
|
||||
description={$i18n.t('LDAP attribute used as the username.')}
|
||||
label={$i18n.t('settings.admin.authentication.attributeForUsername.label')}
|
||||
description={$i18n.t('settings.admin.authentication.attributeForUsername.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
|
|
@ -426,8 +434,8 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Search Base')}
|
||||
description={$i18n.t('Base DN used when searching for users.')}
|
||||
label={$i18n.t('settings.admin.authentication.searchBase.label')}
|
||||
description={$i18n.t('settings.admin.authentication.searchBase.description')}
|
||||
>
|
||||
<Tooltip content={$i18n.t('The base to search for users')} placement="top-start">
|
||||
<input
|
||||
|
|
@ -440,8 +448,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Search Filters')}
|
||||
description={$i18n.t('LDAP filter used to match signing-in users.')}
|
||||
label={$i18n.t('settings.admin.authentication.searchFilters.label')}
|
||||
description={$i18n.t('settings.admin.authentication.searchFilters.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -458,8 +466,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('TLS')}
|
||||
description={$i18n.t('Use TLS when connecting to the LDAP server.')}
|
||||
label={$i18n.t('settings.admin.authentication.tls.label')}
|
||||
description={$i18n.t('settings.admin.authentication.tls.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={LDAP_SERVER.use_tls} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -467,8 +475,8 @@
|
|||
|
||||
{#if LDAP_SERVER.use_tls}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Certificate Path')}
|
||||
description={$i18n.t('Certificate file used for TLS verification.')}
|
||||
label={$i18n.t('settings.admin.authentication.certificatePath.label')}
|
||||
description={$i18n.t('settings.admin.authentication.certificatePath.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -478,16 +486,16 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Validate Certificate')}
|
||||
description={$i18n.t('Verify the LDAP server certificate when TLS is enabled.')}
|
||||
label={$i18n.t('settings.admin.authentication.validateCertificate.label')}
|
||||
description={$i18n.t('settings.admin.authentication.validateCertificate.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={LDAP_SERVER.validate_cert} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Ciphers')}
|
||||
description={$i18n.t('TLS cipher list for LDAP connections.')}
|
||||
label={$i18n.t('settings.admin.authentication.ciphers.label')}
|
||||
description={$i18n.t('settings.admin.authentication.ciphers.description')}
|
||||
>
|
||||
<Tooltip content={$i18n.t('Default to ALL')} placement="top-start">
|
||||
<input
|
||||
|
|
@ -503,8 +511,8 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Group Mapping')}
|
||||
description={$i18n.t('Map LDAP groups to Open WebUI groups.')}
|
||||
label={$i18n.t('settings.admin.authentication.enableGroupManagement.label')}
|
||||
description={$i18n.t('settings.admin.authentication.enableGroupManagement.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={LDAP_SERVER.enable_group_management} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -512,16 +520,16 @@
|
|||
|
||||
{#if LDAP_SERVER.enable_group_management}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Auto-Create Groups')}
|
||||
description={$i18n.t('Create missing groups from LDAP groups.')}
|
||||
label={$i18n.t('settings.admin.authentication.enableGroupCreation.label')}
|
||||
description={$i18n.t('settings.admin.authentication.enableGroupCreation.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={LDAP_SERVER.enable_group_creation} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Group Attribute')}
|
||||
description={$i18n.t('LDAP attribute containing the user group memberships.')}
|
||||
label={$i18n.t('settings.admin.authentication.groupAttribute.label')}
|
||||
description={$i18n.t('settings.admin.authentication.groupAttribute.description')}
|
||||
>
|
||||
<Tooltip content={$i18n.t('Default to memberOf')} placement="top-start">
|
||||
<input
|
||||
|
|
@ -536,7 +544,9 @@
|
|||
</AdminSettingSection>
|
||||
|
||||
{#if oauthConfig}
|
||||
<AdminSettingSection title={$i18n.t('OAuth / OIDC')}>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.authentication.sections.oauthOidc.title')}
|
||||
>
|
||||
{#if !oauthEditable}
|
||||
<div
|
||||
class="rounded-lg bg-yellow-500/10 px-2 py-1.5 text-[0.6875rem] text-yellow-700 dark:text-yellow-200"
|
||||
|
|
@ -553,8 +563,8 @@
|
|||
disabled={!oauthEditable}
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('OAuth / OIDC')}
|
||||
description={$i18n.t('Allow users to authenticate with an OAuth / OIDC provider.')}
|
||||
label={$i18n.t('settings.admin.authentication.oauthOidc.label')}
|
||||
description={$i18n.t('settings.admin.authentication.oauthOidc.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={oauthConfig.ENABLE_OAUTH} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -563,8 +573,8 @@
|
|||
{#if oauthConfig.ENABLE_OAUTH}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Provider Name')}
|
||||
description={$i18n.t('Display name shown for the OAuth provider.')}
|
||||
label={$i18n.t('settings.admin.authentication.providerName.label')}
|
||||
description={$i18n.t('settings.admin.authentication.providerName.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -574,8 +584,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Provider URL')}
|
||||
description={$i18n.t('OpenID discovery URL for this provider.')}
|
||||
label={$i18n.t('settings.admin.authentication.providerUrl.label')}
|
||||
description={$i18n.t('settings.admin.authentication.providerUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -587,8 +597,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Client ID')}
|
||||
description={$i18n.t('OAuth client identifier from the provider.')}
|
||||
label={$i18n.t('settings.admin.authentication.clientId.label')}
|
||||
description={$i18n.t('settings.admin.authentication.clientId.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -598,8 +608,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Client Secret')}
|
||||
description={$i18n.t('OAuth client secret from the provider.')}
|
||||
label={$i18n.t('settings.admin.authentication.clientSecret.label')}
|
||||
description={$i18n.t('settings.admin.authentication.clientSecret.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -612,8 +622,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Redirect URI')}
|
||||
description={$i18n.t('Callback URI registered with the provider.')}
|
||||
label={$i18n.t('settings.admin.authentication.redirectUri.label')}
|
||||
description={$i18n.t('settings.admin.authentication.redirectUri.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -623,8 +633,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Scopes')}
|
||||
description={$i18n.t('OAuth scopes requested during sign-in.')}
|
||||
label={$i18n.t('settings.admin.authentication.scopes.label')}
|
||||
description={$i18n.t('settings.admin.authentication.scopes.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -636,8 +646,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Email Claim')}
|
||||
description={$i18n.t('Claim used as the user email address.')}
|
||||
label={$i18n.t('settings.admin.authentication.emailClaim.label')}
|
||||
description={$i18n.t('settings.admin.authentication.emailClaim.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -647,8 +657,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Username Claim')}
|
||||
description={$i18n.t('Claim used as the display name.')}
|
||||
label={$i18n.t('settings.admin.authentication.usernameClaim.label')}
|
||||
description={$i18n.t('settings.admin.authentication.usernameClaim.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -660,8 +670,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Picture Claim')}
|
||||
description={$i18n.t('Claim used as the profile picture URL.')}
|
||||
label={$i18n.t('settings.admin.authentication.pictureClaim.label')}
|
||||
description={$i18n.t('settings.admin.authentication.pictureClaim.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -671,8 +681,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Sub Claim')}
|
||||
description={$i18n.t('Claim used as the stable user identifier.')}
|
||||
label={$i18n.t('settings.admin.authentication.subClaim.label')}
|
||||
description={$i18n.t('settings.admin.authentication.subClaim.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -683,16 +693,18 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('OAuth Signup')}
|
||||
description={$i18n.t('Allow users to create accounts through OAuth.')}
|
||||
label={$i18n.t('settings.admin.authentication.oauthSignup.label')}
|
||||
description={$i18n.t('settings.admin.authentication.oauthSignup.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={oauthConfig.ENABLE_OAUTH_SIGNUP} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Merge Accounts by Email')}
|
||||
description={$i18n.t('Link OAuth sign-ins to existing accounts with the same email.')}
|
||||
label={$i18n.t('settings.admin.authentication.mergeAccountsByEmail.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.authentication.mergeAccountsByEmail.description'
|
||||
)}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -702,18 +714,16 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Auto Redirect')}
|
||||
description={$i18n.t(
|
||||
'Send users directly to the OAuth provider from the sign-in page.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.authentication.autoRedirect.label')}
|
||||
description={$i18n.t('settings.admin.authentication.autoRedirect.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={oauthConfig.OAUTH_AUTO_REDIRECT} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Allowed Domains')}
|
||||
description={$i18n.t('Email domains allowed to sign in with OAuth.')}
|
||||
label={$i18n.t('settings.admin.authentication.allowedDomains.label')}
|
||||
description={$i18n.t('settings.admin.authentication.allowedDomains.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -726,8 +736,8 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Role Mapping')}
|
||||
description={$i18n.t('Map OAuth claims to Open WebUI roles.')}
|
||||
label={$i18n.t('settings.admin.authentication.roleMapping.label')}
|
||||
description={$i18n.t('settings.admin.authentication.roleMapping.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -739,8 +749,8 @@
|
|||
{#if oauthConfig.ENABLE_OAUTH_ROLE_MANAGEMENT}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Roles Claim')}
|
||||
description={$i18n.t('Claim containing provider roles.')}
|
||||
label={$i18n.t('settings.admin.authentication.rolesClaim.label')}
|
||||
description={$i18n.t('settings.admin.authentication.rolesClaim.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -750,8 +760,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Admin Roles')}
|
||||
description={$i18n.t('Provider roles that grant admin access.')}
|
||||
label={$i18n.t('settings.admin.authentication.adminRoles.label')}
|
||||
description={$i18n.t('settings.admin.authentication.adminRoles.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -762,8 +772,8 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Allowed Roles')}
|
||||
description={$i18n.t('Provider roles allowed to sign in.')}
|
||||
label={$i18n.t('settings.admin.authentication.allowedRoles.label')}
|
||||
description={$i18n.t('settings.admin.authentication.allowedRoles.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -777,8 +787,10 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Group Mapping')}
|
||||
description={$i18n.t('Map OAuth claims to Open WebUI groups.')}
|
||||
label={$i18n.t('settings.admin.authentication.enableOauthGroupManagement.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.authentication.enableOauthGroupManagement.description'
|
||||
)}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -789,8 +801,10 @@
|
|||
|
||||
{#if oauthConfig.ENABLE_OAUTH_GROUP_MANAGEMENT}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Auto-Create Groups')}
|
||||
description={$i18n.t('Create missing groups from OAuth claims.')}
|
||||
label={$i18n.t('settings.admin.authentication.enableOauthGroupCreation.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.authentication.enableOauthGroupCreation.description'
|
||||
)}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -801,8 +815,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Group Claim')}
|
||||
description={$i18n.t('Claim containing provider groups.')}
|
||||
label={$i18n.t('settings.admin.authentication.groupClaim.label')}
|
||||
description={$i18n.t('settings.admin.authentication.groupClaim.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -812,8 +826,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Blocked Groups')}
|
||||
description={$i18n.t('Provider groups blocked from signing in.')}
|
||||
label={$i18n.t('settings.admin.authentication.blockedGroups.label')}
|
||||
description={$i18n.t('settings.admin.authentication.blockedGroups.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -825,8 +839,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Update Email')}
|
||||
description={$i18n.t('Refresh the account email from OAuth on sign-in.')}
|
||||
label={$i18n.t('settings.admin.authentication.updateEmail.label')}
|
||||
description={$i18n.t('settings.admin.authentication.updateEmail.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -836,8 +850,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Update Name')}
|
||||
description={$i18n.t('Refresh the account name from OAuth on sign-in.')}
|
||||
label={$i18n.t('settings.admin.authentication.updateName.label')}
|
||||
description={$i18n.t('settings.admin.authentication.updateName.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -847,8 +861,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Update Picture')}
|
||||
description={$i18n.t('Refresh the profile picture from OAuth on sign-in.')}
|
||||
label={$i18n.t('settings.admin.authentication.updatePicture.label')}
|
||||
description={$i18n.t('settings.admin.authentication.updatePicture.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
|
|||
|
|
@ -44,15 +44,18 @@
|
|||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('Code Execution')}
|
||||
{$i18n.t('settings.admin.codeExecution.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if config}
|
||||
<AdminSettingSection title={$i18n.t('Code Execution')} first>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.codeExecution.sections.codeExecution.title')}
|
||||
first
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Enable Code Execution')}
|
||||
description={$i18n.t('Allow models to run generated code and return execution results.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.enableCodeExecution.label')}
|
||||
description={$i18n.t('settings.admin.codeExecution.enableCodeExecution.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={config.ENABLE_CODE_EXECUTION} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -60,12 +63,12 @@
|
|||
|
||||
{#if config.ENABLE_CODE_EXECUTION}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Code Execution Engine')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeExecutionEngine.label')}
|
||||
description={config.CODE_EXECUTION_ENGINE === 'jupyter'
|
||||
? $i18n.t(
|
||||
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.'
|
||||
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks\u2014proceed with extreme caution.'
|
||||
)
|
||||
: $i18n.t('Choose the runtime used for generated code blocks.')}
|
||||
: $i18n.t('settings.admin.codeExecution.codeExecutionEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.CODE_EXECUTION_ENGINE}
|
||||
|
|
@ -81,8 +84,10 @@
|
|||
|
||||
{#if config.CODE_EXECUTION_ENGINE === 'jupyter'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Jupyter URL')}
|
||||
description={$i18n.t('Connect code execution to a Jupyter server endpoint.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeExecutionJupyterUrl.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeExecutionJupyterUrl.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -97,8 +102,10 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Jupyter Auth')}
|
||||
description={$i18n.t('Select how Open WebUI authenticates with the Jupyter server.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeExecutionJupyterAuth.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeExecutionJupyterAuth.description'
|
||||
)}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.CODE_EXECUTION_JUPYTER_AUTH}
|
||||
|
|
@ -113,9 +120,11 @@
|
|||
{#if config.CODE_EXECUTION_JUPYTER_AUTH}
|
||||
<AdminSettingField
|
||||
label={config.CODE_EXECUTION_JUPYTER_AUTH === 'password'
|
||||
? $i18n.t('Jupyter Password')
|
||||
: $i18n.t('Jupyter Token')}
|
||||
description={$i18n.t('Credentials used to authenticate with the Jupyter server.')}
|
||||
? $i18n.t('settings.admin.codeExecution.codeExecutionJupyterAuthPassword.label')
|
||||
: $i18n.t('settings.admin.codeExecution.codeExecutionJupyterAuthToken.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeExecutionJupyterAuthPassword.description'
|
||||
)}
|
||||
>
|
||||
{#if config.CODE_EXECUTION_JUPYTER_AUTH === 'password'}
|
||||
<SensitiveInput
|
||||
|
|
@ -138,8 +147,10 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Code Execution Timeout')}
|
||||
description={$i18n.t('Maximum runtime in seconds before execution is stopped.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeExecutionJupyterTimeout.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeExecutionJupyterTimeout.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -153,10 +164,12 @@
|
|||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Code Interpreter')}>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.codeExecution.sections.codeInterpreter.title')}
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Enable Code Interpreter')}
|
||||
description={$i18n.t('Allow models to use the code interpreter tool during chats.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.enableCodeInterpreter.label')}
|
||||
description={$i18n.t('settings.admin.codeExecution.enableCodeInterpreter.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={config.ENABLE_CODE_INTERPRETER} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -164,12 +177,12 @@
|
|||
|
||||
{#if config.ENABLE_CODE_INTERPRETER}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Code Interpreter Engine')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeInterpreterEngine.label')}
|
||||
description={config.CODE_INTERPRETER_ENGINE === 'jupyter'
|
||||
? $i18n.t(
|
||||
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks—proceed with extreme caution.'
|
||||
'Warning: Jupyter execution enables arbitrary code execution, posing severe security risks\u2014proceed with extreme caution.'
|
||||
)
|
||||
: $i18n.t('Choose the runtime used by the code interpreter tool.')}
|
||||
: $i18n.t('settings.admin.codeExecution.codeInterpreterEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.CODE_INTERPRETER_ENGINE}
|
||||
|
|
@ -185,8 +198,10 @@
|
|||
|
||||
{#if config.CODE_INTERPRETER_ENGINE === 'jupyter'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Jupyter URL')}
|
||||
description={$i18n.t('Connect code interpreter to a Jupyter server endpoint.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeInterpreterJupyterUrl.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeInterpreterJupyterUrl.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -201,8 +216,10 @@
|
|||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Jupyter Auth')}
|
||||
description={$i18n.t('Select how Open WebUI authenticates with the Jupyter server.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeInterpreterJupyterAuth.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeInterpreterJupyterAuth.description'
|
||||
)}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.CODE_INTERPRETER_JUPYTER_AUTH}
|
||||
|
|
@ -217,9 +234,11 @@
|
|||
{#if config.CODE_INTERPRETER_JUPYTER_AUTH}
|
||||
<AdminSettingField
|
||||
label={config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'
|
||||
? $i18n.t('Jupyter Password')
|
||||
: $i18n.t('Jupyter Token')}
|
||||
description={$i18n.t('Credentials used to authenticate with the Jupyter server.')}
|
||||
? $i18n.t('settings.admin.codeExecution.codeInterpreterJupyterAuthPassword.label')
|
||||
: $i18n.t('settings.admin.codeExecution.codeInterpreterJupyterAuthToken.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeInterpreterJupyterAuthPassword.description'
|
||||
)}
|
||||
>
|
||||
{#if config.CODE_INTERPRETER_JUPYTER_AUTH === 'password'}
|
||||
<SensitiveInput
|
||||
|
|
@ -242,8 +261,10 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Code Execution Timeout')}
|
||||
description={$i18n.t('Maximum runtime in seconds before execution is stopped.')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeInterpreterJupyterTimeout.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.codeExecution.codeInterpreterJupyterTimeout.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -256,9 +277,9 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Code Interpreter Prompt Template')}
|
||||
label={$i18n.t('settings.admin.codeExecution.codeInterpreterPromptTemplate.label')}
|
||||
description={$i18n.t(
|
||||
'Leave empty to use the default prompt, or enter a custom prompt.'
|
||||
'settings.admin.codeExecution.codeInterpreterPromptTemplate.description'
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
|
|
|
|||
|
|
@ -233,12 +233,14 @@
|
|||
/>
|
||||
|
||||
<form class="flex h-full flex-col justify-between text-sm" on:submit|preventDefault={submitHandler}>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Connections')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.connections.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if ENABLE_OPENAI_API !== null && ENABLE_OLLAMA_API !== null && connectionsConfig !== null}
|
||||
<AdminSettingSection first>
|
||||
<AdminSettingRow label={$i18n.t('OpenAI API')} let:labelId>
|
||||
<AdminSettingRow label={$i18n.t('settings.admin.connections.openaiApi.label')} let:labelId>
|
||||
<Switch
|
||||
bind:state={ENABLE_OPENAI_API}
|
||||
on:change={async () => {
|
||||
|
|
@ -252,10 +254,10 @@
|
|||
<div>
|
||||
<div class="mb-2 flex items-center justify-between gap-4">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Manage OpenAI API Connections')}
|
||||
{$i18n.t('settings.admin.connections.manageOpenaiApiConnections.label')}
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t(`Add Connection`)}>
|
||||
<Tooltip content={$i18n.t('settings.admin.connections.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-700 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
||||
on:click={() => {
|
||||
|
|
@ -297,7 +299,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<AdminSettingRow label={$i18n.t('Ollama API')} let:labelId>
|
||||
<AdminSettingRow label={$i18n.t('settings.admin.connections.ollamaApi.label')} let:labelId>
|
||||
<Switch
|
||||
bind:state={ENABLE_OLLAMA_API}
|
||||
on:change={async () => {
|
||||
|
|
@ -311,10 +313,10 @@
|
|||
<div>
|
||||
<div class="mb-2 flex items-center justify-between gap-4">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Manage Ollama API Connections')}
|
||||
{$i18n.t('settings.admin.connections.manageOllamaApiConnections.label')}
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t(`Add Connection`)}>
|
||||
<Tooltip content={$i18n.t('settings.admin.connections.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-700 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
||||
on:click={() => {
|
||||
|
|
@ -351,7 +353,7 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-1 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Trouble accessing Ollama?')}
|
||||
{$i18n.t('settings.admin.connections.manageOllamaApiConnections.description')}
|
||||
<a
|
||||
class="font-normal underline hover:text-gray-700 dark:hover:text-gray-300"
|
||||
href="https://github.com/open-webui/open-webui#troubleshooting"
|
||||
|
|
@ -364,12 +366,12 @@
|
|||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('User Connections')}>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.connections.sections.userConnections.title')}
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Direct Connections')}
|
||||
description={$i18n.t(
|
||||
'Direct Connections allow users to connect to their own OpenAI compatible API endpoints.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.connections.directConnections.label')}
|
||||
description={$i18n.t('settings.admin.connections.directConnections.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -382,10 +384,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Direct Integrations')}
|
||||
description={$i18n.t(
|
||||
'Show the Integrations tab for users with permission to manage their own tool and terminal connections, including admins. Hiding it does not disable existing connections.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.connections.directIntegrations.label')}
|
||||
description={$i18n.t('settings.admin.connections.directIntegrations.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -396,21 +396,19 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Cache Base Model List')}
|
||||
description={$i18n.t(
|
||||
'Base Model List Cache speeds up access by fetching base models only at startup or on settings save—faster, but may not show recent base model changes.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.connections.cacheBaseModelList.label')}
|
||||
description={$i18n.t('settings.admin.connections.cacheBaseModelList.description')}
|
||||
let:labelId
|
||||
>
|
||||
<div class="flex items-center gap-1.5">
|
||||
{#if connectionsConfig.ENABLE_BASE_MODELS_CACHE}
|
||||
<Tooltip content={$i18n.t('Refresh')}>
|
||||
<Tooltip content={$i18n.t('settings.admin.connections.refreshModels.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-700 disabled:cursor-not-allowed disabled:opacity-50 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
||||
type="button"
|
||||
disabled={modelListRefreshing}
|
||||
on:click={refreshModelListHandler}
|
||||
aria-label={$i18n.t('Refresh')}
|
||||
aria-label={$i18n.t('settings.admin.connections.refreshModels.label')}
|
||||
>
|
||||
{#if modelListRefreshing}
|
||||
<Spinner className="size-3.5" />
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@
|
|||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Database')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.db.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<input
|
||||
|
|
@ -82,10 +84,10 @@
|
|||
}}
|
||||
/>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Config')} first>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.db.sections.config.title')} first>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Import Config')}
|
||||
description={$i18n.t('Import admin configuration from a JSON export file.')}
|
||||
label={$i18n.t('settings.admin.db.importConfig.label')}
|
||||
description={$i18n.t('settings.admin.db.importConfig.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -99,8 +101,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Export Config')}
|
||||
description={$i18n.t('Download the current admin configuration as JSON.')}
|
||||
label={$i18n.t('settings.admin.db.exportConfig.label')}
|
||||
description={$i18n.t('settings.admin.db.exportConfig.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -119,10 +121,10 @@
|
|||
</AdminSettingSection>
|
||||
|
||||
{#if $config?.features.enable_admin_export ?? true}
|
||||
<AdminSettingSection title={$i18n.t('Export')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.db.sections.export.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Database')}
|
||||
description={$i18n.t('Download the application database when supported.')}
|
||||
label={$i18n.t('settings.admin.db.database.label')}
|
||||
description={$i18n.t('settings.admin.db.database.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -133,13 +135,13 @@
|
|||
}}
|
||||
type="button"
|
||||
>
|
||||
{$i18n.t('Database')}
|
||||
{$i18n.t('settings.admin.db.database.label')}
|
||||
</button>
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('All Chats')}
|
||||
description={$i18n.t("Download every user's chat history as JSON.")}
|
||||
label={$i18n.t('settings.admin.db.allChats.label')}
|
||||
description={$i18n.t('settings.admin.db.allChats.description')}
|
||||
>
|
||||
<button class={actionButtonClass} on:click={exportAllUserChats} type="button">
|
||||
{$i18n.t('Export')}
|
||||
|
|
@ -147,8 +149,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Users')}
|
||||
description={$i18n.t('Download all users as CSV.')}
|
||||
label={$i18n.t('settings.admin.db.users.label')}
|
||||
description={$i18n.t('settings.admin.db.users.description')}
|
||||
>
|
||||
<button class={actionButtonClass} on:click={exportUsers} type="button">
|
||||
{$i18n.t('Export')}
|
||||
|
|
|
|||
|
|
@ -421,7 +421,9 @@
|
|||
submitHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Documents')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.documents.title')}
|
||||
</h2>
|
||||
{#if $config?.features?.slim === true}
|
||||
<p class="mb-4 text-xs text-gray-500">
|
||||
{$i18n.t(
|
||||
|
|
@ -432,10 +434,13 @@
|
|||
|
||||
{#if RAGConfig}
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<AdminSettingSection title={$i18n.t('Content Extraction')} first>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.documents.sections.contentExtraction.title')}
|
||||
first
|
||||
>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Content Extraction Engine')}
|
||||
description={$i18n.t('Choose how uploaded documents are parsed before indexing.')}
|
||||
label={$i18n.t('settings.admin.documents.contentExtractionEngine.label')}
|
||||
description={$i18n.t('settings.admin.documents.contentExtractionEngine.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={RAGConfig.CONTENT_EXTRACTION_ENGINE}>
|
||||
<option value=""
|
||||
|
|
@ -455,10 +460,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Supported Media MIME Types')}
|
||||
description={$i18n.t(
|
||||
'Media upload MIME types the content extraction engine may process.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.supportedMediaMimeTypes.label')}
|
||||
description={$i18n.t('settings.admin.documents.supportedMediaMimeTypes.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -469,18 +472,16 @@
|
|||
|
||||
{#if RAGConfig.CONTENT_EXTRACTION_ENGINE === '' && $config?.features?.slim !== true}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('PDF Extract Images (OCR)')}
|
||||
description={$i18n.t('Extract images from PDFs so OCR can process image-only pages.')}
|
||||
label={$i18n.t('settings.admin.documents.pdfExtractImagesOcr.label')}
|
||||
description={$i18n.t('settings.admin.documents.pdfExtractImagesOcr.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.PDF_EXTRACT_IMAGES} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('PDF Loader Mode')}
|
||||
description={$i18n.t(
|
||||
'Page mode creates one document per page. Single mode keeps pages together for chunking across boundaries.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.pdfLoaderMode.label')}
|
||||
description={$i18n.t('settings.admin.documents.pdfLoaderMode.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={RAGConfig.PDF_LOADER_MODE}>
|
||||
<option value="page">{$i18n.t('Page')}</option>
|
||||
|
|
@ -489,8 +490,8 @@
|
|||
</AdminSettingRow>
|
||||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'datalab_marker'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('Datalab Marker service endpoint used for document parsing.')}
|
||||
label={$i18n.t('settings.admin.documents.datalabMarkerApiBaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.datalabMarkerApiBaseUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -500,8 +501,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key used to authenticate with Datalab Marker.')}
|
||||
label={$i18n.t('settings.admin.documents.datalabMarkerApiKey.label')}
|
||||
description={$i18n.t('settings.admin.documents.datalabMarkerApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -512,8 +513,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Additional Config')}
|
||||
description={$i18n.t('JSON options passed to Marker for advanced parsing behavior.')}
|
||||
label={$i18n.t('settings.admin.documents.additionalConfig.label')}
|
||||
description={$i18n.t('settings.admin.documents.additionalConfig.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
|
|
@ -531,38 +532,36 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Use LLM')}
|
||||
description={$i18n.t(
|
||||
'Use an LLM to improve tables, forms, math, and layout detection.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.useLlm.label')}
|
||||
description={$i18n.t('settings.admin.documents.useLlm.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.DATALAB_MARKER_USE_LLM} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Skip Cache')}
|
||||
description={$i18n.t('Skip cached Marker results and rerun inference.')}
|
||||
label={$i18n.t('settings.admin.documents.skipCache.label')}
|
||||
description={$i18n.t('settings.admin.documents.skipCache.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.DATALAB_MARKER_SKIP_CACHE} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Force OCR')}
|
||||
description={$i18n.t('Run OCR on all PDF pages, even pages with embedded text.')}
|
||||
label={$i18n.t('settings.admin.documents.forceOcr.label')}
|
||||
description={$i18n.t('settings.admin.documents.forceOcr.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.DATALAB_MARKER_FORCE_OCR} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Paginate')}
|
||||
description={$i18n.t('Separate output by page with page markers.')}
|
||||
label={$i18n.t('settings.admin.documents.paginate.label')}
|
||||
description={$i18n.t('settings.admin.documents.paginate.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.DATALAB_MARKER_PAGINATE} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Strip Existing OCR')}
|
||||
description={$i18n.t('Remove existing OCR text and rerun OCR when Force OCR is off.')}
|
||||
label={$i18n.t('settings.admin.documents.stripExistingOcr.label')}
|
||||
description={$i18n.t('settings.admin.documents.stripExistingOcr.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -571,8 +570,8 @@
|
|||
/>
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Disable Image Extraction')}
|
||||
description={$i18n.t('Do not extract images from PDFs during Marker processing.')}
|
||||
label={$i18n.t('settings.admin.documents.disableImageExtraction.label')}
|
||||
description={$i18n.t('settings.admin.documents.disableImageExtraction.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -581,15 +580,15 @@
|
|||
/>
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Format Lines')}
|
||||
description={$i18n.t('Format lines to detect inline math and styles.')}
|
||||
label={$i18n.t('settings.admin.documents.formatLines.label')}
|
||||
description={$i18n.t('settings.admin.documents.formatLines.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.DATALAB_MARKER_FORMAT_LINES} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Output Format')}
|
||||
description={$i18n.t('Text output format returned by Marker.')}
|
||||
label={$i18n.t('settings.admin.documents.outputFormat.label')}
|
||||
description={$i18n.t('settings.admin.documents.outputFormat.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={RAGConfig.DATALAB_MARKER_OUTPUT_FORMAT}>
|
||||
<option value="markdown">{$i18n.t('Markdown')}</option>
|
||||
|
|
@ -600,8 +599,8 @@
|
|||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'external'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Document Loader URL')}
|
||||
description={$i18n.t('External service endpoint used to load document content.')}
|
||||
label={$i18n.t('settings.admin.documents.documentLoaderUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.documentLoaderUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -610,8 +609,10 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key sent to the external document loader.')}
|
||||
label={$i18n.t('settings.admin.documents.externalDocumentLoaderApiKey.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.documents.externalDocumentLoaderApiKey.description'
|
||||
)}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -623,8 +624,8 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Headers')}
|
||||
description={$i18n.t('Additional JSON headers sent to the external document loader.')}
|
||||
label={$i18n.t('settings.admin.documents.headers.label')}
|
||||
description={$i18n.t('settings.admin.documents.headers.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
|
|
@ -644,7 +645,7 @@
|
|||
on:click={() =>
|
||||
(showExternalDocumentLoaderHeadersHint = !showExternalDocumentLoaderHeadersHint)}
|
||||
>
|
||||
{$i18n.t('Header variables')}
|
||||
{$i18n.t('settings.admin.documents.headerVariables.label')}
|
||||
</button>
|
||||
{#if showExternalDocumentLoaderHeadersHint}
|
||||
<div class="mt-1 text-[0.6875rem] leading-5 text-gray-500 dark:text-gray-400">
|
||||
|
|
@ -667,8 +668,8 @@
|
|||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'tika'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Tika Server URL')}
|
||||
description={$i18n.t('Tika server endpoint used for content extraction.')}
|
||||
label={$i18n.t('settings.admin.documents.tikaServerUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.tikaServerUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -677,8 +678,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Tika Server Version')}
|
||||
description={$i18n.t('Select the Tika server API version.')}
|
||||
label={$i18n.t('settings.admin.documents.tikaServerVersion.label')}
|
||||
description={$i18n.t('settings.admin.documents.tikaServerVersion.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={RAGConfig.TIKA_SERVER_VERSION}>
|
||||
<option value="3">{$i18n.t('Tika 3.x')}</option>
|
||||
|
|
@ -689,8 +690,8 @@
|
|||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'docling'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Docling Server URL')}
|
||||
description={$i18n.t('Docling service endpoint used for parsing.')}
|
||||
label={$i18n.t('settings.admin.documents.doclingServerUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.doclingServerUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -699,8 +700,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key sent to Docling.')}
|
||||
label={$i18n.t('settings.admin.documents.doclingApiKey.label')}
|
||||
description={$i18n.t('settings.admin.documents.doclingApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -712,8 +713,8 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Parameters')}
|
||||
description={$i18n.t('Additional Docling parameters in JSON format.')}
|
||||
label={$i18n.t('settings.admin.documents.doclingParams.label')}
|
||||
description={$i18n.t('settings.admin.documents.doclingParams.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -725,8 +726,8 @@
|
|||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Endpoint')}
|
||||
description={$i18n.t('Document Intelligence endpoint used for parsing.')}
|
||||
label={$i18n.t('settings.admin.documents.endpoint.label')}
|
||||
description={$i18n.t('settings.admin.documents.endpoint.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -735,8 +736,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Key')}
|
||||
description={$i18n.t('Credential used for Document Intelligence.')}
|
||||
label={$i18n.t('settings.admin.documents.documentIntelligenceKey.label')}
|
||||
description={$i18n.t('settings.admin.documents.documentIntelligenceKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -748,8 +749,8 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Document Intelligence Model')}
|
||||
description={$i18n.t('Model name used by Document Intelligence.')}
|
||||
label={$i18n.t('settings.admin.documents.documentIntelligenceModel.label')}
|
||||
description={$i18n.t('settings.admin.documents.documentIntelligenceModel.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -760,8 +761,8 @@
|
|||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mistral_ocr'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('Mistral OCR service endpoint.')}
|
||||
label={$i18n.t('settings.admin.documents.mistralOcrApiBaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.mistralOcrApiBaseUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -770,8 +771,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key sent to Mistral OCR.')}
|
||||
label={$i18n.t('settings.admin.documents.mistralOcrApiKey.label')}
|
||||
description={$i18n.t('settings.admin.documents.mistralOcrApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -781,8 +782,8 @@
|
|||
</AdminSettingField>
|
||||
</div>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Use Base64')}
|
||||
description={$i18n.t('Send PDFs as base64 data URLs instead of uploading first.')}
|
||||
label={$i18n.t('settings.admin.documents.useBase64.label')}
|
||||
description={$i18n.t('settings.admin.documents.useBase64.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.MISTRAL_OCR_USE_BASE64} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -790,8 +791,8 @@
|
|||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'paddleocr_vl'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('PaddleOCR-vl service endpoint.')}
|
||||
label={$i18n.t('settings.admin.documents.paddleocrVlBaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.paddleocrVlBaseUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -800,8 +801,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Token')}
|
||||
description={$i18n.t('API token sent to PaddleOCR-vl.')}
|
||||
label={$i18n.t('settings.admin.documents.apiToken.label')}
|
||||
description={$i18n.t('settings.admin.documents.apiToken.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -813,8 +814,8 @@
|
|||
</div>
|
||||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'mineru'}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('API Mode')}
|
||||
description={$i18n.t('Choose the local or cloud MinerU API mode.')}
|
||||
label={$i18n.t('settings.admin.documents.apiMode.label')}
|
||||
description={$i18n.t('settings.admin.documents.apiMode.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={RAGConfig.MINERU_API_MODE}
|
||||
|
|
@ -839,8 +840,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API URL')}
|
||||
description={$i18n.t('MinerU API endpoint for the selected mode.')}
|
||||
label={$i18n.t('settings.admin.documents.apiUrl.label')}
|
||||
description={$i18n.t('settings.admin.documents.apiUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -852,8 +853,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key used for MinerU cloud mode.')}
|
||||
label={$i18n.t('settings.admin.documents.mineruApiKey.label')}
|
||||
description={$i18n.t('settings.admin.documents.mineruApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -863,8 +864,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('API Timeout')}
|
||||
description={$i18n.t('Maximum time in seconds to wait for MinerU API responses.')}
|
||||
label={$i18n.t('settings.admin.documents.apiTimeout.label')}
|
||||
description={$i18n.t('settings.admin.documents.apiTimeout.description')}
|
||||
>
|
||||
<input
|
||||
class="w-16 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-right text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
|
||||
|
|
@ -876,8 +877,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Parameters')}
|
||||
description={$i18n.t('Advanced MinerU parsing parameters in JSON format.')}
|
||||
label={$i18n.t('settings.admin.documents.mineruParams.label')}
|
||||
description={$i18n.t('settings.admin.documents.mineruParams.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -888,8 +889,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('File Extensions')}
|
||||
description={$i18n.t('Comma-separated extensions MinerU should handle.')}
|
||||
label={$i18n.t('settings.admin.documents.fileExtensions.label')}
|
||||
description={$i18n.t('settings.admin.documents.fileExtensions.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -900,9 +901,9 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Bypass Embedding and Retrieval')}
|
||||
label={$i18n.t('settings.admin.documents.bypassEmbeddingAndRetrieval.label')}
|
||||
description={RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL
|
||||
? $i18n.t('Inject the entire content as context for comprehensive processing.')
|
||||
? $i18n.t('settings.admin.documents.bypassEmbeddingAndRetrieval.description')
|
||||
: $i18n.t('Use segmented retrieval for focused and relevant context.')}
|
||||
let:labelId
|
||||
>
|
||||
|
|
@ -914,8 +915,8 @@
|
|||
|
||||
{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Text Splitter')}
|
||||
description={$i18n.t('Choose how extracted text is split before indexing.')}
|
||||
label={$i18n.t('settings.admin.documents.textSplitter.label')}
|
||||
description={$i18n.t('settings.admin.documents.textSplitter.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={RAGConfig.TEXT_SPLITTER}>
|
||||
<option value="">{$i18n.t('Default')} ({$i18n.t('Character')})</option>
|
||||
|
|
@ -928,8 +929,8 @@
|
|||
|
||||
{#if RAGConfig.TEXT_SPLITTER === 'token_transformers'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Tokenizer Model')}
|
||||
description={$i18n.t('Tokenizer model used for transformer token splitting.')}
|
||||
label={$i18n.t('settings.admin.documents.tokenizerModel.label')}
|
||||
description={$i18n.t('settings.admin.documents.tokenizerModel.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -942,10 +943,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Markdown Header Text Splitter')}
|
||||
description={$i18n.t(
|
||||
'Split documents by markdown headers before character or token splitting.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.markdownHeaderTextSplitter.label')}
|
||||
description={$i18n.t('settings.admin.documents.markdownHeaderTextSplitter.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -956,8 +955,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Chunk Size')}
|
||||
description={$i18n.t('Maximum size for each text chunk.')}
|
||||
label={$i18n.t('settings.admin.documents.chunkSize.label')}
|
||||
description={$i18n.t('settings.admin.documents.chunkSize.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -969,8 +968,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Chunk Overlap')}
|
||||
description={$i18n.t('Overlap preserved between neighboring chunks.')}
|
||||
label={$i18n.t('settings.admin.documents.chunkOverlap.label')}
|
||||
description={$i18n.t('settings.admin.documents.chunkOverlap.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -985,10 +984,8 @@
|
|||
|
||||
{#if RAGConfig.ENABLE_MARKDOWN_HEADER_TEXT_SPLITTER}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Chunk Min Size Target')}
|
||||
description={$i18n.t(
|
||||
'Merge chunks smaller than this threshold when possible. Set to 0 to disable merging.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.chunkMinSizeTarget.label')}
|
||||
description={$i18n.t('settings.admin.documents.chunkMinSizeTarget.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1004,10 +1001,10 @@
|
|||
</AdminSettingSection>
|
||||
|
||||
{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
|
||||
<AdminSettingSection title={$i18n.t('Embedding')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.documents.sections.embedding.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Embedding Model Engine')}
|
||||
description={$i18n.t('Provider used to generate document embeddings.')}
|
||||
label={$i18n.t('settings.admin.documents.embeddingModelEngine.label')}
|
||||
description={$i18n.t('settings.admin.documents.embeddingModelEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={RAG_EMBEDDING_ENGINE}
|
||||
|
|
@ -1036,23 +1033,23 @@
|
|||
{#if RAG_EMBEDDING_ENGINE === 'openai'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('OpenAI-compatible embeddings endpoint.')}
|
||||
label={$i18n.t('settings.admin.documents.openaiurl.label')}
|
||||
description={$i18n.t('settings.admin.documents.openaiurl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.documents.openaiurl.label')}
|
||||
bind:value={OpenAIUrl}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key for embedding requests.')}
|
||||
label={$i18n.t('settings.admin.documents.openaikey.label')}
|
||||
description={$i18n.t('settings.admin.documents.openaikey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.documents.openaikey.label')}
|
||||
bind:value={OpenAIKey}
|
||||
required={false}
|
||||
/>
|
||||
|
|
@ -1061,23 +1058,23 @@
|
|||
{:else if RAG_EMBEDDING_ENGINE === 'ollama'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('Ollama endpoint used for embeddings.')}
|
||||
label={$i18n.t('settings.admin.documents.ollamaurl.label')}
|
||||
description={$i18n.t('settings.admin.documents.ollamaurl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.documents.ollamaurl.label')}
|
||||
bind:value={OllamaUrl}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('Optional API key for Ollama requests.')}
|
||||
label={$i18n.t('settings.admin.documents.ollamakey.label')}
|
||||
description={$i18n.t('settings.admin.documents.ollamakey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.documents.ollamakey.label')}
|
||||
bind:value={OllamaKey}
|
||||
required={false}
|
||||
/>
|
||||
|
|
@ -1086,33 +1083,33 @@
|
|||
{:else if RAG_EMBEDDING_ENGINE === 'azure_openai'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('Azure OpenAI endpoint used for embeddings.')}
|
||||
label={$i18n.t('settings.admin.documents.azureopenaiurl.label')}
|
||||
description={$i18n.t('settings.admin.documents.azureopenaiurl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.documents.azureopenaiurl.label')}
|
||||
bind:value={AzureOpenAIUrl}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('Azure OpenAI API key.')}
|
||||
label={$i18n.t('settings.admin.documents.azureopenaikey.label')}
|
||||
description={$i18n.t('settings.admin.documents.azureopenaikey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.documents.azureopenaikey.label')}
|
||||
bind:value={AzureOpenAIKey}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Version')}
|
||||
description={$i18n.t('Azure OpenAI API version.')}
|
||||
label={$i18n.t('settings.admin.documents.version.label')}
|
||||
description={$i18n.t('settings.admin.documents.version.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('Version')}
|
||||
placeholder={$i18n.t('settings.admin.documents.version.label')}
|
||||
bind:value={AzureOpenAIVersion}
|
||||
required
|
||||
/>
|
||||
|
|
@ -1121,10 +1118,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Embedding Model')}
|
||||
description={$i18n.t(
|
||||
'Model used to generate embeddings. Reindex knowledge after changing this.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.embeddingModel.label')}
|
||||
description={$i18n.t('settings.admin.documents.embeddingModel.description')}
|
||||
>
|
||||
<div class="flex w-full gap-2">
|
||||
<input
|
||||
|
|
@ -1146,7 +1141,7 @@
|
|||
embeddingModelUpdateHandler();
|
||||
}}
|
||||
disabled={updateEmbeddingModelLoading}
|
||||
aria-label={$i18n.t('Update embedding model')}
|
||||
aria-label={$i18n.t('settings.admin.documents.updateEmbeddingModel.label')}
|
||||
>
|
||||
{#if updateEmbeddingModelLoading}
|
||||
<Spinner />
|
||||
|
|
@ -1176,8 +1171,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Embedding Batch Size')}
|
||||
description={$i18n.t('Number of items processed per embedding batch.')}
|
||||
label={$i18n.t('settings.admin.documents.embeddingBatchSize.label')}
|
||||
description={$i18n.t('settings.admin.documents.embeddingBatchSize.description')}
|
||||
>
|
||||
<input
|
||||
bind:value={RAG_EMBEDDING_BATCH_SIZE}
|
||||
|
|
@ -1191,16 +1186,16 @@
|
|||
|
||||
{#if RAG_EMBEDDING_ENGINE === 'ollama' || RAG_EMBEDDING_ENGINE === 'openai' || RAG_EMBEDDING_ENGINE === 'azure_openai'}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Async Embedding Processing')}
|
||||
description={$i18n.t('Run embedding tasks concurrently to speed up processing.')}
|
||||
label={$i18n.t('settings.admin.documents.asyncEmbeddingProcessing.label')}
|
||||
description={$i18n.t('settings.admin.documents.asyncEmbeddingProcessing.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={ENABLE_ASYNC_EMBEDDING} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Embedding Concurrent Requests')}
|
||||
label={$i18n.t('settings.admin.documents.embeddingConcurrentRequests.label')}
|
||||
description={$i18n.t(
|
||||
'Maximum concurrent embedding requests. Set to 0 for unlimited.'
|
||||
'settings.admin.documents.embeddingConcurrentRequests.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
|
|
@ -1215,12 +1210,12 @@
|
|||
</AdminSettingSection>
|
||||
{/if}
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Retrieval')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.documents.sections.retrieval.title')}>
|
||||
{#if !RAGConfig.BYPASS_EMBEDDING_AND_RETRIEVAL}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Full Context Mode')}
|
||||
label={$i18n.t('settings.admin.documents.fullContextMode.label')}
|
||||
description={RAGConfig.RAG_FULL_CONTEXT
|
||||
? $i18n.t('Inject entire documents as context for comprehensive processing.')
|
||||
? $i18n.t('settings.admin.documents.fullContextMode.description')
|
||||
: $i18n.t('Use segmented retrieval for focused context.')}
|
||||
let:labelId
|
||||
>
|
||||
|
|
@ -1229,8 +1224,8 @@
|
|||
|
||||
{#if !RAGConfig.RAG_FULL_CONTEXT}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Hybrid Search')}
|
||||
description={$i18n.t('Combine semantic and keyword retrieval.')}
|
||||
label={$i18n.t('settings.admin.documents.hybridSearch.label')}
|
||||
description={$i18n.t('settings.admin.documents.hybridSearch.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -1238,10 +1233,8 @@
|
|||
|
||||
{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Enrich Hybrid Search Text')}
|
||||
description={$i18n.t(
|
||||
'Add filenames, titles, sections, and snippets to improve lexical recall.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.enrichHybridSearchText.label')}
|
||||
description={$i18n.t('settings.admin.documents.enrichHybridSearchText.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -1251,8 +1244,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Reranking Engine')}
|
||||
description={$i18n.t('Provider used to rerank hybrid search results.')}
|
||||
label={$i18n.t('settings.admin.documents.rerankingEngine.label')}
|
||||
description={$i18n.t('settings.admin.documents.rerankingEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={RAGConfig.RAG_RERANKING_ENGINE}
|
||||
|
|
@ -1275,23 +1268,29 @@
|
|||
{#if RAGConfig.RAG_RERANKING_ENGINE === 'external'}
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Base URL')}
|
||||
description={$i18n.t('External reranker endpoint.')}
|
||||
label={$i18n.t('settings.admin.documents.ragExternalRerankerUrl.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.documents.ragExternalRerankerUrl.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.documents.ragExternalRerankerUrl.label')}
|
||||
bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_URL}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('API key sent to the external reranker.')}
|
||||
label={$i18n.t('settings.admin.documents.ragExternalRerankerApiKey.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.documents.ragExternalRerankerApiKey.description'
|
||||
)}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t(
|
||||
'settings.admin.documents.ragExternalRerankerApiKey.label'
|
||||
)}
|
||||
bind:value={RAGConfig.RAG_EXTERNAL_RERANKER_API_KEY}
|
||||
required={false}
|
||||
/>
|
||||
|
|
@ -1300,8 +1299,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Reranking Model')}
|
||||
description={$i18n.t('Model used to rerank retrieved results.')}
|
||||
label={$i18n.t('settings.admin.documents.rerankingModel.label')}
|
||||
description={$i18n.t('settings.admin.documents.rerankingModel.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1314,8 +1313,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Reranking Batch Size')}
|
||||
description={$i18n.t('Number of results processed per reranking batch.')}
|
||||
label={$i18n.t('settings.admin.documents.rerankingBatchSize.label')}
|
||||
description={$i18n.t('settings.admin.documents.rerankingBatchSize.description')}
|
||||
>
|
||||
<input
|
||||
bind:value={RAGConfig.RAG_RERANKING_BATCH_SIZE}
|
||||
|
|
@ -1328,8 +1327,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Top K')}
|
||||
description={$i18n.t('Maximum number of retrieved chunks returned to the model.')}
|
||||
label={$i18n.t('settings.admin.documents.topK.label')}
|
||||
description={$i18n.t('settings.admin.documents.topK.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1343,8 +1342,8 @@
|
|||
|
||||
{#if RAGConfig.ENABLE_RAG_HYBRID_SEARCH === true}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Top K Reranker')}
|
||||
description={$i18n.t('Maximum number of hybrid results sent to the reranker.')}
|
||||
label={$i18n.t('settings.admin.documents.topKReranker.label')}
|
||||
description={$i18n.t('settings.admin.documents.topKReranker.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1357,10 +1356,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Relevance Threshold')}
|
||||
description={$i18n.t(
|
||||
'Only return documents with a score greater than or equal to this value.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.relevanceThreshold.label')}
|
||||
description={$i18n.t('settings.admin.documents.relevanceThreshold.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1375,8 +1372,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('BM25 Weight')}
|
||||
description={$i18n.t('Balance semantic and lexical weighting for hybrid search.')}
|
||||
label={$i18n.t('settings.admin.documents.bm25Weight.label')}
|
||||
description={$i18n.t('settings.admin.documents.bm25Weight.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -1426,8 +1423,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('RAG Template')}
|
||||
description={$i18n.t('Prompt template used when retrieved context is injected.')}
|
||||
label={$i18n.t('settings.admin.documents.ragTemplate.label')}
|
||||
description={$i18n.t('settings.admin.documents.ragTemplate.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t('Leave empty to use the default prompt, or enter a custom prompt')}
|
||||
|
|
@ -1453,12 +1450,10 @@
|
|||
</AdminSettingField>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Files')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.documents.sections.files.title')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Allowed File Extensions')}
|
||||
description={$i18n.t(
|
||||
'Comma-separated upload extensions. Leave empty for all file types.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.allowedFileExtensions.label')}
|
||||
description={$i18n.t('settings.admin.documents.allowedFileExtensions.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1471,8 +1466,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Max Upload Size')}
|
||||
description={$i18n.t('Maximum file size in MB. Leave empty for unlimited.')}
|
||||
label={$i18n.t('settings.admin.documents.maxUploadSize.label')}
|
||||
description={$i18n.t('settings.admin.documents.maxUploadSize.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1484,8 +1479,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Max Upload Count')}
|
||||
description={$i18n.t('Maximum number of files that can be used at once in chat.')}
|
||||
label={$i18n.t('settings.admin.documents.maxUploadCount.label')}
|
||||
description={$i18n.t('settings.admin.documents.maxUploadCount.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1500,10 +1495,8 @@
|
|||
|
||||
<div class="grid grid-cols-1 gap-x-3 gap-y-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Image Compression Width')}
|
||||
description={$i18n.t(
|
||||
'Width in pixels to compress images to. Leave empty for no compression.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.imageCompressionWidth.label')}
|
||||
description={$i18n.t('settings.admin.documents.imageCompressionWidth.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1515,10 +1508,8 @@
|
|||
/>
|
||||
</AdminSettingField>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Image Compression Height')}
|
||||
description={$i18n.t(
|
||||
'Height in pixels to compress images to. Leave empty for no compression.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.documents.imageCompressionHeight.label')}
|
||||
description={$i18n.t('settings.admin.documents.imageCompressionHeight.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1532,10 +1523,10 @@
|
|||
</div>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Integration')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.documents.sections.integration.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Google Drive')}
|
||||
description={$i18n.t('Allow Google Drive as a document source.')}
|
||||
label={$i18n.t('settings.admin.documents.googleDrive.label')}
|
||||
description={$i18n.t('settings.admin.documents.googleDrive.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -1544,18 +1535,18 @@
|
|||
/>
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('OneDrive')}
|
||||
description={$i18n.t('Allow OneDrive as a document source.')}
|
||||
label={$i18n.t('settings.admin.documents.onedrive.label')}
|
||||
description={$i18n.t('settings.admin.documents.onedrive.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={RAGConfig.ENABLE_ONEDRIVE_INTEGRATION} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Danger Zone')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.documents.sections.dangerZone.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Reset Upload Directory')}
|
||||
description={$i18n.t('Delete uploaded files from the upload directory.')}
|
||||
label={$i18n.t('settings.admin.documents.resetUploadDirectory.label')}
|
||||
description={$i18n.t('settings.admin.documents.resetUploadDirectory.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -1568,8 +1559,8 @@
|
|||
</button>
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Reset Vector Storage/Knowledge')}
|
||||
description={$i18n.t('Clear vector storage and knowledge indexing data.')}
|
||||
label={$i18n.t('settings.admin.documents.resetVectorStorageKnowledge.label')}
|
||||
description={$i18n.t('settings.admin.documents.resetVectorStorageKnowledge.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -1582,9 +1573,9 @@
|
|||
</button>
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Reindex Knowledge and Memory Vectors')}
|
||||
label={$i18n.t('settings.admin.documents.reindexKnowledgeAndMemoryVectors.label')}
|
||||
description={$i18n.t(
|
||||
'Rebuild vectors for existing knowledge files, knowledge search, and memories.'
|
||||
'settings.admin.documents.reindexKnowledgeAndMemoryVectors.description'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
|
|
@ -1594,7 +1585,7 @@
|
|||
showReindexConfirm = true;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Reindex')}
|
||||
{$i18n.t('settings.admin.documents.reindex.label')}
|
||||
</button>
|
||||
</AdminSettingRow>
|
||||
</AdminSettingSection>
|
||||
|
|
|
|||
|
|
@ -91,14 +91,16 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Evaluations')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.evaluations.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if evaluationConfig !== null}
|
||||
<AdminSettingSection first>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Arena Models')}
|
||||
description={$i18n.t('Message rating should be enabled to use this feature')}
|
||||
label={$i18n.t('settings.admin.evaluations.arenaModels.label')}
|
||||
description={$i18n.t('settings.admin.evaluations.arenaModels.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Tooltip content={$i18n.t(`Message rating should be enabled to use this feature`)}>
|
||||
|
|
@ -111,11 +113,13 @@
|
|||
</AdminSettingSection>
|
||||
|
||||
{#if evaluationConfig.ENABLE_EVALUATION_ARENA_MODELS}
|
||||
<AdminSettingSection title={$i18n.t('Models')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.evaluations.sections.models.title')}>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Arena Models')}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.evaluations.arenaModels.label')}
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Add Arena Model')}>
|
||||
<Tooltip content={$i18n.t('settings.admin.evaluations.addArenaModel.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -565,7 +565,7 @@
|
|||
<div class="mt-3">
|
||||
<div class="flex justify-between items-center">
|
||||
<label for="event-webhook-all-events" class={`text-xs text-gray-500`}
|
||||
>{$i18n.t('Events')}</label
|
||||
>{$i18n.t('settings.admin.general.events.events.label')}</label
|
||||
>
|
||||
<label class="flex items-center gap-1.5 text-xs text-gray-700 dark:text-gray-300">
|
||||
<input
|
||||
|
|
@ -690,16 +690,18 @@
|
|||
/>
|
||||
|
||||
<div class="mt-5">
|
||||
<div class="mb-2 text-xs text-gray-400 dark:text-gray-600">{$i18n.t('Events')}</div>
|
||||
<div class="mb-2 text-xs text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('settings.admin.general.events.events.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full justify-between gap-2.5">
|
||||
<div class="flex w-full items-start justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Webhooks')}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.general.events.webhooks.label')}
|
||||
</div>
|
||||
<div class="mt-1.5 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t(
|
||||
'Send product events as JSON to external services. Chat destinations receive readable messages.'
|
||||
)}
|
||||
{$i18n.t('settings.admin.general.events.webhooks.description')}
|
||||
</div>
|
||||
</div>
|
||||
<Tooltip content={$i18n.t('Add webhook')}>
|
||||
|
|
|
|||
|
|
@ -797,11 +797,11 @@
|
|||
<div class="flex w-full flex-col justify-between text-xs">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="flex items-center gap-2 leading-none text-gray-600 dark:text-gray-400">
|
||||
<div>{$i18n.t('External Knowledge Sources')}</div>
|
||||
<div>{$i18n.t('settings.admin.integrations.externalKnowledge.title')}</div>
|
||||
<ExperimentalBadge />
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Add Connection')}>
|
||||
<Tooltip content={$i18n.t('settings.admin.integrations.externalKnowledge.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
on:click={openCreateSource}
|
||||
|
|
|
|||
|
|
@ -124,14 +124,18 @@
|
|||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('General')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.general.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if adminConfig !== null}
|
||||
<AdminSettingSection first>
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="min-w-0 text-xs">
|
||||
<div class="text-gray-600 dark:text-gray-400">{$i18n.t('Version')}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.general.version.label')}
|
||||
</div>
|
||||
<div class="mt-1 flex flex-wrap gap-x-1 text-gray-700 dark:text-gray-200">
|
||||
<Tooltip content={WEBUI_BUILD_HASH}>v{WEBUI_VERSION}</Tooltip>
|
||||
|
||||
|
|
@ -163,7 +167,7 @@
|
|||
showChangelog.set(true);
|
||||
}}
|
||||
>
|
||||
{$i18n.t("See what's new")}
|
||||
{$i18n.t('settings.admin.general.seeWhatSNew.label')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -175,7 +179,7 @@
|
|||
checkForVersionUpdates();
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Check for updates')}
|
||||
{$i18n.t('settings.admin.general.checkForUpdates.label')}
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
@ -183,12 +187,14 @@
|
|||
<div class="text-xs">
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="min-w-0">
|
||||
<div class="text-gray-600 dark:text-gray-400">{$i18n.t('Help')}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.general.help.label')}
|
||||
</div>
|
||||
<div class="mt-0.5 text-gray-400 dark:text-gray-600">
|
||||
<!-- LICENSE covers this Open WebUI wordmark.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
{$i18n.t('Discover how to use Open WebUI and seek support from the community.')}
|
||||
{$i18n.t('settings.admin.general.help.description')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -224,7 +230,9 @@
|
|||
<!-- LICENSE covers this Open WebUI license attribution.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<div class="text-gray-600 dark:text-gray-400">{$i18n.t('License')}</div>
|
||||
<div class="text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.general.license.label')}
|
||||
</div>
|
||||
|
||||
{#if $config?.license_metadata}
|
||||
<a
|
||||
|
|
@ -263,10 +271,10 @@
|
|||
</div>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Features')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.general.sections.features.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Community Sharing')}
|
||||
description={$i18n.t('Allow users to share chats with the Open WebUI community.')}
|
||||
label={$i18n.t('settings.admin.general.communitySharing.label')}
|
||||
description={$i18n.t('settings.admin.general.communitySharing.description')}
|
||||
let:labelId
|
||||
>
|
||||
<!-- LICENSE covers this Open WebUI Community wordmark.
|
||||
|
|
@ -275,15 +283,15 @@
|
|||
<Switch bind:state={adminConfig.ENABLE_COMMUNITY_SHARING} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Message Rating')}
|
||||
description={$i18n.t('Let users rate assistant responses.')}
|
||||
label={$i18n.t('settings.admin.general.messageRating.label')}
|
||||
description={$i18n.t('settings.admin.general.messageRating.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_MESSAGE_RATING} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Folders')}
|
||||
description={$i18n.t('Allow users to organize chats into folders.')}
|
||||
label={$i18n.t('settings.admin.general.folders.label')}
|
||||
description={$i18n.t('settings.admin.general.folders.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_FOLDERS} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -291,8 +299,8 @@
|
|||
|
||||
{#if adminConfig.ENABLE_FOLDERS}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Folder Max File Count')}
|
||||
description={$i18n.t('Maximum number of files allowed per folder.')}
|
||||
label={$i18n.t('settings.admin.general.folderMaxFileCount.label')}
|
||||
description={$i18n.t('settings.admin.general.folderMaxFileCount.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -305,16 +313,16 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Memories')}
|
||||
description={$i18n.t('Allow users to save memories for more personalized responses.')}
|
||||
label={$i18n.t('settings.admin.general.memories.label')}
|
||||
description={$i18n.t('settings.admin.general.memories.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_MEMORIES} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
{#if adminConfig.ENABLE_MEMORIES}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Memory System Context')}
|
||||
description={$i18n.t('Include saved memories in the system context.')}
|
||||
label={$i18n.t('settings.admin.general.memorySystemContext.label')}
|
||||
description={$i18n.t('settings.admin.general.memorySystemContext.description')}
|
||||
labelClassName="text-gray-500 dark:text-gray-500"
|
||||
let:labelId
|
||||
>
|
||||
|
|
@ -325,25 +333,23 @@
|
|||
</AdminSettingRow>
|
||||
{/if}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Notes')}
|
||||
description={$i18n.t('Allow users to create and manage notes.')}
|
||||
label={$i18n.t('settings.admin.general.notes.label')}
|
||||
description={$i18n.t('settings.admin.general.notes.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_NOTES} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Channels')}
|
||||
description={$i18n.t('Allow users to use channels for shared conversations.')}
|
||||
label={$i18n.t('settings.admin.general.channels.label')}
|
||||
description={$i18n.t('settings.admin.general.channels.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_CHANNELS} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
{#if adminConfig.ENABLE_CHANNELS}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Model Response Mode')}
|
||||
description={$i18n.t(
|
||||
'Choose where model responses to root-level channel mentions are posted.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.general.modelResponseMode.label')}
|
||||
description={$i18n.t('settings.admin.general.modelResponseMode.description')}
|
||||
labelClassName="text-gray-500 dark:text-gray-500"
|
||||
let:labelId
|
||||
>
|
||||
|
|
@ -357,37 +363,37 @@
|
|||
</AdminSettingRow>
|
||||
{/if}
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Calendar')}
|
||||
description={$i18n.t('Allow users to access calendar features.')}
|
||||
label={$i18n.t('settings.admin.general.calendar.label')}
|
||||
description={$i18n.t('settings.admin.general.calendar.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_CALENDAR} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Automations')}
|
||||
description={$i18n.t('Allow users to create and run automations.')}
|
||||
label={$i18n.t('settings.admin.general.automations.label')}
|
||||
description={$i18n.t('settings.admin.general.automations.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_AUTOMATIONS} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('User Webhooks')}
|
||||
description={$i18n.t('Allow users to configure webhooks from their account.')}
|
||||
label={$i18n.t('settings.admin.general.userWebhooks.label')}
|
||||
description={$i18n.t('settings.admin.general.userWebhooks.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_USER_WEBHOOKS} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('User Status')}
|
||||
description={$i18n.t('Show user status information in the app.')}
|
||||
label={$i18n.t('settings.admin.general.userStatus.label')}
|
||||
description={$i18n.t('settings.admin.general.userStatus.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={adminConfig.ENABLE_USER_STATUS} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Response Watermark')}
|
||||
description={$i18n.t('Append a watermark to assistant responses when configured.')}
|
||||
label={$i18n.t('settings.admin.general.responseWatermark.label')}
|
||||
description={$i18n.t('settings.admin.general.responseWatermark.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -397,10 +403,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('WebUI URL')}
|
||||
description={$i18n.t(
|
||||
'Enter the public URL of your WebUI. This URL will be used to generate links in the notifications.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.general.webuiUrl.label')}
|
||||
description={$i18n.t('settings.admin.general.webuiUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -413,7 +417,7 @@
|
|||
|
||||
<Events />
|
||||
|
||||
<AdminSettingSection title={$i18n.t('UI')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.general.sections.ui.title')}>
|
||||
<fieldset id="ui-i18n-settings" disabled={saving} class="min-w-0">
|
||||
<I18nSettings bind:entries={uiI18nEntries} />
|
||||
</fieldset>
|
||||
|
|
@ -478,7 +482,9 @@
|
|||
|
||||
<div>
|
||||
<div class="mb-1 flex min-h-7 w-full items-center justify-between gap-2">
|
||||
<div class="min-w-0 text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Banners')}</div>
|
||||
<div class="min-w-0 text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.general.banners.label')}
|
||||
</div>
|
||||
<div class="flex shrink-0 items-center gap-1">
|
||||
{#if banners.length > 0}
|
||||
<LanguageModeSelect bind:value={bannerLocale} className="w-fit" />
|
||||
|
|
@ -486,7 +492,7 @@
|
|||
<button
|
||||
class="flex size-6 items-center justify-center text-gray-400 dark:text-gray-600"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add banner')}
|
||||
aria-label={$i18n.t('settings.admin.general.addBanner.label')}
|
||||
on:click={() => {
|
||||
if (banners.length === 0 || banners[banners.length - 1]?.content !== '') {
|
||||
banners = [
|
||||
|
|
|
|||
|
|
@ -27,17 +27,19 @@
|
|||
|
||||
<div class="space-y-1.5">
|
||||
<div class="flex min-h-7 flex-wrap items-center justify-between gap-x-2 gap-y-1">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('UI Translations')}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.general.translations.uiTranslations.label')}
|
||||
</div>
|
||||
<div class="ms-auto flex shrink-0 items-center gap-1">
|
||||
{#if entries.length}<LanguageModeSelect
|
||||
bind:value={locale}
|
||||
includeDefault={false}
|
||||
className="w-fit"
|
||||
/>{/if}
|
||||
<Tooltip content={$i18n.t('Add translation')}>
|
||||
<Tooltip content={$i18n.t('settings.admin.general.translations.addTranslation.label')}>
|
||||
<button
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add translation')}
|
||||
aria-label={$i18n.t('settings.admin.general.translations.addTranslation.label')}
|
||||
on:click={add}
|
||||
class="flex size-6 items-center justify-center text-gray-400 dark:text-gray-600"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -299,25 +299,27 @@
|
|||
saveHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Images')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.images.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if config}
|
||||
<div class="flex flex-col">
|
||||
<AdminSettingSection first>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Image Generation')}
|
||||
description={$i18n.t('Allow users to generate images from prompts.')}
|
||||
label={$i18n.t('settings.admin.images.imageGeneration.label')}
|
||||
description={$i18n.t('settings.admin.images.imageGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={config.ENABLE_IMAGE_GENERATION} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Create Image')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.images.sections.createImage.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Image Generation Engine')}
|
||||
description={$i18n.t('Choose the provider used for image generation.')}
|
||||
label={$i18n.t('settings.admin.images.imageGenerationEngine.label')}
|
||||
description={$i18n.t('settings.admin.images.imageGenerationEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.IMAGE_GENERATION_ENGINE}
|
||||
|
|
@ -332,7 +334,9 @@
|
|||
|
||||
{#if config.ENABLE_IMAGE_GENERATION}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('Model')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imageGenerationModel.label')}
|
||||
>
|
||||
<input
|
||||
list="model-list"
|
||||
class={inputClass}
|
||||
|
|
@ -348,7 +352,7 @@
|
|||
</datalist>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('Image Size')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.images.imageSize.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
|
||||
|
|
@ -357,7 +361,7 @@
|
|||
</AdminSettingField>
|
||||
|
||||
{#if ['comfyui', 'automatic1111', ''].includes(config?.IMAGE_GENERATION_ENGINE)}
|
||||
<AdminSettingField label={$i18n.t('Steps')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.images.steps.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('Enter Number of Steps (e.g. 50)')}
|
||||
|
|
@ -369,8 +373,8 @@
|
|||
</div>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Image Prompt Generation')}
|
||||
description={$i18n.t('Generate an image prompt before sending the request.')}
|
||||
label={$i18n.t('settings.admin.images.imagePromptGeneration.label')}
|
||||
description={$i18n.t('settings.admin.images.imagePromptGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -382,37 +386,39 @@
|
|||
|
||||
{#if config?.IMAGE_GENERATION_ENGINE === 'openai'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('API Base URL')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesOpenaiApiBaseUrl.label')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesOpenaiApiBaseUrl.label')}
|
||||
bind:value={config.IMAGES_OPENAI_API_BASE_URL}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.images.imagesOpenaiApiKey.label')}>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesOpenaiApiKey.label')}
|
||||
bind:value={config.IMAGES_OPENAI_API_KEY}
|
||||
required={false}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
</div>
|
||||
|
||||
<AdminSettingField label={$i18n.t('API Version')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesOpenaiApiVersion.label')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Version')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesOpenaiApiVersion.label')}
|
||||
bind:value={config.IMAGES_OPENAI_API_VERSION}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Additional Parameters')}
|
||||
description={$i18n.t(
|
||||
'Send extra JSON parameters with each image generation request.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.images.imagesOpenaiApiParams.label')}
|
||||
description={$i18n.t('settings.admin.images.imagesOpenaiApiParams.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -423,10 +429,8 @@
|
|||
</AdminSettingField>
|
||||
{:else if (config?.IMAGE_GENERATION_ENGINE ?? 'automatic1111') === 'automatic1111'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Base URL')}
|
||||
description={$i18n.t(
|
||||
'Connect to a stable-diffusion-webui server running with the `--api` flag.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.images.automatic1111BaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.images.automatic1111BaseUrl.description')}
|
||||
>
|
||||
<div class="flex w-full gap-2">
|
||||
<input
|
||||
|
|
@ -437,7 +441,7 @@
|
|||
<button
|
||||
class="shrink-0 text-gray-400 transition-colors hover:text-gray-900 dark:text-gray-600 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('verify connection')}
|
||||
aria-label={$i18n.t('settings.admin.images.verifyConnection.label')}
|
||||
on:click={async () => {
|
||||
await updateConfigHandler();
|
||||
const res = await verifyConfigUrl(localStorage.token).catch((error) => {
|
||||
|
|
@ -446,7 +450,9 @@
|
|||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
toast.success(
|
||||
$i18n.t('settings.admin.images.serverConnectionVerified.label')
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -467,8 +473,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Auth String')}
|
||||
description={$i18n.t('Provide the --api-auth username and password when required.')}
|
||||
label={$i18n.t('settings.admin.images.apiAuthString.label')}
|
||||
description={$i18n.t('settings.admin.images.apiAuthString.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -479,8 +485,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Additional Parameters')}
|
||||
description={$i18n.t('Send extra JSON parameters with each AUTOMATIC1111 request.')}
|
||||
label={$i18n.t('settings.admin.images.automatic1111Params.label')}
|
||||
description={$i18n.t('settings.admin.images.automatic1111Params.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -491,8 +497,8 @@
|
|||
</AdminSettingField>
|
||||
{:else if config?.IMAGE_GENERATION_ENGINE === 'comfyui'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Base URL')}
|
||||
description={$i18n.t('Connect to the ComfyUI server used for generation.')}
|
||||
label={$i18n.t('settings.admin.images.comfyuiBaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.images.comfyuiBaseUrl.description')}
|
||||
>
|
||||
<div class="flex w-full gap-2">
|
||||
<input
|
||||
|
|
@ -503,7 +509,7 @@
|
|||
<button
|
||||
class="shrink-0 text-gray-400 transition-colors hover:text-gray-900 dark:text-gray-600 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('verify connection')}
|
||||
aria-label={$i18n.t('settings.admin.images.verifyConnection.label')}
|
||||
on:click={async () => {
|
||||
await updateConfigHandler();
|
||||
const res = await verifyConfigUrl(localStorage.token).catch((error) => {
|
||||
|
|
@ -512,7 +518,9 @@
|
|||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
toast.success(
|
||||
$i18n.t('settings.admin.images.serverConnectionVerified.label')
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -533,8 +541,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('Use an API key when your ComfyUI server requires one.')}
|
||||
label={$i18n.t('settings.admin.images.comfyuiApiKey.label')}
|
||||
description={$i18n.t('settings.admin.images.comfyuiApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -563,17 +571,15 @@
|
|||
}}
|
||||
/>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('ComfyUI Workflow')}
|
||||
description={$i18n.t(
|
||||
'Upload a workflow.json file exported as API format from ComfyUI.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.images.comfyuiComfyuiWorkflow.label')}
|
||||
description={$i18n.t('settings.admin.images.comfyuiComfyuiWorkflow.description')}
|
||||
>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
{#if config.COMFYUI_WORKFLOW}
|
||||
<button
|
||||
class="text-xs text-gray-500 transition-colors hover:text-gray-900 hover:underline dark:text-gray-500 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Edit workflow.json content')}
|
||||
aria-label={$i18n.t('settings.admin.images.editWorkflowJsonContent.label')}
|
||||
on:click={() => {
|
||||
// open code editor modal
|
||||
showComfyUIWorkflowEditor = true;
|
||||
|
|
@ -583,11 +589,17 @@
|
|||
</button>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Click here to upload a workflow.json file.')}>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'settings.admin.images.clickHereToUploadAWorkflowJsonFile.label'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
class="text-xs text-gray-500 transition-colors hover:text-gray-900 hover:underline dark:text-gray-500 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Click here to upload a workflow.json file.')}
|
||||
aria-label={$i18n.t(
|
||||
'settings.admin.images.clickHereToUploadAWorkflowJsonFile.label'
|
||||
)}
|
||||
on:click={() => {
|
||||
document.getElementById('upload-comfyui-workflow-input')?.click();
|
||||
}}
|
||||
|
|
@ -623,8 +635,8 @@
|
|||
|
||||
{#if config.COMFYUI_WORKFLOW}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('ComfyUI Workflow Nodes')}
|
||||
description={$i18n.t('Map workflow node inputs used for image generation.')}
|
||||
label={$i18n.t('settings.admin.images.generationWorkflowNodes.label')}
|
||||
description={$i18n.t('settings.admin.images.generationWorkflowNodes.description')}
|
||||
>
|
||||
<div class="flex flex-col gap-1.5 text-xs">
|
||||
{#each REQUIRED_WORKFLOW_NODES as node}
|
||||
|
|
@ -673,31 +685,31 @@
|
|||
{/if}
|
||||
{:else if config?.IMAGE_GENERATION_ENGINE === 'gemini'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Base URL')}
|
||||
description={$i18n.t('Override the Gemini image generation endpoint.')}
|
||||
label={$i18n.t('settings.admin.images.imagesGeminiApiBaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.images.imagesGeminiApiBaseUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesOpenaiApiBaseUrl.label')}
|
||||
bind:value={config.IMAGES_GEMINI_API_BASE_URL}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('Use a Gemini API key for image generation.')}
|
||||
label={$i18n.t('settings.admin.images.imagesGeminiApiKey.label')}
|
||||
description={$i18n.t('settings.admin.images.imagesGeminiApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesGeminiApiKey.label')}
|
||||
bind:value={config.IMAGES_GEMINI_API_KEY}
|
||||
required={true}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Gemini Endpoint Method')}
|
||||
description={$i18n.t('Select the Gemini endpoint method to call.')}
|
||||
label={$i18n.t('settings.admin.images.geminiEndpointMethod.label')}
|
||||
description={$i18n.t('settings.admin.images.geminiEndpointMethod.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.IMAGES_GEMINI_ENDPOINT_METHOD}
|
||||
|
|
@ -710,18 +722,18 @@
|
|||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Edit Image')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.images.sections.editImage.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Image Edit')}
|
||||
description={$i18n.t('Allow users to edit existing images.')}
|
||||
label={$i18n.t('settings.admin.images.imageEdit.label')}
|
||||
description={$i18n.t('settings.admin.images.imageEdit.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={config.ENABLE_IMAGE_EDIT} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Image Edit Engine')}
|
||||
description={$i18n.t('Choose the provider used for image edits.')}
|
||||
label={$i18n.t('settings.admin.images.imageEditEngine.label')}
|
||||
description={$i18n.t('settings.admin.images.imageEditEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={config.IMAGE_EDIT_ENGINE}
|
||||
|
|
@ -735,7 +747,7 @@
|
|||
|
||||
{#if config?.ENABLE_IMAGE_GENERATION && config?.ENABLE_IMAGE_EDIT}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('Model')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.images.imageEditModel.label')}>
|
||||
<input
|
||||
list="model-list"
|
||||
class={inputClass}
|
||||
|
|
@ -750,7 +762,7 @@
|
|||
</datalist>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('Image Size')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.images.imageEditSize.label')}>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('Enter Image Size (e.g. 512x512)')}
|
||||
|
|
@ -762,35 +774,41 @@
|
|||
|
||||
{#if config?.IMAGE_EDIT_ENGINE === 'openai'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('API Base URL')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesEditOpenaiApiBaseUrl.label')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesEditOpenaiApiBaseUrl.label')}
|
||||
bind:value={config.IMAGES_EDIT_OPENAI_API_BASE_URL}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesEditOpenaiApiKey.label')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesEditOpenaiApiKey.label')}
|
||||
bind:value={config.IMAGES_EDIT_OPENAI_API_KEY}
|
||||
required={false}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
</div>
|
||||
|
||||
<AdminSettingField label={$i18n.t('API Version')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesEditOpenaiApiVersion.label')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Version')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesEditOpenaiApiVersion.label')}
|
||||
bind:value={config.IMAGES_EDIT_OPENAI_API_VERSION}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
{:else if config?.IMAGE_EDIT_ENGINE === 'comfyui'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Base URL')}
|
||||
description={$i18n.t('Connect to the ComfyUI server used for image edits.')}
|
||||
label={$i18n.t('settings.admin.images.imagesEditComfyuiBaseUrl.label')}
|
||||
description={$i18n.t('settings.admin.images.imagesEditComfyuiBaseUrl.description')}
|
||||
>
|
||||
<div class="flex w-full gap-2">
|
||||
<input
|
||||
|
|
@ -801,7 +819,7 @@
|
|||
<button
|
||||
class="shrink-0 text-gray-400 transition-colors hover:text-gray-900 dark:text-gray-600 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('verify connection')}
|
||||
aria-label={$i18n.t('settings.admin.images.verifyConnection.label')}
|
||||
on:click={async () => {
|
||||
await updateConfigHandler();
|
||||
const res = await verifyConfigUrl(localStorage.token).catch((error) => {
|
||||
|
|
@ -810,7 +828,9 @@
|
|||
});
|
||||
|
||||
if (res) {
|
||||
toast.success($i18n.t('Server connection verified'));
|
||||
toast.success(
|
||||
$i18n.t('settings.admin.images.serverConnectionVerified.label')
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
@ -831,8 +851,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('Use an API key when your ComfyUI server requires one.')}
|
||||
label={$i18n.t('settings.admin.images.imagesEditComfyuiApiKey.label')}
|
||||
description={$i18n.t('settings.admin.images.imagesEditComfyuiApiKey.description')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
|
|
@ -861,17 +881,15 @@
|
|||
}}
|
||||
/>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('ComfyUI Workflow')}
|
||||
description={$i18n.t(
|
||||
'Upload a workflow.json file exported as API format from ComfyUI.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.images.comfyuiComfyuiWorkflow.label')}
|
||||
description={$i18n.t('settings.admin.images.comfyuiComfyuiWorkflow.description')}
|
||||
>
|
||||
<div class="flex items-center justify-end gap-2">
|
||||
{#if config.IMAGES_EDIT_COMFYUI_WORKFLOW}
|
||||
<button
|
||||
class="text-xs text-gray-500 transition-colors hover:text-gray-900 hover:underline dark:text-gray-500 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Edit workflow.json content')}
|
||||
aria-label={$i18n.t('settings.admin.images.editWorkflowJsonContent.label')}
|
||||
on:click={() => {
|
||||
// open code editor modal
|
||||
showComfyUIEditWorkflowEditor = true;
|
||||
|
|
@ -881,11 +899,17 @@
|
|||
</button>
|
||||
{/if}
|
||||
|
||||
<Tooltip content={$i18n.t('Click here to upload a workflow.json file.')}>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'settings.admin.images.clickHereToUploadAWorkflowJsonFile.label'
|
||||
)}
|
||||
>
|
||||
<button
|
||||
class="text-xs text-gray-500 transition-colors hover:text-gray-900 hover:underline dark:text-gray-500 dark:hover:text-white"
|
||||
type="button"
|
||||
aria-label={$i18n.t('Click here to upload a workflow.json file.')}
|
||||
aria-label={$i18n.t(
|
||||
'settings.admin.images.clickHereToUploadAWorkflowJsonFile.label'
|
||||
)}
|
||||
on:click={() => {
|
||||
document.getElementById('upload-comfyui-edit-workflow-input')?.click();
|
||||
}}
|
||||
|
|
@ -911,8 +935,8 @@
|
|||
|
||||
{#if config.IMAGES_EDIT_COMFYUI_WORKFLOW}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('ComfyUI Workflow Nodes')}
|
||||
description={$i18n.t('Map workflow node inputs used for image edits.')}
|
||||
label={$i18n.t('settings.admin.images.editingWorkflowNodes.label')}
|
||||
description={$i18n.t('settings.admin.images.editingWorkflowNodes.description')}
|
||||
>
|
||||
<div class="flex flex-col gap-1.5 text-xs">
|
||||
{#each REQUIRED_EDIT_WORKFLOW_NODES as node}
|
||||
|
|
@ -961,18 +985,22 @@
|
|||
{/if}
|
||||
{:else if config?.IMAGE_EDIT_ENGINE === 'gemini'}
|
||||
<div class="grid grid-cols-1 gap-2 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('Base URL')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesEditGeminiApiBaseUrl.label')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('API Base URL')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesOpenaiApiBaseUrl.label')}
|
||||
bind:value={config.IMAGES_EDIT_GEMINI_API_BASE_URL}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('API Key')}>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('settings.admin.images.imagesEditGeminiApiKey.label')}
|
||||
>
|
||||
<SensitiveInput
|
||||
variant="settings"
|
||||
placeholder={$i18n.t('API Key')}
|
||||
placeholder={$i18n.t('settings.admin.images.imagesEditGeminiApiKey.label')}
|
||||
bind:value={config.IMAGES_EDIT_GEMINI_API_KEY}
|
||||
required={true}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -164,18 +164,23 @@
|
|||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Integrations')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.integrations.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if servers !== null}
|
||||
<AdminSettingSection title={$i18n.t('Tools')} first>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.integrations.sections.tools.title')}
|
||||
first
|
||||
>
|
||||
<div>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('External Tool Servers')}
|
||||
{$i18n.t('settings.admin.integrations.externalToolServers.label')}
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t(`Add Connection`)}>
|
||||
<Tooltip content={$i18n.t('settings.admin.integrations.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
on:click={() => {
|
||||
|
|
@ -215,12 +220,14 @@
|
|||
</div>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Terminal')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.integrations.sections.terminal.title')}>
|
||||
<div>
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Open Terminal')}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.integrations.openTerminal.label')}
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Add Connection')}>
|
||||
<Tooltip content={$i18n.t('settings.admin.integrations.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-6 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
on:click={() => {
|
||||
|
|
@ -245,7 +252,9 @@
|
|||
? 'opacity-50'
|
||||
: ''}"
|
||||
>
|
||||
<Tooltip content={$i18n.t('Terminal')}>
|
||||
<Tooltip
|
||||
content={$i18n.t('settings.admin.integrations.sections.terminal.title')}
|
||||
>
|
||||
<Cloud className="size-4" strokeWidth="1.5" />
|
||||
</Tooltip>
|
||||
|
||||
|
|
@ -311,7 +320,7 @@
|
|||
</div>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Knowledge')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.integrations.sections.knowledge.title')}>
|
||||
<ExternalKnowledge />
|
||||
</AdminSettingSection>
|
||||
{:else}
|
||||
|
|
|
|||
|
|
@ -169,23 +169,23 @@
|
|||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('Interface')}
|
||||
{$i18n.t('settings.admin.interface.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<AdminSettingSection title={$i18n.t('Tasks')} first>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.interface.sections.tasks.title')} first>
|
||||
<div>
|
||||
<div class="mb-2">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Task Model')}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.admin.interface.taskModel.label')}
|
||||
</div>
|
||||
<div class="mt-1.5 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t(
|
||||
'Choose fallback models for background tasks. Current Model follows the active chat model.'
|
||||
)}
|
||||
{$i18n.t('settings.admin.interface.taskModel.description')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid w-full grid-cols-1 gap-2.5 sm:grid-cols-2">
|
||||
<AdminSettingField label={$i18n.t('Local Task Model')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.interface.localTaskModel.label')}>
|
||||
<SettingsSelect
|
||||
bind:value={taskConfig.TASK_MODEL}
|
||||
className="w-full"
|
||||
|
|
@ -204,7 +204,7 @@
|
|||
</SettingsSelect>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField label={$i18n.t('External Task Model')}>
|
||||
<AdminSettingField label={$i18n.t('settings.admin.interface.externalTaskModel.label')}>
|
||||
<SettingsSelect
|
||||
bind:value={taskConfig.TASK_MODEL_EXTERNAL}
|
||||
className="w-full"
|
||||
|
|
@ -235,7 +235,7 @@
|
|||
}}
|
||||
>
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Task Model Parameters')}
|
||||
{$i18n.t('settings.admin.interface.taskModelParameters.label')}
|
||||
</span>
|
||||
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{showTaskParameters ? $i18n.t('Close') : $i18n.t('Configure')}
|
||||
|
|
@ -255,24 +255,22 @@
|
|||
</div>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Chat')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.interface.sections.chat.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Tool Permissions')}
|
||||
description={$i18n.t('Show Full access and Ask for approval in the chat input menu.')}
|
||||
label={$i18n.t('settings.admin.interface.toolPermissions.label')}
|
||||
description={$i18n.t('settings.admin.interface.toolPermissions.description')}
|
||||
let:labelId
|
||||
>
|
||||
<div slot="label" class="flex items-center gap-2">
|
||||
<span>{$i18n.t('Tool Permissions')}</span>
|
||||
<span>{$i18n.t('settings.admin.interface.toolPermissions.label')}</span>
|
||||
<ExperimentalBadge />
|
||||
</div>
|
||||
<Switch bind:state={chatConfig.ENABLE_TOOL_PERMISSIONS} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Context Compaction')}
|
||||
description={$i18n.t(
|
||||
'Summarize older chat history when the conversation context grows large.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.interface.contextCompaction.label')}
|
||||
description={$i18n.t('settings.admin.interface.contextCompaction.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={chatConfig.ENABLE_CONTEXT_COMPACTION} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -280,10 +278,8 @@
|
|||
|
||||
{#if chatConfig.ENABLE_CONTEXT_COMPACTION}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Context Compaction Model')}
|
||||
description={$i18n.t(
|
||||
'Choose a dedicated model for context compaction summaries. Current Model follows the active chat model.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.interface.contextCompactionModel.label')}
|
||||
description={$i18n.t('settings.admin.interface.contextCompactionModel.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={chatConfig.CONTEXT_COMPACTION_MODEL}
|
||||
|
|
@ -306,10 +302,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Token Threshold')}
|
||||
description={$i18n.t(
|
||||
'Older messages are summarized when estimated context exceeds this token limit.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.interface.tokenThreshold.label')}
|
||||
description={$i18n.t('settings.admin.interface.tokenThreshold.description')}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -321,10 +315,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Token Cap')}
|
||||
description={$i18n.t(
|
||||
'Model-specific context compaction thresholds cannot exceed this token limit.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.interface.tokenCap.label')}
|
||||
description={$i18n.t('settings.admin.interface.tokenCap.description')}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -336,10 +328,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Retained Messages')}
|
||||
description={$i18n.t(
|
||||
'Percentage of recent messages to keep after older messages are summarized.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.interface.retainedMessages.label')}
|
||||
description={$i18n.t('settings.admin.interface.retainedMessages.description')}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -352,10 +342,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Context Compaction Prompt')}
|
||||
description={$i18n.t(
|
||||
'Controls how older messages are rewritten into a running summary.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.interface.contextCompactionPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.contextCompactionPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -365,7 +353,7 @@
|
|||
)}
|
||||
/>
|
||||
<div class="mt-1 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Available variables')}:
|
||||
{$i18n.t('settings.admin.interface.availableVariables.label')}:
|
||||
<code>{'{{PREVIOUS_SUMMARY}}'}</code>,
|
||||
<code>{'{{COMPACTED_MESSAGES}}'}</code>,
|
||||
<code>{'{{RECENT_MESSAGES}}'}</code>,
|
||||
|
|
@ -376,10 +364,10 @@
|
|||
{/if}
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Generation')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.interface.sections.generation.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Title Generation')}
|
||||
description={$i18n.t('Allow automatic names for new chats.')}
|
||||
label={$i18n.t('settings.admin.interface.titleGeneration.label')}
|
||||
description={$i18n.t('settings.admin.interface.titleGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={taskConfig.ENABLE_TITLE_GENERATION} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -387,8 +375,8 @@
|
|||
|
||||
{#if taskConfig.ENABLE_TITLE_GENERATION}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Title Generation Prompt')}
|
||||
description={$i18n.t('Shapes the short label generated for each chat.')}
|
||||
label={$i18n.t('settings.admin.interface.titleGenerationPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.titleGenerationPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -401,8 +389,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Voice Mode Prompt')}
|
||||
description={$i18n.t('Apply voice-specific instructions while voice mode is active.')}
|
||||
label={$i18n.t('settings.admin.interface.voiceModePrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.voiceModePrompt.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={taskConfig.ENABLE_VOICE_MODE_PROMPT} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -410,8 +398,8 @@
|
|||
|
||||
{#if taskConfig.ENABLE_VOICE_MODE_PROMPT}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Prompt Template')}
|
||||
description={$i18n.t('Defines the behavior used for spoken conversations.')}
|
||||
label={$i18n.t('settings.admin.interface.promptTemplate.label')}
|
||||
description={$i18n.t('settings.admin.interface.promptTemplate.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -424,8 +412,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Follow Up Generation')}
|
||||
description={$i18n.t('Show suggested next questions after assistant responses.')}
|
||||
label={$i18n.t('settings.admin.interface.followUpGeneration.label')}
|
||||
description={$i18n.t('settings.admin.interface.followUpGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={taskConfig.ENABLE_FOLLOW_UP_GENERATION} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -433,8 +421,8 @@
|
|||
|
||||
{#if taskConfig.ENABLE_FOLLOW_UP_GENERATION}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Follow Up Generation Prompt')}
|
||||
description={$i18n.t('Guides the suggestions shown after an assistant response.')}
|
||||
label={$i18n.t('settings.admin.interface.followUpGenerationPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.followUpGenerationPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -447,8 +435,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Tags Generation')}
|
||||
description={$i18n.t('Create chat tags from conversation content.')}
|
||||
label={$i18n.t('settings.admin.interface.tagsGeneration.label')}
|
||||
description={$i18n.t('settings.admin.interface.tagsGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={taskConfig.ENABLE_TAGS_GENERATION} ariaLabelledbyId={labelId} />
|
||||
|
|
@ -456,8 +444,8 @@
|
|||
|
||||
{#if taskConfig.ENABLE_TAGS_GENERATION}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Tags Generation Prompt')}
|
||||
description={$i18n.t('Controls how chat tags are inferred.')}
|
||||
label={$i18n.t('settings.admin.interface.tagsGenerationPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.tagsGenerationPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -470,8 +458,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Retrieval Query Generation')}
|
||||
description={$i18n.t('Rewrite user requests for knowledge retrieval.')}
|
||||
label={$i18n.t('settings.admin.interface.retrievalQueryGeneration.label')}
|
||||
description={$i18n.t('settings.admin.interface.retrievalQueryGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -481,8 +469,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Web Search Query Generation')}
|
||||
description={$i18n.t('Rewrite user requests into web-search queries.')}
|
||||
label={$i18n.t('settings.admin.interface.webSearchQueryGeneration.label')}
|
||||
description={$i18n.t('settings.admin.interface.webSearchQueryGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -492,8 +480,8 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Query Generation Prompt')}
|
||||
description={$i18n.t('Shared prompt for retrieval and web-search query rewriting.')}
|
||||
label={$i18n.t('settings.admin.interface.queryGenerationPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.queryGenerationPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -503,8 +491,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Autocomplete Generation')}
|
||||
description={$i18n.t('Suggest completions while users type chat messages.')}
|
||||
label={$i18n.t('settings.admin.interface.autocompleteGeneration.label')}
|
||||
description={$i18n.t('settings.admin.interface.autocompleteGeneration.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -515,8 +503,10 @@
|
|||
|
||||
{#if taskConfig.ENABLE_AUTOCOMPLETE_GENERATION}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Autocomplete Generation Input Max Length')}
|
||||
description={$i18n.t('Limit how much draft text is sent for suggestion generation.')}
|
||||
label={$i18n.t('settings.admin.interface.autocompleteGenerationInputMaxLength.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.interface.autocompleteGenerationInputMaxLength.description'
|
||||
)}
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
|
|
@ -529,8 +519,10 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Autocomplete Generation Prompt')}
|
||||
description={$i18n.t('Guides inline completions while users type a message.')}
|
||||
label={$i18n.t('settings.admin.interface.autocompleteGenerationPrompt.label')}
|
||||
description={$i18n.t(
|
||||
'settings.admin.interface.autocompleteGenerationPrompt.description'
|
||||
)}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -543,8 +535,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Image Prompt Generation Prompt')}
|
||||
description={$i18n.t('Rewrites user intent into an image-generation prompt.')}
|
||||
label={$i18n.t('settings.admin.interface.imagePromptGenerationPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.imagePromptGenerationPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -554,8 +546,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Tools Function Calling Prompt')}
|
||||
description={$i18n.t('Guides how the assistant formats tool and function calls.')}
|
||||
label={$i18n.t('settings.admin.interface.toolsFunctionCallingPrompt.label')}
|
||||
description={$i18n.t('settings.admin.interface.toolsFunctionCallingPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
|
|||
|
|
@ -726,7 +726,7 @@
|
|||
<div class="flex h-full min-h-0 flex-col text-sm">
|
||||
<div class="mb-2 flex items-center justify-between">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{$i18n.t('Models')}
|
||||
{$i18n.t('settings.admin.models.title')}
|
||||
<span class="ml-2 font-normal text-gray-500 dark:text-gray-500">
|
||||
{filteredModels.length}
|
||||
</span>
|
||||
|
|
@ -855,7 +855,9 @@
|
|||
}}
|
||||
>
|
||||
<DocumentArrowUp className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Import')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.importModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -866,7 +868,9 @@
|
|||
}}
|
||||
>
|
||||
<Download className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Export')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.exportModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
|
|
@ -878,7 +882,9 @@
|
|||
}}
|
||||
>
|
||||
<Wrench className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Manage')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.manageModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -889,7 +895,9 @@
|
|||
}}
|
||||
>
|
||||
<GarbageBin className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Reset')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.resetModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<hr class="mx-1 my-0.5 border-gray-100 dark:border-gray-800" />
|
||||
|
|
@ -902,7 +910,9 @@
|
|||
}}
|
||||
>
|
||||
<CheckCircle className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Enable All')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.enableAllModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -913,7 +923,9 @@
|
|||
}}
|
||||
>
|
||||
<Minus className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Disable All')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.disableAllModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<hr class="mx-1 my-0.5 border-gray-100 dark:border-gray-800" />
|
||||
|
|
@ -926,7 +938,9 @@
|
|||
}}
|
||||
>
|
||||
<Eye className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Show All')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.showAllModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -937,7 +951,9 @@
|
|||
}}
|
||||
>
|
||||
<EyeSlash className="size-3.5" />
|
||||
<div class="flex items-center">{$i18n.t('Hide All')}</div>
|
||||
<div class="flex items-center">
|
||||
{$i18n.t('settings.admin.models.hideAllModels.label')}
|
||||
</div>
|
||||
</button>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@
|
|||
expanded = !expanded;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Model Defaults')}
|
||||
{$i18n.t('settings.admin.models.defaults.modelDefaults.label')}
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -178,7 +178,7 @@
|
|||
}}
|
||||
>
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Model Capabilities')}
|
||||
{$i18n.t('settings.admin.models.defaults.modelCapabilities.label')}
|
||||
</span>
|
||||
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{showCapabilities ? $i18n.t('Close') : $i18n.t('Configure')}
|
||||
|
|
@ -217,7 +217,7 @@
|
|||
}}
|
||||
>
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Model Parameters')}
|
||||
{$i18n.t('settings.admin.models.defaults.modelParameters.label')}
|
||||
</span>
|
||||
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{showParameters ? $i18n.t('Close') : $i18n.t('Configure')}
|
||||
|
|
@ -245,7 +245,7 @@
|
|||
}}
|
||||
>
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Prompt Suggestions')}
|
||||
{$i18n.t('settings.admin.models.defaults.promptSuggestions.label')}
|
||||
</span>
|
||||
<span class="text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{showPromptSuggestions ? $i18n.t('Close') : $i18n.t('Configure')}
|
||||
|
|
|
|||
|
|
@ -254,7 +254,9 @@
|
|||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Pipelines')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.pipelines.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if PIPELINES_LIST !== null}
|
||||
|
|
@ -267,10 +269,13 @@
|
|||
hidden
|
||||
/>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Source')} first>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.pipelines.sections.source.title')}
|
||||
first
|
||||
>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Pipeline URL')}
|
||||
description={$i18n.t('Select the Pipelines server to manage.')}
|
||||
label={$i18n.t('settings.admin.pipelines.pipelineUrl.label')}
|
||||
description={$i18n.t('settings.admin.pipelines.pipelineUrl.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={selectedPipelinesUrlIdx}
|
||||
|
|
@ -294,8 +299,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Upload Pipeline')}
|
||||
description={$i18n.t('Upload a local Python pipeline file to the selected server.')}
|
||||
label={$i18n.t('settings.admin.pipelines.uploadPipeline.label')}
|
||||
description={$i18n.t('settings.admin.pipelines.uploadPipeline.description')}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
|
|
@ -308,7 +313,7 @@
|
|||
{#if pipelineFiles}
|
||||
{$i18n.t('{{COUNT}} pipeline(s) selected', { COUNT: pipelineFiles.length })}
|
||||
{:else}
|
||||
{$i18n.t('Select a .py file')}
|
||||
{$i18n.t('settings.admin.pipelines.selectAPyFile.label')}
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
|
|
@ -326,7 +331,7 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('GitHub URL')}
|
||||
label={$i18n.t('settings.admin.pipelines.githubUrl.label')}
|
||||
description={`${$i18n.t('Pipelines are a plugin system with arbitrary code execution.')} ${$i18n.t("Don't fetch random pipelines from sources you don't trust.")}`}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
|
|
@ -343,7 +348,9 @@
|
|||
disabled={downloading}
|
||||
type="button"
|
||||
>
|
||||
{downloading ? $i18n.t('Installing') : $i18n.t('Install')}
|
||||
{downloading
|
||||
? $i18n.t('settings.admin.pipelines.installing.label')
|
||||
: $i18n.t('settings.admin.pipelines.install.label')}
|
||||
</button>
|
||||
</div>
|
||||
</AdminSettingField>
|
||||
|
|
@ -351,10 +358,12 @@
|
|||
|
||||
{#if pipelines !== null}
|
||||
{#if pipelines.length > 0}
|
||||
<AdminSettingSection title={$i18n.t('Pipelines')}>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.pipelines.sections.pipelines.title')}
|
||||
>
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Pipeline')}
|
||||
description={$i18n.t('Select an installed pipeline to configure or remove.')}
|
||||
label={$i18n.t('settings.admin.pipelines.pipeline.label')}
|
||||
description={$i18n.t('settings.admin.pipelines.pipeline.description')}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<SettingsSelect
|
||||
|
|
@ -386,7 +395,7 @@
|
|||
</AdminSettingField>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Valves')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.pipelines.sections.valves.title')}>
|
||||
{#if pipelines[selectedPipelineIdx ?? 0]?.valves}
|
||||
{#if valves && valves_spec}
|
||||
{#each Object.keys(valves_spec.properties) as property}
|
||||
|
|
@ -450,7 +459,9 @@
|
|||
{/if}
|
||||
</AdminSettingSection>
|
||||
{:else}
|
||||
<AdminSettingSection title={$i18n.t('Pipelines')}>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.pipelines.sections.pipelines.title')}
|
||||
>
|
||||
<div class={mutedMessageClass}>{$i18n.t('Pipelines Not Detected')}</div>
|
||||
</AdminSettingSection>
|
||||
{/if}
|
||||
|
|
@ -460,7 +471,10 @@
|
|||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<AdminSettingSection title={$i18n.t('Source')} first>
|
||||
<AdminSettingSection
|
||||
title={$i18n.t('settings.admin.pipelines.sections.source.title')}
|
||||
first
|
||||
>
|
||||
<div class={mutedMessageClass}>{$i18n.t('Pipelines Not Detected')}</div>
|
||||
</AdminSettingSection>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,9 @@
|
|||
</script>
|
||||
|
||||
<form class="flex h-full flex-col justify-between text-sm" on:submit|preventDefault={save}>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Sub-agents')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.subagents.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if loading}
|
||||
|
|
@ -66,7 +68,7 @@
|
|||
<div class="flex flex-col gap-2.5">
|
||||
<label class="flex cursor-pointer items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Enable sub-agents')}
|
||||
{$i18n.t('settings.admin.subagents.enableSubAgents.label')}
|
||||
</span>
|
||||
<Switch bind:state={enabled} />
|
||||
</label>
|
||||
|
|
@ -79,7 +81,7 @@
|
|||
{#if enabled}
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 dark:text-gray-400" for="sa-concurrent">
|
||||
{$i18n.t('Max concurrent')}
|
||||
{$i18n.t('settings.admin.subagents.maxConcurrent.label')}
|
||||
</label>
|
||||
<div class="mt-1 flex items-center gap-1.5">
|
||||
<input
|
||||
|
|
@ -98,21 +100,19 @@
|
|||
<div>
|
||||
<label class="flex cursor-pointer items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Enable background sub-agents')}
|
||||
{$i18n.t('settings.admin.subagents.enableBackgroundSubAgents.label')}
|
||||
</span>
|
||||
<Switch bind:state={backgroundEnabled} />
|
||||
</label>
|
||||
<p class="mt-1 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t(
|
||||
'Allow delegated sub-agents to keep running while the parent chat continues.'
|
||||
)}
|
||||
{$i18n.t('settings.admin.subagents.enableBackgroundSubAgents.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if backgroundEnabled}
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 dark:text-gray-400" for="sa-async">
|
||||
{$i18n.t('Max background')}
|
||||
{$i18n.t('settings.admin.subagents.maxBackground.label')}
|
||||
</label>
|
||||
<div class="mt-1 flex items-center gap-1.5">
|
||||
<input
|
||||
|
|
@ -131,7 +131,7 @@
|
|||
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 dark:text-gray-400" for="sa-iterations">
|
||||
{$i18n.t('Max iterations')}
|
||||
{$i18n.t('settings.admin.subagents.maxIterations.label')}
|
||||
</label>
|
||||
<div class="mt-1 flex items-center gap-1.5">
|
||||
<input
|
||||
|
|
@ -150,7 +150,7 @@
|
|||
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 dark:text-gray-400" for="sa-output">
|
||||
{$i18n.t('Max output')}
|
||||
{$i18n.t('settings.admin.subagents.maxOutput.label')}
|
||||
</label>
|
||||
<div class="mt-1 flex items-center gap-1.5">
|
||||
<input
|
||||
|
|
@ -168,7 +168,7 @@
|
|||
|
||||
<div>
|
||||
<label class="text-xs text-gray-600 dark:text-gray-400" for="sa-prompt">
|
||||
{$i18n.t('System prompt')}
|
||||
{$i18n.t('settings.admin.subagents.systemPrompt.label')}
|
||||
</label>
|
||||
<textarea
|
||||
id="sa-prompt"
|
||||
|
|
@ -178,7 +178,7 @@
|
|||
class="mt-1 w-full resize-y rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 font-mono text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
|
||||
></textarea>
|
||||
<p class="mt-0.5 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Leave empty for the built-in default.')}
|
||||
{$i18n.t('settings.admin.subagents.systemPrompt.description')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -163,22 +163,24 @@
|
|||
saveHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Web Search')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.admin.web.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if webConfig}
|
||||
<AdminSettingSection first title={$i18n.t('Search')}>
|
||||
<AdminSettingSection first title={$i18n.t('settings.admin.web.sections.search.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Web Search')}
|
||||
description={$i18n.t('Allow users to search the web from chats.')}
|
||||
label={$i18n.t('settings.admin.web.webSearch.label')}
|
||||
description={$i18n.t('settings.admin.web.webSearch.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={webConfig.ENABLE_WEB_SEARCH} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Web Search Confirmation')}
|
||||
description={$i18n.t('Require users to confirm before using Web Search.')}
|
||||
label={$i18n.t('settings.admin.web.webSearchConfirmation.label')}
|
||||
description={$i18n.t('settings.admin.web.webSearchConfirmation.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -189,8 +191,8 @@
|
|||
|
||||
{#if webConfig.ENABLE_WEB_SEARCH_CONFIRMATION}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Web Search Confirmation Content')}
|
||||
description={$i18n.t('Message shown before web search runs.')}
|
||||
label={$i18n.t('settings.admin.web.webSearchConfirmationContent.label')}
|
||||
description={$i18n.t('settings.admin.web.webSearchConfirmationContent.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
|
|
@ -203,8 +205,8 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Web Search Engine')}
|
||||
description={$i18n.t('Choose the provider used for web search queries.')}
|
||||
label={$i18n.t('settings.admin.web.webSearchEngine.label')}
|
||||
description={$i18n.t('settings.admin.web.webSearchEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={webConfig.WEB_SEARCH_ENGINE}
|
||||
|
|
@ -229,7 +231,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Ollama Cloud API Key')}
|
||||
{$i18n.t('settings.admin.web.ollamaCloudApiKey.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -247,7 +249,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Perplexity Search API URL')}
|
||||
{$i18n.t('settings.admin.web.perplexitySearchApiUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -267,7 +269,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Perplexity API Key')}
|
||||
{$i18n.t('settings.admin.web.perplexityApiKey.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -285,7 +287,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-left text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Searxng Query URL')}
|
||||
{$i18n.t('settings.admin.web.searxngQueryUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -303,7 +305,7 @@
|
|||
</div>
|
||||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div class=" self-left text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Searxng search language (all, en, es, de, fr, etc.)')}
|
||||
{$i18n.t('settings.admin.web.searxngSearchLanguageAllEnEsDeFrEtc.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -324,7 +326,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Yacy Instance URL')}
|
||||
{$i18n.t('settings.admin.web.yacyInstanceUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -344,7 +346,7 @@
|
|||
<div class="flex gap-2">
|
||||
<div class="w-full">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Yacy Username')}
|
||||
{$i18n.t('settings.admin.web.yacyUsername.label')}
|
||||
</div>
|
||||
|
||||
<input
|
||||
|
|
@ -357,7 +359,7 @@
|
|||
|
||||
<div class="w-full">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Yacy Password')}
|
||||
{$i18n.t('settings.admin.web.yacyPassword.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -372,7 +374,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Google PSE API Key')}
|
||||
{$i18n.t('settings.admin.web.googlePseApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -383,7 +385,7 @@
|
|||
</div>
|
||||
<div class="mt-1.5">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Google PSE Engine Id')}
|
||||
{$i18n.t('settings.admin.web.googlePseEngineId.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -403,7 +405,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Brave Search API Key')}
|
||||
{$i18n.t('settings.admin.web.braveSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -417,7 +419,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Brave Search API Key')}
|
||||
{$i18n.t('settings.admin.web.braveSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -428,7 +430,7 @@
|
|||
</div>
|
||||
<div class="mt-1.5">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Context Tokens')}
|
||||
{$i18n.t('settings.admin.web.contextTokens.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -451,7 +453,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Kagi Search API Key')}
|
||||
{$i18n.t('settings.admin.web.kagiSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -465,7 +467,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Mojeek Search API Key')}
|
||||
{$i18n.t('settings.admin.web.mojeekSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -479,7 +481,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Bocha Search API Key')}
|
||||
{$i18n.t('settings.admin.web.bochaSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -493,7 +495,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Serpstack API Key')}
|
||||
{$i18n.t('settings.admin.web.serpstackApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -507,7 +509,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Serper API Key')}
|
||||
{$i18n.t('settings.admin.web.serperApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -521,7 +523,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('SERPHouse API Key')}
|
||||
{$i18n.t('settings.admin.web.serphouseApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -532,7 +534,7 @@
|
|||
</div>
|
||||
<div class="mt-1.5">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('SERPHouse Domain')}
|
||||
{$i18n.t('settings.admin.web.serphouseDomain.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -552,7 +554,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Serply API Key')}
|
||||
{$i18n.t('settings.admin.web.serplyApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -566,7 +568,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Tavily API Key')}
|
||||
{$i18n.t('settings.admin.web.tavilyApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -580,7 +582,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('SearchApi API Key')}
|
||||
{$i18n.t('settings.admin.web.searchapiApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -591,7 +593,7 @@
|
|||
</div>
|
||||
<div class="mt-1.5">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('SearchApi Engine')}
|
||||
{$i18n.t('settings.admin.web.searchapiEngine.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -611,7 +613,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('SerpApi API Key')}
|
||||
{$i18n.t('settings.admin.web.serpapiApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -622,7 +624,7 @@
|
|||
</div>
|
||||
<div class="mt-1.5">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('SerpApi Engine')}
|
||||
{$i18n.t('settings.admin.web.serpapiEngine.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -642,7 +644,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Jina API Base URL')}
|
||||
{$i18n.t('settings.admin.web.jinaApiBaseUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -660,7 +662,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Jina API Key')}
|
||||
{$i18n.t('settings.admin.web.jinaApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -674,7 +676,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Bing Search V7 Endpoint')}
|
||||
{$i18n.t('settings.admin.web.bingSearchV7Endpoint.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -692,7 +694,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Bing Search V7 Subscription Key')}
|
||||
{$i18n.t('settings.admin.web.bingSearchV7SubscriptionKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -706,7 +708,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Exa API Key')}
|
||||
{$i18n.t('settings.admin.web.exaApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -717,11 +719,9 @@
|
|||
</div>
|
||||
<AdminSettingField
|
||||
className="mt-1.5"
|
||||
label={$i18n.t('Max Content Length')}
|
||||
label={$i18n.t('settings.admin.web.maxContentLength.label')}
|
||||
forId="exa-max-content-length"
|
||||
description={$i18n.t(
|
||||
'Maximum characters per Exa search result. Leave empty for no limit.'
|
||||
)}
|
||||
description={$i18n.t('settings.admin.web.maxContentLength.description')}
|
||||
>
|
||||
<input
|
||||
id="exa-max-content-length"
|
||||
|
|
@ -738,7 +738,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Perplexity API Key')}
|
||||
{$i18n.t('settings.admin.web.perplexityApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -752,7 +752,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class="self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Perplexity Model')}
|
||||
{$i18n.t('settings.admin.web.perplexityModel.label')}
|
||||
</div>
|
||||
<input
|
||||
list="perplexity-model-list"
|
||||
|
|
@ -773,7 +773,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Perplexity Search Context Usage')}
|
||||
{$i18n.t('settings.admin.web.perplexitySearchContextUsage.label')}
|
||||
</div>
|
||||
<SettingsSelect
|
||||
bind:value={webConfig.PERPLEXITY_SEARCH_CONTEXT_USAGE}
|
||||
|
|
@ -789,7 +789,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Microsoft Web IQ API Base URL')}
|
||||
{$i18n.t('settings.admin.web.microsoftWebIqApiBaseUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -807,7 +807,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Microsoft Web IQ API Key')}
|
||||
{$i18n.t('settings.admin.web.microsoftWebIqApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -819,7 +819,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Language')}
|
||||
{$i18n.t('settings.admin.web.language.label')}
|
||||
</div>
|
||||
|
||||
<input
|
||||
|
|
@ -835,7 +835,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Sougou Search API sID')}
|
||||
{$i18n.t('settings.admin.web.sougouSearchApiSid.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -848,7 +848,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Sougou Search API SK')}
|
||||
{$i18n.t('settings.admin.web.sougouSearchApiSk.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -862,7 +862,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Firecrawl API Base URL')}
|
||||
{$i18n.t('settings.admin.web.firecrawlApiBaseUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -880,7 +880,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Firecrawl API Key')}
|
||||
{$i18n.t('settings.admin.web.firecrawlApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -892,7 +892,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Firecrawl Timeout (s)')}
|
||||
{$i18n.t('settings.admin.web.firecrawlTimeoutS.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -912,7 +912,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('External Web Search URL')}
|
||||
{$i18n.t('settings.admin.web.externalWebSearchUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -930,7 +930,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('External Web Search API Key')}
|
||||
{$i18n.t('settings.admin.web.externalWebSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -944,7 +944,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Yandex Web Search URL')}
|
||||
{$i18n.t('settings.admin.web.yandexWebSearchUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -962,7 +962,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Yandex Web Search API Key')}
|
||||
{$i18n.t('settings.admin.web.yandexWebSearchApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -974,7 +974,7 @@
|
|||
|
||||
<div class="mb-2.5">
|
||||
<div class=" mb-1 text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Yandex Web Search config')}
|
||||
{$i18n.t('settings.admin.web.yandexWebSearchConfig.label')}
|
||||
</div>
|
||||
|
||||
<Tooltip
|
||||
|
|
@ -997,7 +997,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('You.com API Key')}
|
||||
{$i18n.t('settings.admin.web.youComApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -1011,7 +1011,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Linkup API Key')}
|
||||
{$i18n.t('settings.admin.web.linkupApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -1023,7 +1023,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Parameters')}
|
||||
{$i18n.t('settings.admin.web.parameters.label')}
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
|
|
@ -1037,7 +1037,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('OpenSERP URL')}
|
||||
{$i18n.t('settings.admin.web.openserpUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
|
|
@ -1058,7 +1058,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('DDGS Backend')}
|
||||
{$i18n.t('settings.admin.web.ddgsBackend.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1084,18 +1084,18 @@
|
|||
|
||||
{#if webConfig.ENABLE_WEB_SEARCH}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Search Limits')}
|
||||
description={$i18n.t('Control result volume and parallel search requests.')}
|
||||
label={$i18n.t('settings.admin.web.searchLimits.label')}
|
||||
description={$i18n.t('settings.admin.web.searchLimits.description')}
|
||||
>
|
||||
<div class="flex gap-2">
|
||||
<div class="w-full">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Search Result Count')}
|
||||
{$i18n.t('settings.admin.web.searchResultCount.label')}
|
||||
</div>
|
||||
|
||||
<input
|
||||
class="w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
|
||||
placeholder={$i18n.t('Search Result Count')}
|
||||
placeholder={$i18n.t('settings.admin.web.searchResultCount.label')}
|
||||
bind:value={webConfig.WEB_SEARCH_RESULT_COUNT}
|
||||
required
|
||||
/>
|
||||
|
|
@ -1104,18 +1104,16 @@
|
|||
<div class="w-full">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Limit concurrent search queries. 0 = unlimited (default). Set to 1 for sequential execution (recommended for APIs with strict rate limits like Brave free tier).'
|
||||
)}
|
||||
content={$i18n.t('settings.admin.web.searchResultCount.description')}
|
||||
placement="top-start"
|
||||
>
|
||||
{$i18n.t('Concurrent Requests')}
|
||||
{$i18n.t('settings.admin.web.concurrentRequests.label')}
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<input
|
||||
class="w-full rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 py-1.5 text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
|
||||
placeholder={$i18n.t('Concurrent Requests')}
|
||||
placeholder={$i18n.t('settings.admin.web.concurrentRequests.label')}
|
||||
bind:value={webConfig.WEB_SEARCH_CONCURRENT_REQUESTS}
|
||||
type="number"
|
||||
min="0"
|
||||
|
|
@ -1125,10 +1123,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Fetch URL Content Length Limit')}
|
||||
description={$i18n.t(
|
||||
'Maximum characters to return from fetched URLs. Leave empty for no limit.'
|
||||
)}
|
||||
label={$i18n.t('settings.admin.web.fetchUrlContentLengthLimit.label')}
|
||||
description={$i18n.t('settings.admin.web.fetchUrlContentLengthLimit.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1140,8 +1136,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Domain Filter List')}
|
||||
description={$i18n.t('Restrict or exclude domains using a comma-separated list.')}
|
||||
label={$i18n.t('settings.admin.web.domainFilterList.label')}
|
||||
description={$i18n.t('settings.admin.web.domainFilterList.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1154,9 +1150,9 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Bypass Embedding and Retrieval')}
|
||||
label={$i18n.t('settings.admin.web.bypassEmbeddingAndRetrieval.label')}
|
||||
description={webConfig.BYPASS_WEB_SEARCH_EMBEDDING_AND_RETRIEVAL
|
||||
? $i18n.t('Inject the entire content as context for comprehensive processing.')
|
||||
? $i18n.t('settings.admin.web.bypassEmbeddingAndRetrieval.description')
|
||||
: $i18n.t('Use segmented retrieval for focused and relevant content extraction.')}
|
||||
let:labelId
|
||||
>
|
||||
|
|
@ -1167,17 +1163,17 @@
|
|||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Bypass Web Loader')}
|
||||
description={$i18n.t('Use search results without fetching page contents.')}
|
||||
label={$i18n.t('settings.admin.web.bypassWebLoader.label')}
|
||||
description={$i18n.t('settings.admin.web.bypassWebLoader.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch bind:state={webConfig.BYPASS_WEB_SEARCH_WEB_LOADER} ariaLabelledbyId={labelId} />
|
||||
</AdminSettingRow>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Trust Proxy Environment')}
|
||||
label={$i18n.t('settings.admin.web.trustProxyEnvironment.label')}
|
||||
description={webConfig.WEB_SEARCH_TRUST_ENV
|
||||
? $i18n.t('Use proxy environment variables to fetch page contents.')
|
||||
? $i18n.t('settings.admin.web.trustProxyEnvironment.description')
|
||||
: $i18n.t('Fetch page contents without proxy environment variables.')}
|
||||
let:labelId
|
||||
>
|
||||
|
|
@ -1185,10 +1181,10 @@
|
|||
</AdminSettingRow>
|
||||
</AdminSettingSection>
|
||||
|
||||
<AdminSettingSection title={$i18n.t('Loader')}>
|
||||
<AdminSettingSection title={$i18n.t('settings.admin.web.sections.loader.title')}>
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Web Loader Engine')}
|
||||
description={$i18n.t('Choose how web result pages are fetched and read.')}
|
||||
label={$i18n.t('settings.admin.web.webLoaderEngine.label')}
|
||||
description={$i18n.t('settings.admin.web.webLoaderEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={webConfig.WEB_LOADER_ENGINE}
|
||||
|
|
@ -1205,19 +1201,19 @@
|
|||
|
||||
{#if webConfig.WEB_LOADER_ENGINE === '' || webConfig.WEB_LOADER_ENGINE === 'safe_web'}
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Timeout')}
|
||||
description={$i18n.t('Maximum time to wait while loading web content.')}
|
||||
label={$i18n.t('settings.admin.web.timeout.label')}
|
||||
description={$i18n.t('settings.admin.web.timeout.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('Timeout')}
|
||||
placeholder={$i18n.t('settings.admin.web.timeout.label')}
|
||||
bind:value={webConfig.WEB_LOADER_TIMEOUT}
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingRow
|
||||
label={$i18n.t('Verify SSL Certificate')}
|
||||
description={$i18n.t('Validate SSL certificates when fetching web content.')}
|
||||
label={$i18n.t('settings.admin.web.verifySslCertificate.label')}
|
||||
description={$i18n.t('settings.admin.web.verifySslCertificate.description')}
|
||||
let:labelId
|
||||
>
|
||||
<Switch
|
||||
|
|
@ -1229,7 +1225,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Playwright WebSocket URL')}
|
||||
{$i18n.t('settings.admin.web.playwrightWebsocketUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1247,7 +1243,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Playwright Timeout (ms)')}
|
||||
{$i18n.t('settings.admin.web.playwrightTimeoutMs.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1266,7 +1262,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Firecrawl API Base URL')}
|
||||
{$i18n.t('settings.admin.web.firecrawlApiBaseUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1284,7 +1280,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Firecrawl API Key')}
|
||||
{$i18n.t('settings.admin.web.firecrawlApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -1298,7 +1294,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Tavily Extract Depth')}
|
||||
{$i18n.t('settings.admin.web.tavilyExtractDepth.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1317,7 +1313,7 @@
|
|||
{#if webConfig.WEB_SEARCH_ENGINE !== 'tavily'}
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Tavily API Key')}
|
||||
{$i18n.t('settings.admin.web.tavilyApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -1333,7 +1329,7 @@
|
|||
{#if webConfig.WEB_SEARCH_ENGINE !== 'microsoft_web_iq'}
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Microsoft Web IQ API Base URL')}
|
||||
{$i18n.t('settings.admin.web.microsoftWebIqApiBaseUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1351,7 +1347,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Microsoft Web IQ API Key')}
|
||||
{$i18n.t('settings.admin.web.microsoftWebIqApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -1363,7 +1359,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('Language')}
|
||||
{$i18n.t('settings.admin.web.language.label')}
|
||||
</div>
|
||||
|
||||
<input
|
||||
|
|
@ -1380,7 +1376,7 @@
|
|||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('External Web Loader URL')}
|
||||
{$i18n.t('settings.admin.web.externalWebLoaderUrl.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex w-full">
|
||||
|
|
@ -1398,7 +1394,7 @@
|
|||
|
||||
<div class="mt-2">
|
||||
<div class=" self-center text-xs text-gray-600 dark:text-gray-400 mb-1">
|
||||
{$i18n.t('External Web Loader API Key')}
|
||||
{$i18n.t('settings.admin.web.externalWebLoaderApiKey.label')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
|
|
@ -1411,20 +1407,20 @@
|
|||
{/if}
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Concurrent Requests')}
|
||||
description={$i18n.t('Limit parallel web loader requests.')}
|
||||
label={$i18n.t('settings.admin.web.concurrentRequests.label')}
|
||||
description={$i18n.t('settings.admin.web.concurrentRequests.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
placeholder={$i18n.t('Concurrent Requests')}
|
||||
placeholder={$i18n.t('settings.admin.web.concurrentRequests.label')}
|
||||
bind:value={webConfig.WEB_LOADER_CONCURRENT_REQUESTS}
|
||||
required
|
||||
/>
|
||||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Youtube Language')}
|
||||
description={$i18n.t('Preferred transcript language codes, separated by commas.')}
|
||||
label={$i18n.t('settings.admin.web.youtubeLanguage.label')}
|
||||
description={$i18n.t('settings.admin.web.youtubeLanguage.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
@ -1436,8 +1432,8 @@
|
|||
</AdminSettingField>
|
||||
|
||||
<AdminSettingField
|
||||
label={$i18n.t('Youtube Proxy URL')}
|
||||
description={$i18n.t('Proxy URL used for Youtube loader requests.')}
|
||||
label={$i18n.t('settings.admin.web.youtubeProxyUrl.label')}
|
||||
description={$i18n.t('settings.admin.web.youtubeProxyUrl.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
|
|
|
|||
|
|
@ -49,16 +49,19 @@
|
|||
</script>
|
||||
|
||||
<div id="tab-about" class="flex flex-col h-full justify-between text-sm">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('About')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.personal.about.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<!-- LICENSE covers this Open WebUI About identifier.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
https://docs.openwebui.com/license. -->
|
||||
<UserSettingSection title={`${$WEBUI_NAME} ${$i18n.t('Version')}`} first>
|
||||
<UserSettingRow
|
||||
description={$i18n.t('View the installed version and check release updates.')}
|
||||
>
|
||||
<UserSettingSection
|
||||
title={`${$WEBUI_NAME} ${$i18n.t('settings.personal.about.sections.version.title')}`}
|
||||
first
|
||||
>
|
||||
<UserSettingRow description={$i18n.t('settings.personal.about.seeWhatSNew.description')}>
|
||||
<div slot="label" class="flex flex-col text-xs text-gray-600 dark:text-gray-400">
|
||||
<div class="flex gap-1">
|
||||
<Tooltip content={WEBUI_BUILD_HASH}>
|
||||
|
|
@ -89,7 +92,7 @@
|
|||
showChangelog.set(true);
|
||||
}}
|
||||
>
|
||||
<div>{$i18n.t("See what's new")}</div>
|
||||
<div>{$i18n.t('settings.personal.about.seeWhatSNew.label')}</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -100,21 +103,21 @@
|
|||
checkForVersionUpdates();
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Check for updates')}
|
||||
{$i18n.t('settings.personal.about.checkForUpdates.label')}
|
||||
</button>
|
||||
{/if}
|
||||
</UserSettingRow>
|
||||
</UserSettingSection>
|
||||
|
||||
{#if ollamaVersion}
|
||||
<UserSettingSection title={$i18n.t('Ollama Version')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.about.sections.ollamaVersion.title')}>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{ollamaVersion ?? 'N/A'}
|
||||
</div>
|
||||
</UserSettingSection>
|
||||
{/if}
|
||||
|
||||
<UserSettingSection title={$i18n.t('Community')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.about.sections.community.title')}>
|
||||
{#if $config?.license_metadata}
|
||||
<!-- LICENSE covers this Open WebUI license attribution.
|
||||
Do not alter, remove, obscure, or replace it except as LICENSE permits:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { canUseApiKeys } from '$lib/utils/settings-access';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
|
||||
|
|
@ -237,9 +238,11 @@
|
|||
|
||||
<div id="tab-account" class="flex h-full flex-col text-sm">
|
||||
<div class="flex-1 min-h-0 w-full overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<h2 class="mb-4 text-sm font-medium text-gray-900 dark:text-white">{$i18n.t('Account')}</h2>
|
||||
<h2 class="mb-4 text-sm font-medium text-gray-900 dark:text-white">
|
||||
{$i18n.t('settings.personal.account.title')}
|
||||
</h2>
|
||||
|
||||
<UserSettingSection title={$i18n.t('Profile')} first>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.account.sections.profile.title')} first>
|
||||
<UserProfileImage
|
||||
bind:profileImageUrl
|
||||
user={$user}
|
||||
|
|
@ -248,40 +251,40 @@
|
|||
/>
|
||||
|
||||
<UserSettingField
|
||||
label={$i18n.t('Name')}
|
||||
description={$i18n.t('Set the display name shown across your account.')}
|
||||
label={$i18n.t('settings.personal.account.name.label')}
|
||||
description={$i18n.t('settings.personal.account.name.description')}
|
||||
>
|
||||
<input
|
||||
class={inputClass}
|
||||
type="text"
|
||||
bind:value={name}
|
||||
aria-label={$i18n.t('Name')}
|
||||
aria-label={$i18n.t('settings.personal.account.name.label')}
|
||||
required
|
||||
placeholder={$i18n.t('Enter your name')}
|
||||
/>
|
||||
</UserSettingField>
|
||||
|
||||
<UserSettingField
|
||||
label={$i18n.t('Bio')}
|
||||
description={$i18n.t('Add optional profile context visible where profiles are shown.')}
|
||||
label={$i18n.t('settings.personal.account.bio.label')}
|
||||
description={$i18n.t('settings.personal.account.bio.description')}
|
||||
>
|
||||
<Textarea
|
||||
className={textareaClass}
|
||||
minSize={60}
|
||||
bind:value={bio}
|
||||
ariaLabel={$i18n.t('Bio')}
|
||||
ariaLabel={$i18n.t('settings.personal.account.bio.label')}
|
||||
placeholder={$i18n.t('Share your background and interests')}
|
||||
/>
|
||||
</UserSettingField>
|
||||
|
||||
<UserSettingField
|
||||
label={$i18n.t('Gender')}
|
||||
description={$i18n.t('Choose the gender value stored on your profile.')}
|
||||
label={$i18n.t('settings.personal.account.gender.label')}
|
||||
description={$i18n.t('settings.personal.account.gender.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={_gender}
|
||||
className="w-full"
|
||||
ariaLabel={$i18n.t('Gender')}
|
||||
ariaLabel={$i18n.t('settings.personal.account.gender.label')}
|
||||
on:change={() => {
|
||||
console.log(_gender);
|
||||
|
||||
|
|
@ -312,13 +315,13 @@
|
|||
</UserSettingField>
|
||||
|
||||
<UserSettingField
|
||||
label={$i18n.t('Birth Date')}
|
||||
description={$i18n.t('Set the birth date saved with your profile.')}
|
||||
label={$i18n.t('settings.personal.account.birthDate.label')}
|
||||
description={$i18n.t('settings.personal.account.birthDate.description')}
|
||||
>
|
||||
<input
|
||||
class="{inputClass} dark:scheme-dark"
|
||||
type="date"
|
||||
aria-label={$i18n.t('Birth Date')}
|
||||
aria-label={$i18n.t('settings.personal.account.birthDate.label')}
|
||||
bind:value={dateOfBirth}
|
||||
required
|
||||
/>
|
||||
|
|
@ -367,15 +370,15 @@
|
|||
</section>
|
||||
|
||||
{#if $config?.features.enable_login_form && $config?.features.enable_password_change_form}
|
||||
<UserSettingSection title={$i18n.t('Password')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.account.sections.password.title')}>
|
||||
<UpdatePassword />
|
||||
</UserSettingSection>
|
||||
{/if}
|
||||
|
||||
{#if ($config?.features?.enable_api_keys ?? true) && ($user?.role === 'admin' || ($user?.permissions?.features?.api_keys ?? false))}
|
||||
<UserSettingSection title={$i18n.t('API keys')}>
|
||||
<UserSettingRow description={$i18n.t('Show or hide sensitive account secrets.')}>
|
||||
<span slot="label">{$i18n.t('Secrets')}</span>
|
||||
{#if canUseApiKeys({ user: $user, config: $config })}
|
||||
<UserSettingSection title={$i18n.t('settings.personal.account.sections.apiKeys.title')}>
|
||||
<UserSettingRow description={$i18n.t('settings.personal.account.secrets.description')}>
|
||||
<span slot="label">{$i18n.t('settings.personal.account.secrets.label')}</span>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
type="button"
|
||||
|
|
@ -389,8 +392,8 @@
|
|||
<div class="flex flex-col gap-2.5">
|
||||
{#if $user?.role === 'admin'}
|
||||
<UserSettingField
|
||||
label={$i18n.t('JWT Token')}
|
||||
description={$i18n.t('Copy the current session token for authenticated requests.')}
|
||||
label={$i18n.t('settings.personal.account.jwtToken.label')}
|
||||
description={$i18n.t('settings.personal.account.jwtToken.description')}
|
||||
>
|
||||
<div class="flex">
|
||||
<SensitiveInput variant="settings" value={localStorage.token} readOnly={true} />
|
||||
|
|
@ -443,10 +446,10 @@
|
|||
</UserSettingField>
|
||||
{/if}
|
||||
|
||||
{#if ($config?.features?.enable_api_keys ?? true) && ($user?.role === 'admin' || ($user?.permissions?.features?.api_keys ?? false))}
|
||||
{#if canUseApiKeys({ user: $user, config: $config })}
|
||||
<UserSettingField
|
||||
label={$i18n.t('API Key')}
|
||||
description={$i18n.t('Create, copy, or rotate your API key.')}
|
||||
label={$i18n.t('settings.personal.account.apiKey.label')}
|
||||
description={$i18n.t('settings.personal.account.apiKey.description')}
|
||||
>
|
||||
<div class="flex">
|
||||
{#if APIKey}
|
||||
|
|
@ -546,7 +549,7 @@
|
|||
>
|
||||
<Plus strokeWidth="2" className="size-3.5" />
|
||||
|
||||
{$i18n.t('Create new secret key')}</button
|
||||
{$i18n.t('settings.personal.account.createNewSecretKey.label')}</button
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@
|
|||
}}
|
||||
>
|
||||
<div class="flex items-center justify-between gap-2.5">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">{$i18n.t('Change Password')}</div>
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('settings.personal.account.changePassword.label')}
|
||||
</div>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
type="button"
|
||||
|
|
@ -65,14 +67,14 @@
|
|||
>
|
||||
</div>
|
||||
<p class="mt-0.5 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Update the password used for email and password sign-in.')}
|
||||
{$i18n.t('settings.personal.account.changePassword.description')}
|
||||
</p>
|
||||
|
||||
{#if show}
|
||||
<div class="py-2.5 space-y-2.5">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="mb-1 text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Current Password')}
|
||||
{$i18n.t('settings.personal.account.currentPassword.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
|
|
@ -89,7 +91,7 @@
|
|||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="mb-1 text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('New Password')}
|
||||
{$i18n.t('settings.personal.account.newPassword.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
|
|
@ -106,7 +108,7 @@
|
|||
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="mb-1 text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Confirm Password')}
|
||||
{$i18n.t('settings.personal.account.confirmPassword.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex-1">
|
||||
|
|
@ -124,7 +126,7 @@
|
|||
|
||||
<div class="flex justify-end">
|
||||
<button class={actionButtonClass}>
|
||||
{$i18n.t('Update password')}
|
||||
{$i18n.t('settings.personal.account.updatePassword.label')}
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -113,15 +113,13 @@
|
|||
>
|
||||
<div>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'When enabled, the model will respond to each chat message in real-time, generating a response as soon as the user sends a message. This mode is useful for live chat applications, but may impact performance on slower hardware.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.streamChatResponse.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Stream Chat Response')}
|
||||
{$i18n.t('settings.personal.general.parameters.streamChatResponse.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition"
|
||||
|
|
@ -150,15 +148,13 @@
|
|||
{#if admin}
|
||||
<div>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'The stream delta chunk size for the model. Increasing the chunk size will make the model respond with larger pieces of text at once.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.streamDeltaChunkSize.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Stream Delta Chunk Size')}
|
||||
{$i18n.t('settings.personal.general.parameters.streamDeltaChunkSize.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -180,7 +176,7 @@
|
|||
{#if (params?.stream_delta_chunk_size ?? null) !== null}
|
||||
{@render rangeParam(
|
||||
'stream_delta_chunk_size',
|
||||
$i18n.t('Stream Delta Chunk Size'),
|
||||
$i18n.t('settings.personal.general.parameters.streamDeltaChunkSize.label'),
|
||||
1,
|
||||
128,
|
||||
1,
|
||||
|
|
@ -192,15 +188,13 @@
|
|||
|
||||
<div>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Set a model-specific context compaction token threshold. When set, this overrides the global threshold up to the global cap.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.contextCompactionThreshold.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Context Compaction Threshold')}
|
||||
{$i18n.t('settings.personal.general.parameters.contextCompactionThreshold.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -225,7 +219,7 @@
|
|||
<input
|
||||
class="text-sm w-full bg-transparent outline-hidden outline-none"
|
||||
type="number"
|
||||
aria-label={$i18n.t('Context Compaction Threshold')}
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.contextCompactionThreshold.label')}
|
||||
placeholder={$i18n.t('Enter token threshold')}
|
||||
bind:value={params.compact_token_threshold}
|
||||
autocomplete="off"
|
||||
|
|
@ -240,15 +234,13 @@
|
|||
|
||||
<div>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
"Native mode (default) leverages the model's built-in tool-calling capabilities. Legacy mode works with a wider range of models by calling tools once before execution via prompt injection."
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.functionCalling.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Function Calling')}
|
||||
{$i18n.t('settings.personal.general.parameters.functionCalling.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition"
|
||||
|
|
@ -277,15 +269,13 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Enable, disable, or customize the reasoning tags used by the model. "Enabled" uses default tags, "Disabled" turns off reasoning tags, and "Custom" lets you specify your own start and end tags.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.reasoningTags.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Reasoning Tags')}
|
||||
{$i18n.t('settings.personal.general.parameters.reasoningTags.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -344,15 +334,13 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.seed.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Seed')}
|
||||
{$i18n.t('settings.personal.general.parameters.seed.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -377,7 +365,7 @@
|
|||
<input
|
||||
class="text-sm w-full bg-transparent outline-hidden outline-none"
|
||||
type="number"
|
||||
aria-label={$i18n.t('Seed')}
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.seed.label')}
|
||||
placeholder={$i18n.t('Enter Seed')}
|
||||
bind:value={params.seed}
|
||||
autocomplete="off"
|
||||
|
|
@ -390,15 +378,13 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Sets the stop sequences to use. When this pattern is encountered, the LLM will stop generating text and return. Multiple stop patterns may be set by specifying multiple separate stop parameters in a modelfile.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.stopSequence.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Stop Sequence')}
|
||||
{$i18n.t('settings.personal.general.parameters.stopSequence.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -423,7 +409,7 @@
|
|||
<input
|
||||
class="text-sm w-full bg-transparent outline-hidden outline-none"
|
||||
type="text"
|
||||
aria-label={$i18n.t('Stop Sequence')}
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.stopSequence.label')}
|
||||
placeholder={$i18n.t('Enter stop sequence')}
|
||||
bind:value={params.stop}
|
||||
autocomplete="off"
|
||||
|
|
@ -435,15 +421,13 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'The temperature of the model. Increasing the temperature will make the model answer more creatively.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.temperature.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Temperature')}
|
||||
{$i18n.t('settings.personal.general.parameters.temperature.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -462,21 +446,25 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.temperature ?? null) !== null}
|
||||
{@render rangeParam('temperature', $i18n.t('Temperature'), 0, 2, 0.05)}
|
||||
{@render rangeParam(
|
||||
'temperature',
|
||||
$i18n.t('settings.personal.general.parameters.temperature.label'),
|
||||
0,
|
||||
2,
|
||||
0.05
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Constrains effort on reasoning for reasoning models. Only applicable to reasoning models from specific providers that support reasoning effort.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.reasoningEffort.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{$i18n.t('Reasoning Effort')}
|
||||
{$i18n.t('settings.personal.general.parameters.reasoningEffort.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -500,7 +488,7 @@
|
|||
<input
|
||||
class="text-sm w-full bg-transparent outline-hidden outline-none"
|
||||
type="text"
|
||||
aria-label={$i18n.t('Reasoning Effort')}
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.reasoningEffort.label')}
|
||||
placeholder={$i18n.t('Enter reasoning effort')}
|
||||
bind:value={params.reasoning_effort}
|
||||
autocomplete="off"
|
||||
|
|
@ -520,7 +508,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'logit_bias'}
|
||||
{$i18n.t('settings.personal.general.parameters.logitBias.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -544,7 +532,7 @@
|
|||
<input
|
||||
class="text-sm w-full bg-transparent outline-hidden outline-none"
|
||||
type="text"
|
||||
aria-label="logit_bias"
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.logitBias.label')}
|
||||
placeholder={$i18n.t(
|
||||
'Enter comma-separated "token:bias_value" pairs (example: 5432:100, 413:-100)'
|
||||
)}
|
||||
|
|
@ -566,7 +554,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'max_tokens'}
|
||||
{$i18n.t('settings.personal.general.parameters.maxTokens.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -586,7 +574,15 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.max_tokens ?? null) !== null}
|
||||
{@render rangeParam('max_tokens', 'max_tokens', -2, 131072, 1, 1, undefined)}
|
||||
{@render rangeParam(
|
||||
'max_tokens',
|
||||
$i18n.t('settings.personal.general.parameters.maxTokens.label'),
|
||||
-2,
|
||||
131072,
|
||||
1,
|
||||
1,
|
||||
undefined
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -600,7 +596,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'top_k'}
|
||||
{$i18n.t('settings.personal.general.parameters.topK.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -623,7 +619,7 @@
|
|||
<div class=" flex-1">
|
||||
<input
|
||||
type="range"
|
||||
aria-label="top_k"
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.topK.label')}
|
||||
min="0"
|
||||
max="1000"
|
||||
step="1"
|
||||
|
|
@ -636,7 +632,7 @@
|
|||
<input
|
||||
value={params.top_k}
|
||||
type="number"
|
||||
aria-label="top_k"
|
||||
aria-label={$i18n.t('settings.personal.general.parameters.topK.label')}
|
||||
class=" bg-transparent text-center w-14"
|
||||
min="0"
|
||||
max="1000"
|
||||
|
|
@ -658,7 +654,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'top_p'}
|
||||
{$i18n.t('settings.personal.general.parameters.topP.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -678,7 +674,7 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.top_p ?? null) !== null}
|
||||
{@render rangeParam('top_p', 'top_p', 0, 1, 0.05)}
|
||||
{@render rangeParam('top_p', $i18n.t('settings.personal.general.parameters.topP.label'), 0, 1, 0.05)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -692,7 +688,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'min_p'}
|
||||
{$i18n.t('settings.personal.general.parameters.minP.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -711,7 +707,7 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.min_p ?? null) !== null}
|
||||
{@render rangeParam('min_p', 'min_p', 0, 1, 0.05)}
|
||||
{@render rangeParam('min_p', $i18n.t('settings.personal.general.parameters.minP.label'), 0, 1, 0.05)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -725,7 +721,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'frequency_penalty'}
|
||||
{$i18n.t('settings.personal.general.parameters.frequencyPenalty.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -745,7 +741,13 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.frequency_penalty ?? null) !== null}
|
||||
{@render rangeParam('frequency_penalty', 'frequency_penalty', -2, 2, 0.05)}
|
||||
{@render rangeParam(
|
||||
'frequency_penalty',
|
||||
$i18n.t('settings.personal.general.parameters.frequencyPenalty.label'),
|
||||
-2,
|
||||
2,
|
||||
0.05
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -759,7 +761,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'presence_penalty'}
|
||||
{$i18n.t('settings.personal.general.parameters.presencePenalty.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -779,7 +781,13 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.presence_penalty ?? null) !== null}
|
||||
{@render rangeParam('presence_penalty', 'presence_penalty', -2, 2, 0.05)}
|
||||
{@render rangeParam(
|
||||
'presence_penalty',
|
||||
$i18n.t('settings.personal.general.parameters.presencePenalty.label'),
|
||||
-2,
|
||||
2,
|
||||
0.05
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -791,7 +799,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'mirostat'}
|
||||
{$i18n.t('settings.personal.general.parameters.mirostat.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -810,7 +818,14 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.mirostat ?? null) !== null}
|
||||
{@render rangeParam('mirostat', 'mirostat', 0, 2, 1, 1)}
|
||||
{@render rangeParam(
|
||||
'mirostat',
|
||||
$i18n.t('settings.personal.general.parameters.mirostat.label'),
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
1
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -824,7 +839,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'mirostat_eta'}
|
||||
{$i18n.t('settings.personal.general.parameters.mirostatEta.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -843,7 +858,13 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.mirostat_eta ?? null) !== null}
|
||||
{@render rangeParam('mirostat_eta', 'mirostat_eta', 0, 1, 0.05)}
|
||||
{@render rangeParam(
|
||||
'mirostat_eta',
|
||||
$i18n.t('settings.personal.general.parameters.mirostatEta.label'),
|
||||
0,
|
||||
1,
|
||||
0.05
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -857,7 +878,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'mirostat_tau'}
|
||||
{$i18n.t('settings.personal.general.parameters.mirostatTau.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -877,7 +898,13 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.mirostat_tau ?? null) !== null}
|
||||
{@render rangeParam('mirostat_tau', 'mirostat_tau', 0, 10, 0.5)}
|
||||
{@render rangeParam(
|
||||
'mirostat_tau',
|
||||
$i18n.t('settings.personal.general.parameters.mirostatTau.label'),
|
||||
0,
|
||||
10,
|
||||
0.5
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -889,7 +916,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'repeat_last_n'}
|
||||
{$i18n.t('settings.personal.general.parameters.repeatLastN.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -909,7 +936,14 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.repeat_last_n ?? null) !== null}
|
||||
{@render rangeParam('repeat_last_n', 'repeat_last_n', -1, 128, 1, 1)}
|
||||
{@render rangeParam(
|
||||
'repeat_last_n',
|
||||
$i18n.t('settings.personal.general.parameters.repeatLastN.label'),
|
||||
-1,
|
||||
128,
|
||||
1,
|
||||
1
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -923,7 +957,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'tfs_z'}
|
||||
{$i18n.t('settings.personal.general.parameters.tfsZ.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -943,7 +977,7 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.tfs_z ?? null) !== null}
|
||||
{@render rangeParam('tfs_z', 'tfs_z', 0, 2, 0.05)}
|
||||
{@render rangeParam('tfs_z', $i18n.t('settings.personal.general.parameters.tfsZ.label'), 0, 2, 0.05)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -957,7 +991,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'repeat_penalty'}
|
||||
{$i18n.t('settings.personal.general.parameters.repeatPenalty.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -977,7 +1011,13 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.repeat_penalty ?? null) !== null}
|
||||
{@render rangeParam('repeat_penalty', 'repeat_penalty', -2, 2, 0.05)}
|
||||
{@render rangeParam(
|
||||
'repeat_penalty',
|
||||
$i18n.t('settings.personal.general.parameters.repeatPenalty.label'),
|
||||
-2,
|
||||
2,
|
||||
0.05
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -992,7 +1032,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'use_mmap'}
|
||||
{$i18n.t('settings.personal.general.parameters.useMmap.label')}
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition shrink-0 outline-hidden"
|
||||
|
|
@ -1016,7 +1056,10 @@
|
|||
{params.use_mmap ? $i18n.t('Enabled') : $i18n.t('Disabled')}
|
||||
</div>
|
||||
<div class=" pr-2">
|
||||
<Switch bind:state={params.use_mmap} ariaLabel="use_mmap" />
|
||||
<Switch
|
||||
bind:state={params.use_mmap}
|
||||
ariaLabel={$i18n.t('settings.personal.general.parameters.useMmap.label')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1032,7 +1075,7 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'use_mlock'}
|
||||
{$i18n.t('settings.personal.general.parameters.useMlock.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1058,7 +1101,10 @@
|
|||
</div>
|
||||
|
||||
<div class=" pr-2">
|
||||
<Switch bind:state={params.use_mlock} ariaLabel="use_mlock" />
|
||||
<Switch
|
||||
bind:state={params.use_mlock}
|
||||
ariaLabel={$i18n.t('settings.personal.general.parameters.useMlock.label')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1067,15 +1113,15 @@
|
|||
|
||||
<div class=" py-0.5 w-full justify-between">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'This option enables or disables the use of the reasoning feature in Ollama, which allows the model to think before generating a response. When enabled, the model can take a moment to process the conversation context and generate a more thoughtful response.'
|
||||
)}
|
||||
content={$i18n.t('settings.personal.general.parameters.ollama.description')}
|
||||
placement="top-start"
|
||||
className="inline-tooltip"
|
||||
>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'think'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.think.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition"
|
||||
|
|
@ -1111,7 +1157,7 @@
|
|||
<input
|
||||
class="text-sm w-full bg-transparent outline-hidden outline-none"
|
||||
type="text"
|
||||
aria-label={`think (${$i18n.t('Ollama')})`}
|
||||
aria-label={`${$i18n.t('settings.personal.general.parameters.think.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`}
|
||||
placeholder={$i18n.t("e.g. 'low', 'medium', 'high'")}
|
||||
bind:value={params.think}
|
||||
autocomplete="off"
|
||||
|
|
@ -1129,7 +1175,9 @@
|
|||
>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'format'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.format.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition"
|
||||
|
|
@ -1151,7 +1199,7 @@
|
|||
<div class="flex mt-0.5 space-x-2">
|
||||
<Textarea
|
||||
className="w-full text-sm bg-transparent outline-hidden"
|
||||
ariaLabel={`format (${$i18n.t('Ollama')})`}
|
||||
ariaLabel={`${$i18n.t('settings.personal.general.parameters.format.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`}
|
||||
placeholder={$i18n.t('e.g. "json" or a JSON schema')}
|
||||
bind:value={params.format}
|
||||
/>
|
||||
|
|
@ -1169,7 +1217,9 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'num_keep'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.numKeep.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1191,7 +1241,7 @@
|
|||
{#if (params?.num_keep ?? null) !== null}
|
||||
{@render rangeParam(
|
||||
'num_keep',
|
||||
`num_keep (${$i18n.t('Ollama')})`,
|
||||
`${$i18n.t('settings.personal.general.parameters.numKeep.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`,
|
||||
-1,
|
||||
10240000,
|
||||
1,
|
||||
|
|
@ -1209,7 +1259,9 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'num_ctx'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.numCtx.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1231,7 +1283,7 @@
|
|||
{#if (params?.num_ctx ?? null) !== null}
|
||||
{@render rangeParam(
|
||||
'num_ctx',
|
||||
`num_ctx (${$i18n.t('Ollama')})`,
|
||||
`${$i18n.t('settings.personal.general.parameters.numCtx.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`,
|
||||
-1,
|
||||
10240000,
|
||||
1,
|
||||
|
|
@ -1251,7 +1303,9 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'num_batch'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.numBatch.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1273,7 +1327,7 @@
|
|||
{#if (params?.num_batch ?? null) !== null}
|
||||
{@render rangeParam(
|
||||
'num_batch',
|
||||
`num_batch (${$i18n.t('Ollama')})`,
|
||||
`${$i18n.t('settings.personal.general.parameters.numBatch.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`,
|
||||
256,
|
||||
8192,
|
||||
256,
|
||||
|
|
@ -1294,7 +1348,9 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'num_thread'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.numThread.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1314,7 +1370,14 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.num_thread ?? null) !== null}
|
||||
{@render rangeParam('num_thread', `num_thread (${$i18n.t('Ollama')})`, 1, 256, 1, 1)}
|
||||
{@render rangeParam(
|
||||
'num_thread',
|
||||
`${$i18n.t('settings.personal.general.parameters.numThread.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`,
|
||||
1,
|
||||
256,
|
||||
1,
|
||||
1
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -1328,7 +1391,9 @@
|
|||
>
|
||||
<div class="flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'num_gpu'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.numGpu.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1348,7 +1413,14 @@
|
|||
</Tooltip>
|
||||
|
||||
{#if (params?.num_gpu ?? null) !== null}
|
||||
{@render rangeParam('num_gpu', `num_gpu (${$i18n.t('Ollama')})`, 0, 256, 1, 1)}
|
||||
{@render rangeParam(
|
||||
'num_gpu',
|
||||
`${$i18n.t('settings.personal.general.parameters.numGpu.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`,
|
||||
0,
|
||||
256,
|
||||
1,
|
||||
1
|
||||
)}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
|
@ -1362,7 +1434,9 @@
|
|||
>
|
||||
<div class=" py-0.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs">
|
||||
{'keep_alive'} ({$i18n.t('Ollama')})
|
||||
{$i18n.t('settings.personal.general.parameters.keepAlive.label')} ({$i18n.t(
|
||||
'settings.personal.general.parameters.ollama.label'
|
||||
)})
|
||||
</div>
|
||||
<button
|
||||
class="p-1 px-3 text-xs flex rounded-sm transition"
|
||||
|
|
@ -1385,7 +1459,7 @@
|
|||
<input
|
||||
class="w-full text-sm bg-transparent outline-hidden"
|
||||
type="text"
|
||||
aria-label={`keep_alive (${$i18n.t('Ollama')})`}
|
||||
aria-label={`${$i18n.t('settings.personal.general.parameters.keepAlive.label')} (${$i18n.t('settings.personal.general.parameters.ollama.label')})`}
|
||||
placeholder={$i18n.t("e.g. '30s','10m'. Valid time units are 's', 'm', 'h'.")}
|
||||
bind:value={params.keep_alive}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@
|
|||
<ConfirmDialog
|
||||
bind:show={showUnarchiveAllConfirmDialog}
|
||||
message={$i18n.t('Are you sure you want to unarchive all archived chats?')}
|
||||
confirmLabel={$i18n.t('Unarchive All')}
|
||||
confirmLabel={$i18n.t('settings.personal.archivedChats.unarchiveAll.label')}
|
||||
on:confirm={() => {
|
||||
unarchiveAllHandler();
|
||||
}}
|
||||
|
|
@ -183,7 +183,7 @@
|
|||
<div id="tab-archived-chats" class="flex flex-col h-full text-sm">
|
||||
<div class="mb-3 flex items-center justify-between">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{$i18n.t('Archived Chats')}
|
||||
{$i18n.t('settings.personal.archivedChats.title')}
|
||||
{#if chatCount !== null}
|
||||
<span class="ml-2 font-normal text-gray-500 dark:text-gray-500">
|
||||
{formatNumber(chatCount)}
|
||||
|
|
@ -246,7 +246,9 @@
|
|||
{:else}
|
||||
<UndoAction className="size-3.5 shrink-0" strokeWidth="1.5" />
|
||||
{/if}
|
||||
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Unarchive All')}</div>
|
||||
<div class="min-w-0 flex-1 truncate text-left">
|
||||
{$i18n.t('settings.personal.archivedChats.unarchiveAll.label')}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
|
@ -256,7 +258,9 @@
|
|||
on:click={exportChatsHandler}
|
||||
>
|
||||
<Download className="size-3.5 shrink-0" strokeWidth="1.5" />
|
||||
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Export')}</div>
|
||||
<div class="min-w-0 flex-1 truncate text-left">
|
||||
{$i18n.t('settings.personal.archivedChats.exportArchivedChats.label')}
|
||||
</div>
|
||||
</button>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -181,18 +181,20 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Audio')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.personal.audio.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<UserSettingSection title={$i18n.t('STT Settings')} first>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.audio.sections.sttSettings.title')} first>
|
||||
{#if $config.audio.stt.engine !== 'web'}
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Speech-to-Text Engine')}
|
||||
description={$i18n.t('Choose the engine used to transcribe voice input.')}
|
||||
label={$i18n.t('settings.personal.audio.speechToTextEngine.label')}
|
||||
description={$i18n.t('settings.personal.audio.speechToTextEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={STTEngine}
|
||||
ariaLabel={$i18n.t('Speech-to-Text Engine')}
|
||||
ariaLabel={$i18n.t('settings.personal.audio.speechToTextEngine.label')}
|
||||
placeholder={$i18n.t('Select an engine')}
|
||||
>
|
||||
<option value="">{$i18n.t('Default')}</option>
|
||||
|
|
@ -201,10 +203,8 @@
|
|||
</UserSettingRow>
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Language')}
|
||||
description={$i18n.t(
|
||||
'Set a speech recognition language or leave it blank to detect automatically.'
|
||||
)}
|
||||
label={$i18n.t('settings.personal.audio.language.label')}
|
||||
description={$i18n.t('settings.personal.audio.language.description')}
|
||||
>
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
|
|
@ -224,14 +224,16 @@
|
|||
{/if}
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Instant Auto-Send After Voice Transcription')}
|
||||
label={$i18n.t('settings.personal.audio.instantAutoSendAfterVoiceTranscription.label')}
|
||||
description={$i18n.t(
|
||||
'Send transcribed voice input immediately after speech recognition finishes.'
|
||||
'settings.personal.audio.instantAutoSendAfterVoiceTranscription.description'
|
||||
)}
|
||||
>
|
||||
<Switch
|
||||
state={speechAutoSend}
|
||||
ariaLabel={$i18n.t('Instant Auto-Send After Voice Transcription')}
|
||||
ariaLabel={$i18n.t(
|
||||
'settings.personal.audio.instantAutoSendAfterVoiceTranscription.label'
|
||||
)}
|
||||
on:change={(event) => {
|
||||
setSpeechAutoSend(event.detail);
|
||||
}}
|
||||
|
|
@ -239,14 +241,14 @@
|
|||
</UserSettingRow>
|
||||
</UserSettingSection>
|
||||
|
||||
<UserSettingSection title={$i18n.t('TTS Settings')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.audio.sections.ttsSettings.title')}>
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Text-to-Speech Engine')}
|
||||
description={$i18n.t('Choose the engine used to read assistant responses aloud.')}
|
||||
label={$i18n.t('settings.personal.audio.textToSpeechEngine.label')}
|
||||
description={$i18n.t('settings.personal.audio.textToSpeechEngine.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={TTSEngine}
|
||||
ariaLabel={$i18n.t('Text-to-Speech Engine')}
|
||||
ariaLabel={$i18n.t('settings.personal.audio.textToSpeechEngine.label')}
|
||||
placeholder={$i18n.t('Select an engine')}
|
||||
>
|
||||
<option value="">{$i18n.t('Default')}</option>
|
||||
|
|
@ -256,12 +258,12 @@
|
|||
|
||||
{#if TTSEngine === 'browser-kokoro'}
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Kokoro.js Dtype')}
|
||||
description={$i18n.t('Select the local model precision used by Kokoro.js.')}
|
||||
label={$i18n.t('settings.personal.audio.kokoroJsDtype.label')}
|
||||
description={$i18n.t('settings.personal.audio.kokoroJsDtype.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={TTSEngineConfig.dtype}
|
||||
ariaLabel={$i18n.t('Kokoro.js Dtype')}
|
||||
ariaLabel={$i18n.t('settings.personal.audio.kokoroJsDtype.label')}
|
||||
placeholder={$i18n.t('Select dtype')}
|
||||
>
|
||||
<option value="" disabled selected>{$i18n.t('Select dtype')}</option>
|
||||
|
|
@ -274,12 +276,12 @@
|
|||
{/if}
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Auto-Playback Response')}
|
||||
description={$i18n.t('Play assistant responses aloud automatically.')}
|
||||
label={$i18n.t('settings.personal.audio.autoPlaybackResponse.label')}
|
||||
description={$i18n.t('settings.personal.audio.autoPlaybackResponse.description')}
|
||||
>
|
||||
<Switch
|
||||
state={responseAutoPlayback}
|
||||
ariaLabel={$i18n.t('Auto-Playback Response')}
|
||||
ariaLabel={$i18n.t('settings.personal.audio.autoPlaybackResponse.label')}
|
||||
on:change={(event) => {
|
||||
setResponseAutoPlayback(event.detail);
|
||||
}}
|
||||
|
|
@ -287,8 +289,8 @@
|
|||
</UserSettingRow>
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Speech Playback Speed')}
|
||||
description={$i18n.t('Adjust how quickly spoken responses are played.')}
|
||||
label={$i18n.t('settings.personal.audio.speechPlaybackSpeed.label')}
|
||||
description={$i18n.t('settings.personal.audio.speechPlaybackSpeed.description')}
|
||||
>
|
||||
<div class="relative flex items-center gap-1.5 text-xs text-gray-400 dark:text-gray-600">
|
||||
<input
|
||||
|
|
@ -296,7 +298,7 @@
|
|||
min="0"
|
||||
step="0.01"
|
||||
bind:value={playbackRate}
|
||||
aria-label={$i18n.t('Speech Playback Speed')}
|
||||
aria-label={$i18n.t('settings.personal.audio.speechPlaybackSpeed.label')}
|
||||
class="h-7 w-16 rounded-lg border border-gray-100/50 bg-gray-50/40 px-2 text-right text-xs text-gray-700 outline-hidden transition-colors focus:border-blue-400 dark:border-white/[0.04] dark:bg-white/[0.03] dark:text-gray-300 dark:focus:border-blue-500"
|
||||
/>
|
||||
x
|
||||
|
|
@ -306,16 +308,16 @@
|
|||
|
||||
{#if TTSEngine === 'browser-kokoro'}
|
||||
{#if TTSModel}
|
||||
<UserSettingSection title={$i18n.t('Voice')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.audio.sections.voice.title')}>
|
||||
<UserSettingField
|
||||
label={$i18n.t('Set Voice')}
|
||||
description={$i18n.t('Choose the Kokoro.js voice used for speech output.')}
|
||||
label={$i18n.t('settings.personal.audio.kokoroVoice.label')}
|
||||
description={$i18n.t('settings.personal.audio.kokoroVoice.description')}
|
||||
>
|
||||
<input
|
||||
list="voice-list"
|
||||
class={inputClass}
|
||||
bind:value={voice}
|
||||
aria-label={$i18n.t('Voice')}
|
||||
aria-label={$i18n.t('settings.personal.audio.sections.voice.title')}
|
||||
placeholder={$i18n.t('Select a voice')}
|
||||
/>
|
||||
|
||||
|
|
@ -327,7 +329,7 @@
|
|||
</UserSettingField>
|
||||
</UserSettingSection>
|
||||
{:else}
|
||||
<UserSettingSection title={$i18n.t('Voice')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.audio.sections.voice.title')}>
|
||||
<div class="flex items-center gap-2 text-xs text-gray-600 dark:text-gray-400">
|
||||
<Spinner className="size-4" />
|
||||
|
||||
|
|
@ -345,12 +347,16 @@
|
|||
</UserSettingSection>
|
||||
{/if}
|
||||
{:else if $config.audio.tts.engine === ''}
|
||||
<UserSettingSection title={$i18n.t('Voice')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.audio.sections.voice.title')}>
|
||||
<UserSettingField
|
||||
label={$i18n.t('Set Voice')}
|
||||
description={$i18n.t('Choose the browser voice used for speech output.')}
|
||||
label={$i18n.t('settings.personal.audio.browserVoice.label')}
|
||||
description={$i18n.t('settings.personal.audio.browserVoice.description')}
|
||||
>
|
||||
<SettingsSelect bind:value={voice} className="w-full" ariaLabel={$i18n.t('Voice')}>
|
||||
<SettingsSelect
|
||||
bind:value={voice}
|
||||
className="w-full"
|
||||
ariaLabel={$i18n.t('settings.personal.audio.sections.voice.title')}
|
||||
>
|
||||
<option value="" selected={voice !== ''}>{$i18n.t('Default')}</option>
|
||||
{#each voices.filter((v) => nonLocalVoices || v.localService === true) as _voice}
|
||||
<option
|
||||
|
|
@ -362,23 +368,23 @@
|
|||
</SettingsSelect>
|
||||
</UserSettingField>
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Allow non-local voices')}
|
||||
description={$i18n.t('Include voices that are not provided by a local speech service.')}
|
||||
label={$i18n.t('settings.personal.audio.allowNonLocalVoices.label')}
|
||||
description={$i18n.t('settings.personal.audio.allowNonLocalVoices.description')}
|
||||
>
|
||||
<Switch bind:state={nonLocalVoices} />
|
||||
</UserSettingRow>
|
||||
</UserSettingSection>
|
||||
{:else if $config.audio.tts.engine !== ''}
|
||||
<UserSettingSection title={$i18n.t('Voice')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.audio.sections.voice.title')}>
|
||||
<UserSettingField
|
||||
label={$i18n.t('Set Voice')}
|
||||
description={$i18n.t('Choose the configured text-to-speech service voice.')}
|
||||
label={$i18n.t('settings.personal.audio.serviceVoice.label')}
|
||||
description={$i18n.t('settings.personal.audio.serviceVoice.description')}
|
||||
>
|
||||
<input
|
||||
list="voice-list"
|
||||
class={inputClass}
|
||||
bind:value={voice}
|
||||
aria-label={$i18n.t('Voice')}
|
||||
aria-label={$i18n.t('settings.personal.audio.sections.voice.title')}
|
||||
placeholder={$i18n.t('Select a voice')}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,9 @@
|
|||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Connections')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.personal.connections.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-1 min-h-0 flex-col overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if config !== null}
|
||||
|
|
@ -80,17 +82,17 @@
|
|||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<h3 id="direct-connections-heading" class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Direct Connections')}
|
||||
{$i18n.t('settings.personal.connections.sections.directConnections.title')}
|
||||
</h3>
|
||||
<p class="mt-1 text-[0.6875rem] leading-relaxed text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Connect to your own OpenAI compatible API endpoints.')}
|
||||
{$i18n.t('settings.personal.connections.addConnection.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t(`Add Connection`)}>
|
||||
<Tooltip content={$i18n.t('settings.personal.connections.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-7 shrink-0 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
aria-label={$i18n.t('Add Connection')}
|
||||
aria-label={$i18n.t('settings.personal.connections.addConnection.label')}
|
||||
on:click={() => {
|
||||
showConnectionModal = true;
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { canManageChats } from '$lib/utils/settings-access';
|
||||
import fileSaver from 'file-saver';
|
||||
const { saveAs } = fileSaver;
|
||||
|
||||
|
|
@ -117,7 +118,7 @@
|
|||
<FilesModal bind:show={showFilesModal} />
|
||||
|
||||
<ConfirmDialog
|
||||
title={$i18n.t('Archive All Chats')}
|
||||
title={$i18n.t('settings.personal.dataControls.archiveAllChats.label')}
|
||||
message={$i18n.t('Are you sure you want to archive all chats? This action cannot be undone.')}
|
||||
bind:show={showArchiveConfirmDialog}
|
||||
on:confirm={archiveAllChatsHandler}
|
||||
|
|
@ -127,7 +128,7 @@
|
|||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
title={$i18n.t('Delete All Chats')}
|
||||
title={$i18n.t('settings.personal.dataControls.deleteAllChats.label')}
|
||||
message={$i18n.t('Are you sure you want to delete all chats? This action cannot be undone.')}
|
||||
bind:show={showDeleteConfirmDialog}
|
||||
on:confirm={deleteAllChatsHandler}
|
||||
|
|
@ -138,7 +139,7 @@
|
|||
|
||||
<div id="tab-chats" class="flex flex-col h-full text-sm">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('Data Controls')}
|
||||
{$i18n.t('settings.personal.dataControls.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
|
|
@ -151,11 +152,14 @@
|
|||
hidden
|
||||
/>
|
||||
|
||||
<UserSettingSection title={$i18n.t('Chats')} first>
|
||||
{#if $user?.role === 'admin' || ($user.permissions?.chat?.import ?? true)}
|
||||
<UserSettingSection
|
||||
title={$i18n.t('settings.personal.dataControls.sections.chats.title')}
|
||||
first
|
||||
>
|
||||
{#if canManageChats({ user: $user, config: null }, 'import')}
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Import Chats')}
|
||||
description={$i18n.t('Import chat history from a JSON export file.')}
|
||||
label={$i18n.t('settings.personal.dataControls.importChats.label')}
|
||||
description={$i18n.t('settings.personal.dataControls.importChats.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -169,10 +173,10 @@
|
|||
</UserSettingRow>
|
||||
{/if}
|
||||
|
||||
{#if $user?.role === 'admin' || ($user.permissions?.chat?.export ?? true)}
|
||||
{#if canManageChats({ user: $user, config: null }, 'export')}
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Export Chats')}
|
||||
description={$i18n.t('Download your chat history as a JSON export.')}
|
||||
label={$i18n.t('settings.personal.dataControls.exportChats.label')}
|
||||
description={$i18n.t('settings.personal.dataControls.exportChats.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -187,8 +191,8 @@
|
|||
{/if}
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Shared Chats')}
|
||||
description={$i18n.t('Review and manage chats you have shared.')}
|
||||
label={$i18n.t('settings.personal.dataControls.sharedChats.label')}
|
||||
description={$i18n.t('settings.personal.dataControls.sharedChats.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -202,8 +206,8 @@
|
|||
</UserSettingRow>
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Archive All Chats')}
|
||||
description={$i18n.t('Move every chat into the archive after confirmation.')}
|
||||
label={$i18n.t('settings.personal.dataControls.archiveAllChats.label')}
|
||||
description={$i18n.t('settings.personal.dataControls.archiveAllChats.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -212,14 +216,14 @@
|
|||
}}
|
||||
type="button"
|
||||
>
|
||||
{$i18n.t('Archive All')}
|
||||
{$i18n.t('settings.personal.dataControls.archiveAll.label')}
|
||||
</button>
|
||||
</UserSettingRow>
|
||||
|
||||
{#if $user?.role === 'admin' || ($user?.permissions?.chat?.delete ?? true)}
|
||||
{#if canManageChats({ user: $user, config: null }, 'delete')}
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Delete All Chats')}
|
||||
description={$i18n.t('Permanently delete every chat after confirmation.')}
|
||||
label={$i18n.t('settings.personal.dataControls.deleteAllChats.label')}
|
||||
description={$i18n.t('settings.personal.dataControls.deleteAllChats.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
@ -234,10 +238,10 @@
|
|||
{/if}
|
||||
</UserSettingSection>
|
||||
|
||||
<UserSettingSection title={$i18n.t('Files')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.dataControls.sections.files.title')}>
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Manage Files')}
|
||||
description={$i18n.t('Open the file manager for uploaded files.')}
|
||||
label={$i18n.t('settings.personal.dataControls.manageFiles.label')}
|
||||
description={$i18n.t('settings.personal.dataControls.manageFiles.description')}
|
||||
>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
<script lang="ts">
|
||||
import {
|
||||
canEditSystemPrompt as allowSystemPrompt,
|
||||
canEditParameters
|
||||
} from '$lib/utils/settings-access';
|
||||
import { createEventDispatcher, onMount, getContext } from 'svelte';
|
||||
import { getLanguages, changeLanguage } from '$lib/i18n';
|
||||
const dispatch = createEventDispatcher();
|
||||
|
|
@ -66,13 +70,8 @@
|
|||
keep_alive: null
|
||||
};
|
||||
|
||||
$: canEditSystemPrompt =
|
||||
$user?.role === 'admin' ||
|
||||
(($user?.permissions.chat?.controls ?? true) &&
|
||||
($user?.permissions.chat?.system_prompt ?? true));
|
||||
$: canEditParams =
|
||||
$user?.role === 'admin' ||
|
||||
(($user?.permissions.chat?.controls ?? true) && ($user?.permissions.chat?.params ?? true));
|
||||
$: canEditSystemPrompt = allowSystemPrompt({ user: $user, config: $config });
|
||||
$: canEditParams = canEditParameters({ user: $user, config: $config });
|
||||
|
||||
const saveHandler = async () => {
|
||||
const updated: Record<string, any> = {};
|
||||
|
|
@ -213,17 +212,22 @@
|
|||
</script>
|
||||
|
||||
<div class="flex flex-col h-full justify-between text-sm" id="tab-general">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('General')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.personal.general.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<UserSettingSection title={$i18n.t('WebUI Settings')} first>
|
||||
<UserSettingSection
|
||||
title={$i18n.t('settings.personal.general.sections.webuiSettings.title')}
|
||||
first
|
||||
>
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Theme')}
|
||||
description={$i18n.t('Choose the color theme used by the interface.')}
|
||||
label={$i18n.t('settings.personal.general.theme.label')}
|
||||
description={$i18n.t('settings.personal.general.theme.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={selectedTheme}
|
||||
ariaLabel={$i18n.t('Theme')}
|
||||
ariaLabel={$i18n.t('settings.personal.general.theme.label')}
|
||||
placeholder={$i18n.t('Select a theme')}
|
||||
on:change={() => themeChangeHandler(selectedTheme)}
|
||||
>
|
||||
|
|
@ -238,12 +242,12 @@
|
|||
</UserSettingRow>
|
||||
|
||||
<UserSettingRow
|
||||
label={$i18n.t('Language')}
|
||||
description={$i18n.t('Choose the language used for interface text.')}
|
||||
label={$i18n.t('settings.personal.general.language.label')}
|
||||
description={$i18n.t('settings.personal.general.language.description')}
|
||||
>
|
||||
<SettingsSelect
|
||||
bind:value={lang}
|
||||
ariaLabel={$i18n.t('Language')}
|
||||
ariaLabel={$i18n.t('settings.personal.general.language.label')}
|
||||
placeholder={$i18n.t('Select a language')}
|
||||
on:change={(e) => {
|
||||
changeLanguage(lang);
|
||||
|
|
@ -272,8 +276,10 @@
|
|||
</UserSettingSection>
|
||||
|
||||
{#if canEditSystemPrompt}
|
||||
<UserSettingSection title={$i18n.t('System Prompt')}>
|
||||
<UserSettingField description={$i18n.t('Set the default system prompt for new chats.')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.general.sections.systemPrompt.title')}>
|
||||
<UserSettingField
|
||||
description={$i18n.t('settings.personal.general.sections.systemPrompt.description')}
|
||||
>
|
||||
<Textarea
|
||||
bind:value={system}
|
||||
className={systemPromptTextareaClass}
|
||||
|
|
@ -285,9 +291,13 @@
|
|||
{/if}
|
||||
|
||||
{#if canEditParams}
|
||||
<UserSettingSection title={$i18n.t('Advanced Parameters')}>
|
||||
<UserSettingRow description={$i18n.t('Show or hide custom generation parameters.')}>
|
||||
<span slot="label">{$i18n.t('Model parameters')}</span>
|
||||
<UserSettingSection
|
||||
title={$i18n.t('settings.personal.general.sections.advancedParameters.title')}
|
||||
>
|
||||
<UserSettingRow
|
||||
description={$i18n.t('settings.personal.general.modelParameters.description')}
|
||||
>
|
||||
<span slot="label">{$i18n.t('settings.personal.general.modelParameters.label')}</span>
|
||||
<button
|
||||
class={actionButtonClass}
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -100,7 +100,9 @@
|
|||
updateHandler();
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Integrations')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.personal.tools.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-1 min-h-0 flex-col overflow-y-auto scrollbar-hover pr-1.5">
|
||||
{#if servers !== null}
|
||||
|
|
@ -109,16 +111,16 @@
|
|||
<div class="mb-3 flex items-start justify-between gap-3">
|
||||
<div class="min-w-0">
|
||||
<h3 id="tool-servers-heading" class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('External Tool Servers')}
|
||||
{$i18n.t('settings.personal.tools.sections.externalToolServers.title')}
|
||||
</h3>
|
||||
<p class="mt-1 text-[0.6875rem] leading-relaxed text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Connect to your own OpenAPI compatible external tool servers.')}
|
||||
{$i18n.t('settings.personal.tools.addConnection.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Tooltip content={$i18n.t('Add Connection')}>
|
||||
<Tooltip content={$i18n.t('settings.personal.tools.addConnection.label')}>
|
||||
<button
|
||||
aria-label={$i18n.t('Add Connection')}
|
||||
aria-label={$i18n.t('settings.personal.tools.addConnection.label')}
|
||||
class="flex size-7 shrink-0 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
on:click={() => (showConnectionModal = true)}
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -60,20 +60,18 @@
|
|||
<div class="flex justify-between items-start gap-3 mb-3">
|
||||
<div class="min-w-0">
|
||||
<h3 id="terminal-connections-heading" class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Open Terminal')}
|
||||
{$i18n.t('settings.personal.tools.terminals.sections.openTerminal.title')}
|
||||
</h3>
|
||||
<p class="mt-1 text-[0.6875rem] leading-relaxed text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t(
|
||||
'Connect to Open Terminal instances to browse files and use them as always-on tools. Only one can be active at a time.'
|
||||
)}
|
||||
{$i18n.t('settings.personal.tools.terminals.addConnection.description')}
|
||||
</p>
|
||||
</div>
|
||||
<Tooltip content={$i18n.t('Add Connection')}>
|
||||
<Tooltip content={$i18n.t('settings.personal.tools.terminals.addConnection.label')}>
|
||||
<button
|
||||
class="flex size-7 shrink-0 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-black/5 hover:text-gray-900 dark:text-gray-600 dark:hover:bg-white/5 dark:hover:text-white"
|
||||
on:click={() => (showAddModal = true)}
|
||||
type="button"
|
||||
aria-label={$i18n.t('Add Connection')}
|
||||
aria-label={$i18n.t('settings.personal.tools.terminals.addConnection.label')}
|
||||
>
|
||||
<Plus />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@
|
|||
dispatch('save');
|
||||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">{$i18n.t('Interface')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('settings.personal.interface.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<InterfaceSettings bind:this={interfaceSettings} {saveSettings} {personalSettingsValue} />
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { canUseNotificationTargets } from '$lib/utils/settings-access';
|
||||
import { getContext, onMount } from 'svelte';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
|
@ -50,9 +51,7 @@
|
|||
};
|
||||
let loadedTargets = false;
|
||||
|
||||
$: canUseWebhooks =
|
||||
($config?.features as any)?.enable_user_webhooks &&
|
||||
($user?.role === 'admin' || ($user?.permissions?.features?.webhooks ?? false));
|
||||
$: canUseWebhooks = canUseNotificationTargets({ user: $user, config: $config });
|
||||
|
||||
$: if (canUseWebhooks && !loadedTargets) {
|
||||
void loadTargets();
|
||||
|
|
@ -192,17 +191,17 @@
|
|||
<div id="tab-notifications" class="flex h-full flex-col text-sm">
|
||||
<div class="flex-1 min-h-0 w-full overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<h2 class="mb-4 text-sm font-medium text-gray-900 dark:text-white">
|
||||
{$i18n.t('Notifications')}
|
||||
{$i18n.t('settings.personal.notifications.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex flex-col gap-2.5">
|
||||
<label class="flex cursor-pointer items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Browser Notifications')}
|
||||
{$i18n.t('settings.personal.notifications.browserNotifications.label')}
|
||||
</span>
|
||||
<Switch
|
||||
state={notificationEnabled}
|
||||
ariaLabel={$i18n.t('Browser Notifications')}
|
||||
ariaLabel={$i18n.t('settings.personal.notifications.browserNotifications.label')}
|
||||
on:change={toggleNotifications}
|
||||
/>
|
||||
</label>
|
||||
|
|
@ -212,11 +211,11 @@
|
|||
|
||||
<label class="flex cursor-pointer items-center justify-between">
|
||||
<span class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Notification Sound')}
|
||||
{$i18n.t('settings.personal.notifications.notificationSound.label')}
|
||||
</span>
|
||||
<Switch
|
||||
bind:state={notificationSound}
|
||||
ariaLabel={$i18n.t('Notification Sound')}
|
||||
ariaLabel={$i18n.t('settings.personal.notifications.notificationSound.label')}
|
||||
on:change={() => {
|
||||
saveSettings({ notificationSound });
|
||||
}}
|
||||
|
|
@ -226,14 +225,14 @@
|
|||
{#if canUseWebhooks}
|
||||
<div class="mt-3 flex items-center justify-between">
|
||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">
|
||||
{$i18n.t('Notification Targets')}
|
||||
{$i18n.t('settings.personal.notifications.notificationTargets.label')}
|
||||
</span>
|
||||
<button
|
||||
class="flex h-6 w-6 items-center justify-center rounded-lg text-gray-400 transition-colors duration-75 hover:text-gray-700 dark:text-gray-600 dark:hover:text-gray-300"
|
||||
type="button"
|
||||
on:click={openNewTarget}
|
||||
title={$i18n.t('Add Notification Target')}
|
||||
aria-label={$i18n.t('Add Notification Target')}
|
||||
title={$i18n.t('settings.personal.notifications.addNotificationTarget.label')}
|
||||
aria-label={$i18n.t('settings.personal.notifications.addNotificationTarget.label')}
|
||||
>
|
||||
<Plus className="size-3.5" strokeWidth="2" />
|
||||
</button>
|
||||
|
|
@ -345,7 +344,9 @@
|
|||
<Modal size="sm" bind:show={formOpen}>
|
||||
<div class="p-4">
|
||||
<h2 class="mb-3 text-sm font-medium text-gray-900 dark:text-white">
|
||||
{editingId ? $i18n.t('Edit') : $i18n.t('Add Notification Target')}
|
||||
{editingId
|
||||
? $i18n.t('Edit')
|
||||
: $i18n.t('settings.personal.notifications.addNotificationTarget.label')}
|
||||
</h2>
|
||||
|
||||
<div class="text-[0.625rem] text-gray-400 dark:text-gray-600">
|
||||
|
|
|
|||
|
|
@ -110,20 +110,24 @@
|
|||
}}
|
||||
>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white mb-4">
|
||||
{$i18n.t('Personalization')}
|
||||
{$i18n.t('settings.personal.personalization.title')}
|
||||
</h2>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<UserSettingSection title={$i18n.t('Memory')} first>
|
||||
<UserSettingSection
|
||||
title={$i18n.t('settings.personal.personalization.sections.memory.title')}
|
||||
first
|
||||
>
|
||||
<UserSettingRow
|
||||
description={$i18n
|
||||
.t(
|
||||
"You can personalize your interactions with LLMs by adding memories through the 'Manage' button below, making them more helpful and tailored to you."
|
||||
)
|
||||
.replace($i18n.t('Manage'), $i18n.t('Add Memory'))}
|
||||
.t('settings.personal.personalization.memory.description')
|
||||
.replace(
|
||||
$i18n.t('settings.personal.personalization.manageMemories.label'),
|
||||
$i18n.t('Add Memory')
|
||||
)}
|
||||
>
|
||||
<div slot="label" class="flex items-center gap-2">
|
||||
{$i18n.t('Memory')}
|
||||
{$i18n.t('settings.personal.personalization.memory.label')}
|
||||
<ExperimentalBadge />
|
||||
</div>
|
||||
|
||||
|
|
@ -139,7 +143,7 @@
|
|||
<div>
|
||||
<div class="mb-1 flex items-center">
|
||||
<div class="text-xs text-gray-600 dark:text-gray-400">
|
||||
{$i18n.t('Saved Memories')}
|
||||
{$i18n.t('settings.personal.personalization.savedMemories.label')}
|
||||
{#if !loadingMemories}
|
||||
<span class="ml-1 text-gray-400 dark:text-gray-600">{memories.length}</span>
|
||||
{/if}
|
||||
|
|
@ -215,7 +219,9 @@
|
|||
}}
|
||||
>
|
||||
<Trash className="size-3.5 shrink-0" strokeWidth="1.5" />
|
||||
<div class="min-w-0 flex-1 truncate text-left">{$i18n.t('Clear memory')}</div>
|
||||
<div class="min-w-0 flex-1 truncate text-left">
|
||||
{$i18n.t('settings.personal.personalization.clearMemory.label')}
|
||||
</div>
|
||||
</button>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
|
@ -225,7 +231,7 @@
|
|||
{#if sortedMemories.length === 0}
|
||||
<div class="min-h-16 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{#if memories.length === 0}
|
||||
{$i18n.t('Memories accessible by LLMs will be shown here.')}
|
||||
{$i18n.t('settings.personal.personalization.noResultsFound.description')}
|
||||
{:else}
|
||||
{$i18n.t('No results found')}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -106,14 +106,14 @@
|
|||
<div id="tab-shortcuts" class="flex h-full flex-col text-sm">
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{$i18n.t('Keyboard')}
|
||||
{$i18n.t('settings.personal.shortcuts.title')}
|
||||
</h2>
|
||||
|
||||
<button
|
||||
class="text-xs text-gray-500 transition-colors hover:text-gray-900 dark:text-gray-500 dark:hover:text-white"
|
||||
on:click={resetBindings}
|
||||
>
|
||||
{$i18n.t('Reset Defaults')}
|
||||
{$i18n.t('settings.personal.shortcuts.resetDefaults.label')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -123,7 +123,7 @@
|
|||
id="enable-keyboard-shortcuts-label"
|
||||
class="min-w-0 text-xs text-gray-600 dark:text-gray-400"
|
||||
>
|
||||
{$i18n.t('Enable Keyboard Shortcuts')}
|
||||
{$i18n.t('settings.personal.shortcuts.enableKeyboardShortcuts.label')}
|
||||
</div>
|
||||
|
||||
<div class="flex shrink-0 items-center justify-end gap-1.5">
|
||||
|
|
@ -138,14 +138,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class="mt-1.5 text-[0.6875rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('When disabled, keyboard shortcuts will not trigger any actions.')}
|
||||
{$i18n.t('settings.personal.shortcuts.enableKeyboardShortcuts.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 min-h-0 overflow-y-auto scrollbar-hover pr-1.5">
|
||||
<div class="flex items-center gap-2 px-1 pb-1">
|
||||
<span class="flex-1 text-[0.625rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Command')}
|
||||
{$i18n.t('settings.personal.shortcuts.command.label')}
|
||||
</span>
|
||||
<span class="w-[9.5rem] shrink-0 text-right text-[0.625rem] text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('Key')}
|
||||
|
|
@ -240,7 +240,7 @@
|
|||
<span
|
||||
class="text-[0.5625rem] text-amber-500"
|
||||
title={$i18n.t('Also bound to {{action}}', {
|
||||
action: $i18n.t(shortcuts[conflict]?.name ?? conflict)
|
||||
action: shortcuts[conflict]?.name($i18n.t) ?? conflict
|
||||
})}>!</span
|
||||
>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -271,7 +271,9 @@
|
|||
|
||||
<div class="flex h-full min-h-0 flex-col">
|
||||
<div class="mb-4">
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white">{$i18n.t('Usage')}</h2>
|
||||
<h2 class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
{$i18n.t('settings.personal.usage.title')}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{#if loading}
|
||||
|
|
@ -284,7 +286,7 @@
|
|||
</div>
|
||||
{:else}
|
||||
<div class="scrollbar-hover min-h-0 flex-1 overflow-y-auto pr-1.5">
|
||||
<UserSettingSection title={$i18n.t('Overview')} first>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.usage.sections.overview.title')} first>
|
||||
<div class="grid grid-cols-2 gap-x-6 gap-y-3 md:grid-cols-5">
|
||||
<div>
|
||||
<div class="text-sm font-medium text-gray-900 dark:text-white">
|
||||
|
|
@ -406,13 +408,15 @@
|
|||
</section>
|
||||
|
||||
{#if !hasUsage}
|
||||
<UserSettingSection title={$i18n.t('Activity')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.usage.sections.activity.title')}>
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('No usage data found')}
|
||||
</div>
|
||||
</UserSettingSection>
|
||||
{:else}
|
||||
<UserSettingSection title={$i18n.t('Activity insights')}>
|
||||
<UserSettingSection
|
||||
title={$i18n.t('settings.personal.usage.sections.activityInsights.title')}
|
||||
>
|
||||
<UserSettingRow label={$i18n.t('Models')}>
|
||||
<span class="text-xs text-gray-900 dark:text-white">
|
||||
{usage.totals.models_used.toLocaleString()}
|
||||
|
|
@ -446,7 +450,7 @@
|
|||
</UserSettingRow>
|
||||
</UserSettingSection>
|
||||
|
||||
<UserSettingSection title={$i18n.t('Top models')}>
|
||||
<UserSettingSection title={$i18n.t('settings.personal.usage.sections.topModels.title')}>
|
||||
{#if usage.top_models.length === 0}
|
||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||
{$i18n.t('No model usage found')}
|
||||
|
|
@ -464,7 +468,9 @@
|
|||
</UserSettingSection>
|
||||
|
||||
{#if usage.top_tools.length > 0}
|
||||
<UserSettingSection title={$i18n.t('Most used tools')}>
|
||||
<UserSettingSection
|
||||
title={$i18n.t('settings.personal.usage.sections.mostUsedTools.title')}
|
||||
>
|
||||
{#each usage.top_tools as tool}
|
||||
<UserSettingRow label={tool.name}>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -123,12 +123,12 @@
|
|||
{#if shortcut.tooltip}
|
||||
<Tooltip content={$i18n.t(shortcut.tooltip)}>
|
||||
<span class="inline-flex max-w-full items-baseline gap-1">
|
||||
<span class="truncate whitespace-pre-line">{$i18n.t(shortcut.name)}</span>
|
||||
<span class="truncate whitespace-pre-line">{shortcut.name($i18n.t)}</span>
|
||||
<span class="text-[0.625rem] text-gray-400 dark:text-gray-600">*</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
{:else}
|
||||
<span class="whitespace-pre-line">{$i18n.t(shortcut.name)}</span>
|
||||
<span class="whitespace-pre-line">{shortcut.name($i18n.t)}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if !compact}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { canUseTemporaryChats, isSettingsAdmin } from '$lib/utils/settings-access';
|
||||
import { config, settings, user } from '$lib/stores';
|
||||
import { onMount, getContext } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
|
@ -458,12 +459,12 @@
|
|||
/>
|
||||
|
||||
<div class="flex flex-col gap-2.5">
|
||||
<h3 class={firstSectionHeadingClass}>{$i18n.t('UI')}</h3>
|
||||
<h3 class={firstSectionHeadingClass}>{$i18n.t('settings.personal.interface.sections.ui.title')}</h3>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<label id="ui-scale-label" class={settingLabelClass} for="ui-scale-slider">
|
||||
{$i18n.t('UI Scale')}
|
||||
{$i18n.t('settings.personal.interface.uiScale.label')}
|
||||
</label>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -504,7 +505,7 @@
|
|||
setTextScaleHandler(textScale);
|
||||
}}
|
||||
aria-labelledby="ui-scale-label"
|
||||
aria-label={$i18n.t('Decrease UI Scale')}
|
||||
aria-label={$i18n.t('settings.personal.interface.decreaseUiScale.label')}
|
||||
>
|
||||
<Minus className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
|
@ -537,7 +538,7 @@
|
|||
setTextScaleHandler(textScale);
|
||||
}}
|
||||
aria-labelledby="ui-scale-label"
|
||||
aria-label={$i18n.t('Increase UI Scale')}
|
||||
aria-label={$i18n.t('settings.personal.interface.increaseUiScale.label')}
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5" />
|
||||
</button>
|
||||
|
|
@ -551,7 +552,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<label id="font-family-label" class={settingLabelClass} for="font-family-input">
|
||||
{$i18n.t('Font Family')}
|
||||
{$i18n.t('settings.personal.interface.fontFamily.label')}
|
||||
</label>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -584,14 +585,14 @@
|
|||
</div>
|
||||
{/if}
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Use a local font family for the app interface.')}
|
||||
{$i18n.t('settings.personal.interface.fontFamily.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="accessibility-mode-label" class={settingLabelClass}>
|
||||
{$i18n.t('Accessibility Mode')}
|
||||
{$i18n.t('settings.personal.interface.accessibilityMode.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -607,14 +608,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Enable accessibility-focused visual enhancements.')}
|
||||
{$i18n.t('settings.personal.interface.accessibilityMode.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="use-chat-title-as-tab-title-label" class={settingLabelClass}>
|
||||
{$i18n.t('Display Chat Title in Tab')}
|
||||
{$i18n.t('settings.personal.interface.displayChatTitleInTab.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -630,13 +631,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Use the active chat title as the browser tab title.')}
|
||||
{$i18n.t('settings.personal.interface.displayChatTitleInTab.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div id="allow-user-location-label" class={settingRowClass}>
|
||||
<div class={settingLabelClass}>{$i18n.t('Allow User Location')}</div>
|
||||
<div class={settingLabelClass}>
|
||||
{$i18n.t('settings.personal.interface.allowUserLocation.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
<Switch
|
||||
|
|
@ -651,14 +654,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Share your current location with features that can use it.')}
|
||||
{$i18n.t('settings.personal.interface.allowUserLocation.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="haptic-feedback-label" class={settingLabelClass}>
|
||||
{$i18n.t('Haptic Feedback')} ({$i18n.t('Android')})
|
||||
{$i18n.t('settings.personal.interface.hapticFeedback.label')} ({$i18n.t(
|
||||
'settings.personal.interface.android.label'
|
||||
)})
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -681,7 +686,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="copy-formatted-label" class={settingLabelClass}>
|
||||
{$i18n.t('Copy Formatted Text')}
|
||||
{$i18n.t('settings.personal.interface.copyFormattedText.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -697,15 +702,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Copy rich formatted content instead of plain text.')}
|
||||
{$i18n.t('settings.personal.interface.copyFormattedText.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if $user?.role === 'admin'}
|
||||
{#if isSettingsAdmin({ user: $user, config: $config })}
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="toast-notifications-label" class={settingLabelClass}>
|
||||
{$i18n.t('Toast Notifications for New Updates')}
|
||||
{$i18n.t('settings.personal.interface.toastNotificationsForNewUpdates.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -721,7 +726,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Show update toasts to admins when new versions are available.')}
|
||||
{$i18n.t('settings.personal.interface.toastNotificationsForNewUpdates.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -749,12 +754,12 @@
|
|||
</div>
|
||||
{/if}
|
||||
|
||||
<div class={sectionHeadingClass}>{$i18n.t('Chat')}</div>
|
||||
<div class={sectionHeadingClass}>{$i18n.t('settings.personal.interface.sections.chat.title')}</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="enable-message-queue-label" class={settingLabelClass}>
|
||||
{$i18n.t('Enable Message Queue')}
|
||||
{$i18n.t('settings.personal.interface.enableMessageQueue.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -770,14 +775,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Queue outgoing messages instead of interrupting active responses.')}
|
||||
{$i18n.t('settings.personal.interface.enableMessageQueue.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="chat-direction-label" class={settingLabelClass}>
|
||||
{$i18n.t('Chat Direction')}
|
||||
{$i18n.t('settings.personal.interface.chatDirection.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -796,14 +801,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Choose automatic, left-to-right, or right-to-left text flow.')}
|
||||
{$i18n.t('settings.personal.interface.chatDirection.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="landing-page-mode-label" class={settingLabelClass}>
|
||||
{$i18n.t('Landing Page Mode')}
|
||||
{$i18n.t('settings.personal.interface.landingPageMode.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -815,19 +820,21 @@
|
|||
type="button"
|
||||
>
|
||||
<span id="notification-sound-state"
|
||||
>{landingPageMode === '' ? $i18n.t('Default') : $i18n.t('Chat')}</span
|
||||
>{landingPageMode === ''
|
||||
? $i18n.t('Default')
|
||||
: $i18n.t('settings.personal.interface.sections.chat.title')}</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Choose whether the app opens to the default home or chat view.')}
|
||||
{$i18n.t('settings.personal.interface.landingPageMode.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="chat-background-label" class={settingLabelClass}>
|
||||
{$i18n.t('Chat Background Image')}
|
||||
{$i18n.t('settings.personal.interface.chatBackgroundImage.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -849,14 +856,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Upload or reset the image shown behind chat content.')}
|
||||
{$i18n.t('settings.personal.interface.chatBackgroundImage.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="chat-bubble-ui-label" class={settingLabelClass}>
|
||||
{$i18n.t('Chat Bubble UI')}
|
||||
{$i18n.t('settings.personal.interface.chatBubbleUi.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -872,7 +879,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Render messages in compact bubble containers.')}
|
||||
{$i18n.t('settings.personal.interface.chatBubbleUi.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -880,7 +887,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="chat-bubble-username-label" class={settingLabelClass}>
|
||||
{$i18n.t('Display the Username Instead of You in the Chat')}
|
||||
{$i18n.t('settings.personal.interface.displayTheUsernameInsteadOfYouInTheChat.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -896,7 +903,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Show your username label instead of You in chat bubbles.')}
|
||||
{$i18n.t('settings.personal.interface.displayTheUsernameInsteadOfYouInTheChat.description')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -904,7 +911,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="widescreen-mode-label" class={settingLabelClass}>
|
||||
{$i18n.t('Widescreen Mode')}
|
||||
{$i18n.t('settings.personal.interface.widescreenMode.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -920,15 +927,15 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Use a wider chat layout on large displays.')}
|
||||
{$i18n.t('settings.personal.interface.widescreenMode.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if $user?.role === 'admin' || $user?.permissions?.chat?.temporary}
|
||||
{#if canUseTemporaryChats({ user: $user, config: $config })}
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="temp-chat-default-label" class={settingLabelClass}>
|
||||
{$i18n.t('Temporary Chat by Default')}
|
||||
{$i18n.t('settings.personal.interface.temporaryChatByDefault.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -944,7 +951,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Start new chats as temporary unless changed.')}
|
||||
{$i18n.t('settings.personal.interface.temporaryChatByDefault.description')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -952,7 +959,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="fade-streaming-label" class={settingLabelClass}>
|
||||
{$i18n.t('Fade Effect for Streaming Text')}
|
||||
{$i18n.t('settings.personal.interface.fadeEffectForStreamingText.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -968,14 +975,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Fade streaming text as it arrives.')}
|
||||
{$i18n.t('settings.personal.interface.fadeEffectForStreamingText.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="render-markdown-user-label" class={settingLabelClass}>
|
||||
{$i18n.t('Render Markdown in User Messages')}
|
||||
{$i18n.t('settings.personal.interface.renderMarkdownInUserMessages.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -991,14 +998,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Format Markdown syntax in your own messages.')}
|
||||
{$i18n.t('settings.personal.interface.renderMarkdownInUserMessages.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="render-markdown-assistant-label" class={settingLabelClass}>
|
||||
{$i18n.t('Render Markdown in Assistant Messages')}
|
||||
{$i18n.t('settings.personal.interface.renderMarkdownInAssistantMessages.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1014,14 +1021,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Format Markdown syntax in assistant responses.')}
|
||||
{$i18n.t('settings.personal.interface.renderMarkdownInAssistantMessages.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="render-markdown-in-previews-label" class={settingLabelClass}>
|
||||
{$i18n.t('Render Markdown in Previews')}
|
||||
{$i18n.t('settings.personal.interface.renderMarkdownInPreviews.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1037,14 +1044,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Format Markdown in previews and compact content surfaces.')}
|
||||
{$i18n.t('settings.personal.interface.renderMarkdownInPreviews.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="auto-generation-label" class={settingLabelClass}>
|
||||
{$i18n.t('Title Auto-Generation')}
|
||||
{$i18n.t('settings.personal.interface.titleAutoGeneration.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1060,14 +1067,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Generate chat titles automatically from conversation content.')}
|
||||
{$i18n.t('settings.personal.interface.titleAutoGeneration.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div class={settingLabelClass} id="follow-up-auto-generation-label">
|
||||
{$i18n.t('Follow-Up Auto-Generation')}
|
||||
{$i18n.t('settings.personal.interface.followUpAutoGeneration.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1083,14 +1090,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Generate suggested follow-up prompts after responses.')}
|
||||
{$i18n.t('settings.personal.interface.followUpAutoGeneration.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="chat-tags-label" class={settingLabelClass}>
|
||||
{$i18n.t('Chat Tags Auto-Generation')}
|
||||
{$i18n.t('settings.personal.interface.chatTagsAutoGeneration.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1106,14 +1113,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Generate tags for chats automatically.')}
|
||||
{$i18n.t('settings.personal.interface.chatTagsAutoGeneration.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="auto-copy-label" class={settingLabelClass}>
|
||||
{$i18n.t('Auto-Copy Response to Clipboard')}
|
||||
{$i18n.t('settings.personal.interface.autoCopyResponseToClipboard.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1129,14 +1136,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Copy the latest assistant response when it completes.')}
|
||||
{$i18n.t('settings.personal.interface.autoCopyResponseToClipboard.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="response-auto-scroll-label" class={settingLabelClass}>
|
||||
{$i18n.t('Response Auto-Scroll')}
|
||||
{$i18n.t('settings.personal.interface.responseAutoScroll.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1152,14 +1159,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Follow assistant responses as they are generated.')}
|
||||
{$i18n.t('settings.personal.interface.responseAutoScroll.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="scroll-on-branch-change-label" class={settingLabelClass}>
|
||||
{$i18n.t('Scroll On Branch Change')}
|
||||
{$i18n.t('settings.personal.interface.scrollOnBranchChange.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1175,14 +1182,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Scroll to the active branch when switching response branches.')}
|
||||
{$i18n.t('settings.personal.interface.scrollOnBranchChange.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="insert-suggestion-prompt-label" class={settingLabelClass}>
|
||||
{$i18n.t('Insert Suggestion Prompt to Input')}
|
||||
{$i18n.t('settings.personal.interface.insertSuggestionPromptToInput.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1198,14 +1205,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Place selected suggestion text into the composer.')}
|
||||
{$i18n.t('settings.personal.interface.insertSuggestionPromptToInput.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="keep-follow-up-prompts-label" class={settingLabelClass}>
|
||||
{$i18n.t('Keep Follow-Up Prompts in Chat')}
|
||||
{$i18n.t('settings.personal.interface.keepFollowUpPromptsInChat.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1221,14 +1228,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Keep generated follow-up prompts visible in the chat.')}
|
||||
{$i18n.t('settings.personal.interface.keepFollowUpPromptsInChat.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="insert-follow-up-prompt-label" class={settingLabelClass}>
|
||||
{$i18n.t('Insert Follow-Up Prompt to Input')}
|
||||
{$i18n.t('settings.personal.interface.insertFollowUpPromptToInput.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1244,14 +1251,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Insert selected follow-up prompts directly into the composer.')}
|
||||
{$i18n.t('settings.personal.interface.insertFollowUpPromptToInput.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="regenerate-menu-label" class={settingLabelClass}>
|
||||
{$i18n.t('Regenerate Menu')}
|
||||
{$i18n.t('settings.personal.interface.regenerateMenu.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1267,14 +1274,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Show the regenerate action menu for assistant responses.')}
|
||||
{$i18n.t('settings.personal.interface.regenerateMenu.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="always-collapse-label" class={settingLabelClass}>
|
||||
{$i18n.t('Always Collapse Code Blocks')}
|
||||
{$i18n.t('settings.personal.interface.alwaysCollapseCodeBlocks.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1290,14 +1297,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Collapse code blocks by default.')}
|
||||
{$i18n.t('settings.personal.interface.alwaysCollapseCodeBlocks.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="always-expand-label" class={settingLabelClass}>
|
||||
{$i18n.t('Always Expand Details')}
|
||||
{$i18n.t('settings.personal.interface.alwaysExpandDetails.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1313,14 +1320,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Open detail blocks by default.')}
|
||||
{$i18n.t('settings.personal.interface.alwaysExpandDetails.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="chat-hover-preview-label" class={settingLabelClass}>
|
||||
{$i18n.t('Chat Hover Previews')}
|
||||
{$i18n.t('settings.personal.interface.chatHoverPreviews.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1336,14 +1343,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Show a floating preview of recent messages when hovering a chat in the sidebar.')}
|
||||
{$i18n.t('settings.personal.interface.chatHoverPreviews.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="keep-followup-prompts-label" class={settingLabelClass}>
|
||||
{$i18n.t('Display Multi-model Responses in Tabs')}
|
||||
{$i18n.t('settings.personal.interface.displayMultiModelResponsesInTabs.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1359,14 +1366,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Group multi-model responses into tabs.')}
|
||||
{$i18n.t('settings.personal.interface.displayMultiModelResponsesInTabs.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="terminal-file-display-label" class={settingLabelClass}>
|
||||
{$i18n.t('Terminal File Display')}
|
||||
{$i18n.t('settings.personal.interface.terminalFileDisplay.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1384,14 +1391,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Choose where terminal display_file results appear by default.')}
|
||||
{$i18n.t('settings.personal.interface.terminalFileDisplay.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="show-files-on-terminal-select-label" class={settingLabelClass}>
|
||||
{$i18n.t('Show Files on Terminal Select')}
|
||||
{$i18n.t('settings.personal.interface.showFilesOnTerminalSelect.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1407,14 +1414,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Open the file browser after selecting a terminal.')}
|
||||
{$i18n.t('settings.personal.interface.showFilesOnTerminalSelect.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="terminal-preview-allow-same-origin-label" class={settingLabelClass}>
|
||||
{$i18n.t('Terminal Preview Allow Same Origin')}
|
||||
{$i18n.t('settings.personal.interface.terminalPreviewAllowSameOrigin.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1430,14 +1437,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Allow terminal previews to access same-origin browser APIs.')}
|
||||
{$i18n.t('settings.personal.interface.terminalPreviewAllowSameOrigin.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="stylized-pdf-export-label" class={settingLabelClass}>
|
||||
{$i18n.t('Stylized PDF Export')}
|
||||
{$i18n.t('settings.personal.interface.stylizedPdfExport.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1453,14 +1460,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Use styled formatting when exporting chats to PDF.')}
|
||||
{$i18n.t('settings.personal.interface.stylizedPdfExport.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="floating-action-buttons-label" class={settingLabelClass}>
|
||||
{$i18n.t('Floating Quick Actions')}
|
||||
{$i18n.t('settings.personal.interface.floatingQuickActions.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1468,7 +1475,9 @@
|
|||
<button
|
||||
class={actionButtonClass}
|
||||
type="button"
|
||||
aria-label={$i18n.t('Open Modal To Manage Floating Quick Actions')}
|
||||
aria-label={$i18n.t(
|
||||
'settings.personal.interface.openModalToManageFloatingQuickActions.label'
|
||||
)}
|
||||
on:click={() => {
|
||||
showManageFloatingActionButtonsModal = true;
|
||||
}}
|
||||
|
|
@ -1496,7 +1505,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="web-search-in-chat-label" class={settingLabelClass}>
|
||||
{$i18n.t('Web Search in Chat')}
|
||||
{$i18n.t('settings.personal.interface.webSearchInChat.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1513,16 +1522,16 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Set web search availability for new chats.')}
|
||||
{$i18n.t('settings.personal.interface.webSearchInChat.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class={sectionHeadingClass}>{$i18n.t('Input')}</div>
|
||||
<div class={sectionHeadingClass}>{$i18n.t('settings.personal.interface.sections.input.title')}</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="enter-key-behavior-label ctrl-enter-to-send-state" class={settingLabelClass}>
|
||||
{$i18n.t('Enter Key Behavior')}
|
||||
{$i18n.t('settings.personal.interface.enterKeyBehavior.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1541,14 +1550,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Choose whether Enter sends immediately or uses Ctrl+Enter.')}
|
||||
{$i18n.t('settings.personal.interface.enterKeyBehavior.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="rich-input-label" class={settingLabelClass}>
|
||||
{$i18n.t('Rich Text Input for Chat')}
|
||||
{$i18n.t('settings.personal.interface.richTextInputForChat.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1564,7 +1573,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Use the rich composer instead of a plain textarea.')}
|
||||
{$i18n.t('settings.personal.interface.richTextInputForChat.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -1572,7 +1581,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="prompt-autocompletion-label" class={settingLabelClass}>
|
||||
{$i18n.t('Prompt Autocompletion')}
|
||||
{$i18n.t('settings.personal.interface.promptAutocompletion.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1588,7 +1597,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Suggest completions while composing prompts.')}
|
||||
{$i18n.t('settings.personal.interface.promptAutocompletion.description')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1597,7 +1606,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="show-formatting-toolbar-label" class={settingLabelClass}>
|
||||
{$i18n.t('Show Formatting Toolbar')}
|
||||
{$i18n.t('settings.personal.interface.showFormattingToolbar.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1613,14 +1622,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Show formatting controls in the rich text composer.')}
|
||||
{$i18n.t('settings.personal.interface.showFormattingToolbar.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="insert-prompt-as-rich-text-label" class={settingLabelClass}>
|
||||
{$i18n.t('Insert Prompt as Rich Text')}
|
||||
{$i18n.t('settings.personal.interface.insertPromptAsRichText.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1636,7 +1645,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Paste inserted prompts as rich text when possible.')}
|
||||
{$i18n.t('settings.personal.interface.insertPromptAsRichText.description')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
@ -1644,7 +1653,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="paste-large-label" class={settingLabelClass}>
|
||||
{$i18n.t('Paste Large Text as File')}
|
||||
{$i18n.t('settings.personal.interface.pasteLargeTextAsFile.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1660,16 +1669,18 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Convert long pasted text into a file attachment.')}
|
||||
{$i18n.t('settings.personal.interface.pasteLargeTextAsFile.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class={sectionHeadingClass}>{$i18n.t('Artifacts')}</div>
|
||||
<div class={sectionHeadingClass}>
|
||||
{$i18n.t('settings.personal.interface.sections.artifacts.title')}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="detect-artifacts-label" class={settingLabelClass}>
|
||||
{$i18n.t('Detect Artifacts Automatically')}
|
||||
{$i18n.t('settings.personal.interface.detectArtifactsAutomatically.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1685,14 +1696,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Detect generated artifacts and show them in the artifact workspace.')}
|
||||
{$i18n.t('settings.personal.interface.detectArtifactsAutomatically.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="iframe-sandbox-allow-scripts-label" class={settingLabelClass}>
|
||||
{$i18n.t('iframe Sandbox Allow Scripts')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowScripts.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1708,14 +1719,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Allow scripts inside sandboxed iframes.')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowScripts.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="iframe-sandbox-allow-same-origin-label" class={settingLabelClass}>
|
||||
{$i18n.t('iframe Sandbox Allow Same Origin')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowSameOrigin.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1731,14 +1742,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Allow artifacts to access same-origin browser APIs inside the sandbox.')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowSameOrigin.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="iframe-sandbox-allow-forms-label" class={settingLabelClass}>
|
||||
{$i18n.t('iframe Sandbox Allow Forms')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowForms.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1754,14 +1765,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Allow forms inside sandboxed artifact iframes.')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowForms.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="iframe-sandbox-allow-downloads-label" class={settingLabelClass}>
|
||||
{$i18n.t('iframe Sandbox Allow Downloads')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowDownloads.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1777,16 +1788,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Allow downloads inside sandboxed iframes.')}
|
||||
{$i18n.t('settings.personal.interface.iframeSandboxAllowDownloads.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class={sectionHeadingClass}>{$i18n.t('Voice')}</div>
|
||||
<div class={sectionHeadingClass}>{$i18n.t('settings.personal.interface.sections.voice.title')}</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div class={settingLabelClass} id="allow-voice-interruption-in-call-label">
|
||||
{$i18n.t('Allow Voice Interruption in Call')}
|
||||
{$i18n.t('settings.personal.interface.allowVoiceInterruptionInCall.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1802,14 +1813,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Let speech interrupt the assistant during a voice call.')}
|
||||
{$i18n.t('settings.personal.interface.allowVoiceInterruptionInCall.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="display-emoji-label" class={settingLabelClass}>
|
||||
{$i18n.t('Display Emoji in Call')}
|
||||
{$i18n.t('settings.personal.interface.displayEmojiInCall.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1825,16 +1836,16 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Show emoji feedback in the call interface.')}
|
||||
{$i18n.t('settings.personal.interface.displayEmojiInCall.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class={sectionHeadingClass}>{$i18n.t('File')}</div>
|
||||
<div class={sectionHeadingClass}>{$i18n.t('settings.personal.interface.sections.file.title')}</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="default-upload-mode-label" class={settingLabelClass}>
|
||||
{$i18n.t('Default Upload Mode')}
|
||||
{$i18n.t('settings.personal.interface.defaultUploadMode.label')}
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
|
@ -1854,14 +1865,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Attach files with full content or focused retrieval by default.')}
|
||||
{$i18n.t('settings.personal.interface.defaultUploadMode.description')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="image-compression-label" class={settingLabelClass}>
|
||||
{$i18n.t('Image Compression')}
|
||||
{$i18n.t('settings.personal.interface.imageCompression.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1869,7 +1880,9 @@
|
|||
<button
|
||||
class={actionButtonClass}
|
||||
type="button"
|
||||
aria-label={$i18n.t('Open Modal To Manage Image Compression')}
|
||||
aria-label={$i18n.t(
|
||||
'settings.personal.interface.openModalToManageImageCompression.label'
|
||||
)}
|
||||
on:click={() => {
|
||||
showManageImageCompressionModal = true;
|
||||
}}
|
||||
|
|
@ -1898,7 +1911,7 @@
|
|||
<div>
|
||||
<div class={settingRowClass}>
|
||||
<div id="image-compression-in-channels-label" class={settingLabelClass}>
|
||||
{$i18n.t('Compress Images in Channels')}
|
||||
{$i18n.t('settings.personal.interface.compressImagesInChannels.label')}
|
||||
</div>
|
||||
|
||||
<div class={settingControlClass}>
|
||||
|
|
@ -1914,7 +1927,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<p class={settingDescriptionClass}>
|
||||
{$i18n.t('Apply image compression to channel uploads too.')}
|
||||
{$i18n.t('settings.personal.interface.compressImagesInChannels.description')}
|
||||
</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -8,70 +8,71 @@
|
|||
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
const toolLabels = {
|
||||
let toolLabels;
|
||||
$: toolLabels = {
|
||||
time: {
|
||||
label: $i18n.t('Time & Calculation'),
|
||||
description: $i18n.t('Get current time and perform date/time calculations')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.time.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.time.description')
|
||||
},
|
||||
user_input: {
|
||||
label: $i18n.t('Ask User'),
|
||||
description: $i18n.t('Pause a response to ask the user a clarifying question')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.userInput.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.userInput.description')
|
||||
},
|
||||
memory: {
|
||||
label: $i18n.t('Memory'),
|
||||
description: $i18n.t('Search and manage user memories')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.memory.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.memory.description')
|
||||
},
|
||||
chats: {
|
||||
label: $i18n.t('Chat History'),
|
||||
description: $i18n.t('Search and view user chat history')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.chats.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.chats.description')
|
||||
},
|
||||
notes: {
|
||||
label: $i18n.t('Notes'),
|
||||
description: $i18n.t('Search, view, and manage user notes')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.notes.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.notes.description')
|
||||
},
|
||||
knowledge: {
|
||||
label: $i18n.t('Knowledge Base'),
|
||||
description: $i18n.t('Browse and query knowledge bases')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.knowledge.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.knowledge.description')
|
||||
},
|
||||
files: {
|
||||
label: $i18n.t('Files'),
|
||||
description: $i18n.t('List, search, and read files attached to the current chat')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.files.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.files.description')
|
||||
},
|
||||
channels: {
|
||||
label: $i18n.t('Channels'),
|
||||
description: $i18n.t('Search channels and channel messages')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.channels.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.channels.description')
|
||||
},
|
||||
notifications: {
|
||||
label: $i18n.t('Notifications'),
|
||||
description: $i18n.t('Send notifications to configured webhook targets')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.notifications.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.notifications.description')
|
||||
},
|
||||
web_search: {
|
||||
label: $i18n.t('Web Search'),
|
||||
description: $i18n.t('Search the web and fetch URLs')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.webSearch.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.webSearch.description')
|
||||
},
|
||||
image_generation: {
|
||||
label: $i18n.t('Image Generation'),
|
||||
description: $i18n.t('Generate and edit images')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.imageGeneration.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.imageGeneration.description')
|
||||
},
|
||||
code_interpreter: {
|
||||
label: $i18n.t('Code Interpreter'),
|
||||
description: $i18n.t('Execute code')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.codeInterpreter.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.codeInterpreter.description')
|
||||
},
|
||||
tasks: {
|
||||
label: $i18n.t('Task Management'),
|
||||
description: $i18n.t('Break down complex requests into trackable steps')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.tasks.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.tasks.description')
|
||||
},
|
||||
automations: {
|
||||
label: $i18n.t('Automations'),
|
||||
description: $i18n.t('Create and manage scheduled automations')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.automations.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.automations.description')
|
||||
},
|
||||
calendar: {
|
||||
label: $i18n.t('Calendar'),
|
||||
description: $i18n.t('List calendars, search, create, update, and delete calendar events')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.calendar.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.calendar.description')
|
||||
},
|
||||
subagents: {
|
||||
label: $i18n.t('Sub-agents'),
|
||||
description: $i18n.t('Delegate focused work to parallel sub-agents')
|
||||
label: $i18n.t('settings.admin.models.builtinTools.subagents.label'),
|
||||
description: $i18n.t('settings.admin.models.builtinTools.subagents.description')
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,7 +91,9 @@
|
|||
</script>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-xs text-gray-400 dark:text-gray-600">{$i18n.t('Builtin Tools')}</div>
|
||||
<div class="mb-1.5 text-xs text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('settings.admin.models.builtinTools.title')}
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-x-5 gap-y-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each allTools as tool}
|
||||
<div class="flex min-h-6 items-center gap-2.5">
|
||||
|
|
|
|||
|
|
@ -8,60 +8,55 @@
|
|||
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
const capabilityLabels = {
|
||||
let capabilityLabels;
|
||||
$: capabilityLabels = {
|
||||
vision: {
|
||||
label: $i18n.t('Vision'),
|
||||
description: $i18n.t('Model accepts image inputs')
|
||||
label: $i18n.t('settings.admin.models.capabilities.vision.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.vision.description')
|
||||
},
|
||||
file_upload: {
|
||||
label: $i18n.t('File Upload'),
|
||||
description: $i18n.t('Model accepts file inputs')
|
||||
label: $i18n.t('settings.admin.models.capabilities.fileUpload.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.fileUpload.description')
|
||||
},
|
||||
file_context: {
|
||||
label: $i18n.t('File Context'),
|
||||
description: $i18n.t('Inject file content into conversation context')
|
||||
label: $i18n.t('settings.admin.models.capabilities.fileContext.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.fileContext.description')
|
||||
},
|
||||
web_search: {
|
||||
label: $i18n.t('Web Search'),
|
||||
description: $i18n.t('Model can search the web for information')
|
||||
label: $i18n.t('settings.admin.models.capabilities.webSearch.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.webSearch.description')
|
||||
},
|
||||
image_generation: {
|
||||
label: $i18n.t('Image Generation'),
|
||||
description: $i18n.t('Model can generate images based on text prompts')
|
||||
label: $i18n.t('settings.admin.models.capabilities.imageGeneration.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.imageGeneration.description')
|
||||
},
|
||||
code_interpreter: {
|
||||
label: $i18n.t('Code Interpreter'),
|
||||
description: $i18n.t('Model can execute code and perform calculations')
|
||||
label: $i18n.t('settings.admin.models.capabilities.codeInterpreter.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.codeInterpreter.description')
|
||||
},
|
||||
terminal: {
|
||||
label: $i18n.t('Terminal'),
|
||||
description: $i18n.t(
|
||||
'Model can access Open Terminal for command execution and file management'
|
||||
)
|
||||
label: $i18n.t('settings.admin.models.capabilities.terminal.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.terminal.description')
|
||||
},
|
||||
usage: {
|
||||
label: $i18n.t('Usage'),
|
||||
description: $i18n.t(
|
||||
'Sends `stream_options: { include_usage: true }` in the request.\nSupported providers will return token usage information in the response when set.'
|
||||
)
|
||||
label: $i18n.t('settings.admin.models.capabilities.usage.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.usage.description')
|
||||
},
|
||||
citations: {
|
||||
label: $i18n.t('Citations'),
|
||||
description: $i18n.t('Displays citations in the response')
|
||||
label: $i18n.t('settings.admin.models.capabilities.citations.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.citations.description')
|
||||
},
|
||||
status_updates: {
|
||||
label: $i18n.t('Status Updates'),
|
||||
description: $i18n.t('Displays status updates (e.g., web search progress) in the response')
|
||||
label: $i18n.t('settings.admin.models.capabilities.statusUpdates.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.statusUpdates.description')
|
||||
},
|
||||
memory: {
|
||||
label: $i18n.t('Memory'),
|
||||
description: $i18n.t('Inject stored memories into conversation context')
|
||||
label: $i18n.t('settings.admin.models.capabilities.memory.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.memory.description')
|
||||
},
|
||||
builtin_tools: {
|
||||
label: $i18n.t('Builtin Tools'),
|
||||
description: $i18n.t(
|
||||
'Automatically inject system tools in native function calling mode (e.g., timestamps, memory, chat history, notes, etc.)'
|
||||
)
|
||||
label: $i18n.t('settings.admin.models.capabilities.builtinTools.label'),
|
||||
description: $i18n.t('settings.admin.models.capabilities.builtinTools.description')
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -84,7 +79,9 @@
|
|||
</script>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-xs text-gray-400 dark:text-gray-600">{$i18n.t('Capabilities')}</div>
|
||||
<div class="mb-1.5 text-xs text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('settings.admin.models.capabilities.title')}
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-x-5 gap-y-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each visibleCapabilities as capability}
|
||||
<div class="flex min-h-6 items-center gap-2.5">
|
||||
|
|
|
|||
|
|
@ -8,18 +8,19 @@
|
|||
|
||||
const i18n: Writable<i18nType> = getContext('i18n');
|
||||
|
||||
const featureLabels = {
|
||||
let featureLabels;
|
||||
$: featureLabels = {
|
||||
web_search: {
|
||||
label: $i18n.t('Web Search'),
|
||||
description: $i18n.t('Model can search the web for information')
|
||||
label: $i18n.t('settings.admin.models.defaultFeatures.webSearch.label'),
|
||||
description: $i18n.t('settings.admin.models.defaultFeatures.webSearch.description')
|
||||
},
|
||||
image_generation: {
|
||||
label: $i18n.t('Image Generation'),
|
||||
description: $i18n.t('Model can generate images based on text prompts')
|
||||
label: $i18n.t('settings.admin.models.defaultFeatures.imageGeneration.label'),
|
||||
description: $i18n.t('settings.admin.models.defaultFeatures.imageGeneration.description')
|
||||
},
|
||||
code_interpreter: {
|
||||
label: $i18n.t('Code Interpreter'),
|
||||
description: $i18n.t('Model can execute code and perform calculations')
|
||||
label: $i18n.t('settings.admin.models.defaultFeatures.codeInterpreter.label'),
|
||||
description: $i18n.t('settings.admin.models.defaultFeatures.codeInterpreter.description')
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -42,7 +43,9 @@
|
|||
</script>
|
||||
|
||||
<div>
|
||||
<div class="mb-1.5 text-xs text-gray-400 dark:text-gray-600">{$i18n.t('Default Features')}</div>
|
||||
<div class="mb-1.5 text-xs text-gray-400 dark:text-gray-600">
|
||||
{$i18n.t('settings.admin.models.defaultFeatures.title')}
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-x-5 gap-y-1 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{#each availableFeatures as feature}
|
||||
<div class="flex min-h-6 items-center gap-2.5">
|
||||
|
|
|
|||
|
|
@ -5,15 +5,20 @@ import type { i18n as i18nType } from 'i18next';
|
|||
import { writable } from 'svelte/store';
|
||||
import type { I18nOverrides } from '$lib/utils/translationDictionary';
|
||||
|
||||
import { assembleSettingsTranslations } from './settings-translations';
|
||||
|
||||
let overrides: I18nOverrides = {};
|
||||
|
||||
export const loadBundledResource = async (language: string): Promise<Record<string, string>> =>
|
||||
(await import(`./locales/${language}/translation.json`)).default;
|
||||
|
||||
const loadResource = async (language: string) => ({
|
||||
...(await loadBundledResource(language)),
|
||||
...overrides[language]
|
||||
});
|
||||
const loadResource = async (language: string) =>
|
||||
assembleSettingsTranslations(
|
||||
await loadBundledResource(language),
|
||||
await loadBundledResource('en-US'),
|
||||
overrides[language],
|
||||
overrides['en-US']
|
||||
);
|
||||
|
||||
export const updateI18n = async (value: I18nOverrides = {}) => {
|
||||
overrides = value;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue