1use crate::config::Profile;
2use crate::models::{
3 CacheQuantType, GpuLayersMode, Mirostat, ModelSettings, NumMode, SplitMode,
4};
5use ratatui::{
6 style::{Color, Modifier, Style},
7 text::{Line, Span},
8};
9
10pub type DisplayFn = fn(&ModelSettings) -> String;
13pub type DirtyFn = fn(&ModelSettings, &ModelSettings) -> bool;
14pub type AdjustFn = fn(&mut ModelSettings, i32, u32); pub type ApplyEditFn = fn(&mut ModelSettings, &str);
16pub type CtrlEToggleFn = fn(&mut ModelSettings);
17
18#[derive(Debug, Clone, Copy, PartialEq)]
21pub enum EditKind {
22 Direct,
24 Toggle,
26 Modal,
28}
29
30pub struct SettingField {
33 pub id: &'static str,
34 pub name: &'static str,
35 pub section: &'static str,
36 pub display: DisplayFn,
37 pub dirty: DirtyFn,
38 pub adjust: AdjustFn,
39 pub apply_edit: ApplyEditFn,
40 pub ctrl_e_toggle: Option<CtrlEToggleFn>,
41 #[allow(dead_code)]
42 pub edit_kind: EditKind,
43 pub is_expert: bool,
44 pub is_ultra: bool,
45}
46
47impl SettingField {
48 pub fn name(&self) -> &str {
49 self.name
50 }
51
52 pub fn display(&self, settings: &ModelSettings) -> String {
53 (self.display)(settings)
54 }
55
56 pub fn is_dirty(&self, settings: &ModelSettings, cached: &ModelSettings) -> bool {
57 (self.dirty)(settings, cached)
58 }
59
60 pub fn adjust(&self, settings: &mut ModelSettings, delta: i32, context_limit: u32) {
61 (self.adjust)(settings, delta, context_limit);
62 }
63
64 pub fn apply_edit(&self, settings: &mut ModelSettings, buf: &str) {
65 (self.apply_edit)(settings, buf);
66 }
67
68 pub fn ctrl_e_toggle(&self, settings: &mut ModelSettings) {
69 if let Some(toggle) = self.ctrl_e_toggle {
70 toggle(settings);
71 }
72 }
73
74 pub fn is_new_section(&self, prev_section: Option<&str>) -> bool {
76 Some(self.section) != prev_section
77 }
78}
79
80macro_rules! make_field_fn {
87 ($fn:ident, $expert:expr, $ultra:expr, toggle) => {
88 fn $fn(
89 id: &'static str,
90 name: &'static str,
91 section: &'static str,
92 display: DisplayFn,
93 dirty: DirtyFn,
94 adjust: AdjustFn,
95 apply_edit: ApplyEditFn,
96 ctrl_e_toggle: CtrlEToggleFn,
97 edit_kind: EditKind,
98 ) -> SettingField {
99 SettingField {
100 id,
101 name,
102 section,
103 display,
104 dirty,
105 adjust,
106 apply_edit,
107 ctrl_e_toggle: Some(ctrl_e_toggle),
108 edit_kind,
109 is_expert: $expert,
110 is_ultra: $ultra,
111 }
112 }
113 };
114 ($fn:ident, $expert:expr, $ultra:expr, @none) => {
115 fn $fn(
116 id: &'static str,
117 name: &'static str,
118 section: &'static str,
119 display: DisplayFn,
120 dirty: DirtyFn,
121 adjust: AdjustFn,
122 apply_edit: ApplyEditFn,
123 edit_kind: EditKind,
124 ) -> SettingField {
125 SettingField {
126 id,
127 name,
128 section,
129 display,
130 dirty,
131 adjust,
132 apply_edit,
133 ctrl_e_toggle: None,
134 edit_kind,
135 is_expert: $expert,
136 is_ultra: $ultra,
137 }
138 }
139 };
140}
141
142make_field_fn!(field, false, false, @none);
143make_field_fn!(expert_field, true, false, @none);
144make_field_fn!(ultra_field, true, true, @none);
145make_field_fn!(field_with_toggle, false, false, toggle);
146make_field_fn!(expert_field_with_toggle, true, false, toggle);
147make_field_fn!(ultra_field_with_toggle, true, true, toggle);
148
149fn gpu_layers_adjust(settings: &mut ModelSettings, delta: i32, _context_limit: u32) {
152 settings.gpu_layers_mode = match (delta, &settings.gpu_layers_mode) {
153 (1, GpuLayersMode::Auto) => GpuLayersMode::Specific(1),
154 (1, GpuLayersMode::Specific(n)) => GpuLayersMode::Specific(n + 1),
155 (1, GpuLayersMode::All) => GpuLayersMode::Auto,
156 (-1, GpuLayersMode::Auto) => GpuLayersMode::Auto,
157 (-1, GpuLayersMode::Specific(n)) if *n == 0 => GpuLayersMode::Auto,
158 (-1, GpuLayersMode::Specific(n)) if *n == 1 => GpuLayersMode::Specific(0),
159 (-1, GpuLayersMode::Specific(n)) => GpuLayersMode::Specific(n - 1),
160 (-1, GpuLayersMode::All) => GpuLayersMode::All,
161 _ => settings.gpu_layers_mode,
162 };
163}
164
165fn gpu_layers_apply(settings: &mut ModelSettings, buf: &str) {
166 if let Ok(v) = buf.parse::<i32>() {
167 settings.gpu_layers_mode = if v < 0 {
168 GpuLayersMode::All
169 } else {
170 GpuLayersMode::Specific(v as u32)
171 };
172 }
173}
174
175fn toggle_mlock(settings: &mut ModelSettings) {
176 settings.mlock = !settings.mlock;
177}
178fn toggle_flash_attn(settings: &mut ModelSettings) {
179 settings.flash_attn = !settings.flash_attn;
180}
181
182fn toggle_fit(settings: &mut ModelSettings) {
183 settings.fit = !settings.fit;
184}
185fn toggle_kv_cache_offload(settings: &mut ModelSettings) {
186 settings.kv_cache_offload = !settings.kv_cache_offload;
187}
188fn toggle_uniform_cache(settings: &mut ModelSettings) {
189 settings.uniform_cache = !settings.uniform_cache;
190}
191fn toggle_mtp(settings: &mut ModelSettings) {
192 if settings.spec_type.is_empty() {
193 settings.spec_type = "draft-mtp".to_string();
194 } else {
195 settings.spec_type = String::new();
196 }
197}
198
199fn toggle_rope_yarn_enabled(settings: &mut ModelSettings) {
200 settings.rope_yarn_enabled = !settings.rope_yarn_enabled;
201}
202fn toggle_ignore_eos(settings: &mut ModelSettings) {
203 settings.ignore_eos = !settings.ignore_eos;
204}
205fn toggle_max_tokens(settings: &mut ModelSettings) {
206 settings.max_tokens = settings.max_tokens.map_or(Some(2048), |_| None);
207}
208fn toggle_max_concurrent_predictions(settings: &mut ModelSettings) {
209 settings.max_concurrent_predictions = settings
210 .max_concurrent_predictions
211 .map_or(Some(1), |_| None);
212}
213fn toggle_cache_type_k(settings: &mut ModelSettings) {
214 settings.cache_type_k = settings.cache_type_k.map_or(Some(CacheQuantType::F16), |_| None);
215}
216fn toggle_cache_type_v(settings: &mut ModelSettings) {
217 settings.cache_type_v = settings.cache_type_v.map_or(Some(CacheQuantType::F16), |_| None);
218}
219fn toggle_expert_count(settings: &mut ModelSettings) {
220 settings.expert_count = match settings.expert_count {
221 0 => 1,
222 -1 => 0,
223 _ => -1,
224 };
225}
226fn toggle_presence_penalty(settings: &mut ModelSettings) {
227 settings.presence_penalty = settings.presence_penalty.map_or(Some(0.0), |_| None);
228}
229fn toggle_frequency_penalty(settings: &mut ModelSettings) {
230 settings.frequency_penalty = settings.frequency_penalty.map_or(Some(0.0), |_| None);
231}
232
233macro_rules! diff_int {
236 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
237 if let Some(v) = $s.$field && v != $c.$field {
238 $parts.push(format!("{}={}", $label, v));
239 }
240 };
241}
242macro_rules! diff_float {
243 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
244 if let Some(v) = $s.$field && (v - $c.$field).abs() > 0.001 {
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 && v != $c.$field {
252 $parts.push(format!("{}={}", $label, v));
253 }
254 };
255}
256macro_rules! diff_string {
257 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
258 if let Some(v) = &$s.$field && v != &$c.$field {
259 $parts.push(format!("{}={}", $label, v));
260 }
261 };
262}
263macro_rules! diff_enum {
264 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
265 if let Some(ref v) = $s.$field && *v != $c.$field {
266 $parts.push(format!("{}={}", $label, v));
267 }
268 };
269}
270macro_rules! diff_option {
271 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
272 if $s.$field != $c.$field {
273 if let Some(ref v) = $s.$field {
274 $parts.push(format!("{}={}", $label, v));
275 }
276 }
277 };
278}
279macro_rules! diff_option_float {
280 ($parts:expr, $s:expr, $c:expr, $field:ident, $label:literal) => {
281 if let Some(v) = $s.$field {
282 let current_val = $c.$field.unwrap_or(0.0);
283 if (v - current_val).abs() > 0.001 {
284 $parts.push(format!("{}={:.2}", $label, v));
285 }
286 }
287 };
288}
289
290macro_rules! make_cache_type_field {
293 ($field:ident, $name:literal, $toggle:ident) => {
294 expert_field_with_toggle(
295 stringify!($field),
296 $name,
297 "GPU Offload",
298 |s| s.$field.map(|v| v.to_string()).unwrap_or_else(|| "Disabled".to_string()),
299 |s, c| s.$field != c.$field,
300 |s, delta, _| {
301 let mut val = s.$field.unwrap_or(CacheQuantType::F16);
302 val = if delta > 0 { val.next() } else { val.prev() };
303 s.$field = Some(val);
304 },
305 |s, buf| {
306 if let Ok(n) = buf.parse::<u8>() {
307 s.$field = Some(CacheQuantType::from_u8(n));
308 }
309 },
310 $toggle,
311 EditKind::Direct,
312 )
313 };
314}
315
316pub fn all_fields() -> Vec<SettingField> {
317 vec![
318 field(
320 "system_prompt_preset_name",
321 "Prompt",
322 "Loading",
323 |s| s.system_prompt_preset_name.clone(),
324 |s, c| s.system_prompt_preset_name != c.system_prompt_preset_name,
325 |_, _, _| {},
326 |_, _| {},
327 EditKind::Modal,
328 ),
329 field(
330 "context_length",
331 "Context",
332 "Loading",
333 |s| s.context_length.to_string(),
334 |s, c| s.context_length != c.context_length,
335 |s, delta, ctx_limit| {
336 let mut val = (s.context_length as i32 + delta * 128).max(128) as u32;
337 if ctx_limit > 0 {
338 val = val.min(ctx_limit);
339 }
340 s.context_length = val;
341 },
342 |s, buf| {
343 if let Ok(v) = buf.parse::<u32>() {
344 s.context_length = v.max(128);
345 }
346 },
347 EditKind::Direct,
348 ),
349 expert_field_with_toggle(
350 "rope_yarn_enabled",
351 "Yarn RoPE",
352 "Loading",
353 |s| s.rope_yarn_enabled.to_string(),
354 |s, c| s.rope_yarn_enabled != c.rope_yarn_enabled,
355 |_, _, _| {},
356 |_, _| {},
357 toggle_rope_yarn_enabled,
358 EditKind::Toggle,
359 ),
360 expert_field(
361 "yarn_params",
362 "Yarn Params",
363 "Loading",
364 |s| {
365 format!(
366 "scale={:.2} base={:.2} scale_f={:.2}",
367 s.rope_scale, s.rope_freq_base, s.rope_freq_scale
368 )
369 },
370 |s, c| {
371 s.rope_scale != c.rope_scale
372 || s.rope_freq_base != c.rope_freq_base
373 || s.rope_freq_scale != c.rope_freq_scale
374 },
375 |_, _, _| {},
376 |_, _| {},
377 EditKind::Modal,
378 ),
379 ultra_field(
380 "threads_batch",
381 "Threads Batch",
382 "Loading",
383 |s| s.threads_batch.to_string(),
384 |s, c| s.threads_batch != c.threads_batch,
385 |s, delta, _| {
386 s.threads_batch = (s.threads_batch as i32 + delta).max(1) as u32;
387 },
388 |s, buf| {
389 if let Ok(v) = buf.parse::<u32>() {
390 s.threads_batch = v.max(1);
391 }
392 },
393 EditKind::Direct,
394 ),
395 ultra_field(
396 "ubatch_size",
397 "UBatch Size",
398 "Loading",
399 |s| s.ubatch_size.to_string(),
400 |s, c| s.ubatch_size != c.ubatch_size,
401 |s, delta, _| {
402 s.ubatch_size = (s.ubatch_size as i32 + delta * 64).max(1) as u32;
403 },
404 |s, buf| {
405 if let Ok(v) = buf.parse::<u32>() {
406 s.ubatch_size = v.max(1);
407 }
408 },
409 EditKind::Direct,
410 ),
411 ultra_field(
412 "keep",
413 "Keep",
414 "Loading",
415 |s| s.keep.to_string(),
416 |s, c| s.keep != c.keep,
417 |s, delta, _| {
418 s.keep = (s.keep + delta).max(0);
419 },
420 |s, buf| {
421 if let Ok(v) = buf.parse::<i32>() {
422 s.keep = v;
423 }
424 },
425 EditKind::Direct,
426 ),
427 field_with_toggle(
428 "mlock",
429 "Keep in memory (mlock)",
430 "Loading",
431 |s| s.mlock.to_string(),
432 |s, c| s.mlock != c.mlock,
433 |_, _, _| {},
434 |_, _| {},
435 toggle_mlock,
436 EditKind::Toggle,
437 ),
438 expert_field(
439 "numa",
440 "NUMA",
441 "Loading",
442 |s| s.numa.to_string(),
443 |s, c| s.numa != c.numa,
444 |s, delta, _| {
445 let mut val = s.numa;
446 val = match (delta, val) {
447 (1, NumMode::None) => NumMode::Distribute,
448 (1, NumMode::Distribute) => NumMode::Isolate,
449 (1, NumMode::Isolate) => NumMode::Numactl,
450 (1, NumMode::Numactl) => NumMode::None,
451 (-1, NumMode::None) => NumMode::Numactl,
452 (-1, NumMode::Distribute) => NumMode::None,
453 (-1, NumMode::Isolate) => NumMode::Distribute,
454 (-1, NumMode::Numactl) => NumMode::Isolate,
455 _ => val,
456 };
457 s.numa = val;
458 },
459 |_, _| {},
460 EditKind::Toggle,
461 ),
462 field(
464 "gpu_layers_mode",
465 "GPU Layers",
466 "GPU Offload",
467 |s| match s.gpu_layers_mode {
468 GpuLayersMode::Auto => "Auto".to_string(),
469 GpuLayersMode::Specific(n) => n.to_string(),
470 GpuLayersMode::All => "All".to_string(),
471 },
472 |s, c| s.gpu_layers_mode != c.gpu_layers_mode,
473 gpu_layers_adjust,
474 gpu_layers_apply,
475 EditKind::Direct,
476 ),
477 ultra_field(
478 "split_mode",
479 "Split Mode",
480 "GPU Offload",
481 |s| s.split_mode.to_string(),
482 |s, c| s.split_mode != c.split_mode,
483 |s, delta, _| {
484 let mut val = s.split_mode;
485 val = match (delta, val) {
486 (1, SplitMode::None) => SplitMode::Layer,
487 (1, SplitMode::Layer) => SplitMode::Row,
488 (1, SplitMode::Row) => SplitMode::Tensor,
489 (1, SplitMode::Tensor) => SplitMode::None,
490 (-1, SplitMode::None) => SplitMode::Tensor,
491 (-1, SplitMode::Layer) => SplitMode::None,
492 (-1, SplitMode::Row) => SplitMode::Layer,
493 (-1, SplitMode::Tensor) => SplitMode::Row,
494 _ => val,
495 };
496 s.split_mode = val;
497 },
498 |_, _| {},
499 EditKind::Toggle,
500 ),
501 ultra_field(
502 "tensor_split",
503 "Tensor Split",
504 "GPU Offload",
505 |s| s.tensor_split.clone(),
506 |s, c| s.tensor_split != c.tensor_split,
507 |_, _, _| {},
508 |_, _| {},
509 EditKind::Modal,
510 ),
511 expert_field(
512 "main_gpu",
513 "Main GPU",
514 "GPU Offload",
515 |s| s.main_gpu.to_string(),
516 |s, c| s.main_gpu != c.main_gpu,
517 |s, delta, _| {
518 s.main_gpu = (s.main_gpu + delta).max(0);
519 },
520 |s, buf| {
521 if let Ok(v) = buf.parse::<i32>() {
522 s.main_gpu = v;
523 }
524 },
525 EditKind::Direct,
526 ),
527 field_with_toggle(
528 "fit",
529 "Fit",
530 "GPU Offload",
531 |s| s.fit.to_string(),
532 |s, c| s.fit != c.fit,
533 |_, _, _| {},
534 |_, _| {},
535 toggle_fit,
536 EditKind::Toggle,
537 ),
538 field_with_toggle(
539 "flash_attn",
540 "Flash Attention",
541 "GPU Offload",
542 |s| s.flash_attn.to_string(),
543 |s, c| s.flash_attn != c.flash_attn,
544 |_, _, _| {},
545 |_, _| {},
546 toggle_flash_attn,
547 EditKind::Toggle,
548 ),
549 field_with_toggle(
550 "kv_cache_offload",
551 "KV Cache Offload",
552 "GPU Offload",
553 |s| s.kv_cache_offload.to_string(),
554 |s, c| s.kv_cache_offload != c.kv_cache_offload,
555 |_, _, _| {},
556 |_, _| {},
557 toggle_kv_cache_offload,
558 EditKind::Toggle,
559 ),
560 make_cache_type_field!(cache_type_k, "Cache Type K", toggle_cache_type_k),
563 make_cache_type_field!(cache_type_v, "Cache Type V", toggle_cache_type_v),
564 expert_field_with_toggle(
565 "expert_count",
566 "Active Experts",
567 "GPU Offload",
568 |s| {
569 if s.expert_count > 0 {
570 s.expert_count.to_string()
571 } else if s.expert_count == -1 {
572 "Auto".to_string()
573 } else {
574 "Disabled".to_string()
575 }
576 },
577 |s, c| s.expert_count != c.expert_count,
578 |s, delta, _| {
579 s.expert_count = (s.expert_count + delta).clamp(-1, 99);
580 },
581 |s, buf| {
582 if let Ok(v) = buf.parse::<i32>() {
583 s.expert_count = v.clamp(-1, 99);
584 }
585 },
586 toggle_expert_count,
587 EditKind::Direct,
588 ),
589 field(
591 "batch_size",
592 "Eval Batch",
593 "Evaluation",
594 |s| s.batch_size.to_string(),
595 |s, c| s.batch_size != c.batch_size,
596 |s, delta, _| {
597 s.batch_size = (s.batch_size as i32 + delta * 64).max(1) as u32;
598 },
599 |s, buf| {
600 if let Ok(v) = buf.parse::<u32>() {
601 s.batch_size = v.max(1);
602 }
603 },
604 EditKind::Direct,
605 ),
606 field_with_toggle(
607 "uniform_cache",
608 "Unified KV",
609 "Evaluation",
610 |s| s.uniform_cache.to_string(),
611 |s, c| s.uniform_cache != c.uniform_cache,
612 |_, _, _| {},
613 |_, _| {},
614 toggle_uniform_cache,
615 EditKind::Toggle,
616 ),
617 expert_field_with_toggle(
618 "max_concurrent_predictions",
619 "Max Concurrent Pred",
620 "Evaluation",
621 |s| {
622 s.max_concurrent_predictions
623 .map(|v| v.to_string())
624 .unwrap_or_else(|| "Off".to_string())
625 },
626 |s, c| s.max_concurrent_predictions != c.max_concurrent_predictions,
627 |s, delta, _| match s.max_concurrent_predictions {
628 Some(n) => {
629 s.max_concurrent_predictions = Some(((n as i32) + delta).clamp(1, 10) as u32)
630 }
631 None => s.max_concurrent_predictions = Some(1),
632 },
633 |s, buf| {
634 if let Ok(v) = buf.parse::<u32>() {
635 s.max_concurrent_predictions = Some(v.clamp(1, 10));
636 }
637 },
638 toggle_max_concurrent_predictions,
639 EditKind::Direct,
640 ),
641 field(
643 "seed",
644 "Seed",
645 "Sampling",
646 |s| s.seed.to_string(),
647 |s, c| s.seed != c.seed,
648 |s, delta, _| {
649 s.seed = (s.seed + delta).max(-1);
650 },
651 |s, buf| {
652 if let Ok(v) = buf.parse::<i32>() {
653 s.seed = v;
654 }
655 },
656 EditKind::Direct,
657 ),
658 field(
659 "temperature",
660 "Temp",
661 "Sampling",
662 |s| format!("{:.2}", s.temperature),
663 |s, c| (s.temperature - c.temperature).abs() > 0.001,
664 |s, delta, _| {
665 s.temperature =
666 ((s.temperature * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 2.0);
667 },
668 |s, buf| {
669 if let Ok(v) = buf.parse::<i32>() {
670 s.temperature = (v as f32 / 100.0).clamp(0.0, 2.0);
671 }
672 },
673 EditKind::Direct,
674 ),
675 field(
676 "top_k",
677 "Top-k",
678 "Sampling",
679 |s| s.top_k.to_string(),
680 |s, c| s.top_k != c.top_k,
681 |s, delta, _| {
682 s.top_k = (s.top_k + delta).max(1);
683 },
684 |s, buf| {
685 if let Ok(v) = buf.parse::<i32>() {
686 s.top_k = v.max(0);
687 }
688 },
689 EditKind::Direct,
690 ),
691 field(
692 "top_p",
693 "Top-p",
694 "Sampling",
695 |s| format!("{:.2}", s.top_p),
696 |s, c| (s.top_p - c.top_p).abs() > 0.001,
697 |s, delta, _| {
698 s.top_p = ((s.top_p * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
699 },
700 |s, buf| {
701 if let Ok(v) = buf.parse::<i32>() {
702 s.top_p = (v as f32 / 100.0).clamp(0.0, 1.0);
703 }
704 },
705 EditKind::Direct,
706 ),
707 field(
708 "min_p",
709 "Min P",
710 "Sampling",
711 |s| format!("{:.2}", s.min_p),
712 |s, c| (s.min_p - c.min_p).abs() > 0.001,
713 |s, delta, _| {
714 s.min_p = ((s.min_p * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
715 },
716 |s, buf| {
717 if let Ok(v) = buf.parse::<i32>() {
718 s.min_p = (v as f32 / 100.0).clamp(0.0, 1.0);
719 }
720 },
721 EditKind::Direct,
722 ),
723 ultra_field(
724 "typical_p",
725 "Typical P",
726 "Sampling",
727 |s| format!("{:.2}", s.typical_p),
728 |s, c| (s.typical_p - c.typical_p).abs() > 0.001,
729 |s, delta, _| {
730 s.typical_p = ((s.typical_p * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
731 },
732 |s, buf| {
733 if let Ok(v) = buf.parse::<i32>() {
734 s.typical_p = (v as f32 / 100.0).clamp(0.0, 1.0);
735 }
736 },
737 EditKind::Direct,
738 ),
739 ultra_field(
740 "mirostat",
741 "Mirostat",
742 "Sampling",
743 |s| s.mirostat.to_string(),
744 |s, c| s.mirostat != c.mirostat,
745 |s, delta, _| {
746 let mut val = s.mirostat;
747 val = match (delta, val) {
748 (1, Mirostat::Off) => Mirostat::V1,
749 (1, Mirostat::V1) => Mirostat::Mirostat2,
750 (1, Mirostat::Mirostat2) => Mirostat::Off,
751 (-1, Mirostat::Off) => Mirostat::Mirostat2,
752 (-1, Mirostat::V1) => Mirostat::Off,
753 (-1, Mirostat::Mirostat2) => Mirostat::V1,
754 _ => val,
755 };
756 s.mirostat = val;
757 },
758 |_, _| {},
759 EditKind::Toggle,
760 ),
761 ultra_field(
762 "mirostat_lr",
763 "Mirostat LR",
764 "Sampling",
765 |s| format!("{:.2}", s.mirostat_lr),
766 |s, c| (s.mirostat_lr - c.mirostat_lr).abs() > 0.001,
767 |s, delta, _| {
768 s.mirostat_lr =
769 ((s.mirostat_lr * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 1.0);
770 },
771 |s, buf| {
772 if let Ok(v) = buf.parse::<i32>() {
773 s.mirostat_lr = (v as f32 / 100.0).clamp(0.0, 1.0);
774 }
775 },
776 EditKind::Direct,
777 ),
778 ultra_field(
779 "mirostat_ent",
780 "Mirostat Ent",
781 "Sampling",
782 |s| format!("{:.2}", s.mirostat_ent),
783 |s, c| (s.mirostat_ent - c.mirostat_ent).abs() > 0.001,
784 |s, delta, _| {
785 s.mirostat_ent =
786 ((s.mirostat_ent * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 10.0);
787 },
788 |s, buf| {
789 if let Ok(v) = buf.parse::<i32>() {
790 s.mirostat_ent = (v as f32 / 100.0).clamp(0.0, 10.0);
791 }
792 },
793 EditKind::Direct,
794 ),
795 ultra_field_with_toggle(
796 "ignore_eos",
797 "Ignore EOS",
798 "Sampling",
799 |s| s.ignore_eos.to_string(),
800 |s, c| s.ignore_eos != c.ignore_eos,
801 |_, _, _| {},
802 |_, _| {},
803 toggle_ignore_eos,
804 EditKind::Toggle,
805 ),
806 ultra_field(
807 "samplers",
808 "Samplers",
809 "Sampling",
810 |s| s.samplers.0.clone(),
811 |s, c| s.samplers.0 != c.samplers.0,
812 |_, _, _| {},
813 |_, _| {},
814 EditKind::Modal,
815 ),
816 field_with_toggle(
817 "max_tokens",
818 "Max Tokens",
819 "Sampling",
820 |s| {
821 s.max_tokens
822 .map(|v| v.to_string())
823 .unwrap_or_else(|| "Disabled".to_string())
824 },
825 |s, c| s.max_tokens != c.max_tokens,
826 |s, delta, _| {
827 let current = s.max_tokens.unwrap_or(2048);
828 s.max_tokens = Some((current as i32 + delta * 16).max(16) as u32);
829 },
830 |s, buf| {
831 if let Ok(v) = buf.parse::<i32>() {
832 s.max_tokens = if v == 0 { None } else { Some(v as u32) };
833 }
834 },
835 toggle_max_tokens,
836 EditKind::Direct,
837 ),
838 field(
840 "repeat_penalty",
841 "Repeat Penalty",
842 "Repetition",
843 |s| format!("{:.2}", s.repeat_penalty),
844 |s, c| (s.repeat_penalty - c.repeat_penalty).abs() > 0.001,
845 |s, delta, _| {
846 s.repeat_penalty =
847 ((s.repeat_penalty * 100.0 + delta as f32 * 5.0) / 100.0).clamp(1.0, 2.0);
848 },
849 |s, buf| {
850 if let Ok(v) = buf.parse::<i32>() {
851 s.repeat_penalty = (v as f32 / 100.0).clamp(0.0, 2.0);
852 }
853 },
854 EditKind::Direct,
855 ),
856 field(
857 "repeat_last_n",
858 "Repeat Last N",
859 "Repetition",
860 |s| s.repeat_last_n.to_string(),
861 |s, c| s.repeat_last_n != c.repeat_last_n,
862 |s, delta, _| {
863 s.repeat_last_n = (s.repeat_last_n + delta).max(0);
864 },
865 |s, buf| {
866 if let Ok(v) = buf.parse::<i32>() {
867 s.repeat_last_n = v.max(0);
868 }
869 },
870 EditKind::Direct,
871 ),
872 expert_field_with_toggle(
873 "presence_penalty",
874 "Presence Penalty",
875 "Repetition",
876 |s| {
877 s.presence_penalty
878 .map(|v| {
879 if (v - 0.0).abs() < 0.001 {
880 "Off".to_string()
881 } else {
882 format!("{:.2}", v)
883 }
884 })
885 .unwrap_or_else(|| "Off".to_string())
886 },
887 |s, c| match (s.presence_penalty, c.presence_penalty) {
888 (Some(v1), Some(v2)) => (v1 - v2).abs() > 0.001,
889 (None, None) => false,
890 _ => true,
891 },
892 |s, delta, _| {
893 let current = s.presence_penalty.unwrap_or(0.0);
894 s.presence_penalty =
895 Some(((current * 100.0 + delta as f32 * 5.0) / 100.0).clamp(-2.0, 2.0));
896 },
897 |s, buf| {
898 if let Ok(v) = buf.parse::<i32>() {
899 s.presence_penalty = Some((v as f32 / 100.0).clamp(0.0, 1.0));
900 }
901 },
902 toggle_presence_penalty,
903 EditKind::Direct,
904 ),
905 expert_field_with_toggle(
906 "frequency_penalty",
907 "Freq Penalty",
908 "Repetition",
909 |s| {
910 s.frequency_penalty
911 .map(|v| {
912 if (v - 0.0).abs() < 0.001 {
913 "Off".to_string()
914 } else {
915 format!("{:.2}", v)
916 }
917 })
918 .unwrap_or_else(|| "Off".to_string())
919 },
920 |s, c| match (s.frequency_penalty, c.frequency_penalty) {
921 (Some(v1), Some(v2)) => (v1 - v2).abs() > 0.001,
922 (None, None) => false,
923 _ => true,
924 },
925 |s, delta, _| {
926 let current = s.frequency_penalty.unwrap_or(0.0);
927 s.frequency_penalty =
928 Some(((current * 100.0 + delta as f32 * 5.0) / 100.0).clamp(-2.0, 2.0));
929 },
930 |s, buf| {
931 if let Ok(v) = buf.parse::<i32>() {
932 s.frequency_penalty = Some((v as f32 / 100.0).clamp(0.0, 1.0));
933 }
934 },
935 toggle_frequency_penalty,
936 EditKind::Direct,
937 ),
938 ultra_field(
940 "dry_multiplier",
941 "DRY Multiplier",
942 "DRY",
943 |s| format!("{:.2}", s.dry_multiplier),
944 |s, c| (s.dry_multiplier - c.dry_multiplier).abs() > 0.001,
945 |s, delta, _| {
946 s.dry_multiplier =
947 ((s.dry_multiplier * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 10.0);
948 },
949 |s, buf| {
950 if let Ok(v) = buf.parse::<i32>() {
951 s.dry_multiplier = (v as f32 / 100.0).clamp(0.0, 10.0);
952 }
953 },
954 EditKind::Direct,
955 ),
956 ultra_field(
957 "dry_base",
958 "DRY Base",
959 "DRY",
960 |s| format!("{:.2}", s.dry_base),
961 |s, c| (s.dry_base - c.dry_base).abs() > 0.001,
962 |s, delta, _| {
963 s.dry_base = ((s.dry_base * 100.0 + delta as f32 * 5.0) / 100.0).clamp(0.0, 10.0);
964 },
965 |s, buf| {
966 if let Ok(v) = buf.parse::<i32>() {
967 s.dry_base = (v as f32 / 100.0).clamp(0.0, 10.0);
968 }
969 },
970 EditKind::Direct,
971 ),
972 ultra_field(
973 "dry_allowed_length",
974 "DRY Allowed Length",
975 "DRY",
976 |s| s.dry_allowed_length.to_string(),
977 |s, c| s.dry_allowed_length != c.dry_allowed_length,
978 |s, delta, _| {
979 s.dry_allowed_length = (s.dry_allowed_length + delta).max(0);
980 },
981 |s, buf| {
982 if let Ok(v) = buf.parse::<i32>() {
983 s.dry_allowed_length = v;
984 }
985 },
986 EditKind::Direct,
987 ),
988 ultra_field(
989 "dry_penalty_last_n",
990 "DRY Penalty Last N",
991 "DRY",
992 |s| s.dry_penalty_last_n.to_string(),
993 |s, c| s.dry_penalty_last_n != c.dry_penalty_last_n,
994 |s, delta, _| {
995 s.dry_penalty_last_n = (s.dry_penalty_last_n + delta).max(0);
996 },
997 |s, buf| {
998 if let Ok(v) = buf.parse::<i32>() {
999 s.dry_penalty_last_n = v;
1000 }
1001 },
1002 EditKind::Direct,
1003 ),
1004 expert_field_with_toggle(
1006 "is_mtp",
1007 "MTP",
1008 "Speculative",
1009 |s| {
1010 if s.spec_type.is_empty() {
1011 "Off".to_string()
1012 } else {
1013 s.spec_type.clone()
1014 }
1015 },
1016 |s, c| s.spec_type != c.spec_type,
1017 |_, _, _| {},
1018 |_, _| {},
1019 toggle_mtp,
1020 EditKind::Modal,
1021 ),
1022 expert_field(
1023 "spec_type",
1024 "Spec Type",
1025 "Speculative",
1026 |s| {
1027 if s.spec_type.is_empty() {
1028 "Off".to_string()
1029 } else {
1030 s.spec_type.clone()
1031 }
1032 },
1033 |s, c| s.spec_type != c.spec_type,
1034 |_, _, _| {},
1035 |_, _| {},
1036 EditKind::Modal,
1037 ),
1038 expert_field(
1039 "draft_tokens",
1040 "Spec Draft N Max",
1041 "Speculative",
1042 |s| s.draft_tokens.to_string(),
1043 |s, c| s.draft_tokens != c.draft_tokens,
1044 |s, delta, _| {
1045 s.draft_tokens = (s.draft_tokens as i32 + delta).clamp(0, 16) as u32;
1046 },
1047 |s, buf| {
1048 if let Ok(v) = buf.parse::<u32>() {
1049 s.draft_tokens = v.min(16);
1050 }
1051 },
1052 EditKind::Direct,
1053 ),
1054 field(
1056 "tags",
1057 "Tags (Enter to edit)",
1058 "Tags",
1059 |s| {
1060 if s.tags.is_empty() {
1061 "None".to_string()
1062 } else {
1063 s.tags.join(", ")
1064 }
1065 },
1066 |s, c| s.tags != c.tags,
1067 |_, _, _| {},
1068 |_, _| {},
1069 EditKind::Modal,
1070 ),
1071 field(
1073 "backend_version",
1074 "LLama.cpp Version",
1075 "Backend",
1076 |s| s.get_active_backend_version_display().to_string(),
1077 |s, c| s.get_active_backend_version() != c.get_active_backend_version(),
1078 |_, _, _| {},
1079 |_, _| {},
1080 EditKind::Modal,
1081 ),
1082 ]
1083}
1084
1085pub fn filtered_fields(expert_mode: bool) -> Vec<SettingField> {
1086 all_fields()
1087 .into_iter()
1088 .filter(|f| {
1089 if !expert_mode {
1090 !f.is_expert
1091 } else {
1092 !f.is_ultra }
1094 })
1095 .collect()
1096}
1097
1098#[allow(clippy::too_many_arguments)]
1102pub fn add_setting(
1103 lines: &mut Vec<Line<'static>>,
1104 total_count: &mut usize,
1105 _settings: &ModelSettings,
1106 _cached: &ModelSettings,
1107 selected_line_idx: &mut usize,
1108 selected_content_line: &mut usize,
1109 idx: usize,
1110 name: &str,
1111 val: &str,
1112 selected: usize,
1113 _edit_buf: &str,
1114 _editing: bool,
1115 disabled: bool,
1116) {
1117 let current_line = lines.len();
1118 let name_style = if disabled {
1119 Style::default().fg(Color::DarkGray)
1120 } else {
1121 Style::default().fg(Color::Yellow)
1122 };
1123 let val_style = if disabled {
1124 Style::default()
1125 .fg(Color::DarkGray)
1126 .add_modifier(Modifier::DIM)
1127 } else {
1128 Style::default().fg(Color::White)
1129 };
1130 if idx == selected {
1131 *selected_line_idx = current_line;
1132 *selected_content_line = current_line;
1133 lines.push(Line::from(vec![
1134 Span::styled(
1135 "> ",
1136 Style::default()
1137 .fg(Color::Yellow)
1138 .add_modifier(if disabled {
1139 Modifier::DIM
1140 } else {
1141 Modifier::BOLD
1142 }),
1143 ),
1144 Span::styled(format!("{name}: "), name_style),
1145 Span::styled(
1146 val.to_string(),
1147 Style::default()
1148 .fg(Color::Black)
1149 .bg(Color::Yellow)
1150 .add_modifier(Modifier::BOLD),
1151 ),
1152 ]));
1153 } else {
1154 lines.push(Line::from(vec![
1155 Span::styled(" ", name_style),
1156 Span::styled(format!("{name}: "), name_style),
1157 Span::styled(val.to_string(), val_style),
1158 ]));
1159 }
1160 *total_count += 1;
1161}
1162
1163pub fn profile_settings_parts(profile: &Profile, current: &ModelSettings) -> Vec<String> {
1165 let mut parts = Vec::new();
1166 let s = &profile.settings;
1167
1168 diff_int!(parts, s, current, context_length, "ctx");
1170 diff_int!(parts, s, current, threads, "threads");
1171 diff_int!(parts, s, current, threads_batch, "threads_batch");
1172 diff_int!(parts, s, current, batch_size, "batch");
1173 diff_int!(parts, s, current, ubatch_size, "ubatch");
1174 diff_int!(parts, s, current, parallel, "parallel");
1175 diff_option!(parts, s, current, max_concurrent_predictions, "concurrent");
1176 diff_int!(parts, s, current, cache_reuse, "cache_reuse");
1177 diff_option!(parts, s, current, max_tokens, "max_tokens");
1178 diff_int!(parts, s, current, draft_tokens, "draft_tokens");
1179
1180 diff_int!(parts, s, current, keep, "keep");
1181 diff_int!(parts, s, current, main_gpu, "main_gpu");
1182 diff_int!(parts, s, current, expert_count, "expert_count");
1183 diff_int!(parts, s, current, seed, "seed");
1184 diff_int!(parts, s, current, top_k, "top_k");
1185 diff_int!(parts, s, current, repeat_last_n, "repeat_last_n");
1186 diff_int!(parts, s, current, dry_allowed_length, "dry_allowed");
1187 diff_int!(parts, s, current, dry_penalty_last_n, "dry_penalty_last_n");
1188
1189 diff_float!(parts, s, current, temperature, "temp");
1191 diff_float!(parts, s, current, top_p, "top_p");
1192 diff_float!(parts, s, current, min_p, "min_p");
1193 diff_float!(parts, s, current, typical_p, "typical_p");
1194 diff_float!(parts, s, current, mirostat_lr, "mirostat_lr");
1195 diff_float!(parts, s, current, mirostat_ent, "mirostat_ent");
1196 diff_float!(parts, s, current, repeat_penalty, "rep_pen");
1197 diff_option_float!(parts, s, current, presence_penalty, "pres_pen");
1198 diff_option_float!(parts, s, current, frequency_penalty, "freq_pen");
1199 diff_float!(parts, s, current, dry_multiplier, "dry_mult");
1200 diff_float!(parts, s, current, dry_base, "dry_base");
1201 diff_float!(parts, s, current, rope_scale, "rope_scale");
1202 diff_float!(parts, s, current, rope_freq_base, "rope_freq_base");
1203 diff_float!(parts, s, current, rope_freq_scale, "rope_freq_scale");
1204
1205 diff_bool!(parts, s, current, swa_full, "swa_full");
1207 diff_bool!(parts, s, current, mlock, "mlock");
1208 diff_bool!(parts, s, current, mmap, "mmap");
1209 diff_bool!(parts, s, current, uniform_cache, "uniform_cache");
1210 diff_bool!(parts, s, current, kv_cache_offload, "kv_cache_offload");
1211 diff_bool!(parts, s, current, fit, "fit");
1212 diff_bool!(parts, s, current, embedding, "embedding");
1213 diff_bool!(parts, s, current, flash_attn, "flash_attn");
1214 diff_bool!(parts, s, current, jinja, "jinja");
1215 diff_bool!(parts, s, current, ignore_eos, "ignore_eos");
1216 diff_bool!(parts, s, current, rope_yarn_enabled, "yarn_enabled");
1217 diff_bool!(parts, s, current, cache_prompt, "cache_prompt");
1218 diff_bool!(parts, s, current, webui, "webui");
1219 diff_string!(parts, s, current, system_prompt_preset_name, "preset");
1221 diff_string!(parts, s, current, tensor_split, "tensor_split");
1222 diff_string!(parts, s, current, rpc, "rpc");
1223 diff_option!(parts, s, current, chat_template, "chat_template");
1224 diff_option!(parts, s, current, chat_template_kwargs, "chat_template_kwargs");
1225 diff_option!(parts, s, current, llama_cpp_version_cpu, "llama_cpp_cpu");
1226 diff_option!(parts, s, current, llama_cpp_version_vulkan, "llama_cpp_vulkan");
1227 diff_option!(parts, s, current, llama_cpp_version_rocm, "llama_cpp_rocm");
1228 diff_option!(parts, s, current, llama_cpp_version_rocm_lemonade, "llama_cpp_rocm_lemonade");
1229 diff_option!(parts, s, current, llama_cpp_version_cuda, "llama_cpp_cuda");
1230 diff_string!(parts, s, current, spec_type, "spec_type");
1231
1232 diff_enum!(parts, s, current, numa, "numa");
1234 diff_enum!(parts, s, current, split_mode, "split_mode");
1235 diff_enum!(parts, s, current, mirostat, "mirostat");
1236 diff_enum!(parts, s, current, samplers, "samplers");
1237 diff_enum!(parts, s, current, rope_scaling, "rope_scaling");
1238 diff_enum!(parts, s, current, cache_type, "cache_type");
1239 diff_option!(parts, s, current, cache_type_k, "cache_type_k");
1240 diff_option!(parts, s, current, cache_type_v, "cache_type_v");
1241
1242 if let Some(v) = s.gpu_layers_mode && v != current.gpu_layers_mode {
1244 let display = match v {
1245 crate::models::GpuLayersMode::Auto => "Auto".to_string(),
1246 crate::models::GpuLayersMode::Specific(n) => n.to_string(),
1247 crate::models::GpuLayersMode::All => "All".to_string(),
1248 };
1249 parts.push(format!("gpu_layers={}", display));
1250 }
1251
1252 parts
1253}