use super::*; use iced::widget::column; impl App { pub(super) fn preferences_panel(&self) -> Element<'_, Message> { let dspark_toggle: Option Message> = self .preference_draft .model .supports_dspark() .then_some(Message::PreferenceDsparkChanged); let dspark = checkbox( "Enable DSpark for this model", self.preference_draft.dspark_enabled, ) .on_toggle_maybe(dspark_toggle); let glm_mtp_toggle: Option Message> = (self.preference_draft.model == ModelChoice::Glm52) .then_some(Message::PreferenceGlmMtpChanged); let glm_mtp_timing_toggle: Option Message> = (self.preference_draft.model == ModelChoice::Glm52) .then_some(Message::PreferenceGlmMtpTimingChanged); let dspark_strict_toggle: Option Message> = self .preference_draft .model .supports_dspark() .then_some(Message::PreferenceDsparkStrictChanged); let effective = self .preference_draft .generation() .and_then(|generation| { self.preference_draft.runtime().and_then(|runtime| { crate::settings::effective_settings( self.preference_draft.model, &generation, &runtime, &models_path(), ) }) }) .ok(); let engine = effective.as_ref().map(|settings| &settings.engine); let turn = effective.as_ref().map(|settings| &settings.turn); let mut power = text_input("100", &self.preference_draft.power_percent); let mut prefill = text_input("Automatic", &self.preference_draft.prefill_chunk); let mut ssd_full_layers = text_input("Automatic", &self.preference_draft.ssd_full_layers); let mut steering_file = text_input( "Direction-vector file path", &self.preference_draft.directional_steering_file, ); let mut steering_ffn = text_input("Automatic", &self.preference_draft.directional_steering_ffn); let mut steering_attn = text_input("0", &self.preference_draft.directional_steering_attn); let mut dspark_confidence = text_input( "0.9 (DS4 default)", &self.preference_draft.dspark_confidence_threshold, ); if self.preference_draft.model != ModelChoice::Glm52 { power = power.on_input(Message::PreferencePowerChanged); prefill = prefill.on_input(Message::PreferencePrefillChunkChanged); steering_file = steering_file.on_input(Message::PreferenceSteeringFileChanged); steering_ffn = steering_ffn.on_input(Message::PreferenceSteeringFfnChanged); steering_attn = steering_attn.on_input(Message::PreferenceSteeringAttnChanged); } else { ssd_full_layers = ssd_full_layers.on_input(Message::PreferenceSsdFullLayersChanged); } if self.preference_draft.model.supports_dspark() { dspark_confidence = dspark_confidence.on_input(Message::PreferenceDsparkConfidenceChanged); } let model_group = preference_group( "MODEL & LIFECYCLE", column![ pick_list( &MODEL_CHOICES[..], Some(self.preference_draft.model), Message::PreferenceModelChanged, ) .width(Length::Fill), text(format!( "Main: {}{}", engine.map_or_else( || "Invalid settings".to_owned(), |engine| engine.artifacts.model.display().to_string(), ), engine .and_then(|engine| engine.artifacts.mtp.as_ref()) .map_or_else(String::new, |path| format!( " • support: {}", path.display() )), )) .size(12), row![ text_input("10", &self.preference_draft.idle_timeout_minutes) .on_input(Message::PreferenceTimeoutChanged) .width(90) .padding(9), text("minutes before unloading the model").size(13), ] .spacing(10) .align_y(Alignment::Center), text("Enter a whole number from 1 to 1440.").size(12), ] .spacing(10), ); let endpoint_group = preference_group( "LOCAL ENDPOINT", column![ preference_input_row( "Port", text_input("4000", &self.preference_draft.endpoint_port) .on_input(Message::PreferenceEndpointPortChanged), ), text("Listens on 127.0.0.1. Saving a changed port restarts the local endpoint.") .size(12), ] .spacing(10), ); let generation_group = preference_group( "GENERATION", column![ preference_input_row( "Context tokens", text_input("32768", &self.preference_draft.context_tokens) .on_input(Message::PreferenceContextChanged), ), preference_input_row( "Maximum generated tokens", text_input("50000", &self.preference_draft.max_generated_tokens) .on_input(Message::PreferenceMaxTokensChanged), ), text("System prompt").size(13), text_input( "You are a helpful assistant", &self.preference_draft.system_prompt, ) .on_input(Message::PreferenceSystemPromptChanged) .padding(9), Space::with_height(4), text("SAMPLING & REASONING").size(11).color(muted_text()), preference_input_row( "Temperature", text_input("DS4 default", &self.preference_draft.temperature) .on_input(Message::PreferenceTemperatureChanged), ), preference_input_row( "Top-p", text_input("DS4 default", &self.preference_draft.top_p) .on_input(Message::PreferenceTopPChanged), ), preference_input_row( "Min-p", text_input("DS4 default", &self.preference_draft.min_p) .on_input(Message::PreferenceMinPChanged), ), preference_input_row( "Seed", text_input("Random", &self.preference_draft.seed) .on_input(Message::PreferenceSeedChanged), ), row![ text("Reasoning").size(13).width(Length::Fill), pick_list( &REASONING_MODES[..], Some(self.preference_draft.reasoning_mode), Message::PreferenceReasoningChanged, ) .width(240), ] .spacing(12) .align_y(Alignment::Center), text("Blank sampling values retain DS4's model-family defaults. Think Max needs at least 393216 context tokens.") .size(12), text(turn.map_or_else( || "Effective settings will appear after valid values are entered.".to_owned(), |settings| format!( "Effective: {} context • {} max • temp {} • top-p {} • min-p {} • seed {} • {} • system prompt {}", settings.context_tokens, settings.max_generated_tokens, settings.temperature, settings.top_p, settings.min_p, settings.seed.map_or_else(|| "random".to_owned(), |seed| seed.to_string()), settings.reasoning_mode, if settings.system_prompt.is_empty() { "off" } else { "on" }, ), )) .size(12), ] .spacing(10), ); let execution_group = preference_group( "EXECUTION", column![ preference_input_row( "CPU helper threads", text_input("Automatic", &self.preference_draft.cpu_threads) .on_input(Message::PreferenceCpuThreadsChanged), ), preference_input_row("GPU power percent", power), preference_input_row("Prefill chunk", prefill), checkbox("Prefer exact quality kernels", self.preference_draft.quality) .on_toggle(Message::PreferenceQualityChanged), checkbox("Warm mapped weights at load time", self.preference_draft.warm_weights) .on_toggle(Message::PreferenceWarmWeightsChanged), text(if self.preference_draft.model == ModelChoice::Glm52 { "GLM 5.2 uses full GPU power and selects prefill chunks automatically." } else { "Blank numeric values preserve DS4's automatic engine behavior." }) .size(12), text(engine.as_ref().map_or_else( || "Effective execution settings will appear after valid values are entered." .to_owned(), |engine| { let settings = engine.execution; format!( "Metal engine: threads {} • power {}% • prefill {} • quality {} • warm weights {}", if settings.cpu_threads == 0 { "auto".to_owned() } else { settings.cpu_threads.to_string() }, if settings.power_percent == 0 { 100 } else { settings.power_percent }, if settings.prefill_chunk == 0 { "auto".to_owned() } else { settings.prefill_chunk.to_string() }, if settings.quality { "on" } else { "off" }, if settings.warm_weights { "on" } else { "off" }, ) }, )) .size(12), ] .spacing(10), ); let acceleration_group = preference_group( "ACCELERATION & MEMORY", column![ text("SPECULATIVE DECODING").size(11).color(muted_text()), preference_input_row( "MTP draft tokens", text_input("1", &self.preference_draft.mtp_draft_tokens) .on_input(Message::PreferenceMtpDraftChanged), ), preference_input_row( "MTP verifier margin", text_input("3", &self.preference_draft.mtp_margin) .on_input(Message::PreferenceMtpMarginChanged), ), checkbox("Enable integrated GLM MTP", self.preference_draft.glm_mtp) .on_toggle_maybe(glm_mtp_toggle), checkbox( "Log GLM MTP timing counters", self.preference_draft.glm_mtp_timing, ) .on_toggle_maybe(glm_mtp_timing_toggle), dspark, preference_input_row("DSpark confidence threshold", dspark_confidence), checkbox( "DSpark target-only decode", self.preference_draft.dspark_strict, ) .on_toggle_maybe(dspark_strict_toggle), text(if self.preference_draft.model.supports_dspark() { "DSpark uses the managed support artifact; entering a threshold or enabling strict mode also enables DSpark." } else if self.preference_draft.model == ModelChoice::Glm52 { "GLM MTP is integrated; DSpark is unavailable for this model." } else { "No managed MTP support artifact is available for this model." }) .size(12), text(engine.as_ref().map_or_else( || "Effective speculative settings will appear after valid values are entered." .to_owned(), |engine| { let settings = engine.speculative; format!( "Engine: MTP draft {} • margin {} • GLM MTP {} • timing {} • DSpark {} • confidence {}{} • target-only {}", settings.mtp_draft_tokens, settings.mtp_margin, if settings.glm_mtp { "on" } else { "off" }, if settings.glm_mtp_timing { "on" } else { "off" }, if settings.dspark { "on" } else { "off" }, settings.dspark_confidence_threshold, if settings.dspark_confidence_threshold_set { " explicit" } else { " default" }, if settings.dspark_strict { "on" } else { "off" }, ) }, )) .size(12), Space::with_height(6), text("SSD STREAMING").size(11).color(muted_text()), checkbox("Enable SSD-backed model streaming", self.preference_draft.ssd_streaming) .on_toggle(Message::PreferenceSsdChanged), checkbox("Skip automatic expert preload", self.preference_draft.ssd_streaming_cold) .on_toggle(Message::PreferenceSsdColdChanged), preference_input_row( "Expert cache count or GiB", text_input("Automatic, 128, or 64GB", &self.preference_draft.ssd_cache) .on_input(Message::PreferenceSsdCacheChanged), ), preference_input_row("Fully resident GLM layers", ssd_full_layers), preference_input_row( "Explicit expert preload count", text_input("Automatic", &self.preference_draft.ssd_preload_experts) .on_input(Message::PreferenceSsdPreloadChanged), ), text("A blank full-layer value is automatic; an explicit 0 disables fully resident GLM layers. SSD streaming and DSpark are mutually exclusive.") .size(12), text(engine.as_ref().map_or_else( || "Effective SSD settings will appear after valid values are entered." .to_owned(), |engine| { let settings = engine.ssd; let cache = if settings.cache_bytes > 0 { format!("{} GiB", settings.cache_bytes / GIB) } else if settings.cache_experts > 0 { format!("{} experts", settings.cache_experts) } else { "auto".to_owned() }; format!( "Engine: streaming {} • cold {} • cache {} • full layers {}{} • preload {}", if settings.enabled { "on" } else { "off" }, if settings.cold { "on" } else { "off" }, cache, settings.full_layers, if settings.full_layers_set { " explicit" } else { " auto" }, if settings.preload_experts == 0 { "auto".to_owned() } else { settings.preload_experts.to_string() }, ) }, )) .size(12), ] .spacing(10), ); let steering_group = preference_group( "STEERING & DIAGNOSTICS", column![ text("DIRECTIONAL STEERING").size(11).color(muted_text()), text("Direction-vector file").size(13), steering_file.padding(9), preference_input_row("FFN scale", steering_ffn), preference_input_row("Attention scale", steering_attn), text(if self.preference_draft.model == ModelChoice::Glm52 { "Directional steering is not supported for GLM 5.2." } else { "With a file and no explicit scale, DS4 defaults the FFN scale to 1. Scales accept -100 through 100." }) .size(12), text(engine.as_ref().map_or_else( || "Effective steering settings will appear after valid values are entered." .to_owned(), |engine| format!( "Engine: file {} • FFN scale {} • attention scale {}", if engine.steering.file.is_some() { "set" } else { "off" }, engine.steering.ffn_scale, engine.steering.attention_scale, ), )) .size(12), Space::with_height(6), text("ADVANCED DIAGNOSTICS").size(11).color(muted_text()), preference_input_row( "Simulated used memory (GiB)", text_input("Disabled", &self.preference_draft.simulated_used_memory_gib) .on_input(Message::PreferenceSimulatedMemoryChanged), ), text("Routed expert profile output").size(13), text_input("Output file path", &self.preference_draft.expert_profile_path) .on_input(Message::PreferenceExpertProfileChanged) .padding(9), text(engine.as_ref().map_or_else( || "Effective diagnostic settings will appear after valid values are entered." .to_owned(), |engine| format!( "{} load: simulated memory {} • expert profile {}", engine.model, if engine.diagnostics.simulated_used_memory_bytes == 0 { "off".to_owned() } else { format!("{} GiB", engine.diagnostics.simulated_used_memory_bytes / GIB) }, if engine.diagnostics.expert_profile_path.is_some() { "set" } else { "off" }, ), )) .size(12), ] .spacing(10), ); let mut fields = column![ model_group, endpoint_group, generation_group, execution_group, acceleration_group, steering_group, ] .spacing(12); if let Some(error) = &self.preference_error { fields = fields.push(text(error).style(iced::widget::text::danger)); } let header = row![ icon(ICON_SETTINGS, 22), text("Preferences").size(24), Space::with_width(Length::Fill), text("⌘,").size(12), ] .spacing(10) .align_y(Alignment::Center); let footer = row![ action_button("Reset DS4 defaults").on_press(Message::ResetPreferences), Space::with_width(Length::Fill), action_button("Cancel").on_press(Message::DismissPanel), action_button("Save").on_press(Message::SavePreferences), ] .spacing(8); let panel = container( column![ header, scrollable(container(fields).padding(iced::Padding::ZERO.right(18))) .height(Length::Fill), footer ] .spacing(16), ) .padding(24) .width(700) .height(Length::Fill) .max_height(660) .style(overview_style); opaque( container(panel) .padding(24) .center_x(Length::Fill) .center_y(Length::Fill) .style(|_| { container::Style::default().background(Color::from_rgba8(0, 0, 0, 0.68)) }), ) } }