Add Metal execution preferences

This commit is contained in:
Georg Bauer
2026-07-24 15:37:13 +02:00
parent bccce697af
commit ac464317d4
8 changed files with 326 additions and 16 deletions

View File

@@ -4,7 +4,7 @@ pub(crate) use view::app_theme;
use crate::database::{AppPreferences, Database, ProjectWithSessions};
use crate::model::{self, DownloadOutcome, DownloadProgress, ManagedArtifactId, ModelChoice};
use crate::settings::{GenerationPreferences, ReasoningMode};
use crate::settings::{ExecutionPreferences, GenerationPreferences, ReasoningMode};
use iced::{Size, Subscription, Task, keyboard, window};
use rfd::AsyncFileDialog;
use std::fs;
@@ -30,6 +30,11 @@ struct PreferenceDraft {
min_p: String,
seed: String,
reasoning_mode: ReasoningMode,
cpu_threads: String,
power_percent: String,
prefill_chunk: String,
quality: bool,
warm_weights: bool,
}
impl PreferenceDraft {
@@ -37,6 +42,8 @@ impl PreferenceDraft {
let model = ModelChoice::from_id(&preferences.selected_model)
.ok_or_else(|| format!("Unsupported model: {}", preferences.selected_model))?;
let generation = preferences.generation()?;
let execution = preferences.execution()?;
execution.validate(model)?;
Ok(Self {
model,
dspark_enabled: model.supports_dspark() && preferences.dspark_enabled,
@@ -57,6 +64,17 @@ impl PreferenceDraft {
.seed
.map_or_else(String::new, |value| value.to_string()),
reasoning_mode: generation.reasoning_mode,
cpu_threads: execution
.cpu_threads
.map_or_else(String::new, |value| value.to_string()),
power_percent: execution
.power_percent
.map_or_else(String::new, |value| value.to_string()),
prefill_chunk: execution
.prefill_chunk
.map_or_else(String::new, |value| value.to_string()),
quality: execution.quality,
warm_weights: execution.warm_weights,
})
}
@@ -80,6 +98,7 @@ impl PreferenceDraft {
fn reset(&mut self) {
let defaults = GenerationPreferences::default();
let execution = ExecutionPreferences::default();
self.model = ModelChoice::default();
self.dspark_enabled = false;
self.idle_timeout_minutes = "10".into();
@@ -91,6 +110,21 @@ impl PreferenceDraft {
self.min_p.clear();
self.seed.clear();
self.reasoning_mode = defaults.reasoning_mode;
self.cpu_threads.clear();
self.power_percent.clear();
self.prefill_chunk.clear();
self.quality = execution.quality;
self.warm_weights = execution.warm_weights;
}
fn execution(&self) -> Result<ExecutionPreferences, String> {
Ok(ExecutionPreferences {
cpu_threads: parse_optional_u32("CPU helper threads", &self.cpu_threads)?,
power_percent: parse_optional_u8("GPU power", &self.power_percent)?,
prefill_chunk: parse_optional_u32("Prefill chunk", &self.prefill_chunk)?,
quality: self.quality,
warm_weights: self.warm_weights,
})
}
}
@@ -127,6 +161,29 @@ fn parse_optional_u64(name: &str, value: &str) -> Result<Option<u64>, String> {
}
}
fn parse_optional_u32(name: &str, value: &str) -> Result<Option<u32>, String> {
parse_optional_number(name, value)
}
fn parse_optional_u8(name: &str, value: &str) -> Result<Option<u8>, String> {
parse_optional_number(name, value)
}
fn parse_optional_number<T: std::str::FromStr>(
name: &str,
value: &str,
) -> Result<Option<T>, String> {
let value = value.trim();
if value.is_empty() {
Ok(None)
} else {
value
.parse()
.map(Some)
.map_err(|_| format!("{name} must be a positive whole number or left blank."))
}
}
pub(crate) struct App {
main_window: window::Id,
pub(super) model_manager_window: Option<window::Id>,
@@ -196,6 +253,11 @@ pub(crate) enum Message {
PreferenceMinPChanged(String),
PreferenceSeedChanged(String),
PreferenceReasoningChanged(ReasoningMode),
PreferenceCpuThreadsChanged(String),
PreferencePowerChanged(String),
PreferencePrefillChunkChanged(String),
PreferenceQualityChanged(bool),
PreferenceWarmWeightsChanged(bool),
ResetPreferences,
SavePreferences,
DownloadArtifact(ManagedArtifactId),
@@ -327,6 +389,10 @@ impl App {
if !model.supports_dspark() {
self.preference_draft.dspark_enabled = false;
}
if model == ModelChoice::Glm52 {
self.preference_draft.power_percent.clear();
self.preference_draft.prefill_chunk.clear();
}
self.preference_error = None;
}
Message::PreferenceDsparkChanged(enabled) => {
@@ -370,6 +436,26 @@ impl App {
self.preference_draft.reasoning_mode = value;
self.preference_error = None;
}
Message::PreferenceCpuThreadsChanged(value) => {
self.preference_draft.cpu_threads = value;
self.preference_error = None;
}
Message::PreferencePowerChanged(value) => {
self.preference_draft.power_percent = value;
self.preference_error = None;
}
Message::PreferencePrefillChunkChanged(value) => {
self.preference_draft.prefill_chunk = value;
self.preference_error = None;
}
Message::PreferenceQualityChanged(value) => {
self.preference_draft.quality = value;
self.preference_error = None;
}
Message::PreferenceWarmWeightsChanged(value) => {
self.preference_draft.warm_weights = value;
self.preference_error = None;
}
Message::ResetPreferences => {
self.preference_draft.reset();
self.preference_error = None;
@@ -610,6 +696,17 @@ impl App {
};
let model = self.preference_draft.model;
let execution = match self.preference_draft.execution() {
Ok(execution) => execution,
Err(error) => {
self.preference_error = Some(error);
return;
}
};
if let Err(error) = execution.validate(model) {
self.preference_error = Some(error);
return;
}
let dspark_enabled = model.supports_dspark() && self.preference_draft.dspark_enabled;
let Some(database) = &mut self.database else {
return;
@@ -619,6 +716,7 @@ impl App {
dspark_enabled,
idle_timeout_minutes,
&generation,
&execution,
) {
Ok(preferences) => {
self.preferences = preferences;