Skip to main content

llm_manager/tui/app/
profiles.rs

1use super::types::App;
2
3impl App {
4    /// Apply a profile's settings to the current settings.
5    pub fn apply_profile(&mut self, profile: &crate::config::Profile) {
6        self.settings = profile.apply(self.settings.clone());
7        self.resolve_system_prompt();
8        self.settings_state.settings_render_cache = None;
9        self.add_log(
10            format!("Applied profile: {}", profile.name),
11            crate::config::LogLevel::Info,
12        );
13    }
14
15    /// Resolve system_prompt from the preset name.
16    pub fn resolve_system_prompt(&mut self) {
17        if let Some(content) = self
18            .config
19            .get_preset_content(&self.settings.system_prompt_preset_name)
20        {
21            self.settings.system_prompt = content;
22        }
23    }
24
25    /// Save the current settings as a new profile.
26    pub fn save_current_as_profile(&mut self, name: &str) {
27        let profile = crate::config::Profile {
28            name: name.to_string(),
29            description: format!("User-defined profile: {}", name),
30            settings: crate::config::ModelOverride::from_settings(&self.settings),
31        };
32        self.config.profiles.save(&profile);
33        if let Err(e) = self.config.save() {
34            self.add_log(
35                format!("Failed to save profile: {}", e),
36                crate::config::LogLevel::Error,
37            );
38        } else {
39            self.add_log(
40                format!("Saved profile: {}", name),
41                crate::config::LogLevel::Info,
42            );
43        }
44    }
45
46    /// Save current settings as an override for the selected model.
47    pub fn save_model_settings(&mut self) {
48        if let Some(model) = self.selected_model() {
49            let name = model.name.clone();
50            let override_cfg = crate::config::ModelOverride::from_settings(&self.settings);
51            self.config.model_overrides.save(&name, &override_cfg);
52            if let Err(e) = self.config.save() {
53                self.add_log(
54                    format!("Failed to save settings for {}: {}", name, e),
55                    crate::config::LogLevel::Error,
56                );
57            } else {
58                self.add_log(
59                    format!("Saved settings for {}", name),
60                    crate::config::LogLevel::Info,
61                );
62                // Update the cache so it reflects the newly saved settings
63                self.model_settings_cache = self.settings.clone();
64            }
65        } else {
66            self.add_log(
67                "No model selected to save settings for",
68                crate::config::LogLevel::Warning,
69            );
70        }
71        self.settings_state.settings_render_cache = None;
72    }
73
74    /// Check if any LLM settings have been modified since last save.
75    pub fn is_settings_dirty(&self) -> bool {
76        self.settings.is_dirty(&self.model_settings_cache)
77    }
78
79    /// Compute a fingerprint of the current settings for cache invalidation.
80    pub fn settings_fingerprint(&self) -> u64 {
81        use std::collections::hash_map::DefaultHasher;
82        use std::hash::{Hash, Hasher};
83        let mut h = DefaultHasher::new();
84        self.settings.context_length.hash(&mut h);
85        self.settings.system_prompt_preset_name.hash(&mut h);
86        self.settings.mlock.hash(&mut h);
87        self.settings.gpu_layers_mode.hash(&mut h);
88        self.settings.flash_attn.hash(&mut h);
89        self.settings.fit.hash(&mut h);
90        self.settings.kv_cache_offload.hash(&mut h);
91        self.settings.cache_type_k.hash(&mut h);
92        self.settings.cache_type_v.hash(&mut h);
93        self.settings.expert_count.hash(&mut h);
94        self.settings.batch_size.hash(&mut h);
95        self.settings.uniform_cache.hash(&mut h);
96        self.settings.max_concurrent_predictions.hash(&mut h);
97        self.settings.seed.hash(&mut h);
98        self.settings.temperature.to_bits().hash(&mut h);
99        self.settings.top_k.hash(&mut h);
100        self.settings.top_p.to_bits().hash(&mut h);
101        self.settings.min_p.to_bits().hash(&mut h);
102        self.settings.max_tokens.hash(&mut h);
103        self.settings.repeat_penalty.to_bits().hash(&mut h);
104        self.settings.repeat_last_n.hash(&mut h);
105        self.settings
106            .presence_penalty
107            .map(|v| v.to_bits())
108            .hash(&mut h);
109        self.settings
110            .frequency_penalty
111            .map(|v| v.to_bits())
112            .hash(&mut h);
113        self.settings.keep.hash(&mut h);
114        self.settings.mmap.hash(&mut h);
115        self.settings.numa.hash(&mut h);
116        self.settings.threads.hash(&mut h);
117        self.settings.threads_batch.hash(&mut h);
118        self.settings.get_active_backend_version().hash(&mut h);
119        self.settings_state.settings_edit_buffer.hash(&mut h);
120        self.settings_state.expert_mode.hash(&mut h);
121        h.finish()
122    }
123
124    /// Delete a user profile by index in the merged display list.
125    /// Returns true if a profile was deleted, false otherwise.
126    pub fn delete_profile(&mut self, selected_idx: usize) -> bool {
127        let builtin = crate::config::builtin_profiles();
128        let all_profiles = self.config.profiles.all();
129
130        // Check if selection is valid
131        if selected_idx >= all_profiles.len() {
132            self.add_log("Invalid profile selection", crate::config::LogLevel::Info);
133            return false;
134        }
135
136        // Check if it's a built-in profile
137        if selected_idx < builtin.len() {
138            self.add_log(
139                "Cannot delete built-in profiles",
140                crate::config::LogLevel::Info,
141            );
142            return false;
143        }
144
145        let profile = all_profiles[selected_idx].clone();
146        let profile_name = profile.name.clone();
147
148        self.config.profiles.delete(&profile_name);
149
150        if let Err(e) = self.config.save() {
151            self.add_log(
152                format!("Failed to delete profile: {}", e),
153                crate::config::LogLevel::Error,
154            );
155            return false;
156        }
157
158        self.add_log(
159            format!("Deleted profile: {}", profile_name),
160            crate::config::LogLevel::Info,
161        );
162        true
163    }
164
165    pub fn get_api_port_str(&self) -> String {
166        let port = self.settings.api_endpoint_port;
167        let mut cache = super::types::API_PORT_CACHE
168            .lock()
169            .unwrap_or_else(|e| e.into_inner());
170        if cache.0 == port && !cache.1.is_empty() {
171            return cache.1.clone();
172        }
173        cache.0 = port;
174        cache.1 = port.to_string();
175        cache.1.clone()
176    }
177}