summaryrefslogtreecommitdiff
path: root/llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-02-12 20:57:17 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-02-12 20:57:17 +0100
commitb333b06772c89d96aacb5490d6a219fba7c09cc6 (patch)
tree211df60083a5946baa2ed61d33d8121b7e251b06 /llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts
downloadllmnpc-b333b06772c89d96aacb5490d6a219fba7c09cc6.tar.gz
Engage!
Diffstat (limited to 'llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts')
-rw-r--r--llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts279
1 files changed, 279 insertions, 0 deletions
diff --git a/llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts b/llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts
new file mode 100644
index 0000000..d124cf5
--- /dev/null
+++ b/llama.cpp/tools/server/webui/src/lib/services/parameter-sync.ts
@@ -0,0 +1,279 @@
+/**
+ * ParameterSyncService - Handles synchronization between server defaults and user settings
+ *
+ * This service manages the complex logic of merging server-provided default parameters
+ * with user-configured overrides, ensuring the UI reflects the actual server state
+ * while preserving user customizations.
+ *
+ * **Key Responsibilities:**
+ * - Extract syncable parameters from server props
+ * - Merge server defaults with user overrides
+ * - Track parameter sources (server, user, default)
+ * - Provide sync utilities for settings store integration
+ */
+
+import { normalizeFloatingPoint } from '$lib/utils';
+
+export type ParameterSource = 'default' | 'custom';
+export type ParameterValue = string | number | boolean;
+export type ParameterRecord = Record<string, ParameterValue>;
+
+export interface ParameterInfo {
+ value: string | number | boolean;
+ source: ParameterSource;
+ serverDefault?: string | number | boolean;
+ userOverride?: string | number | boolean;
+}
+
+export interface SyncableParameter {
+ key: string;
+ serverKey: string;
+ type: 'number' | 'string' | 'boolean';
+ canSync: boolean;
+}
+
+/**
+ * Mapping of webui setting keys to server parameter keys
+ * Only parameters that should be synced from server are included
+ */
+export const SYNCABLE_PARAMETERS: SyncableParameter[] = [
+ { key: 'temperature', serverKey: 'temperature', type: 'number', canSync: true },
+ { key: 'top_k', serverKey: 'top_k', type: 'number', canSync: true },
+ { key: 'top_p', serverKey: 'top_p', type: 'number', canSync: true },
+ { key: 'min_p', serverKey: 'min_p', type: 'number', canSync: true },
+ { key: 'dynatemp_range', serverKey: 'dynatemp_range', type: 'number', canSync: true },
+ { key: 'dynatemp_exponent', serverKey: 'dynatemp_exponent', type: 'number', canSync: true },
+ { key: 'xtc_probability', serverKey: 'xtc_probability', type: 'number', canSync: true },
+ { key: 'xtc_threshold', serverKey: 'xtc_threshold', type: 'number', canSync: true },
+ { key: 'typ_p', serverKey: 'typ_p', type: 'number', canSync: true },
+ { key: 'repeat_last_n', serverKey: 'repeat_last_n', type: 'number', canSync: true },
+ { key: 'repeat_penalty', serverKey: 'repeat_penalty', type: 'number', canSync: true },
+ { key: 'presence_penalty', serverKey: 'presence_penalty', type: 'number', canSync: true },
+ { key: 'frequency_penalty', serverKey: 'frequency_penalty', type: 'number', canSync: true },
+ { key: 'dry_multiplier', serverKey: 'dry_multiplier', type: 'number', canSync: true },
+ { key: 'dry_base', serverKey: 'dry_base', type: 'number', canSync: true },
+ { key: 'dry_allowed_length', serverKey: 'dry_allowed_length', type: 'number', canSync: true },
+ { key: 'dry_penalty_last_n', serverKey: 'dry_penalty_last_n', type: 'number', canSync: true },
+ { key: 'max_tokens', serverKey: 'max_tokens', type: 'number', canSync: true },
+ { key: 'samplers', serverKey: 'samplers', type: 'string', canSync: true },
+ {
+ key: 'pasteLongTextToFileLen',
+ serverKey: 'pasteLongTextToFileLen',
+ type: 'number',
+ canSync: true
+ },
+ { key: 'pdfAsImage', serverKey: 'pdfAsImage', type: 'boolean', canSync: true },
+ {
+ key: 'showThoughtInProgress',
+ serverKey: 'showThoughtInProgress',
+ type: 'boolean',
+ canSync: true
+ },
+ { key: 'showToolCalls', serverKey: 'showToolCalls', type: 'boolean', canSync: true },
+ {
+ key: 'disableReasoningFormat',
+ serverKey: 'disableReasoningFormat',
+ type: 'boolean',
+ canSync: true
+ },
+ { key: 'keepStatsVisible', serverKey: 'keepStatsVisible', type: 'boolean', canSync: true },
+ { key: 'showMessageStats', serverKey: 'showMessageStats', type: 'boolean', canSync: true },
+ {
+ key: 'askForTitleConfirmation',
+ serverKey: 'askForTitleConfirmation',
+ type: 'boolean',
+ canSync: true
+ },
+ { key: 'disableAutoScroll', serverKey: 'disableAutoScroll', type: 'boolean', canSync: true },
+ {
+ key: 'renderUserContentAsMarkdown',
+ serverKey: 'renderUserContentAsMarkdown',
+ type: 'boolean',
+ canSync: true
+ },
+ { key: 'autoMicOnEmpty', serverKey: 'autoMicOnEmpty', type: 'boolean', canSync: true },
+ {
+ key: 'pyInterpreterEnabled',
+ serverKey: 'pyInterpreterEnabled',
+ type: 'boolean',
+ canSync: true
+ },
+ {
+ key: 'enableContinueGeneration',
+ serverKey: 'enableContinueGeneration',
+ type: 'boolean',
+ canSync: true
+ }
+];
+
+export class ParameterSyncService {
+ // ─────────────────────────────────────────────────────────────────────────────
+ // Extraction
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ /**
+ * Round floating-point numbers to avoid JavaScript precision issues
+ */
+ private static roundFloatingPoint(value: ParameterValue): ParameterValue {
+ return normalizeFloatingPoint(value) as ParameterValue;
+ }
+
+ /**
+ * Extract server default parameters that can be synced
+ */
+ static extractServerDefaults(
+ serverParams: ApiLlamaCppServerProps['default_generation_settings']['params'] | null,
+ webuiSettings?: Record<string, string | number | boolean>
+ ): ParameterRecord {
+ const extracted: ParameterRecord = {};
+
+ if (serverParams) {
+ for (const param of SYNCABLE_PARAMETERS) {
+ if (param.canSync && param.serverKey in serverParams) {
+ const value = (serverParams as unknown as Record<string, ParameterValue>)[
+ param.serverKey
+ ];
+ if (value !== undefined) {
+ // Apply precision rounding to avoid JavaScript floating-point issues
+ extracted[param.key] = this.roundFloatingPoint(value);
+ }
+ }
+ }
+
+ // Handle samplers array conversion to string
+ if (serverParams.samplers && Array.isArray(serverParams.samplers)) {
+ extracted.samplers = serverParams.samplers.join(';');
+ }
+ }
+
+ if (webuiSettings) {
+ for (const param of SYNCABLE_PARAMETERS) {
+ if (param.canSync && param.serverKey in webuiSettings) {
+ const value = webuiSettings[param.serverKey];
+ if (value !== undefined) {
+ extracted[param.key] = this.roundFloatingPoint(value);
+ }
+ }
+ }
+ }
+
+ return extracted;
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // Merging
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ /**
+ * Merge server defaults with current user settings
+ * Returns updated settings that respect user overrides while using server defaults
+ */
+ static mergeWithServerDefaults(
+ currentSettings: ParameterRecord,
+ serverDefaults: ParameterRecord,
+ userOverrides: Set<string> = new Set()
+ ): ParameterRecord {
+ const merged = { ...currentSettings };
+
+ for (const [key, serverValue] of Object.entries(serverDefaults)) {
+ // Only update if user hasn't explicitly overridden this parameter
+ if (!userOverrides.has(key)) {
+ merged[key] = this.roundFloatingPoint(serverValue);
+ }
+ }
+
+ return merged;
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // Info
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ /**
+ * Get parameter information including source and values
+ */
+ static getParameterInfo(
+ key: string,
+ currentValue: ParameterValue,
+ propsDefaults: ParameterRecord,
+ userOverrides: Set<string>
+ ): ParameterInfo {
+ const hasPropsDefault = propsDefaults[key] !== undefined;
+ const isUserOverride = userOverrides.has(key);
+
+ // Simple logic: either using default (from props) or custom (user override)
+ const source: ParameterSource = isUserOverride ? 'custom' : 'default';
+
+ return {
+ value: currentValue,
+ source,
+ serverDefault: hasPropsDefault ? propsDefaults[key] : undefined, // Keep same field name for compatibility
+ userOverride: isUserOverride ? currentValue : undefined
+ };
+ }
+
+ /**
+ * Check if a parameter can be synced from server
+ */
+ static canSyncParameter(key: string): boolean {
+ return SYNCABLE_PARAMETERS.some((param) => param.key === key && param.canSync);
+ }
+
+ /**
+ * Get all syncable parameter keys
+ */
+ static getSyncableParameterKeys(): string[] {
+ return SYNCABLE_PARAMETERS.filter((param) => param.canSync).map((param) => param.key);
+ }
+
+ /**
+ * Validate server parameter value
+ */
+ static validateServerParameter(key: string, value: ParameterValue): boolean {
+ const param = SYNCABLE_PARAMETERS.find((p) => p.key === key);
+ if (!param) return false;
+
+ switch (param.type) {
+ case 'number':
+ return typeof value === 'number' && !isNaN(value);
+ case 'string':
+ return typeof value === 'string';
+ case 'boolean':
+ return typeof value === 'boolean';
+ default:
+ return false;
+ }
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────────
+ // Diff
+ // ─────────────────────────────────────────────────────────────────────────────
+
+ /**
+ * Create a diff between current settings and server defaults
+ */
+ static createParameterDiff(
+ currentSettings: ParameterRecord,
+ serverDefaults: ParameterRecord
+ ): Record<string, { current: ParameterValue; server: ParameterValue; differs: boolean }> {
+ const diff: Record<
+ string,
+ { current: ParameterValue; server: ParameterValue; differs: boolean }
+ > = {};
+
+ for (const key of this.getSyncableParameterKeys()) {
+ const currentValue = currentSettings[key];
+ const serverValue = serverDefaults[key];
+
+ if (serverValue !== undefined) {
+ diff[key] = {
+ current: currentValue,
+ server: serverValue,
+ differs: currentValue !== serverValue
+ };
+ }
+ }
+
+ return diff;
+ }
+}