1use crate::config::Profile;
2use crate::models::{CacheQuantType, GpuLayersMode, Mirostat, ModelSettings, NumMode, SplitMode};
3use ratatui::{
4 style::{Color, Modifier, Style},
5 text::{Line, Span},
6};
7
8pub type DisplayFn = fn(&ModelSettings) -> String;
11pub type DirtyFn = fn(&ModelSettings, &ModelSettings) -> bool;
12pub type AdjustFn = fn(&mut ModelSettings, i32, u32); pub type ApplyEditFn = fn(&mut ModelSettings, &str);
14pub type CtrlEToggleFn = fn(&mut ModelSettings);
15
16pub struct SettingField {
19 pub id: &'static str,
20 pub name: &'static str,
21 pub section: &'static str,
22 pub display: DisplayFn,
23 pub dirty: DirtyFn,
24 pub adjust: AdjustFn,
25 pub apply_edit: ApplyEditFn,
26 pub ctrl_e_toggle: Option<CtrlEToggleFn>,
27 pub is_expert: bool,
28 pub is_ultra: bool,
29 pub is_enabled: Option<fn(&ModelSettings) -> bool>,
30 #[allow(dead_code)]
31 pub help_text: &'static str,
32}
33
34impl SettingField {
35 pub fn name(&self) -> &str {
36 self.name
37 }
38
39 pub fn display(&self, settings: &ModelSettings) -> String {
40 (self.display)(settings)
41 }
42
43 pub fn is_dirty(&self, settings: &ModelSettings, cached: &ModelSettings) -> bool {
44 (self.dirty)(settings, cached)
45 }
46
47 pub fn adjust(&self, settings: &mut ModelSettings, delta: i32, context_limit: u32) {
48 (self.adjust)(settings, delta, context_limit);
49 }
50
51 pub fn apply_edit(&self, settings: &mut ModelSettings, buf: &str) {
52 (self.apply_edit)(settings, buf);
53 }
54
55 pub fn ctrl_e_toggle(&self, settings: &mut ModelSettings) {
56 if let Some(toggle) = self.ctrl_e_toggle {
57 toggle(settings);
58 }
59 }
60
61 pub fn is_new_section(&self, prev_section: Option<&str>) -> bool {
63 Some(self.section) != prev_section
64 }
65}
66
67macro_rules! make_field_fn {
74 ($fn:ident, $expert:expr, $ultra:expr, toggle) => {
75 fn $fn(
76 id: &'static str,
77 name: &'static str,
78 section: &'static str,
79 display: DisplayFn,
80 dirty: DirtyFn,
81 adjust: AdjustFn,
82 apply_edit: ApplyEditFn,
83 ctrl_e_toggle: CtrlEToggleFn,
84 _help_text: &'static str,
85 ) -> SettingField {
86 SettingField {
87 id,
88 name,
89 section,
90 display,
91 dirty,
92 adjust,
93 apply_edit,
94 ctrl_e_toggle: Some(ctrl_e_toggle),
95 is_expert: $expert,
96 is_ultra: $ultra,
97 is_enabled: None,
98 help_text: "",
99 }
100 }
101 };
102 ($fn:ident, $expert:expr, $ultra:expr, @none) => {
103 fn $fn(
104 id: &'static str,
105 name: &'static str,
106 section: &'static str,
107 display: DisplayFn,
108 dirty: DirtyFn,
109 adjust: AdjustFn,
110 apply_edit: ApplyEditFn,
111 _help_text: &'static str,
112 ) -> SettingField {
113 SettingField {
114 id,
115 name,
116 section,
117 display,
118 dirty,
119 adjust,
120 apply_edit,
121 ctrl_e_toggle: None,
122 is_expert: $expert,
123 is_ultra: $ultra,
124 is_enabled: None,
125 help_text: "",
126 }
127 }
128 };
129}
130
131make_field_fn!(field, false, false, @none);
132make_field_fn!(expert_field, true, false, @none);
133make_field_fn!(ultra_field, true, true, @none);
134make_field_fn!(field_with_toggle, false, false, toggle);
135make_field_fn!(expert_field_with_toggle, true, false, toggle);
136make_field_fn!(ultra_field_with_toggle, true, true, toggle);
137
138fn gpu_layers_adjust(settings: &mut ModelSettings, delta: i32, _context_limit: u32) {
141 settings.gpu_layers_mode = match (delta, &settings.gpu_layers_mode) {
142 (1, GpuLayersMode::Auto) => GpuLayersMode::Specific(1),
143 (1, GpuLayersMode::Specific(n)) => GpuLayersMode::Specific(n + 1),
144 (1, GpuLayersMode::All) => GpuLayersMode::Auto,
145 (-1, GpuLayersMode::Auto) => GpuLayersMode::Auto,
146 (-1, GpuLayersMode::Specific(n)) if *n == 0 => GpuLayersMode::Auto,
147 (-1, GpuLayersMode::Specific(n)) if *n == 1 => GpuLayersMode::Specific(0),
148 (-1, GpuLayersMode::Specific(n)) => GpuLayersMode::Specific(n - 1),
149 (-1, GpuLayersMode::All) => GpuLayersMode::All,
150 _ => settings.gpu_layers_mode,
151 };
152}
153
154fn gpu_layers_apply(settings: &mut ModelSettings, buf: &str) {
155 if let Ok(v) = buf.parse::<i32>() {
156 settings.gpu_layers_mode = if v < 0 {
157 GpuLayersMode::All
158 } else {
159 GpuLayersMode::Specific(v as u32)
160 };
161 }
162}
163
164fn toggle_mlock(settings: &mut ModelSettings) {
165 settings.mlock = !settings.mlock;
166}
167fn toggle_flash_attn(settings: &mut ModelSettings) {
168 settings.flash_attn = !settings.flash_attn;
169}
170
171fn toggle_fit(settings: &mut ModelSettings) {
172 settings.fit = !settings.fit;
173}
174fn toggle_kv_cache_offload(settings: &mut ModelSettings) {
175 settings.kv_cache_offload = !settings.kv_cache_offload;
176}
177fn toggle_uniform_cache(settings: &mut ModelSettings) {
178 settings.uniform_cache = !settings.uniform_cache;
179}
180fn toggle_swa_full(settings: &mut ModelSettings) {
181 settings.swa_full = !settings.swa_full;
182}
183fn toggle_mtp(settings: &mut ModelSettings) {
184 if settings.spec_type.is_empty() {
185 settings.spec_type = "draft-mtp".to_string();
186 } else {
187 settings.spec_type = String::new();
188 }
189}
190
191fn toggle_rope_yarn_enabled(settings: &mut ModelSettings) {
192 settings.rope_yarn_enabled = !settings.rope_yarn_enabled;
193}
194fn toggle_ignore_eos(settings: &mut ModelSettings) {
195 settings.ignore_eos = !settings.ignore_eos;
196}
197fn toggle_max_tokens(settings: &mut ModelSettings) {
198 settings.max_tokens = settings.max_tokens.map_or(Some(2048), |_| None);
199}
200fn toggle_max_concurrent_predictions(settings: &mut ModelSettings) {
201 settings.max_concurrent_predictions = settings
202 .max_concurrent_predictions
203 .map_or(Some(1), |_| None);
204}
205fn toggle_cache_type_k(settings: &mut ModelSettings) {
206 settings.cache_type_k = settings
207 .cache_type_k
208 .map_or(Some(CacheQuantType::F16), |_| None);
209}
210fn toggle_cache_type_v(settings: &mut ModelSettings) {
211 settings.cache_type_v = settings
212 .cache_type_v
213 .map_or(Some(CacheQuantType::F16), |_| None);
214}
215fn toggle_expert_count(settings: &mut ModelSettings) {
216 settings.expert_count = match settings.expert_count {
217 0 => 1,
218 -1 => 0,
219 _ => -1,
220 };
221}
222fn toggle_presence_penalty(settings: &mut ModelSettings) {
223 settings.presence_penalty = settings.presence_penalty.map_or(Some(0.0), |_| None);
224}
225fn toggle_frequency_penalty(settings: &mut ModelSettings) {
226 settings.frequency_penalty = settings.frequency_penalty.map_or(Some(0.0), |_| None);
227}
228
229macro_rules! diff_int {
232 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
233 if let Some(v) = $s.$field
234 && v != $c.$field
235 {
236 $parts.push(format!("{}={}", $label, v));
237 }
238 };
239}
240macro_rules! diff_float {
241 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
242 if let Some(v) = $s.$field
243 && (v - $c.$field).abs() > 0.001
244 {
245 $parts.push(format!("{}={:.2}", $label, v));
246 }
247 };
248}
249macro_rules! diff_bool {
250 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
251 if let Some(v) = $s.$field
252 && v != $c.$field
253 {
254 $parts.push(format!("{}={}", $label, v));
255 }
256 };
257}
258macro_rules! diff_string {
259 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
260 if let Some(v) = &$s.$field
261 && v != &$c.$field
262 {
263 $parts.push(format!("{}={}", $label, v));
264 }
265 };
266}
267macro_rules! diff_enum {
268 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
269 if let Some(ref v) = $s.$field
270 && *v != $c.$field
271 {
272 $parts.push(format!("{}={}", $label, v));
273 }
274 };
275}
276macro_rules! diff_option {
277 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
278 if $s.$field != $c.$field {
279 if let Some(ref v) = $s.$field {
280 $parts.push(format!("{}={}", $label, v));
281 }
282 }
283 };
284}
285macro_rules! diff_option_float {
286 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
287 if let Some(v) = $s.$field {
288 let current_val = $c.$field.unwrap_or(0.0);
289 if (v - current_val).abs() > 0.001 {
290 $parts.push(format!("{}={:.2}", $label, v));
291 }
292 }
293 };
294}
295
296pub fn all_fields() -> Vec<SettingField> {
299 vec![
300 field(
302 "system_prompt_preset_name",
303 "Prompt",
304 "Loading",
305 |s| s.system_prompt_preset_name.clone(),
306 |s, c| s.system_prompt_preset_name != c.system_prompt_preset_name,
307 |_, _, _| {},
308 |_, _| {},
309 "System prompt preset. Pre-configured prompts that shape how the model behaves (e.g., 'coder', 'assistant', 'creative'). Affects the model's personality and output style.",
310 ),
311 field(
312 "context_length",
313 "Context",
314 "Loading",
315 |s| {
316 if s.rope_yarn_enabled && s.rope_scale > 1.0 {
317 let extended = (s.context_length as f64 * s.rope_scale as f64) as u32;
318 format!("{} ({})", s.context_length, extended)
319 } else {
320 s.context_length.to_string()
321 }
322 },
323 |s, c| s.context_length != c.context_length,
324 |s, delta, ctx_limit| {
325 let mut val = (s.context_length as i32 + delta * 128).max(128) as u32;
326 if ctx_limit > 0 {
327 val = val.min(ctx_limit);
328 }
329 s.context_length = val;
330 },
331 |s, buf| {
332 if let Ok(v) = buf.parse::<u32>() {
333 s.context_length = v.max(128);
334 }
335 },
336 "Context window size in tokens. Determines how much of the conversation history is kept in memory. A larger context allows longer conversations but uses more RAM. Typical: 32k-256k depending on model and RAM.",
337 ),
338 expert_field_with_toggle(
339 "rope_yarn_enabled",
340 "Yarn RoPE",
341 "Loading",
342 |s| s.rope_yarn_enabled.to_string(),
343 |s, c| s.rope_yarn_enabled != c.rope_yarn_enabled,
344 |_, _, _| {},
345 |_, _| {},
346 toggle_rope_yarn_enabled,
347 "Enable YaRN (Yet another RoPE extensioN) for scaling context beyond training limits. YaRN uses a frequency rescaling technique to handle longer contexts. Toggle on/off with Enter.",
348 ),
349 {
350 let mut f = expert_field(
351 "yarn_params",
352 "Yarn Params",
353 "Loading",
354 |s| {
355 format!(
356 "scale={:.2} base={:.2} scale_f={:.2}",
357 s.rope_scale, s.rope_freq_base, s.rope_freq_scale
358 )
359 },
360 |s, c| {
361 s.rope_scale != c.rope_scale
362 || s.rope_freq_base != c.rope_freq_base
363 || s.rope_freq_scale != c.rope_freq_scale
364 },
365 |_, _, _| {},
366 |_, _| {},
367 "YaRN configuration: rope_scale (context multiplier), rope_freq_base (frequency base), rope_freq_scale (frequency scaling). Press Enter to open the YaRN parameter editor.",
368 );
369 f.is_enabled = Some(|s| s.rope_yarn_enabled);
370 f
371 },
372 ultra_field(
373 "threads_batch",
374 "Threads Batch",
375 "Loading",
376 |s| s.threads_batch.to_string(),
377 |s, c| s.threads_batch != c.threads_batch,
378 |s, delta, _| {
379 s.threads_batch = (s.threads_batch as i32 + delta).max(1) as u32;
380 },
381 |s, buf| {
382 if let Ok(v) = buf.parse::<u32>() {
383 s.threads_batch = v.max(1);
384 }
385 },
386 "CPU threads for batch processing (1 to 32). Separate from Threads (inference threads). Keep equal for most workloads, or reduce batch threads to lower CPU usage during batch operations.",
387 ),
388 ultra_field(
389 "ubatch_size",
390 "UBatch Size",
391 "Loading",
392 |s| s.ubatch_size.to_string(),
393 |s, c| s.ubatch_size != c.ubatch_size,
394 |s, delta, _| {
395 s.ubatch_size = (s.ubatch_size as i32 + delta * 64).max(1) as u32;
396 },
397 |s, buf| {
398 if let Ok(v) = buf.parse::<u32>() {
399 s.ubatch_size = v.max(1);
400 }
401 },
402 "Unlimited batch size for prompt processing. Larger values improve prompt evaluation throughput but use more RAM. Typical: 512-2048. Set to 0 to match context_length.",
403 ),
404 ultra_field(
405 "keep",
406 "Keep",
407 "Loading",
408 |s| s.keep.to_string(),
409 |s, c| s.keep != c.keep,
410 |s, delta, _| {
411 s.keep = (s.keep + delta).max(0);
412 },
413 |s, buf| {
414 if let Ok(v) = buf.parse::<i32>() {
415 s.keep = v;
416 }
417 },
418 "Number of layers to keep in memory when swapping (negative = all). Useful for fast reloading of the same model. Typical: -1 (all) or 0 (none).",
419 ),
420 field_with_toggle(
421 "mlock",
422 "Keep in memory (mlock)",
423 "Loading",
424 |s| s.mlock.to_string(),
425 |s, c| s.mlock != c.mlock,
426 |_, _, _| {},
427 |_, _| {},
428 toggle_mlock,
429 "Lock model weights in RAM (mlock). Prevents the OS from swapping model weights to disk. Slows model load time but ensures faster inference once loaded. Useful for repeated use.",
430 ),
431 expert_field(
432 "numa",
433 "NUMA",
434 "Loading",
435 |s| s.numa.to_string(),
436 |s, c| s.numa != c.numa,
437 |s, delta, _| {
438 let mut val = s.numa;
439 val = match (delta, val) {
440 (1, NumMode::None) => NumMode::Distribute,
441 (1, NumMode::Distribute) => NumMode::Isolate,
442 (1, NumMode::Isolate) => NumMode::Numactl,
443 (1, NumMode::Numactl) => NumMode::None,
444 (-1, NumMode::None) => NumMode::Numactl,
445 (-1, NumMode::Distribute) => NumMode::None,
446 (-1, NumMode::Isolate) => NumMode::Distribute,
447 (-1, NumMode::Numactl) => NumMode::Isolate,
448 _ => val,
449 };
450 s.numa = val;
451 },
452 |_, _| {},
453 "NUMA (Non-Uniform Memory Access) strategy: None, Distribute, Isolate, or Numactl. Affects CPU thread affinity on multi-socket systems. None = default.",
454 ),
455 field(
457 "gpu_layers_mode",
458 "GPU Layers",
459 "GPU Offload",
460 |s| match s.gpu_layers_mode {
461 GpuLayersMode::Auto => "Auto".to_string(),
462 GpuLayersMode::Specific(n) => n.to_string(),
463 GpuLayersMode::All => "All".to_string(),
464 },
465 |s, c| s.gpu_layers_mode != c.gpu_layers_mode,
466 gpu_layers_adjust,
467 gpu_layers_apply,
468 "How many model layers to offload to GPU. Arrow keys cycle: Auto → 1 → 2 → ... → N → All → Auto. Auto lets llama.cpp decide based on VRAM. All loads every layer (999). Specific number sets exact offload count.",
469 ),
470 ultra_field(
471 "split_mode",
472 "Split Mode",
473 "GPU Offload",
474 |s| s.split_mode.to_string(),
475 |s, c| s.split_mode != c.split_mode,
476 |s, delta, _| {
477 let mut val = s.split_mode;
478 val = match (delta, val) {
479 (1, SplitMode::None) => SplitMode::Layer,
480 (1, SplitMode::Layer) => SplitMode::Row,
481 (1, SplitMode::Row) => SplitMode::Tensor,
482 (1, SplitMode::Tensor) => SplitMode::None,
483 (-1, SplitMode::None) => SplitMode::Tensor,
484 (-1, SplitMode::Layer) => SplitMode::None,
485 (-1, SplitMode::Row) => SplitMode::Layer,
486 (-1, SplitMode::Tensor) => SplitMode::Row,
487 _ => val,
488 };
489 s.split_mode = val;
490 },
491 |_, _| {},
492 "GPU split strategy: None, Layer (default), Row, or Tensor. Controls how model layers are distributed across multiple GPUs. Layer splits by layer count, Row/Tensor split by matrix dimensions for multi-GPU setups.",
493 ),
494 ultra_field(
495 "tensor_split",
496 "Tensor Split",
497 "GPU Offload",
498 |s| s.tensor_split.clone(),
499 |s, c| s.tensor_split != c.tensor_split,
500 |_, _, _| {},
501 |_, _| {},
502 "Fraction of model weights to load on each GPU (colon-separated for multi-GPU, e.g., '0.5:0.5'). For single GPU, leave empty. Press Enter to edit.",
503 ),
504 expert_field(
505 "main_gpu",
506 "Main GPU",
507 "GPU Offload",
508 |s| s.main_gpu.to_string(),
509 |s, c| s.main_gpu != c.main_gpu,
510 |s, delta, _| {
511 s.main_gpu = (s.main_gpu + delta).max(0);
512 },
513 |s, buf| {
514 if let Ok(v) = buf.parse::<i32>() {
515 s.main_gpu = v;
516 }
517 },
518 "Index of the main GPU (0-based). Handles initial model loading and some computations. Typical: 0 for single GPU, 0 for primary in multi-GPU setups.",
519 ),
520 field_with_toggle(
521 "fit",
522 "Fit",
523 "GPU Offload",
524 |s| s.fit.to_string(),
525 |s, c| s.fit != c.fit,
526 |_, _, _| {},
527 |_, _| {},
528 toggle_fit,
529 "Automatically adjust arguments to fit device memory. Toggle on/off with Enter.",
530 ),
531 field_with_toggle(
532 "flash_attn",
533 "Flash Attention",
534 "GPU Offload",
535 |s| s.flash_attn.to_string(),
536 |s, c| s.flash_attn != c.flash_attn,
537 |_, _, _| {},
538 |_, _| {},
539 toggle_flash_attn,
540 "Enable Flash Attention (flash-attn) for faster inference. Requires compatible GPU (Ampere+ / Ada). Significantly speeds up long-context inference. Only works with certain GGUF formats.",
541 ),
542 field_with_toggle(
543 "kv_cache_offload",
544 "KV Cache Offload",
545 "GPU Offload",
546 |s| s.kv_cache_offload.to_string(),
547 |s, c| s.kv_cache_offload != c.kv_cache_offload,
548 |_, _, _| {},
549 |_, _| {},
550 toggle_kv_cache_offload,
551 "Offload KV cache to RAM when GPU memory is full. Allows larger batch sizes and contexts at the cost of some speed. Useful when VRAM is limited but you still want longer conversations.",
552 ),
553 {
555 let mut f = expert_field(
556 "cache_type_k",
557 "Cache Type K",
558 "GPU Offload",
559 |s| {
560 s.cache_type_k
561 .map(|v| v.to_string())
562 .unwrap_or_else(|| "Disabled".to_string())
563 },
564 |s, c| s.cache_type_k != c.cache_type_k,
565 |s, delta, _| {
566 let mut val = s.cache_type_k.unwrap_or(CacheQuantType::F16);
567 val = if delta > 0 { val.next() } else { val.prev() };
568 s.cache_type_k = Some(val);
569 },
570 |s, buf| {
571 if let Ok(n) = buf.parse::<u8>() {
572 s.cache_type_k = Some(CacheQuantType::from_u8(n));
573 }
574 },
575 "Quantization precision for KV cache keys. Lower precision (e.g., Q4, Q8) saves VRAM but may slightly reduce quality. Default is usually FP16. Use lower values if running out of VRAM.",
576 );
577 f.ctrl_e_toggle = Some(toggle_cache_type_k);
578 f
579 },
580 {
581 let mut f = expert_field(
582 "cache_type_v",
583 "Cache Type V",
584 "GPU Offload",
585 |s| {
586 s.cache_type_v
587 .map(|v| v.to_string())
588 .unwrap_or_else(|| "Disabled".to_string())
589 },
590 |s, c| s.cache_type_v != c.cache_type_v,
591 |s, delta, _| {
592 let mut val = s.cache_type_v.unwrap_or(CacheQuantType::F16);
593 val = if delta > 0 { val.next() } else { val.prev() };
594 s.cache_type_v = Some(val);
595 },
596 |s, buf| {
597 if let Ok(n) = buf.parse::<u8>() {
598 s.cache_type_v = Some(CacheQuantType::from_u8(n));
599 }
600 },
601 "Quantization precision for KV cache values. Lower precision (e.g., Q4, Q8) saves VRAM but may slightly reduce quality. Default is usually FP16. Use lower values if running out of VRAM.",
602 );
603 f.ctrl_e_toggle = Some(toggle_cache_type_v);
604 f
605 },
606 expert_field_with_toggle(
607 "expert_count",
608 "Active Experts",
609 "GPU Offload",
610 |s| {
611 if s.expert_count > 0 {
612 s.expert_count.to_string()
613 } else if s.expert_count == -1 {
614 "Auto".to_string()
615 } else {
616 "Disabled".to_string()
617 }
618 },
619 |s, c| s.expert_count != c.expert_count,
620 |s, delta, _| {
621 s.expert_count = (s.expert_count + delta).clamp(-1, 99);
622 },
623 |s, buf| {
624 if let Ok(v) = buf.parse::<i32>() {
625 s.expert_count = v.clamp(-1, 99);
626 }
627 },
628 toggle_expert_count,
629 "Number of MoE (Mixture of Experts) experts to activate per token. -1 = auto (all active). Reducing this speeds up inference for MoE models like Mixtral but may reduce quality. Typical: 2-8 for Mixtral.",
630 ),
631 field(
633 "batch_size",
634 "Eval Batch",
635 "Evaluation",
636 |s| s.batch_size.to_string(),
637 |s, c| s.batch_size != c.batch_size,
638 |s, delta, _| {
639 s.batch_size = (s.batch_size as i32 + delta * 64).max(1) as u32;
640 },
641 |s, buf| {
642 if let Ok(v) = buf.parse::<u32>() {
643 s.batch_size = v.max(1);
644 }
645 },
646 "Batch size for evaluation (inference). Larger batches use more VRAM but can improve throughput via parallelism. Small values (1-8) for low VRAM, larger (16-128) for high VRAM setups.",
647 ),
648 field_with_toggle(
649 "uniform_cache",
650 "Unified KV",
651 "Evaluation",
652 |s| s.uniform_cache.to_string(),
653 |s, c| s.uniform_cache != c.uniform_cache,
654 |_, _, _| {},
655 |_, _| {},
656 toggle_uniform_cache,
657 "Share KV cache across sequences. Reduces VRAM usage when running multiple requests by reusing allocated cache. May slightly reduce performance but enables more concurrent users.",
658 ),
659 expert_field(
660 "cache_reuse",
661 "Cache Reuse",
662 "Evaluation",
663 |s| s.cache_reuse.to_string(),
664 |s, c| s.cache_reuse != c.cache_reuse,
665 |s, delta, _| {
666 s.cache_reuse = (s.cache_reuse as i32 + delta * 16).max(0) as u32;
667 },
668 |s, buf| {
669 if let Ok(v) = buf.parse::<u32>() {
670 s.cache_reuse = v;
671 }
672 },
673 "Minimum chunk size (in tokens) before KV cache is reused across requests. Higher values (e.g., 128, 256) only cache large shared prefixes, reducing disk write churn. Lower values (0-32) cache more aggressively. Adjust with Left/Right arrows (step 16).",
674 ),
675 expert_field_with_toggle(
676 "swa_full",
677 "SWA Full Cache",
678 "Evaluation",
679 |s| s.swa_full.to_string(),
680 |s, c| s.swa_full != c.swa_full,
681 |_, _, _| {},
682 |_, _| {},
683 toggle_swa_full,
684 "Enable full-size sliding window attention cache. Stores complete KV entries for SWA layers instead of compressed representation. Uses more VRAM but preserves quality on very long contexts. Toggle with Enter.",
685 ),
686 expert_field_with_toggle(
687 "max_concurrent_predictions",
688 "Max Concurrent Pred",
689 "Evaluation",
690 |s| {
691 s.max_concurrent_predictions
692 .map(|v| v.to_string())
693 .unwrap_or_else(|| "Off".to_string())
694 },
695 |s, c| s.max_concurrent_predictions != c.max_concurrent_predictions,
696 |s, delta, _| match s.max_concurrent_predictions {
697 Some(n) => {
698 s.max_concurrent_predictions = Some(((n as i32) + delta).clamp(1, 10) as u32)
699 }
700 None => s.max_concurrent_predictions = Some(1),
701 },
702 |s, buf| {
703 if let Ok(v) = buf.parse::<u32>() {
704 s.max_concurrent_predictions = Some(v.clamp(1, 10));
705 }
706 },
707 toggle_max_concurrent_predictions,
708 "Maximum number of models that can run simultaneously. Press Enter to open a picker that shows how context length divides per model. Each model needs its own VRAM/CPU resources.",
709 ),
710 field(
712 "seed",
713 "Seed",
714 "Sampling",
715 |s| s.seed.to_string(),
716 |s, c| s.seed != c.seed,
717 |s, delta, _| {
718 s.seed = (s.seed + delta).max(-1);
719 },
720 |s, buf| {
721 if let Ok(v) = buf.parse::<i32>() {
722 s.seed = v;
723 }
724 },
725 "Random seed for reproducible outputs. -1 = random (default). Set to a fixed value for deterministic, repeatable responses — useful for debugging or testing prompts.",
726 ),
727 field(
728 "temperature",
729 "Temp",
730 "Sampling",
731 |s| format!("{:.2}", s.temperature),
732 |s, c| (s.temperature - c.temperature).abs() > 0.001,
733 |s, delta, _| {
734 s.temperature =
735 ((s.temperature * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 2.0);
736 },
737 |s, buf| {
738 if let Ok(v) = buf.parse::<f32>() {
739 s.temperature = v.clamp(0.0, 2.0);
740 }
741 },
742 "Sampling temperature. Controls creativity: 0 = deterministic (most predictable), 0.7 = balanced, 1.0+ = creative. Lower values produce more focused, factual outputs. Typical: 0.7-0.9 for general use.",
743 ),
744 field(
745 "top_k",
746 "Top-k",
747 "Sampling",
748 |s| s.top_k.to_string(),
749 |s, c| s.top_k != c.top_k,
750 |s, delta, _| {
751 s.top_k = (s.top_k + delta).max(1);
752 },
753 |s, buf| {
754 if let Ok(v) = buf.parse::<i32>() {
755 s.top_k = v.max(0);
756 }
757 },
758 "Only consider the top k most likely tokens at each step. Smaller top-k (e.g., 10-40) makes output more deterministic. Larger values allow more variety. Typical: 40-50. Set to 0 to disable.",
759 ),
760 field(
761 "top_p",
762 "Top-p",
763 "Sampling",
764 |s| format!("{:.2}", s.top_p),
765 |s, c| (s.top_p - c.top_p).abs() > 0.001,
766 |s, delta, _| {
767 s.top_p = ((s.top_p * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
768 },
769 |s, buf| {
770 if let Ok(v) = buf.parse::<f32>() {
771 s.top_p = v.clamp(0.0, 1.0);
772 }
773 },
774 "Nucleus sampling: only consider tokens whose cumulative probability reaches p. Smaller top-p (e.g., 0.9) is more conservative, larger (e.g., 0.95-0.99) allows more variety. Often preferred over top-k. Typical: 0.9-0.95.",
775 ),
776 field(
777 "min_p",
778 "Min P",
779 "Sampling",
780 |s| format!("{:.2}", s.min_p),
781 |s, c| (s.min_p - c.min_p).abs() > 0.001,
782 |s, delta, _| {
783 s.min_p = ((s.min_p * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
784 },
785 |s, buf| {
786 if let Ok(v) = buf.parse::<f32>() {
787 s.min_p = v.clamp(0.0, 1.0);
788 }
789 },
790 "Minimum probability threshold relative to the most likely token. Tokens below min_p * max_prob are excluded. A filter that's more principled than top-k/top-p for controlling diversity. Typical: 0.01-0.1.",
791 ),
792 ultra_field(
793 "typical_p",
794 "Typical P",
795 "Sampling",
796 |s| format!("{:.2}", s.typical_p),
797 |s, c| (s.typical_p - c.typical_p).abs() > 0.001,
798 |s, delta, _| {
799 s.typical_p = ((s.typical_p * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
800 },
801 |s, buf| {
802 if let Ok(v) = buf.parse::<f32>() {
803 s.typical_p = v.clamp(0.0, 1.0);
804 }
805 },
806 "Locally typical sampling (typ_p). Controls diversity by keeping tokens with typical probability mass. Values near 1.0 = no effect, 0.1-0.5 = moderate diversity. Typical: 1.0 (off).",
807 ),
808 ultra_field(
809 "mirostat",
810 "Mirostat",
811 "Sampling",
812 |s| s.mirostat.to_string(),
813 |s, c| s.mirostat != c.mirostat,
814 |s, delta, _| {
815 let mut val = s.mirostat;
816 val = match (delta, val) {
817 (1, Mirostat::Off) => Mirostat::V1,
818 (1, Mirostat::V1) => Mirostat::Mirostat2,
819 (1, Mirostat::Mirostat2) => Mirostat::Off,
820 (-1, Mirostat::Off) => Mirostat::Mirostat2,
821 (-1, Mirostat::V1) => Mirostat::Off,
822 (-1, Mirostat::Mirostat2) => Mirostat::V1,
823 _ => val,
824 };
825 s.mirostat = val;
826 },
827 |_, _| {},
828 "Mirostat sampling mode: Off (default), Mirostat, or Mirostat2. Adaptive temperature control that maintains target perplexity. Mirostat2 is more aggressive. Useful for consistent output quality.",
829 ),
830 ultra_field(
831 "mirostat_lr",
832 "Mirostat LR",
833 "Sampling",
834 |s| format!("{:.2}", s.mirostat_lr),
835 |s, c| (s.mirostat_lr - c.mirostat_lr).abs() > 0.001,
836 |s, delta, _| {
837 s.mirostat_lr =
838 ((s.mirostat_lr * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
839 },
840 |s, buf| {
841 if let Ok(v) = buf.parse::<f32>() {
842 s.mirostat_lr = v.clamp(0.0, 1.0);
843 }
844 },
845 "Mirostat learning rate (eta). Controls how quickly the temperature adapts. Smaller = smoother adjustments. Typical: 0.1.",
846 ),
847 ultra_field(
848 "mirostat_ent",
849 "Mirostat Ent",
850 "Sampling",
851 |s| format!("{:.2}", s.mirostat_ent),
852 |s, c| (s.mirostat_ent - c.mirostat_ent).abs() > 0.001,
853 |s, delta, _| {
854 s.mirostat_ent =
855 ((s.mirostat_ent * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 10.0);
856 },
857 |s, buf| {
858 if let Ok(v) = buf.parse::<f32>() {
859 s.mirostat_ent = v.clamp(0.0, 10.0);
860 }
861 },
862 "Mirostat target entropy. Controls the diversity of output. Higher = more diverse. Typical: 5.0.",
863 ),
864 ultra_field_with_toggle(
865 "ignore_eos",
866 "Ignore EOS",
867 "Sampling",
868 |s| s.ignore_eos.to_string(),
869 |s, c| s.ignore_eos != c.ignore_eos,
870 |_, _, _| {},
871 |_, _| {},
872 toggle_ignore_eos,
873 "Ignore end-of-sequence tokens during generation. Toggle on/off with Enter. Useful when you want to force the model to continue generating.",
874 ),
875 ultra_field(
876 "samplers",
877 "Samplers",
878 "Sampling",
879 |s| s.samplers.0.clone(),
880 |s, c| s.samplers.0 != c.samplers.0,
881 |_, _, _| {},
882 |_, _| {},
883 "Semicolon-separated sampler order string (e.g., 'mirostat;temperature;top_k;top_p'). Controls which samplers are applied and in what order. Press Enter to edit.",
884 ),
885 field_with_toggle(
886 "max_tokens",
887 "Max Tokens",
888 "Sampling",
889 |s| {
890 s.max_tokens
891 .map(|v| v.to_string())
892 .unwrap_or_else(|| "Disabled".to_string())
893 },
894 |s, c| s.max_tokens != c.max_tokens,
895 |s, delta, _| {
896 let current = s.max_tokens.unwrap_or(2048);
897 s.max_tokens = Some((current as i32 + delta * 16).max(16) as u32);
898 },
899 |s, buf| {
900 if let Ok(v) = buf.parse::<i32>() {
901 s.max_tokens = if v == 0 { None } else { Some(v as u32) };
902 }
903 },
904 toggle_max_tokens,
905 "Maximum number of tokens to generate in the response. Prevents runaway responses. Set to 0 or Disabled for no limit. Typical: 4096-8192 for chat, higher for code generation.",
906 ),
907 field(
909 "repeat_penalty",
910 "Repeat Penalty",
911 "Repetition",
912 |s| format!("{:.2}", s.repeat_penalty),
913 |s, c| (s.repeat_penalty - c.repeat_penalty).abs() > 0.001,
914 |s, delta, _| {
915 s.repeat_penalty =
916 ((s.repeat_penalty * 100.0 + delta as f32 * 5.0) / 100.0).clamp(1.0, 2.0);
917 },
918 |s, buf| {
919 if let Ok(v) = buf.parse::<f32>() {
920 s.repeat_penalty = v.clamp(0.0, 2.0);
921 }
922 },
923 "Controls repetition penalty (1.0 = no penalty, 1.1 = mild, 1.2 = strong). Higher values discourage the model from repeating phrases. Typical: 1.05-1.15 for most use cases.",
924 ),
925 field(
926 "repeat_last_n",
927 "Repeat Last N",
928 "Repetition",
929 |s| s.repeat_last_n.to_string(),
930 |s, c| s.repeat_last_n != c.repeat_last_n,
931 |s, delta, _| {
932 s.repeat_last_n = (s.repeat_last_n + delta).max(0);
933 },
934 |s, buf| {
935 if let Ok(v) = buf.parse::<i32>() {
936 s.repeat_last_n = v.max(0);
937 }
938 },
939 "How many recent tokens to check for repetition (0 = all). Smaller values (32-64) focus on local repetition, larger values (128-256) catch longer patterns. Typical: 64.",
940 ),
941 expert_field_with_toggle(
942 "presence_penalty",
943 "Presence Penalty",
944 "Repetition",
945 |s| {
946 s.presence_penalty
947 .map(|v| {
948 if (v - 0.0).abs() < 0.001 {
949 "Off".to_string()
950 } else {
951 format!("{:.2}", v)
952 }
953 })
954 .unwrap_or_else(|| "Off".to_string())
955 },
956 |s, c| match (s.presence_penalty, c.presence_penalty) {
957 (Some(v1), Some(v2)) => (v1 - v2).abs() > 0.001,
958 (None, None) => false,
959 _ => true,
960 },
961 |s, delta, _| {
962 let current = s.presence_penalty.unwrap_or(0.0);
963 s.presence_penalty =
964 Some(((current * 100.0 + delta as f32 * 5.0) / 100.0).clamp(-2.0, 2.0));
965 },
966 |s, buf| {
967 if let Ok(v) = buf.parse::<f32>() {
968 s.presence_penalty = Some(v.clamp(0.0, 1.0));
969 }
970 },
971 toggle_presence_penalty,
972 "Encourages the model to talk about new topics (+) or stay on topic (-). Positive values reduce topic repetition, negative values encourage deeper exploration. Typical: 0.0 (off).",
973 ),
974 expert_field_with_toggle(
975 "frequency_penalty",
976 "Freq Penalty",
977 "Repetition",
978 |s| {
979 s.frequency_penalty
980 .map(|v| {
981 if (v - 0.0).abs() < 0.001 {
982 "Off".to_string()
983 } else {
984 format!("{:.2}", v)
985 }
986 })
987 .unwrap_or_else(|| "Off".to_string())
988 },
989 |s, c| match (s.frequency_penalty, c.frequency_penalty) {
990 (Some(v1), Some(v2)) => (v1 - v2).abs() > 0.001,
991 (None, None) => false,
992 _ => true,
993 },
994 |s, delta, _| {
995 let current = s.frequency_penalty.unwrap_or(0.0);
996 s.frequency_penalty =
997 Some(((current * 100.0 + delta as f32 * 5.0) / 100.0).clamp(-2.0, 2.0));
998 },
999 |s, buf| {
1000 if let Ok(v) = buf.parse::<f32>() {
1001 s.frequency_penalty = Some(v.clamp(0.0, 1.0));
1002 }
1003 },
1004 toggle_frequency_penalty,
1005 "Penalizes tokens based on how often they appear in the text (+) or rewards them (-). Positive values reduce word repetition, negative values encourage denser language. Typical: 0.0 (off).",
1006 ),
1007 ultra_field(
1009 "dry_multiplier",
1010 "DRY Multiplier",
1011 "DRY",
1012 |s| format!("{:.2}", s.dry_multiplier),
1013 |s, c| (s.dry_multiplier - c.dry_multiplier).abs() > 0.001,
1014 |s, delta, _| {
1015 s.dry_multiplier =
1016 ((s.dry_multiplier * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 10.0);
1017 },
1018 |s, buf| {
1019 if let Ok(v) = buf.parse::<f32>() {
1020 s.dry_multiplier = v.clamp(0.0, 10.0);
1021 }
1022 },
1023 "DRY (Don't Repeat Yourself) multiplier. Scales the penalty for repetition. Higher values = stronger anti-repetition. Typical: 1.75.",
1024 ),
1025 ultra_field(
1026 "dry_base",
1027 "DRY Base",
1028 "DRY",
1029 |s| format!("{:.2}", s.dry_base),
1030 |s, c| (s.dry_base - c.dry_base).abs() > 0.001,
1031 |s, delta, _| {
1032 s.dry_base = ((s.dry_base * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 10.0);
1033 },
1034 |s, buf| {
1035 if let Ok(v) = buf.parse::<f32>() {
1036 s.dry_base = v.clamp(0.0, 10.0);
1037 }
1038 },
1039 "DRY penalty base (log scale). Controls the strength of the repetition penalty. Typical: 1.0 (log2) or 0.0 (linear).",
1040 ),
1041 ultra_field(
1042 "dry_allowed_length",
1043 "DRY Allowed Length",
1044 "DRY",
1045 |s| s.dry_allowed_length.to_string(),
1046 |s, c| s.dry_allowed_length != c.dry_allowed_length,
1047 |s, delta, _| {
1048 s.dry_allowed_length = (s.dry_allowed_length + delta).max(0);
1049 },
1050 |s, buf| {
1051 if let Ok(v) = buf.parse::<i32>() {
1052 s.dry_allowed_length = v;
1053 }
1054 },
1055 "Number of recent tokens to check for repetition (penalty starts after this). Higher values check longer context. Typical: 2.",
1056 ),
1057 ultra_field(
1058 "dry_penalty_last_n",
1059 "DRY Penalty Last N",
1060 "DRY",
1061 |s| s.dry_penalty_last_n.to_string(),
1062 |s, c| s.dry_penalty_last_n != c.dry_penalty_last_n,
1063 |s, delta, _| {
1064 s.dry_penalty_last_n = (s.dry_penalty_last_n + delta).max(0);
1065 },
1066 |s, buf| {
1067 if let Ok(v) = buf.parse::<i32>() {
1068 s.dry_penalty_last_n = v;
1069 }
1070 },
1071 "How many tokens to consider for DRY penalty (0 = all). Larger values catch longer repetition patterns. Typical: -1 (all) or 128.",
1072 ),
1073 expert_field_with_toggle(
1075 "is_mtp",
1076 "MTP",
1077 "Speculative",
1078 |s| {
1079 if s.spec_type.is_empty() {
1080 "Off".to_string()
1081 } else {
1082 s.spec_type.clone()
1083 }
1084 },
1085 |s, c| s.spec_type != c.spec_type,
1086 |_, _, _| {},
1087 |_, _| {},
1088 toggle_mtp,
1089 "Speculative decoding method for faster inference. Options: Off, draft-mtp (MTP-based), draft-simple, draft-eagle3, ngram-simple, ngram-map-k, ngram-map-k4v, ngram-mod, ngram-cache. Draft-mtp requires a compatible model with MTP architecture.",
1090 ),
1091 expert_field(
1092 "spec_type",
1093 "Spec Type",
1094 "Speculative",
1095 |s| {
1096 if s.spec_type.is_empty() {
1097 "Off".to_string()
1098 } else {
1099 s.spec_type.clone()
1100 }
1101 },
1102 |s, c| s.spec_type != c.spec_type,
1103 |_, _, _| {},
1104 |_, _| {},
1105 "Speculative decoding method for faster inference. Options: Off, draft-mtp (MTP-based), draft-simple, draft-eagle3, ngram-simple, ngram-map-k, ngram-map-k4v, ngram-mod, ngram-cache. Draft-mtp requires a compatible model with MTP architecture.",
1106 ),
1107 expert_field(
1108 "draft_tokens",
1109 "Spec Draft N Max",
1110 "Speculative",
1111 |s| s.draft_tokens.to_string(),
1112 |s, c| s.draft_tokens != c.draft_tokens,
1113 |s, delta, _| {
1114 s.draft_tokens = (s.draft_tokens as i32 + delta).clamp(0, 16) as u32;
1115 },
1116 |s, buf| {
1117 if let Ok(v) = buf.parse::<u32>() {
1118 s.draft_tokens = v.min(16);
1119 }
1120 },
1121 "Maximum number of draft tokens per step (0-16). More drafts = more potential speedup but also more wasted computation if drafts are rejected. Typical: 4-8 for draft-mtp.",
1122 ),
1123 field(
1125 "tags",
1126 "Tags (Enter to edit)",
1127 "Tags",
1128 |s| {
1129 if s.tags.is_empty() {
1130 "None".to_string()
1131 } else {
1132 s.tags.join(", ")
1133 }
1134 },
1135 |s, c| s.tags != c.tags,
1136 |_, _, _| {},
1137 |_, _| {},
1138 "Comma-separated labels for the model (e.g., 'coding, chat, reasoning'). Used for filtering and organization. Press Enter to open a tag editor.",
1139 ),
1140 field(
1142 "backend_version",
1143 "LLama.cpp Version",
1144 "Backend",
1145 |s| s.get_active_backend_version_display().to_string(),
1146 |s, c| s.get_active_backend_version() != c.get_active_backend_version(),
1147 |_, _, _| {},
1148 |_, _| {},
1149 "Select the llama.cpp backend binary (CPU / Vulkan / ROCm / CUDA). Press Enter to open a version picker. Different backends support different GPU types and features.",
1150 ),
1151 ]
1152}
1153
1154pub fn filtered_fields(expert_mode: bool) -> Vec<SettingField> {
1155 all_fields()
1156 .into_iter()
1157 .filter(|f| {
1158 if !expert_mode {
1159 !f.is_expert
1160 } else {
1161 !f.is_ultra }
1163 })
1164 .collect()
1165}
1166
1167#[allow(clippy::too_many_arguments)]
1171pub fn add_setting(
1172 lines: &mut Vec<Line<'static>>,
1173 total_count: &mut usize,
1174 _settings: &ModelSettings,
1175 _cached: &ModelSettings,
1176 selected_line_idx: &mut usize,
1177 selected_content_line: &mut usize,
1178 idx: usize,
1179 name: &str,
1180 val: &str,
1181 selected: usize,
1182 _edit_buf: &str,
1183 _editing: bool,
1184 disabled: bool,
1185) {
1186 let current_line = lines.len();
1187 let name_style = if disabled {
1188 Style::default().fg(Color::DarkGray)
1189 } else {
1190 Style::default().fg(Color::Yellow)
1191 };
1192 let val_style = if disabled {
1193 Style::default()
1194 .fg(Color::DarkGray)
1195 .add_modifier(Modifier::DIM)
1196 } else {
1197 Style::default().fg(Color::White)
1198 };
1199 if idx == selected {
1200 *selected_line_idx = current_line;
1201 *selected_content_line = current_line;
1202 lines.push(Line::from(vec![
1203 Span::styled(
1204 "> ",
1205 Style::default()
1206 .fg(Color::Yellow)
1207 .add_modifier(if disabled {
1208 Modifier::DIM
1209 } else {
1210 Modifier::BOLD
1211 }),
1212 ),
1213 Span::styled(format!("{name}: "), name_style),
1214 Span::styled(
1215 val.to_string(),
1216 Style::default()
1217 .fg(Color::Black)
1218 .bg(Color::Yellow)
1219 .add_modifier(Modifier::BOLD),
1220 ),
1221 ]));
1222 } else {
1223 lines.push(Line::from(vec![
1224 Span::styled(" ", name_style),
1225 Span::styled(format!("{name}: "), name_style),
1226 Span::styled(val.to_string(), val_style),
1227 ]));
1228 }
1229 *total_count += 1;
1230}
1231
1232pub fn profile_settings_parts(profile: &Profile, current: &ModelSettings) -> Vec<String> {
1234 let mut parts = Vec::new();
1235 let s = &profile.settings;
1236
1237 diff_int!(parts, s, current, context_length, "ctx");
1239 diff_int!(parts, s, current, threads, "threads");
1240 diff_int!(parts, s, current, threads_batch, "threads_batch");
1241 diff_int!(parts, s, current, batch_size, "batch");
1242 diff_int!(parts, s, current, ubatch_size, "ubatch");
1243 diff_int!(parts, s, current, parallel, "parallel");
1244 diff_option!(parts, s, current, max_concurrent_predictions, "concurrent");
1245 diff_int!(parts, s, current, cache_reuse, "cache_reuse");
1246 diff_option!(parts, s, current, max_tokens, "max_tokens");
1247 diff_int!(parts, s, current, draft_tokens, "draft_tokens");
1248
1249 diff_int!(parts, s, current, keep, "keep");
1250 diff_int!(parts, s, current, main_gpu, "main_gpu");
1251 diff_int!(parts, s, current, expert_count, "expert_count");
1252 diff_int!(parts, s, current, seed, "seed");
1253 diff_int!(parts, s, current, top_k, "top_k");
1254 diff_int!(parts, s, current, repeat_last_n, "repeat_last_n");
1255 diff_int!(parts, s, current, dry_allowed_length, "dry_allowed");
1256 diff_int!(parts, s, current, dry_penalty_last_n, "dry_penalty_last_n");
1257
1258 diff_float!(parts, s, current, temperature, "temp");
1260 diff_float!(parts, s, current, top_p, "top_p");
1261 diff_float!(parts, s, current, min_p, "min_p");
1262 diff_float!(parts, s, current, typical_p, "typical_p");
1263 diff_float!(parts, s, current, mirostat_lr, "mirostat_lr");
1264 diff_float!(parts, s, current, mirostat_ent, "mirostat_ent");
1265 diff_float!(parts, s, current, repeat_penalty, "rep_pen");
1266 diff_option_float!(parts, s, current, presence_penalty, "pres_pen");
1267 diff_option_float!(parts, s, current, frequency_penalty, "freq_pen");
1268 diff_float!(parts, s, current, dry_multiplier, "dry_mult");
1269 diff_float!(parts, s, current, dry_base, "dry_base");
1270 diff_float!(parts, s, current, rope_scale, "rope_scale");
1271 diff_float!(parts, s, current, rope_freq_base, "rope_freq_base");
1272 diff_float!(parts, s, current, rope_freq_scale, "rope_freq_scale");
1273
1274 diff_bool!(parts, s, current, swa_full, "swa_full");
1276 diff_bool!(parts, s, current, mlock, "mlock");
1277 diff_bool!(parts, s, current, mmap, "mmap");
1278 diff_bool!(parts, s, current, uniform_cache, "uniform_cache");
1279 diff_bool!(parts, s, current, kv_cache_offload, "kv_cache_offload");
1280 diff_bool!(parts, s, current, fit, "fit");
1281 diff_bool!(parts, s, current, embedding, "embedding");
1282 diff_bool!(parts, s, current, flash_attn, "flash_attn");
1283 diff_bool!(parts, s, current, jinja, "jinja");
1284 diff_bool!(parts, s, current, ignore_eos, "ignore_eos");
1285 diff_bool!(parts, s, current, rope_yarn_enabled, "yarn_enabled");
1286 diff_bool!(parts, s, current, cache_prompt, "cache_prompt");
1287 diff_bool!(parts, s, current, webui, "webui");
1288 diff_string!(parts, s, current, system_prompt_preset_name, "preset");
1290 diff_string!(parts, s, current, tensor_split, "tensor_split");
1291 diff_string!(parts, s, current, rpc, "rpc");
1292 diff_option!(parts, s, current, chat_template, "chat_template");
1293 diff_option!(
1294 parts,
1295 s,
1296 current,
1297 chat_template_kwargs,
1298 "chat_template_kwargs"
1299 );
1300 diff_option!(parts, s, current, llama_cpp_version_cpu, "llama_cpp_cpu");
1301 diff_option!(
1302 parts,
1303 s,
1304 current,
1305 llama_cpp_version_vulkan,
1306 "llama_cpp_vulkan"
1307 );
1308 diff_option!(parts, s, current, llama_cpp_version_rocm, "llama_cpp_rocm");
1309 diff_option!(
1310 parts,
1311 s,
1312 current,
1313 llama_cpp_version_rocm_lemonade,
1314 "llama_cpp_rocm_lemonade"
1315 );
1316 diff_option!(parts, s, current, llama_cpp_version_cuda, "llama_cpp_cuda");
1317 diff_string!(parts, s, current, spec_type, "spec_type");
1318
1319 diff_enum!(parts, s, current, numa, "numa");
1321 diff_enum!(parts, s, current, split_mode, "split_mode");
1322 diff_enum!(parts, s, current, mirostat, "mirostat");
1323 diff_enum!(parts, s, current, samplers, "samplers");
1324 diff_enum!(parts, s, current, rope_scaling, "rope_scaling");
1325 diff_enum!(parts, s, current, cache_type, "cache_type");
1326 diff_option!(parts, s, current, cache_type_k, "cache_type_k");
1327 diff_option!(parts, s, current, cache_type_v, "cache_type_v");
1328
1329 if let Some(v) = s.gpu_layers_mode
1331 && v != current.gpu_layers_mode
1332 {
1333 let display = match v {
1334 crate::models::GpuLayersMode::Auto => "Auto".to_string(),
1335 crate::models::GpuLayersMode::Specific(n) => n.to_string(),
1336 crate::models::GpuLayersMode::All => "All".to_string(),
1337 };
1338 parts.push(format!("gpu_layers={}", display));
1339 }
1340
1341 parts
1342}