Add Metal execution preferences
This commit is contained in:
8
PLAN.md
8
PLAN.md
@@ -45,7 +45,7 @@ Exit criterion: restart the app and see the same project/session tree.
|
||||
|
||||
## Phase 1 — model library and on-demand loading
|
||||
|
||||
Status: **partially implemented**. Basic preferences, typed generation defaults,
|
||||
Status: **partially implemented**. Typed generation and execution preferences
|
||||
and the complete background download workflow are implemented; the remaining
|
||||
DS4 runtime preferences, loading, inference, and idle unloading remain open.
|
||||
|
||||
@@ -153,9 +153,9 @@ than inventing application-specific behavior.
|
||||
`--nothink`. Preserve DS4's explicit-vs-default distinction because GLM
|
||||
applies model-family sampling defaults only when the user has not overridden
|
||||
them.
|
||||
- **Backend and execution:** backend (`--backend` and its aliases), CPU helper
|
||||
threads, GPU power percentage, prefill chunk, exact/quality kernels, and warm
|
||||
weights. Only offer backend/device choices supported by the current build.
|
||||
- **Execution:** the macOS app uses Metal exclusively; omit the unnecessary CPU
|
||||
backend and backend selector. Persist CPU helper threads for host-side work,
|
||||
GPU power percentage, prefill chunk, exact/quality kernels, and warm weights.
|
||||
- **Speculative decoding:** MTP draft-token count and margin, GLM MTP, GLM MTP
|
||||
timing, DSpark enablement, DSpark confidence threshold, and DSpark strict
|
||||
target-only decode. Enabling a dependent control must enable or require its
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
ALTER TABLE preferences DROP COLUMN warm_weights;
|
||||
ALTER TABLE preferences DROP COLUMN quality;
|
||||
ALTER TABLE preferences DROP COLUMN prefill_chunk;
|
||||
ALTER TABLE preferences DROP COLUMN power_percent;
|
||||
ALTER TABLE preferences DROP COLUMN cpu_threads;
|
||||
10
migrations/20260724200000_add_execution_preferences/up.sql
Normal file
10
migrations/20260724200000_add_execution_preferences/up.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
ALTER TABLE preferences ADD COLUMN cpu_threads INTEGER
|
||||
CHECK (cpu_threads IS NULL OR cpu_threads > 0);
|
||||
ALTER TABLE preferences ADD COLUMN power_percent INTEGER
|
||||
CHECK (power_percent IS NULL OR power_percent BETWEEN 1 AND 100);
|
||||
ALTER TABLE preferences ADD COLUMN prefill_chunk INTEGER
|
||||
CHECK (prefill_chunk IS NULL OR prefill_chunk > 0);
|
||||
ALTER TABLE preferences ADD COLUMN quality BOOLEAN NOT NULL DEFAULT 0
|
||||
CHECK (quality IN (0, 1));
|
||||
ALTER TABLE preferences ADD COLUMN warm_weights BOOLEAN NOT NULL DEFAULT 0
|
||||
CHECK (warm_weights IN (0, 1));
|
||||
100
src/app.rs
100
src/app.rs
@@ -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;
|
||||
|
||||
@@ -305,6 +305,17 @@ impl App {
|
||||
.generation()
|
||||
.ok()
|
||||
.map(|generation| generation.effective(self.preference_draft.model));
|
||||
let execution = self
|
||||
.preference_draft
|
||||
.execution()
|
||||
.ok()
|
||||
.map(|execution| execution.engine_settings());
|
||||
let mut power = text_input("100", &self.preference_draft.power_percent);
|
||||
let mut prefill = text_input("Automatic", &self.preference_draft.prefill_chunk);
|
||||
if self.preference_draft.model != ModelChoice::Glm52 {
|
||||
power = power.on_input(Message::PreferencePowerChanged);
|
||||
prefill = prefill.on_input(Message::PreferencePrefillChunkChanged);
|
||||
}
|
||||
|
||||
let mut fields = column![
|
||||
text("MODEL").size(11),
|
||||
@@ -322,6 +333,38 @@ impl App {
|
||||
})
|
||||
.size(12),
|
||||
Space::with_height(8),
|
||||
text("EXECUTION").size(11),
|
||||
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(execution.map_or_else(
|
||||
|| "Effective execution settings will appear after valid values are entered."
|
||||
.to_owned(),
|
||||
|settings| 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),
|
||||
Space::with_height(8),
|
||||
text("CAPACITY").size(11),
|
||||
preference_input_row(
|
||||
"Context tokens",
|
||||
@@ -424,12 +467,19 @@ impl App {
|
||||
]
|
||||
.spacing(8);
|
||||
|
||||
let panel = container(column![header, scrollable(fields), footer].spacing(16))
|
||||
.padding(24)
|
||||
.width(600)
|
||||
.height(Length::Fill)
|
||||
.max_height(660)
|
||||
.style(overview_style);
|
||||
let panel = container(
|
||||
column![
|
||||
header,
|
||||
scrollable(container(fields).padding(iced::Padding::ZERO.right(18))),
|
||||
footer
|
||||
]
|
||||
.spacing(16),
|
||||
)
|
||||
.padding(24)
|
||||
.width(700)
|
||||
.height(Length::Fill)
|
||||
.max_height(660)
|
||||
.style(overview_style);
|
||||
opaque(
|
||||
container(panel)
|
||||
.padding(24)
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::schema::{preferences, projects, sessions};
|
||||
use crate::settings::{GenerationPreferences, ReasoningMode};
|
||||
use crate::settings::{ExecutionPreferences, GenerationPreferences, ReasoningMode};
|
||||
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
@@ -25,6 +25,11 @@ pub struct AppPreferences {
|
||||
pub min_p: Option<f32>,
|
||||
pub seed: Option<String>,
|
||||
pub reasoning_mode: String,
|
||||
pub cpu_threads: Option<i32>,
|
||||
pub power_percent: Option<i32>,
|
||||
pub prefill_chunk: Option<i32>,
|
||||
pub quality: bool,
|
||||
pub warm_weights: bool,
|
||||
}
|
||||
|
||||
impl Default for AppPreferences {
|
||||
@@ -42,6 +47,11 @@ impl Default for AppPreferences {
|
||||
min_p: None,
|
||||
seed: None,
|
||||
reasoning_mode: "high".into(),
|
||||
cpu_threads: None,
|
||||
power_percent: None,
|
||||
prefill_chunk: None,
|
||||
quality: false,
|
||||
warm_weights: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +77,28 @@ impl AppPreferences {
|
||||
preferences.validate()?;
|
||||
Ok(preferences)
|
||||
}
|
||||
|
||||
pub(crate) fn execution(&self) -> Result<ExecutionPreferences, String> {
|
||||
Ok(ExecutionPreferences {
|
||||
cpu_threads: self
|
||||
.cpu_threads
|
||||
.map(u32::try_from)
|
||||
.transpose()
|
||||
.map_err(|_| "Saved CPU helper threads is invalid.".to_owned())?,
|
||||
power_percent: self
|
||||
.power_percent
|
||||
.map(u8::try_from)
|
||||
.transpose()
|
||||
.map_err(|_| "Saved GPU power is invalid.".to_owned())?,
|
||||
prefill_chunk: self
|
||||
.prefill_chunk
|
||||
.map(u32::try_from)
|
||||
.transpose()
|
||||
.map_err(|_| "Saved prefill chunk is invalid.".to_owned())?,
|
||||
quality: self.quality,
|
||||
warm_weights: self.warm_weights,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(AsChangeset)]
|
||||
@@ -83,6 +115,11 @@ struct PreferenceChanges<'a> {
|
||||
min_p: Option<f32>,
|
||||
seed: Option<&'a str>,
|
||||
reasoning_mode: &'a str,
|
||||
cpu_threads: Option<i32>,
|
||||
power_percent: Option<i32>,
|
||||
prefill_chunk: Option<i32>,
|
||||
quality: bool,
|
||||
warm_weights: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Identifiable, Queryable, Selectable)]
|
||||
@@ -186,8 +223,12 @@ impl Database {
|
||||
dspark_enabled: bool,
|
||||
idle_timeout_minutes: i32,
|
||||
generation: &GenerationPreferences,
|
||||
execution: &ExecutionPreferences,
|
||||
) -> Result<AppPreferences, String> {
|
||||
generation.validate()?;
|
||||
let model = crate::model::ModelChoice::from_id(selected_model)
|
||||
.ok_or_else(|| format!("Unsupported model: {selected_model}"))?;
|
||||
execution.validate(model)?;
|
||||
let seed = generation.seed.map(|seed| seed.to_string());
|
||||
diesel::update(preferences::table.find(1))
|
||||
.set(PreferenceChanges {
|
||||
@@ -202,6 +243,11 @@ impl Database {
|
||||
min_p: generation.min_p,
|
||||
seed: seed.as_deref(),
|
||||
reasoning_mode: generation.reasoning_mode.id(),
|
||||
cpu_threads: execution.cpu_threads.map(|value| value as i32),
|
||||
power_percent: execution.power_percent.map(i32::from),
|
||||
prefill_chunk: execution.prefill_chunk.map(|value| value as i32),
|
||||
quality: execution.quality,
|
||||
warm_weights: execution.warm_weights,
|
||||
})
|
||||
.returning(AppPreferences::as_returning())
|
||||
.get_result(&mut self.connection)
|
||||
@@ -262,14 +308,15 @@ mod tests {
|
||||
assert!(!preferences.dspark_enabled);
|
||||
assert_eq!(preferences.idle_timeout_minutes, 10);
|
||||
let generation = GenerationPreferences::default();
|
||||
let execution = ExecutionPreferences::default();
|
||||
assert!(
|
||||
database
|
||||
.update_preferences("glm-5.2", true, 30, &generation)
|
||||
.update_preferences("glm-5.2", true, 30, &generation, &execution)
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
database
|
||||
.update_preferences("deepseek-v4-flash", false, 0, &generation)
|
||||
.update_preferences("deepseek-v4-flash", false, 0, &generation, &execution)
|
||||
.is_err()
|
||||
);
|
||||
let generation = GenerationPreferences {
|
||||
@@ -278,8 +325,13 @@ mod tests {
|
||||
reasoning_mode: ReasoningMode::Max,
|
||||
..generation
|
||||
};
|
||||
let execution = ExecutionPreferences {
|
||||
cpu_threads: Some(8),
|
||||
quality: true,
|
||||
..execution
|
||||
};
|
||||
database
|
||||
.update_preferences("glm-5.2", false, 30, &generation)
|
||||
.update_preferences("glm-5.2", false, 30, &generation, &execution)
|
||||
.unwrap();
|
||||
|
||||
let project = database.create_project("DS4", "/tmp/ds4").unwrap();
|
||||
@@ -302,6 +354,7 @@ mod tests {
|
||||
assert_eq!(preferences.selected_model, "glm-5.2");
|
||||
assert_eq!(preferences.idle_timeout_minutes, 30);
|
||||
assert_eq!(preferences.generation().unwrap(), generation);
|
||||
assert_eq!(preferences.execution().unwrap(), execution);
|
||||
drop(reopened);
|
||||
fs::remove_file(path).unwrap();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@ diesel::table! {
|
||||
min_p -> Nullable<Float>,
|
||||
seed -> Nullable<Text>,
|
||||
reasoning_mode -> Text,
|
||||
cpu_threads -> Nullable<Integer>,
|
||||
power_percent -> Nullable<Integer>,
|
||||
prefill_chunk -> Nullable<Integer>,
|
||||
quality -> Bool,
|
||||
warm_weights -> Bool,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,74 @@ pub(crate) const REASONING_MODES: [ReasoningMode; 3] = [
|
||||
ReasoningMode::Max,
|
||||
ReasoningMode::Direct,
|
||||
];
|
||||
|
||||
const THINK_MAX_MIN_CONTEXT: i32 = 393_216;
|
||||
const MAX_CPU_THREADS: u32 = 32;
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub(crate) struct ExecutionPreferences {
|
||||
pub(crate) cpu_threads: Option<u32>,
|
||||
pub(crate) power_percent: Option<u8>,
|
||||
pub(crate) prefill_chunk: Option<u32>,
|
||||
pub(crate) quality: bool,
|
||||
pub(crate) warm_weights: bool,
|
||||
}
|
||||
|
||||
impl ExecutionPreferences {
|
||||
pub(crate) fn validate(&self, model: ModelChoice) -> Result<(), String> {
|
||||
if self.cpu_threads == Some(0) {
|
||||
return Err("CPU helper threads must be a positive whole number.".into());
|
||||
}
|
||||
if self
|
||||
.cpu_threads
|
||||
.is_some_and(|threads| threads > i32::MAX as u32)
|
||||
{
|
||||
return Err("CPU helper threads is too large.".into());
|
||||
}
|
||||
if self
|
||||
.power_percent
|
||||
.is_some_and(|power| !(1..=100).contains(&power))
|
||||
{
|
||||
return Err("GPU power must be between 1 and 100 percent.".into());
|
||||
}
|
||||
if self.prefill_chunk == Some(0) {
|
||||
return Err("Prefill chunk must be a positive whole number.".into());
|
||||
}
|
||||
if self
|
||||
.prefill_chunk
|
||||
.is_some_and(|chunk| chunk > i32::MAX as u32)
|
||||
{
|
||||
return Err("Prefill chunk is too large.".into());
|
||||
}
|
||||
if model == ModelChoice::Glm52 {
|
||||
if self.power_percent.is_some_and(|power| power != 100) {
|
||||
return Err("GLM 5.2 currently requires 100% GPU power.".into());
|
||||
}
|
||||
if self.prefill_chunk.is_some() {
|
||||
return Err("GLM 5.2 selects its prefill chunk automatically.".into());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn engine_settings(&self) -> EngineExecutionSettings {
|
||||
EngineExecutionSettings {
|
||||
cpu_threads: self.cpu_threads.unwrap_or(0).min(MAX_CPU_THREADS),
|
||||
power_percent: self.power_percent.unwrap_or(0),
|
||||
prefill_chunk: self.prefill_chunk.unwrap_or(0),
|
||||
quality: self.quality,
|
||||
warm_weights: self.warm_weights,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct EngineExecutionSettings {
|
||||
pub(crate) cpu_threads: u32,
|
||||
pub(crate) power_percent: u8,
|
||||
pub(crate) prefill_chunk: u32,
|
||||
pub(crate) quality: bool,
|
||||
pub(crate) warm_weights: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||
pub(crate) enum ReasoningMode {
|
||||
@@ -156,4 +222,27 @@ mod tests {
|
||||
assert_eq!((effective.top_p, effective.min_p), (0.4, 0.2));
|
||||
assert_eq!(effective.reasoning_mode, ReasoningMode::High);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execution_settings_keep_ds4_automatic_values_and_glm_rules() {
|
||||
let defaults = ExecutionPreferences::default().engine_settings();
|
||||
assert_eq!(
|
||||
(
|
||||
defaults.cpu_threads,
|
||||
defaults.power_percent,
|
||||
defaults.prefill_chunk
|
||||
),
|
||||
(0, 0, 0)
|
||||
);
|
||||
|
||||
let tuned = ExecutionPreferences {
|
||||
cpu_threads: Some(100),
|
||||
power_percent: Some(50),
|
||||
prefill_chunk: Some(4096),
|
||||
..ExecutionPreferences::default()
|
||||
};
|
||||
assert!(tuned.validate(ModelChoice::DeepSeekV4Flash).is_ok());
|
||||
assert!(tuned.validate(ModelChoice::Glm52).is_err());
|
||||
assert_eq!(tuned.engine_settings().cpu_threads, MAX_CPU_THREADS);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user