{$i18n.t('User Webhooks')}
@@ -708,6 +757,14 @@
+
+
+ {$i18n.t('User Status')}
+
+
+
+
+
{$i18n.t('Response Watermark')}
diff --git a/src/lib/components/admin/Settings/Models.svelte b/src/lib/components/admin/Settings/Models.svelte
index 1c1fc6512b..d107e09f70 100644
--- a/src/lib/components/admin/Settings/Models.svelte
+++ b/src/lib/components/admin/Settings/Models.svelte
@@ -17,6 +17,7 @@
} from '$lib/apis/models';
import { copyToClipboard } from '$lib/utils';
import { page } from '$app/stores';
+ import { updateUserSettings } from '$lib/apis/users';
import { getModels } from '$lib/apis';
import Search from '$lib/components/icons/Search.svelte';
@@ -218,6 +219,19 @@
saveAs(blob, `${model.id}-${Date.now()}.json`);
};
+ const pinModelHandler = async (modelId) => {
+ let pinnedModels = $settings?.pinnedModels ?? [];
+
+ if (pinnedModels.includes(modelId)) {
+ pinnedModels = pinnedModels.filter((id) => id !== modelId);
+ } else {
+ pinnedModels = [...new Set([...pinnedModels, modelId])];
+ }
+
+ settings.set({ ...$settings, pinnedModels: pinnedModels });
+ await updateUserSettings(localStorage.token, { ui: $settings });
+ };
+
onMount(async () => {
await init();
const id = $page.url.searchParams.get('id');
@@ -427,6 +441,9 @@
hideHandler={() => {
hideModelHandler(model);
}}
+ pinModelHandler={() => {
+ pinModelHandler(model.id);
+ }}
copyLinkHandler={() => {
copyLinkHandler(model);
}}
diff --git a/src/lib/components/admin/Settings/Models/ModelMenu.svelte b/src/lib/components/admin/Settings/Models/ModelMenu.svelte
index b7e694b165..d4cd48a37d 100644
--- a/src/lib/components/admin/Settings/Models/ModelMenu.svelte
+++ b/src/lib/components/admin/Settings/Models/ModelMenu.svelte
@@ -13,8 +13,10 @@
import DocumentDuplicate from '$lib/components/icons/DocumentDuplicate.svelte';
import Download from '$lib/components/icons/Download.svelte';
import ArrowUpCircle from '$lib/components/icons/ArrowUpCircle.svelte';
+ import Pin from '$lib/components/icons/Pin.svelte';
+ import PinSlash from '$lib/components/icons/PinSlash.svelte';
- import { config } from '$lib/stores';
+ import { config, settings } from '$lib/stores';
import Link from '$lib/components/icons/Link.svelte';
const i18n = getContext('i18n');
@@ -24,6 +26,7 @@
export let exportHandler: Function;
export let hideHandler: Function;
+ export let pinModelHandler: Function;
export let copyLinkHandler: Function;
export let cloneHandler: Function;
@@ -104,6 +107,27 @@
+
{
+ pinModelHandler(model?.id);
+ }}
+ >
+ {#if ($settings?.pinnedModels ?? []).includes(model?.id)}
+
+ {:else}
+
+ {/if}
+
+
+ {#if ($settings?.pinnedModels ?? []).includes(model?.id)}
+ {$i18n.t('Hide from Sidebar')}
+ {:else}
+ {$i18n.t('Keep in Sidebar')}
+ {/if}
+
+
+
{
diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte
index 6c9035f3c9..e91a110f81 100644
--- a/src/lib/components/admin/Settings/WebSearch.svelte
+++ b/src/lib/components/admin/Settings/WebSearch.svelte
@@ -43,29 +43,48 @@
const submitHandler = async () => {
// Convert domain filter string to array before sending
- if (webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST) {
+ if (
+ typeof webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST === 'string' &&
+ webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST
+ ) {
webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.split(',')
.map((domain) => domain.trim())
.filter((domain) => domain.length > 0);
- } else {
+ } else if (!Array.isArray(webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST)) {
webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = [];
}
// Convert Youtube loader language string to array before sending
- if (webConfig.YOUTUBE_LOADER_LANGUAGE) {
+ if (
+ typeof webConfig.YOUTUBE_LOADER_LANGUAGE === 'string' &&
+ webConfig.YOUTUBE_LOADER_LANGUAGE
+ ) {
webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.split(',')
.map((lang) => lang.trim())
.filter((lang) => lang.length > 0);
- } else {
+ } else if (!Array.isArray(webConfig.YOUTUBE_LOADER_LANGUAGE)) {
webConfig.YOUTUBE_LOADER_LANGUAGE = [];
}
+ // Convert numeric timeout values to strings (backend expects strings)
+ if (typeof webConfig.FIRECRAWL_TIMEOUT === 'number') {
+ webConfig.FIRECRAWL_TIMEOUT = webConfig.FIRECRAWL_TIMEOUT.toString();
+ }
+ if (typeof webConfig.PLAYWRIGHT_TIMEOUT === 'number') {
+ webConfig.PLAYWRIGHT_TIMEOUT = webConfig.PLAYWRIGHT_TIMEOUT.toString();
+ }
+
const res = await updateRAGConfig(localStorage.token, {
web: webConfig
});
- webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.join(',');
- webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.join(',');
+ // Convert arrays back to strings for display
+ if (Array.isArray(webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST)) {
+ webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.join(',');
+ }
+ if (Array.isArray(webConfig.YOUTUBE_LOADER_LANGUAGE)) {
+ webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.join(',');
+ }
};
onMount(async () => {
@@ -75,11 +94,31 @@
webConfig = res.web;
// Convert array back to comma-separated string for display
- if (webConfig?.WEB_SEARCH_DOMAIN_FILTER_LIST) {
+ if (Array.isArray(webConfig?.WEB_SEARCH_DOMAIN_FILTER_LIST)) {
webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST.join(',');
+ } else if (!webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST) {
+ webConfig.WEB_SEARCH_DOMAIN_FILTER_LIST = '';
}
- webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.join(',');
+ if (Array.isArray(webConfig?.YOUTUBE_LOADER_LANGUAGE)) {
+ webConfig.YOUTUBE_LOADER_LANGUAGE = webConfig.YOUTUBE_LOADER_LANGUAGE.join(',');
+ } else if (!webConfig.YOUTUBE_LOADER_LANGUAGE) {
+ webConfig.YOUTUBE_LOADER_LANGUAGE = '';
+ }
+
+ // Convert timeout strings to numbers for number input fields
+ if (webConfig.FIRECRAWL_TIMEOUT && typeof webConfig.FIRECRAWL_TIMEOUT === 'string') {
+ const parsed = parseInt(webConfig.FIRECRAWL_TIMEOUT);
+ if (!isNaN(parsed)) {
+ webConfig.FIRECRAWL_TIMEOUT = parsed;
+ }
+ }
+ if (webConfig.PLAYWRIGHT_TIMEOUT && typeof webConfig.PLAYWRIGHT_TIMEOUT === 'string') {
+ const parsed = parseInt(webConfig.PLAYWRIGHT_TIMEOUT);
+ if (!isNaN(parsed)) {
+ webConfig.PLAYWRIGHT_TIMEOUT = parsed;
+ }
+ }
}
});
@@ -469,6 +508,24 @@
{:else if webConfig.WEB_SEARCH_ENGINE === 'jina'}
+
+ {$i18n.t('Jina API Base URL')}
+
+
+
+
+
+
{$i18n.t('Jina API Key')}
@@ -628,6 +685,24 @@
bind:value={webConfig.FIRECRAWL_API_KEY}
/>
+
+
+
+ {$i18n.t('Firecrawl Timeout (s)')}
+
+
+
+
{:else if webConfig.WEB_SEARCH_ENGINE === 'external'}
@@ -661,6 +736,36 @@
{/if}
+
+ {#if webConfig.WEB_SEARCH_ENGINE === 'duckduckgo'}
+
+
+
+ {$i18n.t('DDGS Backend')}
+
+
+
+
+
+
+
+
+
+ {/if}
{/if}
{#if webConfig.ENABLE_WEB_SEARCH}
diff --git a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte
index c0896a17b9..791aa9f0fc 100644
--- a/src/lib/components/admin/Users/Groups/EditGroupModal.svelte
+++ b/src/lib/components/admin/Users/Groups/EditGroupModal.svelte
@@ -8,6 +8,7 @@
import General from './General.svelte';
import Permissions from './Permissions.svelte';
import Users from './Users.svelte';
+ import { DEFAULT_PERMISSIONS } from '$lib/constants/permissions';
import UserPlusSolid from '$lib/components/icons/UserPlusSolid.svelte';
import WrenchSolid from '$lib/components/icons/WrenchSolid.svelte';
import ConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
@@ -36,63 +37,7 @@
export let description = '';
export let data = {};
- export let permissions = {
- workspace: {
- models: false,
- knowledge: false,
- prompts: false,
- tools: false,
- models_import: false,
- models_export: false,
- prompts_import: false,
- prompts_export: false,
- tools_import: false,
- tools_export: false
- },
- sharing: {
- models: false,
- public_models: false,
- knowledge: false,
- public_knowledge: false,
- prompts: false,
- public_prompts: false,
- tools: false,
- public_tools: false,
- notes: false,
- public_notes: false
- },
- chat: {
- controls: true,
- valves: true,
- system_prompt: true,
- params: true,
- file_upload: true,
- delete: true,
- delete_message: true,
- continue_response: true,
- regenerate_response: true,
- rate_response: true,
- edit: true,
- share: true,
- export: true,
- stt: true,
- tts: true,
- call: true,
- multiple_models: true,
- temporary: true,
- temporary_enforced: false
- },
- features: {
- api_keys: false,
- notes: true,
- channels: true,
- folders: true,
- direct_tool_servers: false,
- web_search: true,
- image_generation: true,
- code_interpreter: true
- }
- };
+ export let permissions = DEFAULT_PERMISSIONS;
const submitHandler = async () => {
loading = true;
@@ -114,7 +59,15 @@
if (group) {
name = group.name;
description = group.description;
- permissions = group?.permissions ?? {};
+ const loadedPermissions = group?.permissions ?? {};
+ // Create fresh object from defaults, then overlay loaded values
+ permissions = {
+ workspace: { ...DEFAULT_PERMISSIONS.workspace, ...loadedPermissions.workspace },
+ sharing: { ...DEFAULT_PERMISSIONS.sharing, ...loadedPermissions.sharing },
+ chat: { ...DEFAULT_PERMISSIONS.chat, ...loadedPermissions.chat },
+ features: { ...DEFAULT_PERMISSIONS.features, ...loadedPermissions.features },
+ settings: { ...DEFAULT_PERMISSIONS.settings, ...loadedPermissions.settings }
+ };
data = group?.data ?? {};
userCount = group?.member_count ?? 0;
diff --git a/src/lib/components/admin/Users/Groups/General.svelte b/src/lib/components/admin/Users/Groups/General.svelte
index 745611ec1f..1922f00138 100644
--- a/src/lib/components/admin/Users/Groups/General.svelte
+++ b/src/lib/components/admin/Users/Groups/General.svelte
@@ -2,7 +2,6 @@
import { getContext } from 'svelte';
import Textarea from '$lib/components/common/Textarea.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
- import Switch from '$lib/components/common/Switch.svelte';
const i18n = getContext('i18n');
@@ -73,21 +72,30 @@
- {$i18n.t('Allow Group Sharing')}
+ {$i18n.t('Who can share to this group')}
- {
- if (data?.config?.share) {
- data.config.share = e.detail;
+ const value = e.target.value;
+ let shareValue;
+ if (value === 'false') {
+ shareValue = false;
+ } else if (value === 'true') {
+ shareValue = true;
} else {
- data.config = { ...(data?.config ?? {}), share: e.detail };
+ shareValue = value;
}
+ data.config = { ...(data?.config ?? {}), share: shareValue };
}}
- />
+ >
+
+
+
+
diff --git a/src/lib/components/admin/Users/Groups/Permissions.svelte b/src/lib/components/admin/Users/Groups/Permissions.svelte
index 58c3547d33..3284f6171c 100644
--- a/src/lib/components/admin/Users/Groups/Permissions.svelte
+++ b/src/lib/components/admin/Users/Groups/Permissions.svelte
@@ -5,64 +5,7 @@
import Switch from '$lib/components/common/Switch.svelte';
import Tooltip from '$lib/components/common/Tooltip.svelte';
- // Default values for permissions
- const DEFAULT_PERMISSIONS = {
- workspace: {
- models: false,
- knowledge: false,
- prompts: false,
- tools: false,
- models_import: false,
- models_export: false,
- prompts_import: false,
- prompts_export: false,
- tools_import: false,
- tools_export: false
- },
- sharing: {
- models: false,
- public_models: false,
- knowledge: false,
- public_knowledge: false,
- prompts: false,
- public_prompts: false,
- tools: false,
- public_tools: false,
- notes: false,
- public_notes: false
- },
- chat: {
- controls: true,
- valves: true,
- system_prompt: true,
- params: true,
- file_upload: true,
- delete: true,
- delete_message: true,
- continue_response: true,
- regenerate_response: true,
- rate_response: true,
- edit: true,
- share: true,
- export: true,
- stt: true,
- tts: true,
- call: true,
- multiple_models: true,
- temporary: true,
- temporary_enforced: false
- },
- features: {
- api_keys: false,
- notes: true,
- channels: true,
- folders: true,
- direct_tool_servers: false,
- web_search: true,
- image_generation: true,
- code_interpreter: true
- }
- };
+ import { DEFAULT_PERMISSIONS } from '$lib/constants/permissions';
export let permissions = {};
export let defaultPermissions = {};
@@ -79,7 +22,8 @@
workspace: { ...defaults.workspace, ...obj.workspace },
sharing: { ...defaults.sharing, ...obj.sharing },
chat: { ...defaults.chat, ...obj.chat },
- features: { ...defaults.features, ...obj.features }
+ features: { ...defaults.features, ...obj.features },
+ settings: { ...defaults.settings, ...obj.settings }
};
}
@@ -838,5 +782,43 @@