Skip to content

Commit 0c36854

Browse files
ServeurpersoComallozaur
authored andcommitted
Server becomes the source of truth for sampling parameter defaults (ggml-org#20558)
* 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 <aleksander.grygier@gmail.com>
1 parent 3731c76 commit 0c36854

5 files changed

Lines changed: 69 additions & 88 deletions

File tree

tools/server/public/index.html.gz

53 Bytes
Binary file not shown.

tools/server/webui/src/lib/components/app/chat/ChatSettings/ChatSettingsFields.svelte

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@
55
import Label from '$lib/components/ui/label/label.svelte';
66
import * as Select from '$lib/components/ui/select';
77
import { Textarea } from '$lib/components/ui/textarea';
8-
import { SETTING_CONFIG_DEFAULT, SETTING_CONFIG_INFO, SETTINGS_KEYS } from '$lib/constants';
8+
import { SETTING_CONFIG_INFO, SETTINGS_KEYS } from '$lib/constants';
99
import { SettingsFieldType } from '$lib/enums/settings';
1010
import { settingsStore } from '$lib/stores/settings.svelte';
11+
import { serverStore } from '$lib/stores/server.svelte';
12+
import { modelsStore, selectedModelName } from '$lib/stores/models.svelte';
13+
import { normalizeFloatingPoint } from '$lib/utils/precision';
1114
import { ChatSettingsParameterSourceIndicator } from '$lib/components/app';
1215
import type { Component } from 'svelte';
1316
@@ -20,35 +23,36 @@
2023
2124
let { fields, localConfig, onConfigChange, onThemeChange }: Props = $props();
2225
23-
// Helper function to get parameter source info for syncable parameters
24-
function getParameterSourceInfo(key: string) {
25-
if (!settingsStore.canSyncParameter(key)) {
26-
return null;
26+
// server sampling defaults for placeholders
27+
let sp = $derived.by(() => {
28+
if (serverStore.isRouterMode) {
29+
const m = selectedModelName();
30+
if (m) {
31+
const p = modelsStore.getModelProps(m);
32+
return (p?.default_generation_settings?.params ?? {}) as Record<string, unknown>;
33+
}
2734
}
28-
29-
return settingsStore.getParameterInfo(key);
30-
}
35+
return (serverStore.defaultParams ?? {}) as Record<string, unknown>;
36+
});
3137
</script>
3238

3339
{#each fields as field (field.key)}
3440
<div class="space-y-2">
3541
{#if field.type === SettingsFieldType.INPUT}
36-
{@const paramInfo = getParameterSourceInfo(field.key)}
3742
{@const currentValue = String(localConfig[field.key] ?? '')}
38-
{@const propsDefault = paramInfo?.serverDefault}
43+
{@const serverDefault = sp[field.key]}
3944
{@const isCustomRealTime = (() => {
40-
if (!paramInfo || propsDefault === undefined) return false;
45+
if (serverDefault == null) return false;
46+
if (currentValue === '') return false;
4147

42-
// Apply same rounding logic for real-time comparison
43-
const inputValue = currentValue;
44-
const numericInput = parseFloat(inputValue);
48+
const numericInput = parseFloat(currentValue);
4549
const normalizedInput = !isNaN(numericInput)
4650
? Math.round(numericInput * 1000000) / 1000000
47-
: inputValue;
51+
: currentValue;
4852
const normalizedDefault =
49-
typeof propsDefault === 'number'
50-
? Math.round(propsDefault * 1000000) / 1000000
51-
: propsDefault;
53+
typeof serverDefault === 'number'
54+
? Math.round(serverDefault * 1000000) / 1000000
55+
: serverDefault;
5256

5357
return normalizedInput !== normalizedDefault;
5458
})()}
@@ -74,17 +78,17 @@
7478
// Update local config immediately for real-time badge feedback
7579
onConfigChange(field.key, e.currentTarget.value);
7680
}}
77-
placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`}
81+
placeholder={sp[field.key] != null
82+
? `Default: ${normalizeFloatingPoint(sp[field.key])}`
83+
: ''}
7884
class="w-full {isCustomRealTime ? 'pr-8' : ''}"
7985
/>
8086
{#if isCustomRealTime}
8187
<button
8288
type="button"
8389
onclick={() => {
8490
settingsStore.resetParameterToServerDefault(field.key);
85-
// Trigger UI update by calling onConfigChange with the default value
86-
const defaultValue = propsDefault ?? SETTING_CONFIG_DEFAULT[field.key];
87-
onConfigChange(field.key, String(defaultValue));
91+
onConfigChange(field.key, '');
8892
}}
8993
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"
9094
aria-label="Reset to default"
@@ -112,7 +116,7 @@
112116
id={field.key}
113117
value={String(localConfig[field.key] ?? '')}
114118
onchange={(e) => onConfigChange(field.key, e.currentTarget.value)}
115-
placeholder={`Default: ${SETTING_CONFIG_DEFAULT[field.key] ?? 'none'}`}
119+
placeholder=""
116120
class="min-h-[10rem] w-full md:max-w-2xl"
117121
/>
118122

@@ -140,14 +144,12 @@
140144
(opt: { value: string; label: string; icon?: Component }) =>
141145
opt.value === localConfig[field.key]
142146
)}
143-
{@const paramInfo = getParameterSourceInfo(field.key)}
144147
{@const currentValue = localConfig[field.key]}
145-
{@const propsDefault = paramInfo?.serverDefault}
148+
{@const serverDefault = sp[field.key]}
146149
{@const isCustomRealTime = (() => {
147-
if (!paramInfo || propsDefault === undefined) return false;
148-
149-
// For select fields, do direct comparison (no rounding needed)
150-
return currentValue !== propsDefault;
150+
if (serverDefault == null) return false;
151+
if (currentValue === '' || currentValue === undefined) return false;
152+
return currentValue !== serverDefault;
151153
})()}
152154

153155
<div class="flex items-center gap-2">
@@ -190,9 +192,7 @@
190192
type="button"
191193
onclick={() => {
192194
settingsStore.resetParameterToServerDefault(field.key);
193-
// Trigger UI update by calling onConfigChange with the default value
194-
const defaultValue = propsDefault ?? SETTING_CONFIG_DEFAULT[field.key];
195-
onConfigChange(field.key, String(defaultValue));
195+
onConfigChange(field.key, '');
196196
}}
197197
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"
198198
aria-label="Reset to default"

tools/server/webui/src/lib/constants/settings-config.ts

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { ColorMode } from '$lib/enums/ui';
22
import { Monitor, Moon, Sun } from '@lucide/svelte';
33

4-
export const SETTING_CONFIG_DEFAULT: Record<string, string | number | boolean> = {
5-
// 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.
4+
export const SETTING_CONFIG_DEFAULT: Record<string, string | number | boolean | undefined> = {
5+
// 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.
66
// Do not use nested objects, keep it single level. Prefix the key if you need to group them.
77
apiKey: '',
88
systemMessage: '',
@@ -30,27 +30,30 @@ export const SETTING_CONFIG_DEFAULT: Record<string, string | number | boolean> =
3030
agenticMaxToolPreviewLines: 25,
3131
showToolCallInProgress: false,
3232
alwaysShowAgenticTurns: false,
33-
// make sure these default values are in sync with `common.h`
34-
samplers: 'top_k;typ_p;top_p;min_p;temperature',
33+
// sampling params: empty means "use server default"
34+
// the server / preset is the source of truth
35+
// empty values are shown as placeholders from /props in the UI
36+
// and are NOT sent in API requests, letting the server decide
37+
samplers: '',
3538
backend_sampling: false,
36-
temperature: 0.8,
37-
dynatemp_range: 0.0,
38-
dynatemp_exponent: 1.0,
39-
top_k: 40,
40-
top_p: 0.95,
41-
min_p: 0.05,
42-
xtc_probability: 0.0,
43-
xtc_threshold: 0.1,
44-
typ_p: 1.0,
45-
repeat_last_n: 64,
46-
repeat_penalty: 1.0,
47-
presence_penalty: 0.0,
48-
frequency_penalty: 0.0,
49-
dry_multiplier: 0.0,
50-
dry_base: 1.75,
51-
dry_allowed_length: 2,
52-
dry_penalty_last_n: -1,
53-
max_tokens: -1,
39+
temperature: undefined,
40+
dynatemp_range: undefined,
41+
dynatemp_exponent: undefined,
42+
top_k: undefined,
43+
top_p: undefined,
44+
min_p: undefined,
45+
xtc_probability: undefined,
46+
xtc_threshold: undefined,
47+
typ_p: undefined,
48+
repeat_last_n: undefined,
49+
repeat_penalty: undefined,
50+
presence_penalty: undefined,
51+
frequency_penalty: undefined,
52+
dry_multiplier: undefined,
53+
dry_base: undefined,
54+
dry_allowed_length: undefined,
55+
dry_penalty_last_n: undefined,
56+
max_tokens: undefined,
5457
custom: '', // custom json-stringified object
5558
// experimental features
5659
pyInterpreterEnabled: false,

tools/server/webui/src/lib/stores/settings.svelte.ts

Lines changed: 11 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -289,16 +289,10 @@ class SettingsStore {
289289
const serverDefaults = this.getServerDefaults();
290290

291291
if (serverDefaults[key] !== undefined) {
292-
const value = normalizeFloatingPoint(serverDefaults[key]);
293-
294-
this.config[key as keyof SettingsConfigType] =
295-
value as SettingsConfigType[keyof SettingsConfigType];
296-
} else {
297-
if (key in SETTING_CONFIG_DEFAULT) {
298-
const defaultValue = getConfigValue(SETTING_CONFIG_DEFAULT, key);
299-
300-
setConfigValue(this.config, key, defaultValue);
301-
}
292+
// sampling param known by server: clear it, let server decide
293+
setConfigValue(this.config, key, '');
294+
} else if (key in SETTING_CONFIG_DEFAULT) {
295+
setConfigValue(this.config, key, getConfigValue(SETTING_CONFIG_DEFAULT, key));
302296
}
303297

304298
this.userOverrides.delete(key);
@@ -319,30 +313,22 @@ class SettingsStore {
319313
*/
320314
syncWithServerDefaults(): void {
321315
const propsDefaults = this.getServerDefaults();
322-
323-
if (Object.keys(propsDefaults).length === 0) {
324-
console.warn('No server defaults available for initialization');
325-
326-
return;
327-
}
316+
if (Object.keys(propsDefaults).length === 0) return;
328317

329318
for (const [key, propsValue] of Object.entries(propsDefaults)) {
330319
const currentValue = getConfigValue(this.config, key);
331320

332321
const normalizedCurrent = normalizeFloatingPoint(currentValue);
333322
const normalizedDefault = normalizeFloatingPoint(propsValue);
334323

324+
// if user value matches server, it's not a real override
335325
if (normalizedCurrent === normalizedDefault) {
336326
this.userOverrides.delete(key);
337-
setConfigValue(this.config, key, propsValue);
338-
} else if (!this.userOverrides.has(key)) {
339-
setConfigValue(this.config, key, propsValue);
340327
}
341328
}
342329

343330
this.saveConfig();
344-
console.log('Settings initialized with props defaults:', propsDefaults);
345-
console.log('Current user overrides after sync:', Array.from(this.userOverrides));
331+
console.log('User overrides after sync:', Array.from(this.userOverrides));
346332
}
347333

348334
/**
@@ -352,19 +338,11 @@ class SettingsStore {
352338
*/
353339
forceSyncWithServerDefaults(): void {
354340
const propsDefaults = this.getServerDefaults();
355-
const syncableKeys = ParameterSyncService.getSyncableParameterKeys();
356-
357-
for (const key of syncableKeys) {
341+
for (const key of ParameterSyncService.getSyncableParameterKeys()) {
358342
if (propsDefaults[key] !== undefined) {
359-
const normalizedValue = normalizeFloatingPoint(propsDefaults[key]);
360-
361-
setConfigValue(this.config, key, normalizedValue);
362-
} else {
363-
if (key in SETTING_CONFIG_DEFAULT) {
364-
const defaultValue = getConfigValue(SETTING_CONFIG_DEFAULT, key);
365-
366-
setConfigValue(this.config, key, defaultValue);
367-
}
343+
setConfigValue(this.config, key, '');
344+
} else if (key in SETTING_CONFIG_DEFAULT) {
345+
setConfigValue(this.config, key, getConfigValue(SETTING_CONFIG_DEFAULT, key));
368346
}
369347

370348
this.userOverrides.delete(key);

tools/server/webui/src/lib/types/settings.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { DatabaseMessageExtra } from './database';
55
import type { ParameterSource, SyncableParameterType, SettingsFieldType } from '$lib/enums';
66
import type { Icon } from '@lucide/svelte';
77

8-
export type SettingsConfigValue = string | number | boolean;
8+
export type SettingsConfigValue = string | number | boolean | undefined;
99

1010
export interface SettingsFieldConfig {
1111
key: string;

0 commit comments

Comments
 (0)