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});