diff --git a/tools/server/public/index.html.gz b/tools/server/public/index.html.gz index 493058aa01..6d333c54c2 100644 Binary files a/tools/server/public/index.html.gz and b/tools/server/public/index.html.gz differ diff --git a/tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte b/tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte index b9015c196c..42191be89f 100644 --- a/tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte +++ b/tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte @@ -5,9 +5,12 @@ import Label from '$lib/components/ui/label/label.svelte'; import * as Select from '$lib/components/ui/select'; import { Textarea } from '$lib/components/ui/textarea'; - import { SETTING_CONFIG_DEFAULT, SETTING_CONFIG_INFO, SETTINGS_KEYS } from '$lib/constants'; + import { SETTING_CONFIG_INFO, SETTINGS_KEYS } from '$lib/constants'; import { SettingsFieldType } from '$lib/enums/settings'; import { settingsStore } from '$lib/stores/settings.svelte'; + import { serverStore } from '$lib/stores/server.svelte'; + import { modelsStore, selectedModelName } from '$lib/stores/models.svelte'; + import { normalizeFloatingPoint } from '$lib/utils/precision'; import { ChatSettingsParameterSourceIndicator } from '$lib/components/app'; import type { Component } from 'svelte'; @@ -20,35 +23,36 @@ let { fields, localConfig, onConfigChange, onThemeChange }: Props = $props(); - // Helper function to get parameter source info for syncable parameters - function getParameterSourceInfo(key: string) { - if (!settingsStore.canSyncParameter(key)) { - return null; + // server sampling defaults for placeholders + let sp = $derived.by(() => { + if (serverStore.isRouterMode) { + const m = selectedModelName(); + if (m) { + const p = modelsStore.getModelProps(m); + return (p?.default_generation_settings?.params ?? {}) as Record; + } } - - return settingsStore.getParameterInfo(key); - } + return (serverStore.defaultParams ?? {}) as Record; + }); {#each fields as field (field.key)}
{#if field.type === SettingsFieldType.INPUT} - {@const paramInfo = getParameterSourceInfo(field.key)} {@const currentValue = String(localConfig[field.key] ?? '')} - {@const propsDefault = paramInfo?.serverDefault} + {@const serverDefault = sp[field.key]} {@const isCustomRealTime = (() => { - if (!paramInfo || propsDefault === undefined) return false; + if (serverDefault == null) return false; + if (currentValue === '') return false; - // Apply same rounding logic for real-time comparison - const inputValue = currentValue; - const numericInput = parseFloat(inputValue); + const numericInput = parseFloat(currentValue); const normalizedInput = !isNaN(numericInput) ? Math.round(numericInput * 1000000) / 1000000 - : inputValue; + : currentValue; const normalizedDefault = - typeof propsDefault === 'number' - ? Math.round(propsDefault * 1000000) / 1000000 - : propsDefault; + typeof serverDefault === 'number' + ? Math.round(serverDefault * 1000000) / 1000000 + : serverDefault; return normalizedInput !== normalizedDefault; })()} @@ -74,7 +78,9 @@ // Update local config immediately for real-time badge feedback onConfigChange(field.key, e.currentTarget.value); }} - placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`} + placeholder={sp[field.key] != null + ? `Default: ${normalizeFloatingPoint(sp[field.key])}` + : ''} class="w-full {isCustomRealTime ? 'pr-8' : ''}" /> {#if isCustomRealTime} @@ -82,9 +88,7 @@ type="button" onclick={() => { settingsStore.resetParameterToServerDefault(field.key); - // Trigger UI update by calling onConfigChange with the default value - const defaultValue = propsDefault ?? SETTING_CONFIG_DEFAULT[field.key]; - onConfigChange(field.key, String(defaultValue)); + onConfigChange(field.key, ''); }} class="absolute top-1/2 right-2 inline-flex h-5 w-5 -translate-y-1/2 items-center justify-center rounded transition-colors hover:bg-muted" aria-label="Reset to default" @@ -112,7 +116,7 @@ id={field.key} value={String(localConfig[field.key] ?? '')} onchange={(e) => onConfigChange(field.key, e.currentTarget.value)} - placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`} + placeholder="" class="min-h-[10rem] w-full md:max-w-2xl" /> @@ -140,14 +144,12 @@ (opt: { value: string; label: string; icon?: Component }) => opt.value === localConfig[field.key] )} - {@const paramInfo = getParameterSourceInfo(field.key)} {@const currentValue = localConfig[field.key]} - {@const propsDefault = paramInfo?.serverDefault} + {@const serverDefault = sp[field.key]} {@const isCustomRealTime = (() => { - if (!paramInfo || propsDefault === undefined) return false; - - // For select fields, do direct comparison (no rounding needed) - return currentValue !== propsDefault; + if (serverDefault == null) return false; + if (currentValue === '' || currentValue === undefined) return false; + return currentValue !== serverDefault; })()}
@@ -190,9 +192,7 @@ type="button" onclick={() => { settingsStore.resetParameterToServerDefault(field.key); - // Trigger UI update by calling onConfigChange with the default value - const defaultValue = propsDefault ?? SETTING_CONFIG_DEFAULT[field.key]; - onConfigChange(field.key, String(defaultValue)); + onConfigChange(field.key, ''); }} class="absolute top-1/2 right-8 inline-flex h-5 w-5 -translate-y-1/2 items-center justify-center rounded transition-colors hover:bg-muted" aria-label="Reset to default" diff --git a/tools/server/webui/src/lib/constants/settings-config.ts b/tools/server/webui/src/lib/constants/settings-config.ts index e76fa89e9a..e1ff5f30eb 100644 --- a/tools/server/webui/src/lib/constants/settings-config.ts +++ b/tools/server/webui/src/lib/constants/settings-config.ts @@ -30,27 +30,30 @@ export const SETTING_CONFIG_DEFAULT: Record = agenticMaxToolPreviewLines: 25, showToolCallInProgress: false, alwaysShowAgenticTurns: false, - // make sure these default values are in sync with `common.h` - samplers: 'top_k;typ_p;top_p;min_p;temperature', + // sampling params: empty means "use server default" + // the server / preset is the source of truth + // empty values are shown as placeholders from /props in the UI + // and are NOT sent in API requests, letting the server decide + samplers: '', backend_sampling: false, - temperature: 0.8, - dynatemp_range: 0.0, - dynatemp_exponent: 1.0, - top_k: 40, - top_p: 0.95, - min_p: 0.05, - xtc_probability: 0.0, - xtc_threshold: 0.1, - typ_p: 1.0, - repeat_last_n: 64, - repeat_penalty: 1.0, - presence_penalty: 0.0, - frequency_penalty: 0.0, - dry_multiplier: 0.0, - dry_base: 1.75, - dry_allowed_length: 2, - dry_penalty_last_n: -1, - max_tokens: -1, + temperature: '', + dynatemp_range: '', + dynatemp_exponent: '', + top_k: '', + top_p: '', + min_p: '', + xtc_probability: '', + xtc_threshold: '', + typ_p: '', + repeat_last_n: '', + repeat_penalty: '', + presence_penalty: '', + frequency_penalty: '', + dry_multiplier: '', + dry_base: '', + dry_allowed_length: '', + dry_penalty_last_n: '', + max_tokens: '', custom: '', // custom json-stringified object // experimental features pyInterpreterEnabled: false, diff --git a/tools/server/webui/src/lib/stores/settings.svelte.ts b/tools/server/webui/src/lib/stores/settings.svelte.ts index 8ab817c071..2fbff8312f 100644 --- a/tools/server/webui/src/lib/stores/settings.svelte.ts +++ b/tools/server/webui/src/lib/stores/settings.svelte.ts @@ -289,16 +289,10 @@ class SettingsStore { const serverDefaults = this.getServerDefaults(); if (serverDefaults[key] !== undefined) { - const value = normalizeFloatingPoint(serverDefaults[key]); - - this.config[key as keyof SettingsConfigType] = - value as SettingsConfigType[keyof SettingsConfigType]; - } else { - if (key in SETTING_CONFIG_DEFAULT) { - const defaultValue = getConfigValue(SETTING_CONFIG_DEFAULT, key); - - setConfigValue(this.config, key, defaultValue); - } + // sampling param known by server: clear it, let server decide + setConfigValue(this.config, key, ''); + } else if (key in SETTING_CONFIG_DEFAULT) { + setConfigValue(this.config, key, getConfigValue(SETTING_CONFIG_DEFAULT, key)); } this.userOverrides.delete(key); @@ -319,12 +313,7 @@ class SettingsStore { */ syncWithServerDefaults(): void { const propsDefaults = this.getServerDefaults(); - - if (Object.keys(propsDefaults).length === 0) { - console.warn('No server defaults available for initialization'); - - return; - } + if (Object.keys(propsDefaults).length === 0) return; for (const [key, propsValue] of Object.entries(propsDefaults)) { const currentValue = getConfigValue(this.config, key); @@ -332,17 +321,14 @@ class SettingsStore { const normalizedCurrent = normalizeFloatingPoint(currentValue); const normalizedDefault = normalizeFloatingPoint(propsValue); + // if user value matches server, it's not a real override if (normalizedCurrent === normalizedDefault) { this.userOverrides.delete(key); - setConfigValue(this.config, key, propsValue); - } else if (!this.userOverrides.has(key)) { - setConfigValue(this.config, key, propsValue); } } this.saveConfig(); - console.log('Settings initialized with props defaults:', propsDefaults); - console.log('Current user overrides after sync:', Array.from(this.userOverrides)); + console.log('User overrides after sync:', Array.from(this.userOverrides)); } /** @@ -352,19 +338,11 @@ class SettingsStore { */ forceSyncWithServerDefaults(): void { const propsDefaults = this.getServerDefaults(); - const syncableKeys = ParameterSyncService.getSyncableParameterKeys(); - - for (const key of syncableKeys) { + for (const key of ParameterSyncService.getSyncableParameterKeys()) { if (propsDefaults[key] !== undefined) { - const normalizedValue = normalizeFloatingPoint(propsDefaults[key]); - - setConfigValue(this.config, key, normalizedValue); - } else { - if (key in SETTING_CONFIG_DEFAULT) { - const defaultValue = getConfigValue(SETTING_CONFIG_DEFAULT, key); - - setConfigValue(this.config, key, defaultValue); - } + setConfigValue(this.config, key, ''); + } else if (key in SETTING_CONFIG_DEFAULT) { + setConfigValue(this.config, key, getConfigValue(SETTING_CONFIG_DEFAULT, key)); } this.userOverrides.delete(key);