]> git.djapps.eu Git - pkg/ggml/sources/llama.cpp/commitdiff
Server becomes the source of truth for sampling parameter defaults (#20558)
authorPascal <redacted>
Thu, 19 Mar 2026 12:20:39 +0000 (13:20 +0100)
committerGitHub <redacted>
Thu, 19 Mar 2026 12:20:39 +0000 (13:20 +0100)
* webui: make server the source of truth for sampling defaults

* webui: fix Custom badge for sampling parameters

* webui: log user overrides after server sync

* chore: update webui build output

* fix: Default values for sampling settings config object

* chore: update webui build output

---------

Co-authored-by: Aleksander Grygier <redacted>
tools/server/public/index.html.gz
tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte
tools/server/webui/src/lib/constants/settings-config.ts
tools/server/webui/src/lib/stores/settings.svelte.ts
tools/server/webui/src/lib/types/settings.d.ts

index ea4e6ed8ab1b06817accf57d407549bbd703c560..f1ccf5a755796c98813badaaf93bfc9798ea2400 100644 (file)
Binary files a/tools/server/public/index.html.gz and b/tools/server/public/index.html.gz differ
index b9015c196c1068dbb9e8de9e75f1e3257d44e604..42191be89fe8d2b76117725a57f5284dad102263 100644 (file)
@@ -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';
 
 
        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<string, unknown>;
+                       }
                }
-
-               return settingsStore.getParameterInfo(key);
-       }
+               return (serverStore.defaultParams ?? {}) as Record<string, unknown>;
+       });
 </script>
 
 {#each fields as field (field.key)}
        <div class="space-y-2">
                {#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"
                                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"
                        />
 
                                (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;
                        })()}
 
                        <div class="flex items-center gap-2">
                                                        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"
index e76fa89e9a3eff1dc569e1429744feef82b68615..39aaf561bb79df2b633c901d3247e34a30f00f0c 100644 (file)
@@ -1,8 +1,8 @@
 import { ColorMode } from '$lib/enums/ui';
 import { Monitor, Moon, Sun } from '@lucide/svelte';
 
-export const SETTING_CONFIG_DEFAULT: Record<string, string | number | boolean> = {
-       // Note: in order not to introduce breaking changes, please keep the same data type (number, string, etc) if you want to change the default value. Do not use null or undefined for default value.
+export const SETTING_CONFIG_DEFAULT: Record<string, string | number | boolean | undefined> = {
+       // Note: in order not to introduce breaking changes, please keep the same data type (number, string, etc) if you want to change the default value.
        // Do not use nested objects, keep it single level. Prefix the key if you need to group them.
        apiKey: '',
        systemMessage: '',
@@ -30,27 +30,30 @@ export const SETTING_CONFIG_DEFAULT: Record<string, string | number | boolean> =
        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: undefined,
+       dynatemp_range: undefined,
+       dynatemp_exponent: undefined,
+       top_k: undefined,
+       top_p: undefined,
+       min_p: undefined,
+       xtc_probability: undefined,
+       xtc_threshold: undefined,
+       typ_p: undefined,
+       repeat_last_n: undefined,
+       repeat_penalty: undefined,
+       presence_penalty: undefined,
+       frequency_penalty: undefined,
+       dry_multiplier: undefined,
+       dry_base: undefined,
+       dry_allowed_length: undefined,
+       dry_penalty_last_n: undefined,
+       max_tokens: undefined,
        custom: '', // custom json-stringified object
        // experimental features
        pyInterpreterEnabled: false,
index 8ab817c071aedea95d72ceaad09c79476a8392a8..2fbff8312f4d601dafe7d1bd4a305eab79ae68df 100644 (file)
@@ -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);
index 67194d12ec0174fb2d78ff57dbae4bdc4cb29525..360740ab01983bc052f1d786a53a062568b823bf 100644 (file)
@@ -5,7 +5,7 @@ import type { DatabaseMessageExtra } from './database';
 import type { ParameterSource, SyncableParameterType, SettingsFieldType } from '$lib/enums';
 import type { Icon } from '@lucide/svelte';
 
-export type SettingsConfigValue = string | number | boolean;
+export type SettingsConfigValue = string | number | boolean | undefined;
 
 export interface SettingsFieldConfig {
        key: string;