Merge branch 'dev' into leaderboard-evaluation-additions
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
<script>
|
||||
import { getContext, tick, onMount } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import Leaderboard from './Evaluations/Leaderboard.svelte';
|
||||
import Feedbacks from './Evaluations/Feedbacks.svelte';
|
||||
|
||||
@@ -8,7 +10,24 @@
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let selectedTab = 'leaderboard';
|
||||
let selectedTab;
|
||||
$: {
|
||||
const pathParts = $page.url.pathname.split('/');
|
||||
const tabFromPath = pathParts[pathParts.length - 1];
|
||||
selectedTab = ['leaderboard', 'feedbacks'].includes(tabFromPath) ? tabFromPath : 'leaderboard';
|
||||
}
|
||||
|
||||
$: if (selectedTab) {
|
||||
// scroll to selectedTab
|
||||
scrollToTab(selectedTab);
|
||||
}
|
||||
|
||||
const scrollToTab = (tabId) => {
|
||||
const tabElement = document.getElementById(tabId);
|
||||
if (tabElement) {
|
||||
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||
}
|
||||
};
|
||||
|
||||
let loaded = false;
|
||||
let feedbacks = [];
|
||||
@@ -27,6 +46,9 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll to the selected tab on mount
|
||||
scrollToTab(selectedTab);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -37,12 +59,13 @@
|
||||
class="tabs flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
|
||||
>
|
||||
<button
|
||||
id="leaderboard"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||
'leaderboard'
|
||||
? ''
|
||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'leaderboard';
|
||||
goto('/admin/evaluations/leaderboard');
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
@@ -63,12 +86,13 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="feedbacks"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||
'feedbacks'
|
||||
? ''
|
||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'feedbacks';
|
||||
goto('/admin/evaluations/feedbacks');
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
import Tooltip from '$lib/components/common/Tooltip.svelte';
|
||||
import MagnifyingGlass from '$lib/components/icons/MagnifyingGlass.svelte';
|
||||
|
||||
import ChevronUp from '$lib/components/icons/ChevronUp.svelte';
|
||||
import ChevronDown from '$lib/components/icons/ChevronDown.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
const EMBEDDING_MODEL = 'TaylorAI/bge-micro-v2';
|
||||
@@ -30,6 +33,9 @@
|
||||
let loadingLeaderboard = true;
|
||||
let debounceTimer;
|
||||
|
||||
let orderBy: string = 'rating'; // default sort column
|
||||
let direction: 'asc' | 'desc' = 'desc'; // default sort order
|
||||
|
||||
type Feedback = {
|
||||
id: string;
|
||||
data: {
|
||||
@@ -53,6 +59,15 @@
|
||||
lost: number;
|
||||
};
|
||||
|
||||
function setSortKey(key) {
|
||||
if (orderBy === key) {
|
||||
direction = direction === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
orderBy = key;
|
||||
direction = key === 'name' ? 'asc' : 'desc';
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
//
|
||||
// Aggregate Level Modal
|
||||
@@ -287,6 +302,28 @@
|
||||
onMount(async () => {
|
||||
rankHandler();
|
||||
});
|
||||
|
||||
$: sortedModels = [...rankedModels].sort((a, b) => {
|
||||
let aVal, bVal;
|
||||
if (orderBy === 'name') {
|
||||
aVal = a.name;
|
||||
bVal = b.name;
|
||||
return direction === 'asc' ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
|
||||
} else if (orderBy === 'rating') {
|
||||
aVal = a.rating === '-' ? -Infinity : a.rating;
|
||||
bVal = b.rating === '-' ? -Infinity : b.rating;
|
||||
return direction === 'asc' ? aVal - bVal : bVal - aVal;
|
||||
} else if (orderBy === 'won') {
|
||||
aVal = a.stats.won === '-' ? -Infinity : Number(a.stats.won);
|
||||
bVal = b.stats.won === '-' ? -Infinity : Number(b.stats.won);
|
||||
return direction === 'asc' ? aVal - bVal : bVal - aVal;
|
||||
} else if (orderBy === 'lost') {
|
||||
aVal = a.stats.lost === '-' ? -Infinity : Number(a.stats.lost);
|
||||
bVal = b.stats.lost === '-' ? -Infinity : Number(b.stats.lost);
|
||||
return direction === 'asc' ? aVal - bVal : bVal - aVal;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
</script>
|
||||
|
||||
<ModelModal
|
||||
@@ -352,25 +389,120 @@
|
||||
class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-850 dark:text-gray-400 -translate-y-0.5"
|
||||
>
|
||||
<tr class="">
|
||||
<th scope="col" class="px-3 py-1.5 cursor-pointer select-none w-3">
|
||||
{$i18n.t('RK')}
|
||||
<th
|
||||
scope="col"
|
||||
class="px-3 py-1.5 cursor-pointer select-none w-3"
|
||||
on:click={() => setSortKey('rating')}
|
||||
>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
{$i18n.t('RK')}
|
||||
{#if orderBy === 'rating'}
|
||||
<span class="font-normal">
|
||||
{#if direction === 'asc'}
|
||||
<ChevronUp className="size-2" />
|
||||
{:else}
|
||||
<ChevronDown className="size-2" />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="invisible">
|
||||
<ChevronUp className="size-2" />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
<th scope="col" class="px-3 py-1.5 cursor-pointer select-none">
|
||||
{$i18n.t('Model')}
|
||||
<th
|
||||
scope="col"
|
||||
class="px-3 py-1.5 cursor-pointer select-none"
|
||||
on:click={() => setSortKey('name')}
|
||||
>
|
||||
<div class="flex gap-1.5 items-center">
|
||||
{$i18n.t('Model')}
|
||||
{#if orderBy === 'name'}
|
||||
<span class="font-normal">
|
||||
{#if direction === 'asc'}
|
||||
<ChevronUp className="size-2" />
|
||||
{:else}
|
||||
<ChevronDown className="size-2" />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="invisible">
|
||||
<ChevronUp className="size-2" />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-fit">
|
||||
{$i18n.t('Rating')}
|
||||
<th
|
||||
scope="col"
|
||||
class="px-3 py-1.5 text-right cursor-pointer select-none w-fit"
|
||||
on:click={() => setSortKey('rating')}
|
||||
>
|
||||
<div class="flex gap-1.5 items-center justify-end">
|
||||
{$i18n.t('Rating')}
|
||||
{#if orderBy === 'rating'}
|
||||
<span class="font-normal">
|
||||
{#if direction === 'asc'}
|
||||
<ChevronUp className="size-2" />
|
||||
{:else}
|
||||
<ChevronDown className="size-2" />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="invisible">
|
||||
<ChevronUp className="size-2" />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-5">
|
||||
{$i18n.t('Won')}
|
||||
<th
|
||||
scope="col"
|
||||
class="px-3 py-1.5 text-right cursor-pointer select-none w-5"
|
||||
on:click={() => setSortKey('won')}
|
||||
>
|
||||
<div class="flex gap-1.5 items-center justify-end">
|
||||
{$i18n.t('Won')}
|
||||
{#if orderBy === 'won'}
|
||||
<span class="font-normal">
|
||||
{#if direction === 'asc'}
|
||||
<ChevronUp className="size-2" />
|
||||
{:else}
|
||||
<ChevronDown className="size-2" />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="invisible">
|
||||
<ChevronUp className="size-2" />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
<th scope="col" class="px-3 py-1.5 text-right cursor-pointer select-none w-5">
|
||||
{$i18n.t('Lost')}
|
||||
<th
|
||||
scope="col"
|
||||
class="px-3 py-1.5 text-right cursor-pointer select-none w-5"
|
||||
on:click={() => setSortKey('lost')}
|
||||
>
|
||||
<div class="flex gap-1.5 items-center justify-end">
|
||||
{$i18n.t('Lost')}
|
||||
{#if orderBy === 'lost'}
|
||||
<span class="font-normal">
|
||||
{#if direction === 'asc'}
|
||||
<ChevronUp className="size-2" />
|
||||
{:else}
|
||||
<ChevronDown className="size-2" />
|
||||
{/if}
|
||||
</span>
|
||||
{:else}
|
||||
<span class="invisible">
|
||||
<ChevronUp className="size-2" />
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="">
|
||||
{#each rankedModels as model, modelIdx (model.id)}
|
||||
{#each sortedModels as model, modelIdx (model.id)}
|
||||
<tr
|
||||
class="bg-white dark:bg-gray-900 dark:border-gray-850 text-xs group cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 transition"
|
||||
on:click={() => openFeedbackModal(model)}
|
||||
|
||||
@@ -51,6 +51,18 @@
|
||||
: 'general';
|
||||
}
|
||||
|
||||
$: if (selectedTab) {
|
||||
// scroll to selectedTab
|
||||
scrollToTab(selectedTab);
|
||||
}
|
||||
|
||||
const scrollToTab = (tabId) => {
|
||||
const tabElement = document.getElementById(tabId);
|
||||
if (tabElement) {
|
||||
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||
}
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
const containerElement = document.getElementById('admin-settings-tabs-container');
|
||||
|
||||
@@ -62,6 +74,9 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll to the selected tab on mount
|
||||
scrollToTab(selectedTab);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -71,6 +86,7 @@
|
||||
class="tabs flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
|
||||
>
|
||||
<button
|
||||
id="general"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 lg:flex-none flex text-right transition {selectedTab ===
|
||||
'general'
|
||||
? ''
|
||||
@@ -97,6 +113,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="connections"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'connections'
|
||||
? ''
|
||||
@@ -121,6 +138,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="models"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'models'
|
||||
? ''
|
||||
@@ -147,6 +165,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="evaluations"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'evaluations'
|
||||
? ''
|
||||
@@ -162,6 +181,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="tools"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'tools'
|
||||
? ''
|
||||
@@ -188,6 +208,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="documents"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'documents'
|
||||
? ''
|
||||
@@ -218,6 +239,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="web"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'web'
|
||||
? ''
|
||||
@@ -242,6 +264,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="code-execution"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'code-execution'
|
||||
? ''
|
||||
@@ -268,6 +291,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="interface"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'interface'
|
||||
? ''
|
||||
@@ -294,6 +318,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="audio"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'audio'
|
||||
? ''
|
||||
@@ -321,6 +346,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="images"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'images'
|
||||
? ''
|
||||
@@ -347,6 +373,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="pipelines"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'pipelines'
|
||||
? ''
|
||||
@@ -377,6 +404,7 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="db"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg flex-1 md:flex-none flex text-left transition {selectedTab ===
|
||||
'db'
|
||||
? ''
|
||||
|
||||
@@ -194,17 +194,20 @@
|
||||
await embeddingModelUpdateHandler();
|
||||
}
|
||||
|
||||
RAGConfig.ALLOWED_FILE_EXTENSIONS = (RAGConfig?.ALLOWED_FILE_EXTENSIONS ?? '')
|
||||
.split(',')
|
||||
.map((ext) => ext.trim())
|
||||
.filter((ext) => ext !== '');
|
||||
|
||||
RAGConfig.DATALAB_MARKER_LANGS = RAGConfig.DATALAB_MARKER_LANGS.split(',')
|
||||
.map((code) => code.trim())
|
||||
.filter((code) => code !== '')
|
||||
.join(', ');
|
||||
|
||||
const res = await updateRAGConfig(localStorage.token, RAGConfig);
|
||||
const res = await updateRAGConfig(localStorage.token, {
|
||||
...RAGConfig,
|
||||
ALLOWED_FILE_EXTENSIONS: RAGConfig.ALLOWED_FILE_EXTENSIONS.split(',')
|
||||
.map((ext) => ext.trim())
|
||||
.filter((ext) => ext !== ''),
|
||||
DATALAB_MARKER_LANGS: RAGConfig.DATALAB_MARKER_LANGS.split(',')
|
||||
.map((code) => code.trim())
|
||||
.filter((code) => code !== '')
|
||||
.join(', '),
|
||||
DOCLING_PICTURE_DESCRIPTION_LOCAL: JSON.parse(
|
||||
RAGConfig.DOCLING_PICTURE_DESCRIPTION_LOCAL || '{}'
|
||||
),
|
||||
DOCLING_PICTURE_DESCRIPTION_API: JSON.parse(RAGConfig.DOCLING_PICTURE_DESCRIPTION_API || '{}')
|
||||
});
|
||||
dispatch('save');
|
||||
};
|
||||
|
||||
@@ -232,6 +235,18 @@
|
||||
|
||||
const config = await getRAGConfig(localStorage.token);
|
||||
config.ALLOWED_FILE_EXTENSIONS = (config?.ALLOWED_FILE_EXTENSIONS ?? []).join(', ');
|
||||
|
||||
config.DOCLING_PICTURE_DESCRIPTION_LOCAL = JSON.stringify(
|
||||
config.DOCLING_PICTURE_DESCRIPTION_LOCAL ?? {},
|
||||
null,
|
||||
2
|
||||
);
|
||||
config.DOCLING_PICTURE_DESCRIPTION_API = JSON.stringify(
|
||||
config.DOCLING_PICTURE_DESCRIPTION_API ?? {},
|
||||
null,
|
||||
2
|
||||
);
|
||||
|
||||
RAGConfig = config;
|
||||
});
|
||||
</script>
|
||||
@@ -510,6 +525,71 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if RAGConfig.DOCLING_DO_PICTURE_DESCRIPTION}
|
||||
<div class="flex justify-between w-full mt-2">
|
||||
<div class="self-center text-xs font-medium">
|
||||
<Tooltip content={''} placement="top-start">
|
||||
{$i18n.t('Picture Description Mode')}
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="">
|
||||
<select
|
||||
class="dark:bg-gray-900 w-fit pr-8 rounded-sm px-2 text-xs bg-transparent outline-hidden text-right"
|
||||
bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE}
|
||||
>
|
||||
<option value="">{$i18n.t('Default')}</option>
|
||||
<option value="local">{$i18n.t('Local')}</option>
|
||||
<option value="api">{$i18n.t('API')}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE === 'local'}
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<div class=" flex flex-col w-full justify-between">
|
||||
<div class=" mb-1 text-xs font-medium">
|
||||
{$i18n.t('Picture Description Local Config')}
|
||||
</div>
|
||||
<div class="flex w-full items-center relative">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'Options for running a local vision-language model in the picture description. The parameters refer to a model hosted on Hugging Face. This parameter is mutually exclusive with picture_description_api.'
|
||||
)}
|
||||
placement="top-start"
|
||||
className="w-full"
|
||||
>
|
||||
<Textarea
|
||||
bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_LOCAL}
|
||||
placeholder={$i18n.t('Enter Config in JSON format')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if RAGConfig.DOCLING_PICTURE_DESCRIPTION_MODE === 'api'}
|
||||
<div class="flex flex-col gap-2 mt-2">
|
||||
<div class=" flex flex-col w-full justify-between">
|
||||
<div class=" mb-1 text-xs font-medium">
|
||||
{$i18n.t('Picture Description API Config')}
|
||||
</div>
|
||||
<div class="flex w-full items-center relative">
|
||||
<Tooltip
|
||||
content={$i18n.t(
|
||||
'API details for using a vision-language model in the picture description. This parameter is mutually exclusive with picture_description_local.'
|
||||
)}
|
||||
placement="top-start"
|
||||
className="w-full"
|
||||
>
|
||||
<Textarea
|
||||
bind:value={RAGConfig.DOCLING_PICTURE_DESCRIPTION_API}
|
||||
placeholder={$i18n.t('Enter Config in JSON format')}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{:else if RAGConfig.CONTENT_EXTRACTION_ENGINE === 'document_intelligence'}
|
||||
<div class="my-0.5 flex gap-2 pr-2">
|
||||
<input
|
||||
@@ -830,12 +910,7 @@
|
||||
<div class=" mb-2.5 flex w-full justify-between">
|
||||
<div class=" self-center text-xs font-medium">{$i18n.t('Hybrid Search')}</div>
|
||||
<div class="flex items-center relative">
|
||||
<Switch
|
||||
bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH}
|
||||
on:change={() => {
|
||||
submitHandler();
|
||||
}}
|
||||
/>
|
||||
<Switch bind:state={RAGConfig.ENABLE_RAG_HYBRID_SEARCH} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
const updateHandler = async () => {
|
||||
webhookUrl = await updateWebhookUrl(localStorage.token, webhookUrl);
|
||||
const res = await updateAdminConfig(localStorage.token, adminConfig);
|
||||
await updateLdapConfig(localStorage.token, ENABLE_LDAP);
|
||||
await updateLdapServerHandler();
|
||||
|
||||
if (res) {
|
||||
@@ -311,7 +312,6 @@
|
||||
{$i18n.t('Pending User Overlay Title')}
|
||||
</div>
|
||||
<Textarea
|
||||
rows={2}
|
||||
placeholder={$i18n.t(
|
||||
'Enter a title for the pending user info overlay. Leave empty for default.'
|
||||
)}
|
||||
@@ -401,12 +401,7 @@
|
||||
<div class=" font-medium">{$i18n.t('LDAP')}</div>
|
||||
|
||||
<div class="mt-1">
|
||||
<Switch
|
||||
bind:state={ENABLE_LDAP}
|
||||
on:change={async () => {
|
||||
updateLdapConfig(localStorage.token, ENABLE_LDAP);
|
||||
}}
|
||||
/>
|
||||
<Switch bind:state={ENABLE_LDAP} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -75,6 +75,8 @@
|
||||
};
|
||||
|
||||
const init = async () => {
|
||||
models = null;
|
||||
|
||||
workspaceModels = await getBaseModels(localStorage.token);
|
||||
baseModels = await getModels(localStorage.token, null, true);
|
||||
|
||||
@@ -126,6 +128,7 @@
|
||||
toast.success($i18n.t('Model updated successfully'));
|
||||
}
|
||||
}
|
||||
await init();
|
||||
|
||||
_models.set(
|
||||
await getModels(
|
||||
@@ -133,7 +136,6 @@
|
||||
$config?.features?.enable_direct_connections && ($settings?.directConnections ?? null)
|
||||
)
|
||||
);
|
||||
await init();
|
||||
};
|
||||
|
||||
const toggleModelHandler = async (model) => {
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
<div slot="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[160px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-sm"
|
||||
class="w-full max-w-[170px] rounded-xl px-1 py-1.5 border border-gray-300/30 dark:border-gray-700/50 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-sm"
|
||||
sideOffset={-2}
|
||||
side="bottom"
|
||||
align="start"
|
||||
|
||||
@@ -446,15 +446,54 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else if webConfig.WEB_SEARCH_ENGINE === 'perplexity'}
|
||||
<div>
|
||||
<div class=" self-center text-xs font-medium mb-1">
|
||||
{$i18n.t('Perplexity API Key')}
|
||||
</div>
|
||||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs font-medium mb-1">
|
||||
{$i18n.t('Perplexity API Key')}
|
||||
</div>
|
||||
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('Enter Perplexity API Key')}
|
||||
bind:value={webConfig.PERPLEXITY_API_KEY}
|
||||
/>
|
||||
<SensitiveInput
|
||||
placeholder={$i18n.t('Enter Perplexity API Key')}
|
||||
bind:value={webConfig.PERPLEXITY_API_KEY}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class="self-center text-xs font-medium mb-1">
|
||||
{$i18n.t('Perplexity Model')}
|
||||
</div>
|
||||
<input
|
||||
list="perplexity-model-list"
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
bind:value={webConfig.PERPLEXITY_MODEL}
|
||||
/>
|
||||
|
||||
<datalist id="perplexity-model-list">
|
||||
<option value="sonar">Sonar</option>
|
||||
<option value="sonar-pro">Sonar Pro</option>
|
||||
<option value="sonar-reasoning">Sonar Reasoning</option>
|
||||
<option value="sonar-reasoning-pro">Sonar Reasoning Pro</option>
|
||||
<option value="sonar-deep-research">Sonar Deep Research</option>
|
||||
</datalist>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-2.5 flex w-full flex-col">
|
||||
<div>
|
||||
<div class=" self-center text-xs font-medium mb-1">
|
||||
{$i18n.t('Perplexity Search Context Usage')}
|
||||
</div>
|
||||
<select
|
||||
class="w-full rounded-lg py-2 px-4 text-sm bg-gray-50 dark:text-gray-300 dark:bg-gray-850 outline-hidden"
|
||||
bind:value={webConfig.PERPLEXITY_SEARCH_CONTEXT_USAGE}
|
||||
>
|
||||
<option value="low">Low</option>
|
||||
<option value="medium">Medium</option>
|
||||
<option value="high">High</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{:else if webConfig.WEB_SEARCH_ENGINE === 'sougou'}
|
||||
<div class="mb-2.5 flex w-full flex-col">
|
||||
|
||||
@@ -4,13 +4,32 @@
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import { user } from '$lib/stores';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import UserList from './Users/UserList.svelte';
|
||||
import Groups from './Users/Groups.svelte';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
let selectedTab = 'overview';
|
||||
let selectedTab;
|
||||
$: {
|
||||
const pathParts = $page.url.pathname.split('/');
|
||||
const tabFromPath = pathParts[pathParts.length - 1];
|
||||
selectedTab = ['overview', 'groups'].includes(tabFromPath) ? tabFromPath : 'overview';
|
||||
}
|
||||
|
||||
$: if (selectedTab) {
|
||||
// scroll to selectedTab
|
||||
scrollToTab(selectedTab);
|
||||
}
|
||||
|
||||
const scrollToTab = (tabId) => {
|
||||
const tabElement = document.getElementById(tabId);
|
||||
if (tabElement) {
|
||||
tabElement.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' });
|
||||
}
|
||||
};
|
||||
|
||||
let loaded = false;
|
||||
|
||||
onMount(async () => {
|
||||
@@ -30,6 +49,9 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Scroll to the selected tab on mount
|
||||
scrollToTab(selectedTab);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -39,12 +61,13 @@
|
||||
class=" flex flex-row overflow-x-auto gap-2.5 max-w-full lg:gap-1 lg:flex-col lg:flex-none lg:w-40 dark:text-gray-200 text-sm font-medium text-left scrollbar-none"
|
||||
>
|
||||
<button
|
||||
id="overview"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||
'overview'
|
||||
? ''
|
||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'overview';
|
||||
goto('/admin/users/overview');
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
@@ -63,12 +86,13 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
id="groups"
|
||||
class="px-0.5 py-1 min-w-fit rounded-lg lg:flex-none flex text-right transition {selectedTab ===
|
||||
'groups'
|
||||
? ''
|
||||
: ' text-gray-300 dark:text-gray-600 hover:text-gray-700 dark:hover:text-white'}"
|
||||
on:click={() => {
|
||||
selectedTab = 'groups';
|
||||
goto('/admin/users/groups');
|
||||
}}
|
||||
>
|
||||
<div class=" self-center mr-2">
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
|
||||
<div class="flex-1">
|
||||
<select
|
||||
class="w-full rounded-sm text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-hidden"
|
||||
class="w-full dark:bg-gray-900 rounded-sm text-sm bg-transparent disabled:text-gray-500 dark:disabled:text-gray-500 outline-hidden"
|
||||
bind:value={_user.role}
|
||||
disabled={_user.id == sessionUser.id}
|
||||
required
|
||||
|
||||
Reference in New Issue
Block a user