1import { describe, it, expect } from 'vitest';
  2import { ParameterSyncService } from './parameter-sync';
  3
  4describe('ParameterSyncService', () => {
  5	describe('roundFloatingPoint', () => {
  6		it('should fix JavaScript floating-point precision issues', () => {
  7			// Test the specific values from the screenshot
  8			const mockServerParams = {
  9				top_p: 0.949999988079071,
 10				min_p: 0.009999999776482582,
 11				temperature: 0.800000011920929,
 12				top_k: 40,
 13				samplers: ['top_k', 'typ_p', 'top_p', 'min_p', 'temperature']
 14			};
 15
 16			const result = ParameterSyncService.extractServerDefaults({
 17				...mockServerParams,
 18				// Add other required fields to match the API type
 19				n_predict: 512,
 20				seed: -1,
 21				dynatemp_range: 0.0,
 22				dynatemp_exponent: 1.0,
 23				xtc_probability: 0.0,
 24				xtc_threshold: 0.1,
 25				typ_p: 1.0,
 26				repeat_last_n: 64,
 27				repeat_penalty: 1.0,
 28				presence_penalty: 0.0,
 29				frequency_penalty: 0.0,
 30				dry_multiplier: 0.0,
 31				dry_base: 1.75,
 32				dry_allowed_length: 2,
 33				dry_penalty_last_n: -1,
 34				mirostat: 0,
 35				mirostat_tau: 5.0,
 36				mirostat_eta: 0.1,
 37				stop: [],
 38				max_tokens: -1,
 39				n_keep: 0,
 40				n_discard: 0,
 41				ignore_eos: false,
 42				stream: true,
 43				logit_bias: [],
 44				n_probs: 0,
 45				min_keep: 0,
 46				grammar: '',
 47				grammar_lazy: false,
 48				grammar_triggers: [],
 49				preserved_tokens: [],
 50				chat_format: '',
 51				reasoning_format: '',
 52				reasoning_in_content: false,
 53				thinking_forced_open: false,
 54				'speculative.n_max': 0,
 55				'speculative.n_min': 0,
 56				'speculative.p_min': 0.0,
 57				timings_per_token: false,
 58				post_sampling_probs: false,
 59				lora: [],
 60				top_n_sigma: 0.0,
 61				dry_sequence_breakers: []
 62			} as ApiLlamaCppServerProps['default_generation_settings']['params']);
 63
 64			// Check that the problematic floating-point values are rounded correctly
 65			expect(result.top_p).toBe(0.95);
 66			expect(result.min_p).toBe(0.01);
 67			expect(result.temperature).toBe(0.8);
 68			expect(result.top_k).toBe(40); // Integer should remain unchanged
 69			expect(result.samplers).toBe('top_k;typ_p;top_p;min_p;temperature');
 70		});
 71
 72		it('should preserve non-numeric values', () => {
 73			const mockServerParams = {
 74				samplers: ['top_k', 'temperature'],
 75				max_tokens: -1,
 76				temperature: 0.7
 77			};
 78
 79			const result = ParameterSyncService.extractServerDefaults({
 80				...mockServerParams,
 81				// Minimal required fields
 82				n_predict: 512,
 83				seed: -1,
 84				dynatemp_range: 0.0,
 85				dynatemp_exponent: 1.0,
 86				top_k: 40,
 87				top_p: 0.95,
 88				min_p: 0.05,
 89				xtc_probability: 0.0,
 90				xtc_threshold: 0.1,
 91				typ_p: 1.0,
 92				repeat_last_n: 64,
 93				repeat_penalty: 1.0,
 94				presence_penalty: 0.0,
 95				frequency_penalty: 0.0,
 96				dry_multiplier: 0.0,
 97				dry_base: 1.75,
 98				dry_allowed_length: 2,
 99				dry_penalty_last_n: -1,
100				mirostat: 0,
101				mirostat_tau: 5.0,
102				mirostat_eta: 0.1,
103				stop: [],
104				n_keep: 0,
105				n_discard: 0,
106				ignore_eos: false,
107				stream: true,
108				logit_bias: [],
109				n_probs: 0,
110				min_keep: 0,
111				grammar: '',
112				grammar_lazy: false,
113				grammar_triggers: [],
114				preserved_tokens: [],
115				chat_format: '',
116				reasoning_format: '',
117				reasoning_in_content: false,
118				thinking_forced_open: false,
119				'speculative.n_max': 0,
120				'speculative.n_min': 0,
121				'speculative.p_min': 0.0,
122				timings_per_token: false,
123				post_sampling_probs: false,
124				lora: [],
125				top_n_sigma: 0.0,
126				dry_sequence_breakers: []
127			} as ApiLlamaCppServerProps['default_generation_settings']['params']);
128
129			expect(result.samplers).toBe('top_k;temperature');
130			expect(result.max_tokens).toBe(-1);
131			expect(result.temperature).toBe(0.7);
132		});
133
134		it('should merge webui settings from props when provided', () => {
135			const result = ParameterSyncService.extractServerDefaults(null, {
136				pasteLongTextToFileLen: 0,
137				pdfAsImage: true,
138				renderUserContentAsMarkdown: false,
139				theme: 'dark'
140			});
141
142			expect(result.pasteLongTextToFileLen).toBe(0);
143			expect(result.pdfAsImage).toBe(true);
144			expect(result.renderUserContentAsMarkdown).toBe(false);
145			expect(result.theme).toBeUndefined();
146		});
147	});
148});