Store preferences by model profile
This commit is contained in:
@@ -19,9 +19,10 @@ The composer status row shows the current project and, for Git repositories,
|
|||||||
the current local branch. An unsaved draft can be moved with the project menu;
|
the current local branch. An unsaved draft can be moved with the project menu;
|
||||||
saved chats keep their original project. Use the branch menu to switch local
|
saved chats keep their original project. Use the branch menu to switch local
|
||||||
branches when that project's chats are idle. The thinking menu is preselected
|
branches when that project's chats are idle. The thinking menu is preselected
|
||||||
to the model's current mode: **Thinking** is the DS4 default, while **Direct**
|
to the active model's default mode and loads that model-and-thinking profile:
|
||||||
answers without hidden reasoning. **Think Max** is available when the context
|
**Thinking** is the DS4 default, while **Direct** answers without hidden
|
||||||
window is at least 393216 tokens, matching DS4's long-context requirement.
|
reasoning. **Think Max** is available when its profile uses at least 393216
|
||||||
|
context tokens, matching DS4's long-context requirement.
|
||||||
|
|
||||||
The **Git** pane shows added, changed, and deleted files from the current
|
The **Git** pane shows added, changed, and deleted files from the current
|
||||||
worktree, including separate staged and worktree markers. Select files to stage,
|
worktree, including separate staged and worktree markers. Select files to stage,
|
||||||
@@ -66,6 +67,9 @@ Open **View > Model Manager** (`⇧⌘M`) to download, resume, verify, or remove
|
|||||||
supported model artifacts. Preferences choose the active model and control
|
supported model artifacts. Preferences choose the active model and control
|
||||||
generation, context, speculative decoding, Metal execution, SSD expert
|
generation, context, speculative decoding, Metal execution, SSD expert
|
||||||
streaming, steering, checkpoint storage, diagnostics, and the local endpoint.
|
streaming, steering, checkpoint storage, diagnostics, and the local endpoint.
|
||||||
|
Generation values are stored per model and thinking mode. Acceleration and SSD
|
||||||
|
values are stored per model, while the Prompt section's system prompt is shared
|
||||||
|
by every profile.
|
||||||
|
|
||||||
Model files are large. Verification checks the complete artifact before it is
|
Model files are large. Verification checks the complete artifact before it is
|
||||||
used. Removing a model never removes projects or chat history.
|
used. Removing a model never removes projects or chat history.
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ pub(crate) fn run(args: impl Iterator<Item = String>) -> Result<(), String> {
|
|||||||
.timeout_recv_body(Some(Duration::from_secs(30)))
|
.timeout_recv_body(Some(Duration::from_secs(30)))
|
||||||
.build()
|
.build()
|
||||||
.into();
|
.into();
|
||||||
let mut system = crate::agent::system_prompt(model, &config.generation.system_prompt, false);
|
let mut system = crate::agent::system_prompt(model, &config.system_prompt, false);
|
||||||
system.push_str("\n\n");
|
system.push_str("\n\n");
|
||||||
system.push_str(crate::a2ui::SYSTEM_PROMPT);
|
system.push_str(crate::a2ui::SYSTEM_PROMPT);
|
||||||
let metadata = Store::default().client_metadata();
|
let metadata = Store::default().client_metadata();
|
||||||
|
|||||||
42
src/app.rs
42
src/app.rs
@@ -16,7 +16,7 @@ use preferences::{parse_optional_gib, parse_streaming_cache};
|
|||||||
|
|
||||||
use crate::config::{
|
use crate::config::{
|
||||||
Config, DevBrainConfig, EndpointConfig, GitConfig, GitDiffAlgorithm, GitDiffLayout,
|
Config, DevBrainConfig, EndpointConfig, GitConfig, GitDiffAlgorithm, GitDiffLayout,
|
||||||
GitDiffWhitespace, PermissionMode,
|
GitDiffWhitespace, ModelPreferences, PermissionMode,
|
||||||
};
|
};
|
||||||
use crate::database::{Database, ProjectWithSessions, SessionState, StoredMessage};
|
use crate::database::{Database, ProjectWithSessions, SessionState, StoredMessage};
|
||||||
#[cfg(any(target_os = "macos", test))]
|
#[cfg(any(target_os = "macos", test))]
|
||||||
@@ -37,7 +37,7 @@ use crate::settings::{
|
|||||||
use iced::widget::{markdown, scrollable, text_editor};
|
use iced::widget::{markdown, scrollable, text_editor};
|
||||||
use iced::{Size, Subscription, Task, keyboard, mouse, window};
|
use iced::{Size, Subscription, Task, keyboard, mouse, window};
|
||||||
use rfd::AsyncFileDialog;
|
use rfd::AsyncFileDialog;
|
||||||
use std::collections::{HashMap, HashSet, VecDeque};
|
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -261,6 +261,7 @@ pub(super) enum PreferenceSection {
|
|||||||
Endpoint,
|
Endpoint,
|
||||||
DevBrain,
|
DevBrain,
|
||||||
Git,
|
Git,
|
||||||
|
Prompt,
|
||||||
Generation,
|
Generation,
|
||||||
Execution,
|
Execution,
|
||||||
Acceleration,
|
Acceleration,
|
||||||
@@ -269,11 +270,12 @@ pub(super) enum PreferenceSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PreferenceSection {
|
impl PreferenceSection {
|
||||||
const ALL: [Self; 9] = [
|
const ALL: [Self; 10] = [
|
||||||
Self::Model,
|
Self::Model,
|
||||||
Self::Endpoint,
|
Self::Endpoint,
|
||||||
Self::DevBrain,
|
Self::DevBrain,
|
||||||
Self::Git,
|
Self::Git,
|
||||||
|
Self::Prompt,
|
||||||
Self::Generation,
|
Self::Generation,
|
||||||
Self::Execution,
|
Self::Execution,
|
||||||
Self::Acceleration,
|
Self::Acceleration,
|
||||||
@@ -287,6 +289,7 @@ impl PreferenceSection {
|
|||||||
Self::Endpoint => "preferences-endpoint",
|
Self::Endpoint => "preferences-endpoint",
|
||||||
Self::DevBrain => "preferences-dev-brain",
|
Self::DevBrain => "preferences-dev-brain",
|
||||||
Self::Git => "preferences-git",
|
Self::Git => "preferences-git",
|
||||||
|
Self::Prompt => "preferences-prompt",
|
||||||
Self::Generation => "preferences-generation",
|
Self::Generation => "preferences-generation",
|
||||||
Self::Execution => "preferences-execution",
|
Self::Execution => "preferences-execution",
|
||||||
Self::Acceleration => "preferences-acceleration",
|
Self::Acceleration => "preferences-acceleration",
|
||||||
@@ -301,6 +304,7 @@ impl PreferenceSection {
|
|||||||
Self::Endpoint => "Local endpoint",
|
Self::Endpoint => "Local endpoint",
|
||||||
Self::DevBrain => "Dev Brain",
|
Self::DevBrain => "Dev Brain",
|
||||||
Self::Git => "Git diffs",
|
Self::Git => "Git diffs",
|
||||||
|
Self::Prompt => "Prompt",
|
||||||
Self::Generation => "Generation",
|
Self::Generation => "Generation",
|
||||||
Self::Execution => "Execution",
|
Self::Execution => "Execution",
|
||||||
Self::Acceleration => "Acceleration & memory",
|
Self::Acceleration => "Acceleration & memory",
|
||||||
@@ -350,6 +354,10 @@ pub(crate) enum Message {
|
|||||||
FocusNext,
|
FocusNext,
|
||||||
FocusPrevious,
|
FocusPrevious,
|
||||||
PreferenceModelChanged(ModelChoice),
|
PreferenceModelChanged(ModelChoice),
|
||||||
|
PreferenceDefaultReasoningChanged(ReasoningMode),
|
||||||
|
PreferenceGenerationModelChanged(ModelChoice),
|
||||||
|
PreferenceGenerationReasoningChanged(ReasoningMode),
|
||||||
|
PreferenceAccelerationModelChanged(ModelChoice),
|
||||||
PreferencePermissionModeChanged(PermissionMode),
|
PreferencePermissionModeChanged(PermissionMode),
|
||||||
PreferenceLegacyMtpChanged(bool),
|
PreferenceLegacyMtpChanged(bool),
|
||||||
PreferenceDsparkChanged(bool),
|
PreferenceDsparkChanged(bool),
|
||||||
@@ -379,7 +387,6 @@ pub(crate) enum Message {
|
|||||||
PreferenceTopPChanged(String),
|
PreferenceTopPChanged(String),
|
||||||
PreferenceMinPChanged(String),
|
PreferenceMinPChanged(String),
|
||||||
PreferenceSeedChanged(String),
|
PreferenceSeedChanged(String),
|
||||||
PreferenceReasoningChanged(ReasoningMode),
|
|
||||||
PreferenceCpuThreadsChanged(String),
|
PreferenceCpuThreadsChanged(String),
|
||||||
PreferencePowerChanged(String),
|
PreferencePowerChanged(String),
|
||||||
PreferencePrefillChunkChanged(String),
|
PreferencePrefillChunkChanged(String),
|
||||||
@@ -505,7 +512,7 @@ impl App {
|
|||||||
sweep_orphan_checkpoints(&kv_cache_path(), &projects);
|
sweep_orphan_checkpoints(&kv_cache_path(), &projects);
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
crate::engine::sweep_transient_cache(&transient_cache_path());
|
crate::engine::sweep_transient_cache(&transient_cache_path());
|
||||||
let context_limit = config.generation.context_tokens.max(0) as u32;
|
let context_limit = config.active_generation().context_tokens.max(0) as u32;
|
||||||
let default_permission_mode = config.default_permission_mode;
|
let default_permission_mode = config.default_permission_mode;
|
||||||
// Reopen on the project we left, with a fresh draft chat.
|
// Reopen on the project we left, with a fresh draft chat.
|
||||||
let last_project = config
|
let last_project = config
|
||||||
@@ -658,7 +665,7 @@ impl App {
|
|||||||
fn failed(error: String, main_window: window::Id) -> Self {
|
fn failed(error: String, main_window: window::Id) -> Self {
|
||||||
let config = Config::default();
|
let config = Config::default();
|
||||||
let preference_draft = PreferenceDraft::from_saved(&config);
|
let preference_draft = PreferenceDraft::from_saved(&config);
|
||||||
let context_limit = config.generation.context_tokens.max(0) as u32;
|
let context_limit = config.active_generation().context_tokens.max(0) as u32;
|
||||||
let default_permission_mode = config.default_permission_mode;
|
let default_permission_mode = config.default_permission_mode;
|
||||||
let metrics = Arc::new(Metrics::new(&application_support_path().join("kv-cache")));
|
let metrics = Arc::new(Metrics::new(&application_support_path().join("kv-cache")));
|
||||||
let metrics_snapshot = metrics.snapshot();
|
let metrics_snapshot = metrics.snapshot();
|
||||||
@@ -811,7 +818,7 @@ impl App {
|
|||||||
context_used: std::mem::take(&mut self.context_used),
|
context_used: std::mem::take(&mut self.context_used),
|
||||||
context_limit: std::mem::replace(
|
context_limit: std::mem::replace(
|
||||||
&mut self.context_limit,
|
&mut self.context_limit,
|
||||||
self.config.generation.context_tokens.max(0) as u32,
|
self.config.active_generation().context_tokens.max(0) as u32,
|
||||||
),
|
),
|
||||||
tokens_per_second: self.tokens_per_second.take(),
|
tokens_per_second: self.tokens_per_second.take(),
|
||||||
detail_tab: std::mem::take(&mut self.detail_tab),
|
detail_tab: std::mem::take(&mut self.detail_tab),
|
||||||
@@ -1545,8 +1552,7 @@ impl App {
|
|||||||
Some("Stop all active generations before changing thinking mode.".into());
|
Some("Stop all active generations before changing thinking mode.".into());
|
||||||
} else if !self
|
} else if !self
|
||||||
.config
|
.config
|
||||||
.generation
|
.supported_reasoning_modes(self.config.model)
|
||||||
.supported_reasoning_modes()
|
|
||||||
.contains(&mode)
|
.contains(&mode)
|
||||||
{
|
{
|
||||||
self.error = Some(
|
self.error = Some(
|
||||||
@@ -1554,11 +1560,23 @@ impl App {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let mut config = self.config.clone();
|
let mut config = self.config.clone();
|
||||||
config.generation.reasoning_mode = mode;
|
config
|
||||||
|
.model_profiles
|
||||||
|
.entry(config.model)
|
||||||
|
.or_default()
|
||||||
|
.reasoning_mode = mode;
|
||||||
match config.save(&config_path()) {
|
match config.save(&config_path()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.config = config;
|
self.config = config;
|
||||||
self.preference_draft.reasoning_mode = mode;
|
self.preference_draft.default_reasoning_mode = mode;
|
||||||
|
self.preference_draft
|
||||||
|
.load_generation(self.config.model, mode);
|
||||||
|
self.context_limit =
|
||||||
|
self.config.active_generation().context_tokens.max(0) as u32;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
self.agent_tools = None;
|
||||||
|
}
|
||||||
self.error = None;
|
self.error = None;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
preferences::update_runtime_config(&self.runtime_config, &self.config);
|
preferences::update_runtime_config(&self.runtime_config, &self.config);
|
||||||
@@ -1812,7 +1830,7 @@ impl App {
|
|||||||
self.context_limit = if limit > 0 {
|
self.context_limit = if limit > 0 {
|
||||||
limit as u32
|
limit as u32
|
||||||
} else {
|
} else {
|
||||||
self.config.generation.context_tokens.max(0) as u32
|
self.config.active_generation().context_tokens.max(0) as u32
|
||||||
};
|
};
|
||||||
self.tokens_per_second = tokens_per_second;
|
self.tokens_per_second = tokens_per_second;
|
||||||
self.permission_mode = permission_mode;
|
self.permission_mode = permission_mode;
|
||||||
|
|||||||
@@ -524,12 +524,10 @@ impl App {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let model = self.config.model;
|
let model = self.config.model;
|
||||||
let effective = crate::settings::effective_settings(
|
let generation = self.config.active_generation();
|
||||||
model,
|
let runtime = self.config.runtime_for(model);
|
||||||
&self.config.generation,
|
let effective =
|
||||||
&self.config.runtime,
|
crate::settings::effective_settings(model, &generation, &runtime, &models_path());
|
||||||
&models_path(),
|
|
||||||
);
|
|
||||||
let mut effective = match effective {
|
let mut effective = match effective {
|
||||||
Ok(settings) => settings,
|
Ok(settings) => settings,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -728,8 +726,8 @@ impl App {
|
|||||||
if self.config.a2ui_enabled {
|
if self.config.a2ui_enabled {
|
||||||
reminders.push(crate::a2ui::SYSTEM_PROMPT.to_owned());
|
reminders.push(crate::a2ui::SYSTEM_PROMPT.to_owned());
|
||||||
}
|
}
|
||||||
if !self.config.generation.system_prompt.trim().is_empty() {
|
if !self.config.system_prompt.trim().is_empty() {
|
||||||
reminders.push(self.config.generation.system_prompt.clone());
|
reminders.push(self.config.system_prompt.clone());
|
||||||
}
|
}
|
||||||
if let Some(agents) = self.session_agents_prompt() {
|
if let Some(agents) = self.session_agents_prompt() {
|
||||||
reminders.push(agents.to_owned());
|
reminders.push(agents.to_owned());
|
||||||
@@ -1239,7 +1237,8 @@ impl App {
|
|||||||
.find(|project| project.project.id == project_id)
|
.find(|project| project.project.id == project_id)
|
||||||
.map(|project| PathBuf::from(&project.project.path))
|
.map(|project| PathBuf::from(&project.project.path))
|
||||||
.ok_or_else(|| "The active project is unavailable.".to_owned())?;
|
.ok_or_else(|| "The active project is unavailable.".to_owned())?;
|
||||||
let mut tools = crate::agent::Tools::new(&root, self.config.generation.context_tokens)?;
|
let mut tools =
|
||||||
|
crate::agent::Tools::new(&root, self.config.active_generation().context_tokens)?;
|
||||||
if self.config.dev_brain.enabled {
|
if self.config.dev_brain.enabled {
|
||||||
let projects = self
|
let projects = self
|
||||||
.projects
|
.projects
|
||||||
@@ -1254,10 +1253,12 @@ impl App {
|
|||||||
let approval_mode = match self.permission_mode {
|
let approval_mode = match self.permission_mode {
|
||||||
PermissionMode::Heuristic => crate::agent::ShellApprovalMode::Heuristic,
|
PermissionMode::Heuristic => crate::agent::ShellApprovalMode::Heuristic,
|
||||||
PermissionMode::Ai => {
|
PermissionMode::Ai => {
|
||||||
|
let generation = self.config.active_generation();
|
||||||
|
let runtime = self.config.runtime_for(self.config.model);
|
||||||
let effective = crate::settings::effective_settings(
|
let effective = crate::settings::effective_settings(
|
||||||
self.config.model,
|
self.config.model,
|
||||||
&self.config.generation,
|
&generation,
|
||||||
&self.config.runtime,
|
&runtime,
|
||||||
&models_path(),
|
&models_path(),
|
||||||
)?;
|
)?;
|
||||||
let service = self
|
let service = self
|
||||||
@@ -1290,12 +1291,10 @@ impl App {
|
|||||||
.ok_or_else(|| "The active session is unavailable.".to_owned())?;
|
.ok_or_else(|| "The active session is unavailable.".to_owned())?;
|
||||||
self.skip_compaction_once = false;
|
self.skip_compaction_once = false;
|
||||||
let model = self.config.model;
|
let model = self.config.model;
|
||||||
let mut effective = crate::settings::effective_settings(
|
let generation = self.config.active_generation();
|
||||||
model,
|
let runtime = self.config.runtime_for(model);
|
||||||
&self.config.generation,
|
let mut effective =
|
||||||
&self.config.runtime,
|
crate::settings::effective_settings(model, &generation, &runtime, &models_path())?;
|
||||||
&models_path(),
|
|
||||||
)?;
|
|
||||||
effective.turn.system_prompt = self.chat_system_prompt(
|
effective.turn.system_prompt = self.chat_system_prompt(
|
||||||
model,
|
model,
|
||||||
&effective.turn.system_prompt,
|
&effective.turn.system_prompt,
|
||||||
@@ -1395,12 +1394,10 @@ impl App {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let model = self.config.model;
|
let model = self.config.model;
|
||||||
let mut effective = crate::settings::effective_settings(
|
let generation = self.config.active_generation();
|
||||||
model,
|
let runtime = self.config.runtime_for(model);
|
||||||
&self.config.generation,
|
let mut effective =
|
||||||
&self.config.runtime,
|
crate::settings::effective_settings(model, &generation, &runtime, &models_path())?;
|
||||||
&models_path(),
|
|
||||||
)?;
|
|
||||||
effective.turn.system_prompt = self.chat_system_prompt(
|
effective.turn.system_prompt = self.chat_system_prompt(
|
||||||
model,
|
model,
|
||||||
&effective.turn.system_prompt,
|
&effective.turn.system_prompt,
|
||||||
@@ -1548,12 +1545,10 @@ impl App {
|
|||||||
reason: &str,
|
reason: &str,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let model = self.config.model;
|
let model = self.config.model;
|
||||||
let mut effective = crate::settings::effective_settings(
|
let generation = self.config.active_generation();
|
||||||
model,
|
let runtime = self.config.runtime_for(model);
|
||||||
&self.config.generation,
|
let mut effective =
|
||||||
&self.config.runtime,
|
crate::settings::effective_settings(model, &generation, &runtime, &models_path())?;
|
||||||
&models_path(),
|
|
||||||
)?;
|
|
||||||
effective.turn.system_prompt = self.chat_system_prompt(
|
effective.turn.system_prompt = self.chat_system_prompt(
|
||||||
model,
|
model,
|
||||||
&effective.turn.system_prompt,
|
&effective.turn.system_prompt,
|
||||||
@@ -1788,12 +1783,10 @@ impl App {
|
|||||||
return Err("A title is already being generated.".into());
|
return Err("A title is already being generated.".into());
|
||||||
}
|
}
|
||||||
let model = self.config.model;
|
let model = self.config.model;
|
||||||
let mut effective = crate::settings::effective_settings(
|
let generation = self.config.active_generation();
|
||||||
model,
|
let runtime = self.config.runtime_for(model);
|
||||||
&self.config.generation,
|
let mut effective =
|
||||||
&self.config.runtime,
|
crate::settings::effective_settings(model, &generation, &runtime, &models_path())?;
|
||||||
&models_path(),
|
|
||||||
)?;
|
|
||||||
let database = self
|
let database = self
|
||||||
.database
|
.database
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
|||||||
@@ -4,6 +4,12 @@ use std::sync::RwLock;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(super) struct PreferenceDraft {
|
pub(super) struct PreferenceDraft {
|
||||||
pub(super) model: ModelChoice,
|
pub(super) model: ModelChoice,
|
||||||
|
pub(super) default_reasoning_mode: ReasoningMode,
|
||||||
|
pub(super) generation_model: ModelChoice,
|
||||||
|
pub(super) generation_reasoning_mode: ReasoningMode,
|
||||||
|
pub(super) acceleration_model: ModelChoice,
|
||||||
|
generation_profiles: BTreeMap<ModelChoice, BTreeMap<ReasoningMode, GenerationPreferences>>,
|
||||||
|
model_profiles: BTreeMap<ModelChoice, ModelPreferences>,
|
||||||
pub(super) default_permission_mode: PermissionMode,
|
pub(super) default_permission_mode: PermissionMode,
|
||||||
pub(super) legacy_mtp_enabled: bool,
|
pub(super) legacy_mtp_enabled: bool,
|
||||||
pub(super) dspark_enabled: bool,
|
pub(super) dspark_enabled: bool,
|
||||||
@@ -28,7 +34,6 @@ pub(super) struct PreferenceDraft {
|
|||||||
pub(super) top_p: String,
|
pub(super) top_p: String,
|
||||||
pub(super) min_p: String,
|
pub(super) min_p: String,
|
||||||
pub(super) seed: String,
|
pub(super) seed: String,
|
||||||
pub(super) reasoning_mode: ReasoningMode,
|
|
||||||
pub(super) cpu_threads: String,
|
pub(super) cpu_threads: String,
|
||||||
pub(super) power_percent: String,
|
pub(super) power_percent: String,
|
||||||
pub(super) prefill_chunk: String,
|
pub(super) prefill_chunk: String,
|
||||||
@@ -58,12 +63,19 @@ pub(super) struct PreferenceDraft {
|
|||||||
|
|
||||||
impl PreferenceDraft {
|
impl PreferenceDraft {
|
||||||
pub(super) fn from_saved(config: &Config) -> Self {
|
pub(super) fn from_saved(config: &Config) -> Self {
|
||||||
let generation = &config.generation;
|
let default_reasoning_mode = config.reasoning_mode(config.model);
|
||||||
let runtime = &config.runtime;
|
let generation = config.generation_for(config.model, default_reasoning_mode);
|
||||||
|
let runtime = config.runtime_for(config.model);
|
||||||
let execution = &runtime.execution;
|
let execution = &runtime.execution;
|
||||||
let speculative = &runtime.speculative;
|
let speculative = &runtime.speculative;
|
||||||
Self {
|
Self {
|
||||||
model: config.model,
|
model: config.model,
|
||||||
|
default_reasoning_mode,
|
||||||
|
generation_model: config.model,
|
||||||
|
generation_reasoning_mode: default_reasoning_mode,
|
||||||
|
acceleration_model: config.model,
|
||||||
|
generation_profiles: config.generation_profiles.clone(),
|
||||||
|
model_profiles: config.model_profiles.clone(),
|
||||||
default_permission_mode: config.default_permission_mode,
|
default_permission_mode: config.default_permission_mode,
|
||||||
legacy_mtp_enabled: speculative.legacy_mtp_enabled,
|
legacy_mtp_enabled: speculative.legacy_mtp_enabled,
|
||||||
dspark_enabled: speculative.dspark_enabled,
|
dspark_enabled: speculative.dspark_enabled,
|
||||||
@@ -83,12 +95,11 @@ impl PreferenceDraft {
|
|||||||
git_ignore_blank_lines: config.git.ignore_blank_lines,
|
git_ignore_blank_lines: config.git.ignore_blank_lines,
|
||||||
context_tokens: generation.context_tokens.to_string(),
|
context_tokens: generation.context_tokens.to_string(),
|
||||||
max_generated_tokens: generation.max_generated_tokens.to_string(),
|
max_generated_tokens: generation.max_generated_tokens.to_string(),
|
||||||
system_prompt: text_editor::Content::with_text(&generation.system_prompt),
|
system_prompt: text_editor::Content::with_text(&config.system_prompt),
|
||||||
temperature: optional_string(generation.temperature),
|
temperature: optional_string(generation.temperature),
|
||||||
top_p: optional_string(generation.top_p),
|
top_p: optional_string(generation.top_p),
|
||||||
min_p: optional_string(generation.min_p),
|
min_p: optional_string(generation.min_p),
|
||||||
seed: optional_string(generation.seed),
|
seed: optional_string(generation.seed),
|
||||||
reasoning_mode: generation.reasoning_mode,
|
|
||||||
cpu_threads: optional_string(execution.cpu_threads),
|
cpu_threads: optional_string(execution.cpu_threads),
|
||||||
power_percent: optional_string(execution.power_percent),
|
power_percent: optional_string(execution.power_percent),
|
||||||
prefill_chunk: optional_string(execution.prefill_chunk),
|
prefill_chunk: optional_string(execution.prefill_chunk),
|
||||||
@@ -137,7 +148,7 @@ impl PreferenceDraft {
|
|||||||
top_p: parse_optional_f32("Top-p", &self.top_p)?,
|
top_p: parse_optional_f32("Top-p", &self.top_p)?,
|
||||||
min_p: parse_optional_f32("Min-p", &self.min_p)?,
|
min_p: parse_optional_f32("Min-p", &self.min_p)?,
|
||||||
seed: parse_optional_u64("Seed", &self.seed)?,
|
seed: parse_optional_u64("Seed", &self.seed)?,
|
||||||
reasoning_mode: self.reasoning_mode,
|
reasoning_mode: self.generation_reasoning_mode,
|
||||||
};
|
};
|
||||||
preferences.validate()?;
|
preferences.validate()?;
|
||||||
Ok(preferences)
|
Ok(preferences)
|
||||||
@@ -159,6 +170,39 @@ impl PreferenceDraft {
|
|||||||
*self = Self::from_saved(&Config::default());
|
*self = Self::from_saved(&Config::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store_generation(&mut self) -> Result<(), String> {
|
||||||
|
let generation = self.generation()?;
|
||||||
|
self.generation_profiles
|
||||||
|
.entry(self.generation_model)
|
||||||
|
.or_default()
|
||||||
|
.insert(self.generation_reasoning_mode, generation);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn load_generation(&mut self, model: ModelChoice, mode: ReasoningMode) {
|
||||||
|
let mut generation = self
|
||||||
|
.generation_profiles
|
||||||
|
.get(&model)
|
||||||
|
.and_then(|profiles| profiles.get(&mode))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
generation.reasoning_mode = mode;
|
||||||
|
self.generation_model = model;
|
||||||
|
self.generation_reasoning_mode = mode;
|
||||||
|
self.context_tokens = generation.context_tokens.to_string();
|
||||||
|
self.max_generated_tokens = generation.max_generated_tokens.to_string();
|
||||||
|
self.temperature = optional_string(generation.temperature);
|
||||||
|
self.top_p = optional_string(generation.top_p);
|
||||||
|
self.min_p = optional_string(generation.min_p);
|
||||||
|
self.seed = optional_string(generation.seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_generation(&mut self, model: ModelChoice, mode: ReasoningMode) -> Result<(), String> {
|
||||||
|
self.store_generation()?;
|
||||||
|
self.load_generation(model, mode);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn execution(&self) -> Result<ExecutionPreferences, String> {
|
pub(super) fn execution(&self) -> Result<ExecutionPreferences, String> {
|
||||||
Ok(ExecutionPreferences {
|
Ok(ExecutionPreferences {
|
||||||
cpu_threads: parse_optional_u32("CPU helper threads", &self.cpu_threads)?,
|
cpu_threads: parse_optional_u32("CPU helper threads", &self.cpu_threads)?,
|
||||||
@@ -185,20 +229,92 @@ impl PreferenceDraft {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn acceleration(&self) -> Result<(SpeculativePreferences, SsdPreferences), String> {
|
||||||
|
let speculative = self.speculative()?;
|
||||||
|
let ssd = SsdPreferences {
|
||||||
|
enabled: self.ssd_streaming,
|
||||||
|
cold: self.ssd_streaming_cold,
|
||||||
|
cache: parse_streaming_cache(&self.ssd_cache)?,
|
||||||
|
full_layers: parse_optional_u32("SSD full-layer count", &self.ssd_full_layers)?,
|
||||||
|
preload_experts: parse_optional_u32("SSD preload experts", &self.ssd_preload_experts)?,
|
||||||
|
};
|
||||||
|
speculative.validate(self.acceleration_model)?;
|
||||||
|
ssd.validate(self.acceleration_model)?;
|
||||||
|
Ok((speculative, ssd))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn store_acceleration(&mut self) -> Result<(), String> {
|
||||||
|
let (speculative, ssd) = self.acceleration()?;
|
||||||
|
let profile = self
|
||||||
|
.model_profiles
|
||||||
|
.entry(self.acceleration_model)
|
||||||
|
.or_default();
|
||||||
|
profile.speculative = speculative;
|
||||||
|
profile.ssd = ssd;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_acceleration(&mut self, model: ModelChoice) {
|
||||||
|
let profile = self.model_profiles.get(&model).cloned().unwrap_or_default();
|
||||||
|
let speculative = profile.speculative;
|
||||||
|
let ssd = profile.ssd;
|
||||||
|
self.acceleration_model = model;
|
||||||
|
self.legacy_mtp_enabled = speculative.legacy_mtp_enabled;
|
||||||
|
self.dspark_enabled = speculative.dspark_enabled;
|
||||||
|
self.mtp_draft_tokens = speculative.mtp_draft_tokens.to_string();
|
||||||
|
self.mtp_margin = speculative.mtp_margin.to_string();
|
||||||
|
self.glm_mtp = speculative.glm_mtp;
|
||||||
|
self.glm_mtp_timing = speculative.glm_mtp_timing;
|
||||||
|
self.dspark_confidence_threshold = optional_string(speculative.dspark_confidence_threshold);
|
||||||
|
self.dspark_strict = speculative.dspark_strict;
|
||||||
|
self.ssd_streaming = ssd.enabled;
|
||||||
|
self.ssd_streaming_cold = ssd.cold;
|
||||||
|
self.ssd_cache = optional_string(ssd.cache);
|
||||||
|
self.ssd_full_layers = optional_string(ssd.full_layers);
|
||||||
|
self.ssd_preload_experts = optional_string(ssd.preload_experts);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_acceleration(&mut self, model: ModelChoice) -> Result<(), String> {
|
||||||
|
self.store_acceleration()?;
|
||||||
|
self.load_acceleration(model);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn effective_for(
|
||||||
|
&self,
|
||||||
|
model: ModelChoice,
|
||||||
|
mode: ReasoningMode,
|
||||||
|
) -> Result<crate::settings::EffectiveSettings, String> {
|
||||||
|
let mut draft = self.clone();
|
||||||
|
draft.store_generation()?;
|
||||||
|
draft.store_acceleration()?;
|
||||||
|
let mut generation = draft
|
||||||
|
.generation_profiles
|
||||||
|
.get(&model)
|
||||||
|
.and_then(|profiles| profiles.get(&mode))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
generation.system_prompt = draft.system_prompt.text();
|
||||||
|
generation.reasoning_mode = mode;
|
||||||
|
let mut runtime = draft.runtime()?;
|
||||||
|
if let Some(profile) = draft.model_profiles.get(&model) {
|
||||||
|
runtime.speculative = profile.speculative.clone();
|
||||||
|
runtime.ssd = profile.ssd.clone();
|
||||||
|
}
|
||||||
|
crate::settings::effective_settings(model, &generation, &runtime, &models_path())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn reasoning_mode_for(&self, model: ModelChoice) -> ReasoningMode {
|
||||||
|
self.model_profiles
|
||||||
|
.get(&model)
|
||||||
|
.map_or(ReasoningMode::default(), |profile| profile.reasoning_mode)
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn runtime(&self) -> Result<RuntimePreferences, String> {
|
pub(super) fn runtime(&self) -> Result<RuntimePreferences, String> {
|
||||||
Ok(RuntimePreferences {
|
Ok(RuntimePreferences {
|
||||||
execution: self.execution()?,
|
execution: self.execution()?,
|
||||||
speculative: self.speculative()?,
|
speculative: SpeculativePreferences::default(),
|
||||||
ssd: SsdPreferences {
|
ssd: SsdPreferences::default(),
|
||||||
enabled: self.ssd_streaming,
|
|
||||||
cold: self.ssd_streaming_cold,
|
|
||||||
cache: parse_streaming_cache(&self.ssd_cache)?,
|
|
||||||
full_layers: parse_optional_u32("SSD full-layer count", &self.ssd_full_layers)?,
|
|
||||||
preload_experts: parse_optional_u32(
|
|
||||||
"SSD preload experts",
|
|
||||||
&self.ssd_preload_experts,
|
|
||||||
)?,
|
|
||||||
},
|
|
||||||
steering: SteeringPreferences {
|
steering: SteeringPreferences {
|
||||||
file: optional_text(&self.directional_steering_file),
|
file: optional_text(&self.directional_steering_file),
|
||||||
ffn_scale: parse_optional_f32(
|
ffn_scale: parse_optional_f32(
|
||||||
@@ -386,13 +502,14 @@ impl App {
|
|||||||
self.preference_error = Some("Endpoint port must be a whole number.".into());
|
self.preference_error = Some("Endpoint port must be a whole number.".into());
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let generation = match self.preference_draft.generation() {
|
if let Err(error) = self.preference_draft.store_generation() {
|
||||||
Ok(generation) => generation,
|
self.preference_error = Some(error);
|
||||||
Err(error) => {
|
return;
|
||||||
self.preference_error = Some(error);
|
}
|
||||||
return;
|
if let Err(error) = self.preference_draft.store_acceleration() {
|
||||||
}
|
self.preference_error = Some(error);
|
||||||
};
|
return;
|
||||||
|
}
|
||||||
let runtime = match self.preference_draft.runtime() {
|
let runtime = match self.preference_draft.runtime() {
|
||||||
Ok(runtime) => runtime,
|
Ok(runtime) => runtime,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
@@ -407,6 +524,11 @@ impl App {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
self.preference_draft
|
||||||
|
.model_profiles
|
||||||
|
.entry(self.preference_draft.model)
|
||||||
|
.or_default()
|
||||||
|
.reasoning_mode = self.preference_draft.default_reasoning_mode;
|
||||||
let config = Config {
|
let config = Config {
|
||||||
model: self.preference_draft.model,
|
model: self.preference_draft.model,
|
||||||
default_permission_mode: self.preference_draft.default_permission_mode,
|
default_permission_mode: self.preference_draft.default_permission_mode,
|
||||||
@@ -421,7 +543,9 @@ impl App {
|
|||||||
enabled: self.preference_draft.dev_brain_enabled,
|
enabled: self.preference_draft.dev_brain_enabled,
|
||||||
vault_path: optional_text(&self.preference_draft.dev_brain_vault_path),
|
vault_path: optional_text(&self.preference_draft.dev_brain_vault_path),
|
||||||
},
|
},
|
||||||
generation,
|
system_prompt: self.preference_draft.system_prompt.text(),
|
||||||
|
generation_profiles: self.preference_draft.generation_profiles.clone(),
|
||||||
|
model_profiles: self.preference_draft.model_profiles.clone(),
|
||||||
runtime,
|
runtime,
|
||||||
git,
|
git,
|
||||||
interface: self.config.interface.clone(),
|
interface: self.config.interface.clone(),
|
||||||
@@ -482,6 +606,11 @@ impl App {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.config = config;
|
self.config = config;
|
||||||
|
self.context_limit = self.config.active_generation().context_tokens.max(0) as u32;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
self.agent_tools = None;
|
||||||
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
if dev_brain_changed {
|
if dev_brain_changed {
|
||||||
self.invalidate_dev_brain_context();
|
self.invalidate_dev_brain_context();
|
||||||
@@ -507,33 +636,65 @@ impl App {
|
|||||||
) -> Result<Message, Task<Message>> {
|
) -> Result<Message, Task<Message>> {
|
||||||
match message {
|
match message {
|
||||||
Message::PreferenceModelChanged(model) => {
|
Message::PreferenceModelChanged(model) => {
|
||||||
self.preference_draft.model = model;
|
if let Err(error) = self.preference_draft.store_generation() {
|
||||||
if !model.supports_dspark() {
|
self.preference_error = Some(error);
|
||||||
self.preference_draft.legacy_mtp_enabled = false;
|
return Err(Task::none());
|
||||||
self.preference_draft.dspark_enabled = false;
|
|
||||||
self.preference_draft.dspark_confidence_threshold.clear();
|
|
||||||
self.preference_draft.dspark_strict = false;
|
|
||||||
}
|
}
|
||||||
|
if let Err(error) = self.preference_draft.store_acceleration() {
|
||||||
|
self.preference_error = Some(error);
|
||||||
|
return Err(Task::none());
|
||||||
|
}
|
||||||
|
let mode = self.preference_draft.reasoning_mode_for(model);
|
||||||
|
self.preference_draft.model = model;
|
||||||
|
self.preference_draft.default_reasoning_mode = mode;
|
||||||
|
self.preference_draft.load_generation(model, mode);
|
||||||
|
self.preference_draft.load_acceleration(model);
|
||||||
if model == ModelChoice::Glm52 {
|
if model == ModelChoice::Glm52 {
|
||||||
self.preference_draft.power_percent.clear();
|
self.preference_draft.power_percent.clear();
|
||||||
self.preference_draft.prefill_chunk.clear();
|
self.preference_draft.prefill_chunk.clear();
|
||||||
self.preference_draft.directional_steering_file.clear();
|
self.preference_draft.directional_steering_file.clear();
|
||||||
self.preference_draft.directional_steering_ffn.clear();
|
self.preference_draft.directional_steering_ffn.clear();
|
||||||
self.preference_draft.directional_steering_attn.clear();
|
self.preference_draft.directional_steering_attn.clear();
|
||||||
} else {
|
|
||||||
self.preference_draft.glm_mtp = false;
|
|
||||||
self.preference_draft.glm_mtp_timing = false;
|
|
||||||
self.preference_draft.ssd_full_layers.clear();
|
|
||||||
}
|
}
|
||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
}
|
}
|
||||||
|
Message::PreferenceDefaultReasoningChanged(mode) => {
|
||||||
|
if self.preference_draft.generation_model == self.preference_draft.model
|
||||||
|
&& let Err(error) = self
|
||||||
|
.preference_draft
|
||||||
|
.select_generation(self.preference_draft.model, mode)
|
||||||
|
{
|
||||||
|
self.preference_error = Some(error);
|
||||||
|
return Err(Task::none());
|
||||||
|
}
|
||||||
|
self.preference_draft.default_reasoning_mode = mode;
|
||||||
|
self.preference_draft
|
||||||
|
.model_profiles
|
||||||
|
.entry(self.preference_draft.model)
|
||||||
|
.or_default()
|
||||||
|
.reasoning_mode = mode;
|
||||||
|
self.preference_error = None;
|
||||||
|
}
|
||||||
|
Message::PreferenceGenerationModelChanged(model) => {
|
||||||
|
let mode = self.preference_draft.reasoning_mode_for(model);
|
||||||
|
self.preference_error = self.preference_draft.select_generation(model, mode).err();
|
||||||
|
}
|
||||||
|
Message::PreferenceGenerationReasoningChanged(mode) => {
|
||||||
|
self.preference_error = self
|
||||||
|
.preference_draft
|
||||||
|
.select_generation(self.preference_draft.generation_model, mode)
|
||||||
|
.err();
|
||||||
|
}
|
||||||
|
Message::PreferenceAccelerationModelChanged(model) => {
|
||||||
|
self.preference_error = self.preference_draft.select_acceleration(model).err();
|
||||||
|
}
|
||||||
Message::PreferencePermissionModeChanged(mode) => {
|
Message::PreferencePermissionModeChanged(mode) => {
|
||||||
self.preference_draft.default_permission_mode = mode;
|
self.preference_draft.default_permission_mode = mode;
|
||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
}
|
}
|
||||||
Message::PreferenceLegacyMtpChanged(enabled) => {
|
Message::PreferenceLegacyMtpChanged(enabled) => {
|
||||||
self.preference_draft.legacy_mtp_enabled =
|
self.preference_draft.legacy_mtp_enabled =
|
||||||
self.preference_draft.model.supports_dspark() && enabled;
|
self.preference_draft.acceleration_model.supports_dspark() && enabled;
|
||||||
if self.preference_draft.legacy_mtp_enabled {
|
if self.preference_draft.legacy_mtp_enabled {
|
||||||
self.preference_draft.dspark_enabled = false;
|
self.preference_draft.dspark_enabled = false;
|
||||||
self.preference_draft.dspark_confidence_threshold.clear();
|
self.preference_draft.dspark_confidence_threshold.clear();
|
||||||
@@ -543,7 +704,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
Message::PreferenceDsparkChanged(enabled) => {
|
Message::PreferenceDsparkChanged(enabled) => {
|
||||||
self.preference_draft.dspark_enabled =
|
self.preference_draft.dspark_enabled =
|
||||||
self.preference_draft.model.supports_dspark() && enabled;
|
self.preference_draft.acceleration_model.supports_dspark() && enabled;
|
||||||
if !self.preference_draft.dspark_enabled {
|
if !self.preference_draft.dspark_enabled {
|
||||||
self.preference_draft.dspark_confidence_threshold.clear();
|
self.preference_draft.dspark_confidence_threshold.clear();
|
||||||
self.preference_draft.dspark_strict = false;
|
self.preference_draft.dspark_strict = false;
|
||||||
@@ -675,10 +836,6 @@ impl App {
|
|||||||
self.preference_draft.seed = value;
|
self.preference_draft.seed = value;
|
||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
}
|
}
|
||||||
Message::PreferenceReasoningChanged(value) => {
|
|
||||||
self.preference_draft.reasoning_mode = value;
|
|
||||||
self.preference_error = None;
|
|
||||||
}
|
|
||||||
Message::PreferenceCpuThreadsChanged(value) => {
|
Message::PreferenceCpuThreadsChanged(value) => {
|
||||||
self.preference_draft.cpu_threads = value;
|
self.preference_draft.cpu_threads = value;
|
||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
@@ -709,7 +866,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
Message::PreferenceGlmMtpChanged(value) => {
|
Message::PreferenceGlmMtpChanged(value) => {
|
||||||
self.preference_draft.glm_mtp =
|
self.preference_draft.glm_mtp =
|
||||||
self.preference_draft.model == ModelChoice::Glm52 && value;
|
self.preference_draft.acceleration_model == ModelChoice::Glm52 && value;
|
||||||
if !self.preference_draft.glm_mtp {
|
if !self.preference_draft.glm_mtp {
|
||||||
self.preference_draft.glm_mtp_timing = false;
|
self.preference_draft.glm_mtp_timing = false;
|
||||||
}
|
}
|
||||||
@@ -717,7 +874,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
Message::PreferenceGlmMtpTimingChanged(value) => {
|
Message::PreferenceGlmMtpTimingChanged(value) => {
|
||||||
self.preference_draft.glm_mtp_timing =
|
self.preference_draft.glm_mtp_timing =
|
||||||
self.preference_draft.model == ModelChoice::Glm52 && value;
|
self.preference_draft.acceleration_model == ModelChoice::Glm52 && value;
|
||||||
if self.preference_draft.glm_mtp_timing {
|
if self.preference_draft.glm_mtp_timing {
|
||||||
self.preference_draft.glm_mtp = true;
|
self.preference_draft.glm_mtp = true;
|
||||||
}
|
}
|
||||||
@@ -725,7 +882,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
Message::PreferenceDsparkConfidenceChanged(value) => {
|
Message::PreferenceDsparkConfidenceChanged(value) => {
|
||||||
self.preference_draft.dspark_confidence_threshold = value;
|
self.preference_draft.dspark_confidence_threshold = value;
|
||||||
if self.preference_draft.model.supports_dspark()
|
if self.preference_draft.acceleration_model.supports_dspark()
|
||||||
&& !self
|
&& !self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.dspark_confidence_threshold
|
.dspark_confidence_threshold
|
||||||
@@ -739,7 +896,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
Message::PreferenceDsparkStrictChanged(value) => {
|
Message::PreferenceDsparkStrictChanged(value) => {
|
||||||
self.preference_draft.dspark_strict =
|
self.preference_draft.dspark_strict =
|
||||||
self.preference_draft.model.supports_dspark() && value;
|
self.preference_draft.acceleration_model.supports_dspark() && value;
|
||||||
if self.preference_draft.dspark_strict {
|
if self.preference_draft.dspark_strict {
|
||||||
self.preference_draft.dspark_enabled = true;
|
self.preference_draft.dspark_enabled = true;
|
||||||
self.preference_draft.legacy_mtp_enabled = false;
|
self.preference_draft.legacy_mtp_enabled = false;
|
||||||
@@ -759,7 +916,7 @@ impl App {
|
|||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
}
|
}
|
||||||
Message::PreferenceSsdFullLayersChanged(value) => {
|
Message::PreferenceSsdFullLayersChanged(value) => {
|
||||||
if self.preference_draft.model == ModelChoice::Glm52 {
|
if self.preference_draft.acceleration_model == ModelChoice::Glm52 {
|
||||||
self.preference_draft.ssd_full_layers = value;
|
self.preference_draft.ssd_full_layers = value;
|
||||||
}
|
}
|
||||||
self.preference_error = None;
|
self.preference_error = None;
|
||||||
@@ -842,6 +999,30 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn selectors_rehydrate_their_generation_and_model_profiles() {
|
||||||
|
let mut draft = PreferenceDraft::from_saved(&Config::default());
|
||||||
|
draft.context_tokens = "123".into();
|
||||||
|
draft.ssd_streaming = true;
|
||||||
|
|
||||||
|
draft
|
||||||
|
.select_generation(ModelChoice::Glm52, ReasoningMode::Direct)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(draft.context_tokens, "32768");
|
||||||
|
draft.context_tokens = "456".into();
|
||||||
|
draft
|
||||||
|
.select_generation(ModelChoice::DeepSeekV4Flash, ReasoningMode::High)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(draft.context_tokens, "123");
|
||||||
|
|
||||||
|
draft.select_acceleration(ModelChoice::Glm52).unwrap();
|
||||||
|
assert!(!draft.ssd_streaming);
|
||||||
|
draft
|
||||||
|
.select_acceleration(ModelChoice::DeepSeekV4Flash)
|
||||||
|
.unwrap();
|
||||||
|
assert!(draft.ssd_streaming);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn runtime_config_updates_after_lock_poisoning() {
|
fn runtime_config_updates_after_lock_poisoning() {
|
||||||
let runtime = Arc::new(RwLock::new(Config::default()));
|
let runtime = Arc::new(RwLock::new(Config::default()));
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ impl App {
|
|||||||
self.queued_inputs.clear();
|
self.queued_inputs.clear();
|
||||||
self.system_prompt_seen_at = 0;
|
self.system_prompt_seen_at = 0;
|
||||||
self.context_used = 0;
|
self.context_used = 0;
|
||||||
self.context_limit = self.config.generation.context_tokens.max(0) as u32;
|
self.context_limit = self.config.active_generation().context_tokens.max(0) as u32;
|
||||||
self.tokens_per_second = None;
|
self.tokens_per_second = None;
|
||||||
self.error = None;
|
self.error = None;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,12 +307,12 @@ impl App {
|
|||||||
.text_size(12)
|
.text_size(12)
|
||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
});
|
});
|
||||||
let reasoning_mode = self.config.generation.effective_reasoning_mode();
|
let reasoning_mode = self.config.reasoning_mode(self.config.model);
|
||||||
let reasoning_control: Element<'_, Message> = if self.active_chat_count() > 0 {
|
let reasoning_control: Element<'_, Message> = if self.active_chat_count() > 0 {
|
||||||
text(reasoning_mode.to_string()).size(12).into()
|
text(reasoning_mode.to_string()).size(12).into()
|
||||||
} else {
|
} else {
|
||||||
pick_list(
|
pick_list(
|
||||||
self.config.generation.supported_reasoning_modes(),
|
self.config.supported_reasoning_modes(self.config.model),
|
||||||
Some(reasoning_mode),
|
Some(reasoning_mode),
|
||||||
Message::ReasoningModeChanged,
|
Message::ReasoningModeChanged,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ impl App {
|
|||||||
pub(super) fn preferences_panel(&self) -> Element<'_, Message> {
|
pub(super) fn preferences_panel(&self) -> Element<'_, Message> {
|
||||||
let legacy_mtp_toggle: Option<fn(bool) -> Message> = self
|
let legacy_mtp_toggle: Option<fn(bool) -> Message> = self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.model
|
.acceleration_model
|
||||||
.supports_dspark()
|
.supports_dspark()
|
||||||
.then_some(Message::PreferenceLegacyMtpChanged);
|
.then_some(Message::PreferenceLegacyMtpChanged);
|
||||||
let legacy_mtp = hint(
|
let legacy_mtp = hint(
|
||||||
@@ -16,7 +16,7 @@ impl App {
|
|||||||
);
|
);
|
||||||
let dspark_toggle: Option<fn(bool) -> Message> = self
|
let dspark_toggle: Option<fn(bool) -> Message> = self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.model
|
.acceleration_model
|
||||||
.supports_dspark()
|
.supports_dspark()
|
||||||
.then_some(Message::PreferenceDsparkChanged);
|
.then_some(Message::PreferenceDsparkChanged);
|
||||||
let dspark = hint(
|
let dspark = hint(
|
||||||
@@ -25,33 +25,44 @@ impl App {
|
|||||||
.on_toggle_maybe(dspark_toggle),
|
.on_toggle_maybe(dspark_toggle),
|
||||||
"Speculative decoding with the managed DSpark draft artifact: a small model proposes tokens that the main model verifies in one pass. Usually a large speedup; the target model may also stream routed experts from SSD.",
|
"Speculative decoding with the managed DSpark draft artifact: a small model proposes tokens that the main model verifies in one pass. Usually a large speedup; the target model may also stream routed experts from SSD.",
|
||||||
);
|
);
|
||||||
let glm_mtp_toggle: Option<fn(bool) -> Message> = (self.preference_draft.model
|
let glm_mtp_toggle: Option<fn(bool) -> Message> =
|
||||||
== ModelChoice::Glm52)
|
(self.preference_draft.acceleration_model == ModelChoice::Glm52)
|
||||||
.then_some(Message::PreferenceGlmMtpChanged);
|
.then_some(Message::PreferenceGlmMtpChanged);
|
||||||
let glm_mtp_timing_toggle: Option<fn(bool) -> Message> = (self.preference_draft.model
|
let glm_mtp_timing_toggle: Option<fn(bool) -> Message> =
|
||||||
== ModelChoice::Glm52)
|
(self.preference_draft.acceleration_model == ModelChoice::Glm52)
|
||||||
.then_some(Message::PreferenceGlmMtpTimingChanged);
|
.then_some(Message::PreferenceGlmMtpTimingChanged);
|
||||||
let dspark_strict_toggle: Option<fn(bool) -> Message> = self
|
let dspark_strict_toggle: Option<fn(bool) -> Message> = self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.model
|
.acceleration_model
|
||||||
.supports_dspark()
|
.supports_dspark()
|
||||||
.then_some(Message::PreferenceDsparkStrictChanged);
|
.then_some(Message::PreferenceDsparkStrictChanged);
|
||||||
let effective = self
|
let effective = self
|
||||||
.preference_draft
|
.preference_draft
|
||||||
.generation()
|
.effective_for(
|
||||||
.and_then(|generation| {
|
self.preference_draft.model,
|
||||||
self.preference_draft.runtime().and_then(|runtime| {
|
self.preference_draft.default_reasoning_mode,
|
||||||
crate::settings::effective_settings(
|
)
|
||||||
self.preference_draft.model,
|
.ok();
|
||||||
&generation,
|
let generation_effective = self
|
||||||
&runtime,
|
.preference_draft
|
||||||
&models_path(),
|
.effective_for(
|
||||||
)
|
self.preference_draft.generation_model,
|
||||||
})
|
self.preference_draft.generation_reasoning_mode,
|
||||||
})
|
)
|
||||||
|
.ok();
|
||||||
|
let acceleration_effective = self
|
||||||
|
.preference_draft
|
||||||
|
.effective_for(
|
||||||
|
self.preference_draft.acceleration_model,
|
||||||
|
self.preference_draft
|
||||||
|
.reasoning_mode_for(self.preference_draft.acceleration_model),
|
||||||
|
)
|
||||||
.ok();
|
.ok();
|
||||||
let engine = effective.as_ref().map(|settings| &settings.engine);
|
let engine = effective.as_ref().map(|settings| &settings.engine);
|
||||||
let turn = effective.as_ref().map(|settings| &settings.turn);
|
let turn = generation_effective.as_ref().map(|settings| &settings.turn);
|
||||||
|
let acceleration_engine = acceleration_effective
|
||||||
|
.as_ref()
|
||||||
|
.map(|settings| &settings.engine);
|
||||||
let mut power = text_input("100", &self.preference_draft.power_percent);
|
let mut power = text_input("100", &self.preference_draft.power_percent);
|
||||||
let mut prefill = text_input("Automatic", &self.preference_draft.prefill_chunk);
|
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 ssd_full_layers = text_input("Automatic", &self.preference_draft.ssd_full_layers);
|
||||||
@@ -72,10 +83,11 @@ impl App {
|
|||||||
steering_file = steering_file.on_input(Message::PreferenceSteeringFileChanged);
|
steering_file = steering_file.on_input(Message::PreferenceSteeringFileChanged);
|
||||||
steering_ffn = steering_ffn.on_input(Message::PreferenceSteeringFfnChanged);
|
steering_ffn = steering_ffn.on_input(Message::PreferenceSteeringFfnChanged);
|
||||||
steering_attn = steering_attn.on_input(Message::PreferenceSteeringAttnChanged);
|
steering_attn = steering_attn.on_input(Message::PreferenceSteeringAttnChanged);
|
||||||
} else {
|
}
|
||||||
|
if self.preference_draft.acceleration_model == ModelChoice::Glm52 {
|
||||||
ssd_full_layers = ssd_full_layers.on_input(Message::PreferenceSsdFullLayersChanged);
|
ssd_full_layers = ssd_full_layers.on_input(Message::PreferenceSsdFullLayersChanged);
|
||||||
}
|
}
|
||||||
if self.preference_draft.model.supports_dspark() {
|
if self.preference_draft.acceleration_model.supports_dspark() {
|
||||||
dspark_confidence =
|
dspark_confidence =
|
||||||
dspark_confidence.on_input(Message::PreferenceDsparkConfidenceChanged);
|
dspark_confidence.on_input(Message::PreferenceDsparkConfidenceChanged);
|
||||||
}
|
}
|
||||||
@@ -93,6 +105,20 @@ impl App {
|
|||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
"Which local weights the Metal engine loads for chats and for the HTTP endpoint. The choice decides which accelerations below apply, and the matching artifacts must be downloaded in the model manager.",
|
"Which local weights the Metal engine loads for chats and for the HTTP endpoint. The choice decides which accelerations below apply, and the matching artifacts must be downloaded in the model manager.",
|
||||||
),
|
),
|
||||||
|
row![
|
||||||
|
hint(
|
||||||
|
text("Default thinking mode").size(13).width(Length::Fill),
|
||||||
|
"Thinking mode selected when this model becomes active. Chat can switch it for later turns.",
|
||||||
|
),
|
||||||
|
pick_list(
|
||||||
|
&REASONING_MODES[..],
|
||||||
|
Some(self.preference_draft.default_reasoning_mode),
|
||||||
|
Message::PreferenceDefaultReasoningChanged,
|
||||||
|
)
|
||||||
|
.width(240),
|
||||||
|
]
|
||||||
|
.spacing(12)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
text(format!(
|
text(format!(
|
||||||
"Main: {}{}",
|
"Main: {}{}",
|
||||||
engine.map_or_else(
|
engine.map_or_else(
|
||||||
@@ -282,10 +308,49 @@ impl App {
|
|||||||
]
|
]
|
||||||
.spacing(10),
|
.spacing(10),
|
||||||
);
|
);
|
||||||
|
let prompt_group = preference_group(
|
||||||
|
PreferenceSection::Prompt,
|
||||||
|
"PROMPT",
|
||||||
|
column![
|
||||||
|
hint(
|
||||||
|
text("System prompt").size(13),
|
||||||
|
"Standing instruction sent before every conversation: persona, tone, house rules. Leave it empty to send no system message at all.",
|
||||||
|
),
|
||||||
|
text_editor(&self.preference_draft.system_prompt)
|
||||||
|
.placeholder("Additional system instructions…")
|
||||||
|
.height(140)
|
||||||
|
.on_action(Message::PreferenceSystemPromptAction)
|
||||||
|
.padding(9),
|
||||||
|
text("The system prompt is shared by every model and thinking mode.").size(12),
|
||||||
|
]
|
||||||
|
.spacing(10),
|
||||||
|
);
|
||||||
let generation_group = preference_group(
|
let generation_group = preference_group(
|
||||||
PreferenceSection::Generation,
|
PreferenceSection::Generation,
|
||||||
"GENERATION",
|
"GENERATION",
|
||||||
column![
|
column![
|
||||||
|
row![
|
||||||
|
text("Model").size(13).width(Length::Fill),
|
||||||
|
pick_list(
|
||||||
|
&MODEL_CHOICES[..],
|
||||||
|
Some(self.preference_draft.generation_model),
|
||||||
|
Message::PreferenceGenerationModelChanged,
|
||||||
|
)
|
||||||
|
.width(400),
|
||||||
|
]
|
||||||
|
.spacing(12)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
|
row![
|
||||||
|
text("Thinking mode").size(13).width(Length::Fill),
|
||||||
|
pick_list(
|
||||||
|
&REASONING_MODES[..],
|
||||||
|
Some(self.preference_draft.generation_reasoning_mode),
|
||||||
|
Message::PreferenceGenerationReasoningChanged,
|
||||||
|
)
|
||||||
|
.width(240),
|
||||||
|
]
|
||||||
|
.spacing(12)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
preference_input_row(
|
preference_input_row(
|
||||||
"Context tokens",
|
"Context tokens",
|
||||||
"Size of the window the model can see: system prompt, history, the new question and the answer all have to fit. Larger windows allow longer sessions but reserve much more memory for the key-value cache.",
|
"Size of the window the model can see: system prompt, history, the new question and the answer all have to fit. Larger windows allow longer sessions but reserve much more memory for the key-value cache.",
|
||||||
@@ -298,17 +363,7 @@ impl App {
|
|||||||
text_input("50000", &self.preference_draft.max_generated_tokens)
|
text_input("50000", &self.preference_draft.max_generated_tokens)
|
||||||
.on_input(Message::PreferenceMaxTokensChanged),
|
.on_input(Message::PreferenceMaxTokensChanged),
|
||||||
),
|
),
|
||||||
hint(
|
text("SAMPLING").size(11).color(muted_text()),
|
||||||
text("System prompt").size(13),
|
|
||||||
"Standing instruction sent before every conversation: persona, tone, house rules. Leave it empty to send no system message at all.",
|
|
||||||
),
|
|
||||||
text_editor(&self.preference_draft.system_prompt)
|
|
||||||
.placeholder("Additional system instructions…")
|
|
||||||
.height(140)
|
|
||||||
.on_action(Message::PreferenceSystemPromptAction)
|
|
||||||
.padding(9),
|
|
||||||
Space::new().height(4),
|
|
||||||
text("SAMPLING & REASONING").size(11).color(muted_text()),
|
|
||||||
preference_input_row(
|
preference_input_row(
|
||||||
"Temperature",
|
"Temperature",
|
||||||
"How adventurous token choice is. Near 0 the model repeats the most likely continuation, which suits code and extraction; higher values invent more and drift more.",
|
"How adventurous token choice is. Near 0 the model repeats the most likely continuation, which suits code and extraction; higher values invent more and drift more.",
|
||||||
@@ -333,20 +388,6 @@ impl App {
|
|||||||
text_input("Random", &self.preference_draft.seed)
|
text_input("Random", &self.preference_draft.seed)
|
||||||
.on_input(Message::PreferenceSeedChanged),
|
.on_input(Message::PreferenceSeedChanged),
|
||||||
),
|
),
|
||||||
row![
|
|
||||||
hint(
|
|
||||||
text("Reasoning").size(13).width(Length::Fill),
|
|
||||||
"How much hidden thinking precedes the answer. Direct skips it and replies fastest, Thinking is the balanced default, Think Max reasons longest and needs at least 393216 context tokens.",
|
|
||||||
),
|
|
||||||
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.")
|
text("Blank sampling values retain DS4's model-family defaults. Think Max needs at least 393216 context tokens.")
|
||||||
.size(12),
|
.size(12),
|
||||||
text(turn.map_or_else(
|
text(turn.map_or_else(
|
||||||
@@ -428,6 +469,17 @@ impl App {
|
|||||||
PreferenceSection::Acceleration,
|
PreferenceSection::Acceleration,
|
||||||
"ACCELERATION & MEMORY",
|
"ACCELERATION & MEMORY",
|
||||||
column![
|
column![
|
||||||
|
row![
|
||||||
|
text("Model").size(13).width(Length::Fill),
|
||||||
|
pick_list(
|
||||||
|
&MODEL_CHOICES[..],
|
||||||
|
Some(self.preference_draft.acceleration_model),
|
||||||
|
Message::PreferenceAccelerationModelChanged,
|
||||||
|
)
|
||||||
|
.width(400),
|
||||||
|
]
|
||||||
|
.spacing(12)
|
||||||
|
.align_y(Alignment::Center),
|
||||||
text("SPECULATIVE DECODING").size(11).color(muted_text()),
|
text("SPECULATIVE DECODING").size(11).color(muted_text()),
|
||||||
preference_input_row(
|
preference_input_row(
|
||||||
"MTP draft tokens",
|
"MTP draft tokens",
|
||||||
@@ -466,15 +518,15 @@ impl App {
|
|||||||
.on_toggle_maybe(dspark_strict_toggle),
|
.on_toggle_maybe(dspark_strict_toggle),
|
||||||
"Lets the draft model only propose, never decide: every token is sampled by the full model. Gives up some of the speedup in exchange for output identical to non-speculative decoding.",
|
"Lets the draft model only propose, never decide: every token is sampled by the full model. Gives up some of the speedup in exchange for output identical to non-speculative decoding.",
|
||||||
),
|
),
|
||||||
text(if self.preference_draft.model.supports_dspark() {
|
text(if self.preference_draft.acceleration_model.supports_dspark() {
|
||||||
"Legacy MTP and DSpark use separate managed support artifacts; entering a DSpark threshold or enabling strict mode selects DSpark."
|
"Legacy MTP and DSpark use separate managed support artifacts; entering a DSpark threshold or enabling strict mode selects DSpark."
|
||||||
} else if self.preference_draft.model == ModelChoice::Glm52 {
|
} else if self.preference_draft.acceleration_model == ModelChoice::Glm52 {
|
||||||
"GLM MTP is integrated; DSpark is unavailable for this model."
|
"GLM MTP is integrated; DSpark is unavailable for this model."
|
||||||
} else {
|
} else {
|
||||||
"No managed MTP support artifact is available for this model."
|
"No managed MTP support artifact is available for this model."
|
||||||
})
|
})
|
||||||
.size(12),
|
.size(12),
|
||||||
text(engine.as_ref().map_or_else(
|
text(acceleration_engine.map_or_else(
|
||||||
|| "Effective speculative settings will appear after valid values are entered."
|
|| "Effective speculative settings will appear after valid values are entered."
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
|engine| {
|
|engine| {
|
||||||
@@ -527,7 +579,7 @@ impl App {
|
|||||||
),
|
),
|
||||||
text("A blank full-layer value is automatic; an explicit 0 disables fully resident GLM layers. Flash legacy MTP and DSpark support weights remain resident when target experts stream.")
|
text("A blank full-layer value is automatic; an explicit 0 disables fully resident GLM layers. Flash legacy MTP and DSpark support weights remain resident when target experts stream.")
|
||||||
.size(12),
|
.size(12),
|
||||||
text(engine.as_ref().map_or_else(
|
text(acceleration_engine.map_or_else(
|
||||||
|| "Effective SSD settings will appear after valid values are entered."
|
|| "Effective SSD settings will appear after valid values are entered."
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
|engine| {
|
|engine| {
|
||||||
@@ -680,6 +732,7 @@ impl App {
|
|||||||
endpoint_group,
|
endpoint_group,
|
||||||
dev_brain_group,
|
dev_brain_group,
|
||||||
git_group,
|
git_group,
|
||||||
|
prompt_group,
|
||||||
generation_group,
|
generation_group,
|
||||||
execution_group,
|
execution_group,
|
||||||
acceleration_group,
|
acceleration_group,
|
||||||
|
|||||||
199
src/config.rs
199
src/config.rs
@@ -1,11 +1,23 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_norway::{Mapping, Value};
|
use serde_norway::{Mapping, Value};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::model::ModelChoice;
|
use crate::model::{MODEL_CHOICES, ModelChoice};
|
||||||
use crate::settings::{GenerationPreferences, RuntimePreferences};
|
use crate::settings::{
|
||||||
|
DEFAULT_SYSTEM_PROMPT, GenerationPreferences, REASONING_MODES, ReasoningMode,
|
||||||
|
RuntimePreferences, SpeculativePreferences, SsdPreferences,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
|
||||||
|
#[serde(default, deny_unknown_fields)]
|
||||||
|
pub(crate) struct ModelPreferences {
|
||||||
|
pub(crate) reasoning_mode: ReasoningMode,
|
||||||
|
pub(crate) speculative: SpeculativePreferences,
|
||||||
|
pub(crate) ssd: SsdPreferences,
|
||||||
|
}
|
||||||
|
|
||||||
/// Settings the application persists beside its database, as a YAML file users
|
/// Settings the application persists beside its database, as a YAML file users
|
||||||
/// and agents can edit by hand. Only values that differ from the defaults are
|
/// and agents can edit by hand. Only values that differ from the defaults are
|
||||||
@@ -19,7 +31,10 @@ pub struct Config {
|
|||||||
pub a2ui_enabled: bool,
|
pub a2ui_enabled: bool,
|
||||||
pub dev_brain: DevBrainConfig,
|
pub dev_brain: DevBrainConfig,
|
||||||
pub endpoint: EndpointConfig,
|
pub endpoint: EndpointConfig,
|
||||||
pub generation: GenerationPreferences,
|
pub system_prompt: String,
|
||||||
|
pub(crate) generation_profiles:
|
||||||
|
BTreeMap<ModelChoice, BTreeMap<ReasoningMode, GenerationPreferences>>,
|
||||||
|
pub(crate) model_profiles: BTreeMap<ModelChoice, ModelPreferences>,
|
||||||
pub runtime: RuntimePreferences,
|
pub runtime: RuntimePreferences,
|
||||||
pub git: GitConfig,
|
pub git: GitConfig,
|
||||||
pub interface: InterfaceConfig,
|
pub interface: InterfaceConfig,
|
||||||
@@ -34,7 +49,23 @@ impl Default for Config {
|
|||||||
a2ui_enabled: true,
|
a2ui_enabled: true,
|
||||||
dev_brain: DevBrainConfig::default(),
|
dev_brain: DevBrainConfig::default(),
|
||||||
endpoint: EndpointConfig::default(),
|
endpoint: EndpointConfig::default(),
|
||||||
generation: GenerationPreferences::default(),
|
system_prompt: DEFAULT_SYSTEM_PROMPT.into(),
|
||||||
|
generation_profiles: MODEL_CHOICES
|
||||||
|
.into_iter()
|
||||||
|
.map(|model| {
|
||||||
|
(
|
||||||
|
model,
|
||||||
|
REASONING_MODES
|
||||||
|
.into_iter()
|
||||||
|
.map(|mode| (mode, GenerationPreferences::default()))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
model_profiles: MODEL_CHOICES
|
||||||
|
.into_iter()
|
||||||
|
.map(|model| (model, ModelPreferences::default()))
|
||||||
|
.collect(),
|
||||||
runtime: RuntimePreferences::default(),
|
runtime: RuntimePreferences::default(),
|
||||||
git: GitConfig::default(),
|
git: GitConfig::default(),
|
||||||
interface: InterfaceConfig::default(),
|
interface: InterfaceConfig::default(),
|
||||||
@@ -258,8 +289,12 @@ impl Config {
|
|||||||
}
|
}
|
||||||
Err(error) => return Err(format!("Could not read {}: {error}", path.display())),
|
Err(error) => return Err(format!("Could not read {}: {error}", path.display())),
|
||||||
};
|
};
|
||||||
let config: Self = serde_norway::from_str(&text)
|
let mut value: Value = serde_norway::from_str(&text)
|
||||||
.map_err(|error| format!("Could not read {}: {error}", path.display()))?;
|
.map_err(|error| format!("Could not read {}: {error}", path.display()))?;
|
||||||
|
drop_legacy_model_settings(&mut value);
|
||||||
|
let mut config: Self = serde_norway::from_value(value)
|
||||||
|
.map_err(|error| format!("Could not read {}: {error}", path.display()))?;
|
||||||
|
config.fill_profile_defaults();
|
||||||
config.validate()?;
|
config.validate()?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
@@ -286,10 +321,100 @@ impl Config {
|
|||||||
if !(1..=65_535).contains(&self.endpoint.port) {
|
if !(1..=65_535).contains(&self.endpoint.port) {
|
||||||
return Err("Endpoint port must be between 1 and 65535.".into());
|
return Err("Endpoint port must be between 1 and 65535.".into());
|
||||||
}
|
}
|
||||||
self.generation.validate()?;
|
for (model, profiles) in &self.generation_profiles {
|
||||||
self.runtime.validate(self.model)?;
|
for (mode, generation) in profiles {
|
||||||
|
let mut generation = generation.clone();
|
||||||
|
generation.system_prompt = self.system_prompt.clone();
|
||||||
|
generation.reasoning_mode = *mode;
|
||||||
|
generation.validate().map_err(|error| {
|
||||||
|
format!("Generation settings for {model} / {mode}: {error}")
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (model, preferences) in &self.model_profiles {
|
||||||
|
preferences.speculative.validate(*model)?;
|
||||||
|
preferences.ssd.validate(*model)?;
|
||||||
|
if preferences.reasoning_mode == ReasoningMode::Max
|
||||||
|
&& !self
|
||||||
|
.supported_reasoning_modes(*model)
|
||||||
|
.contains(&ReasoningMode::Max)
|
||||||
|
{
|
||||||
|
return Err(format!(
|
||||||
|
"Default thinking mode for {model} requires its Think Max profile to use at least 393216 context tokens."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.runtime_for(self.model).validate(self.model)?;
|
||||||
self.dev_brain.validate()
|
self.dev_brain.validate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn reasoning_mode(&self, model: ModelChoice) -> ReasoningMode {
|
||||||
|
self.model_profiles
|
||||||
|
.get(&model)
|
||||||
|
.map_or(ReasoningMode::default(), |profile| profile.reasoning_mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn generation_for(
|
||||||
|
&self,
|
||||||
|
model: ModelChoice,
|
||||||
|
reasoning_mode: ReasoningMode,
|
||||||
|
) -> GenerationPreferences {
|
||||||
|
let mut generation = self
|
||||||
|
.generation_profiles
|
||||||
|
.get(&model)
|
||||||
|
.and_then(|profiles| profiles.get(&reasoning_mode))
|
||||||
|
.cloned()
|
||||||
|
.unwrap_or_default();
|
||||||
|
generation.system_prompt = self.system_prompt.clone();
|
||||||
|
generation.reasoning_mode = reasoning_mode;
|
||||||
|
generation
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn active_generation(&self) -> GenerationPreferences {
|
||||||
|
self.generation_for(self.model, self.reasoning_mode(self.model))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn runtime_for(&self, model: ModelChoice) -> RuntimePreferences {
|
||||||
|
let mut runtime = self.runtime.clone();
|
||||||
|
if let Some(profile) = self.model_profiles.get(&model) {
|
||||||
|
runtime.speculative = profile.speculative.clone();
|
||||||
|
runtime.ssd = profile.ssd.clone();
|
||||||
|
}
|
||||||
|
runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn supported_reasoning_modes(&self, model: ModelChoice) -> &'static [ReasoningMode] {
|
||||||
|
let max = self.generation_for(model, ReasoningMode::Max);
|
||||||
|
max.supported_reasoning_modes()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_profile_defaults(&mut self) {
|
||||||
|
for model in MODEL_CHOICES {
|
||||||
|
self.model_profiles.entry(model).or_default();
|
||||||
|
let profiles = self.generation_profiles.entry(model).or_default();
|
||||||
|
for mode in REASONING_MODES {
|
||||||
|
profiles.entry(mode).or_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drop_legacy_model_settings(value: &mut Value) {
|
||||||
|
let Value::Mapping(root) = value else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let generation_key = Value::String("generation".into());
|
||||||
|
let prompt_key = Value::String("system_prompt".into());
|
||||||
|
if let Some(Value::Mapping(mut generation)) = root.remove(&generation_key)
|
||||||
|
&& !root.contains_key(&prompt_key)
|
||||||
|
&& let Some(prompt) = generation.remove(&prompt_key)
|
||||||
|
{
|
||||||
|
root.insert(prompt_key, prompt);
|
||||||
|
}
|
||||||
|
if let Some(Value::Mapping(runtime)) = root.get_mut(Value::String("runtime".into())) {
|
||||||
|
runtime.remove(Value::String("speculative".into()));
|
||||||
|
runtime.remove(Value::String("ssd".into()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Drops every value that still matches the default, so the file lists only what
|
/// Drops every value that still matches the default, so the file lists only what
|
||||||
@@ -299,8 +424,11 @@ fn without_defaults(value: Value, defaults: &Value) -> Option<Value> {
|
|||||||
let kept = entries
|
let kept = entries
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(key, entry)| {
|
.filter_map(|(key, entry)| {
|
||||||
let default = defaults.get(key)?;
|
let kept = match defaults.get(key) {
|
||||||
Some((key.clone(), without_defaults(entry.clone(), default)?))
|
Some(default) => without_defaults(entry.clone(), default)?,
|
||||||
|
None => entry.clone(),
|
||||||
|
};
|
||||||
|
Some((key.clone(), kept))
|
||||||
})
|
})
|
||||||
.collect::<Mapping>();
|
.collect::<Mapping>();
|
||||||
return (!kept.is_empty()).then_some(Value::Mapping(kept));
|
return (!kept.is_empty()).then_some(Value::Mapping(kept));
|
||||||
@@ -328,23 +456,10 @@ mod tests {
|
|||||||
fn only_changed_values_are_written_and_read_back() {
|
fn only_changed_values_are_written_and_read_back() {
|
||||||
let directory = std::env::temp_dir().join(format!("ds4-config-set-{}", std::process::id()));
|
let directory = std::env::temp_dir().join(format!("ds4-config-set-{}", std::process::id()));
|
||||||
let path = directory.join("config.yaml");
|
let path = directory.join("config.yaml");
|
||||||
let config = Config {
|
let mut config = Config {
|
||||||
model: ModelChoice::Glm52,
|
model: ModelChoice::Glm52,
|
||||||
default_permission_mode: PermissionMode::Ai,
|
default_permission_mode: PermissionMode::Ai,
|
||||||
a2ui_enabled: false,
|
a2ui_enabled: false,
|
||||||
generation: GenerationPreferences {
|
|
||||||
context_tokens: 65_536,
|
|
||||||
reasoning_mode: ReasoningMode::Direct,
|
|
||||||
..GenerationPreferences::default()
|
|
||||||
},
|
|
||||||
runtime: RuntimePreferences {
|
|
||||||
ssd: SsdPreferences {
|
|
||||||
enabled: true,
|
|
||||||
cache: Some(StreamingCacheBudget::Gib(64)),
|
|
||||||
..SsdPreferences::default()
|
|
||||||
},
|
|
||||||
..RuntimePreferences::default()
|
|
||||||
},
|
|
||||||
git: GitConfig {
|
git: GitConfig {
|
||||||
diff_layout: GitDiffLayout::Split,
|
diff_layout: GitDiffLayout::Split,
|
||||||
diff_algorithm: GitDiffAlgorithm::Patience,
|
diff_algorithm: GitDiffAlgorithm::Patience,
|
||||||
@@ -359,14 +474,28 @@ mod tests {
|
|||||||
},
|
},
|
||||||
..Config::default()
|
..Config::default()
|
||||||
};
|
};
|
||||||
|
config
|
||||||
|
.generation_profiles
|
||||||
|
.get_mut(&ModelChoice::Glm52)
|
||||||
|
.unwrap()
|
||||||
|
.get_mut(&ReasoningMode::Direct)
|
||||||
|
.unwrap()
|
||||||
|
.context_tokens = 65_536;
|
||||||
|
let model = config.model_profiles.get_mut(&ModelChoice::Glm52).unwrap();
|
||||||
|
model.reasoning_mode = ReasoningMode::Direct;
|
||||||
|
model.ssd = SsdPreferences {
|
||||||
|
enabled: true,
|
||||||
|
cache: Some(StreamingCacheBudget::Gib(64)),
|
||||||
|
..SsdPreferences::default()
|
||||||
|
};
|
||||||
config.save(&path).unwrap();
|
config.save(&path).unwrap();
|
||||||
|
|
||||||
let text = fs::read_to_string(&path).unwrap();
|
let text = fs::read_to_string(&path).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
text,
|
text,
|
||||||
"model: glm-5.2\ndefault_permission_mode: ai\na2ui_enabled: false\n\
|
"model: glm-5.2\ndefault_permission_mode: ai\na2ui_enabled: false\n\
|
||||||
generation:\n context_tokens: 65536\n reasoning_mode: none\n\
|
generation_profiles:\n glm-5.2:\n none:\n context_tokens: 65536\n\
|
||||||
runtime:\n ssd:\n enabled: true\n cache: 64GB\n\
|
model_profiles:\n glm-5.2:\n reasoning_mode: none\n ssd:\n enabled: true\n cache: 64GB\n\
|
||||||
git:\n diff_layout: split\n diff_algorithm: patience\n context_lines: 5\n whitespace: ignore-end-of-line\n\
|
git:\n diff_layout: split\n diff_algorithm: patience\n context_lines: 5\n whitespace: ignore-end-of-line\n\
|
||||||
interface:\n window_size:\n - 1280\n - 800\n window_position:\n - 120\n - -40\n"
|
interface:\n window_size:\n - 1280\n - 800\n window_position:\n - 120\n - -40\n"
|
||||||
);
|
);
|
||||||
@@ -374,6 +503,26 @@ mod tests {
|
|||||||
fs::remove_dir_all(&directory).unwrap();
|
fs::remove_dir_all(&directory).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn legacy_flat_model_settings_are_dropped_but_the_prompt_is_kept() {
|
||||||
|
let directory =
|
||||||
|
std::env::temp_dir().join(format!("ds4-config-legacy-{}", std::process::id()));
|
||||||
|
let path = directory.join("config.yaml");
|
||||||
|
fs::create_dir_all(&directory).unwrap();
|
||||||
|
fs::write(
|
||||||
|
&path,
|
||||||
|
"generation:\n system_prompt: keep me\n temperature: 0.2\nruntime:\n execution:\n quality: true\n ssd:\n enabled: true\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let config = Config::load(&path).unwrap();
|
||||||
|
assert_eq!(config.system_prompt, "keep me");
|
||||||
|
assert_eq!(config.active_generation().temperature, None);
|
||||||
|
assert!(!config.runtime_for(config.model).ssd.enabled);
|
||||||
|
assert!(config.runtime.execution.quality);
|
||||||
|
fs::remove_dir_all(&directory).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn invalid_files_are_reported_instead_of_reset() {
|
fn invalid_files_are_reported_instead_of_reset() {
|
||||||
let directory = std::env::temp_dir().join(format!("ds4-config-bad-{}", std::process::id()));
|
let directory = std::env::temp_dir().join(format!("ds4-config-bad-{}", std::process::id()));
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ const GLM: Artifact = Artifact {
|
|||||||
support: Some(false),
|
support: Some(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
pub(crate) enum ModelChoice {
|
pub(crate) enum ModelChoice {
|
||||||
#[default]
|
#[default]
|
||||||
#[serde(rename = "deepseek-v4-flash")]
|
#[serde(rename = "deepseek-v4-flash")]
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ use crate::engine::ChatTurn;
|
|||||||
use crate::metrics::{Metrics, WorkSource};
|
use crate::metrics::{Metrics, WorkSource};
|
||||||
use crate::model::{self, ModelChoice};
|
use crate::model::{self, ModelChoice};
|
||||||
use crate::runtime::{CheckpointTarget, GenerationEvent, GenerationService};
|
use crate::runtime::{CheckpointTarget, GenerationEvent, GenerationService};
|
||||||
use crate::settings::{ReasoningMode, effective_settings};
|
use crate::settings::{GenerationPreferences, ReasoningMode, effective_settings};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::value::RawValue;
|
use serde_json::value::RawValue;
|
||||||
use serde_json::{Map, Value, json};
|
use serde_json::{Map, Value, json};
|
||||||
@@ -375,10 +375,8 @@ fn handle(mut stream: TcpStream, state: &State) {
|
|||||||
let model = model_alias(id).filter(|model| installed.contains(model));
|
let model = model_alias(id).filter(|model| installed.contains(model));
|
||||||
if let Some(model) = model {
|
if let Some(model) = model {
|
||||||
let (context, tokens) = state.config.read().map_or((32_768, 32_768), |config| {
|
let (context, tokens) = state.config.read().map_or((32_768, 32_768), |config| {
|
||||||
(
|
let generation = config.generation_for(model, config.reasoning_mode(model));
|
||||||
config.generation.context_tokens,
|
(generation.context_tokens, generation.max_generated_tokens)
|
||||||
config.generation.max_generated_tokens,
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
send_json_with_cors(
|
send_json_with_cors(
|
||||||
&mut stream,
|
&mut stream,
|
||||||
@@ -505,17 +503,23 @@ fn installed_endpoint_models(models_path: &std::path::Path) -> Vec<ModelChoice>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn models_json(state: &State) -> Value {
|
fn models_json(state: &State) -> Value {
|
||||||
let (context, tokens) = state.config.read().map_or((32_768, 32_768), |config| {
|
let config = state.config.read().ok().map(|config| config.clone());
|
||||||
(
|
|
||||||
config.generation.context_tokens,
|
|
||||||
config.generation.max_generated_tokens,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
json!({
|
json!({
|
||||||
"object": "list",
|
"object": "list",
|
||||||
"data": installed_endpoint_models(&state.models_path)
|
"data": installed_endpoint_models(&state.models_path)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|model| model_json(model.id(), model, context, tokens))
|
.map(|model| {
|
||||||
|
let generation = config.as_ref().map_or_else(
|
||||||
|
GenerationPreferences::default,
|
||||||
|
|config| config.generation_for(model, config.reasoning_mode(model)),
|
||||||
|
);
|
||||||
|
model_json(
|
||||||
|
model.id(),
|
||||||
|
model,
|
||||||
|
generation.context_tokens,
|
||||||
|
generation.max_generated_tokens,
|
||||||
|
)
|
||||||
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -499,8 +499,8 @@ pub(super) fn parse_chat_request(
|
|||||||
return Err((400, format!("model is not installed and verified: {model}")));
|
return Err((400, format!("model is not installed and verified: {model}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut generation = config.generation.clone();
|
let reasoning_mode = request_reasoning(&request, requested_id)?;
|
||||||
generation.system_prompt.clear();
|
let mut generation = config.generation_for(model, reasoning_mode);
|
||||||
generation.max_generated_tokens = request
|
generation.max_generated_tokens = request
|
||||||
.max_completion_tokens
|
.max_completion_tokens
|
||||||
.or(request.max_tokens)
|
.or(request.max_tokens)
|
||||||
@@ -512,7 +512,6 @@ pub(super) fn parse_chat_request(
|
|||||||
generation.top_p = request.top_p.or(Some(1.0));
|
generation.top_p = request.top_p.or(Some(1.0));
|
||||||
generation.min_p = request.min_p.or(Some(0.05));
|
generation.min_p = request.min_p.or(Some(0.05));
|
||||||
generation.seed = request.seed.filter(|seed| *seed > 0);
|
generation.seed = request.seed.filter(|seed| *seed > 0);
|
||||||
generation.reasoning_mode = request_reasoning(&request, requested_id)?;
|
|
||||||
|
|
||||||
let tools_enabled = (!request.tools.is_empty() || !request.tool_schemas.is_empty())
|
let tools_enabled = (!request.tools.is_empty() || !request.tool_schemas.is_empty())
|
||||||
&& request.tool_choice.as_ref().and_then(Value::as_str) != Some("none");
|
&& request.tool_choice.as_ref().and_then(Value::as_str) != Some("none");
|
||||||
@@ -525,7 +524,8 @@ pub(super) fn parse_chat_request(
|
|||||||
protocol,
|
protocol,
|
||||||
)?;
|
)?;
|
||||||
generation.system_prompt = system;
|
generation.system_prompt = system;
|
||||||
let mut effective = effective_settings(model, &generation, &config.runtime, &state.models_path)
|
let runtime = config.runtime_for(model);
|
||||||
|
let mut effective = effective_settings(model, &generation, &runtime, &state.models_path)
|
||||||
.map_err(|error| (400, error))?;
|
.map_err(|error| (400, error))?;
|
||||||
effective.turn.top_k = request.top_k.unwrap_or(0);
|
effective.turn.top_k = request.top_k.unwrap_or(0);
|
||||||
if effective.turn.top_k < 0 {
|
if effective.turn.top_k < 0 {
|
||||||
|
|||||||
@@ -380,7 +380,9 @@ pub(crate) struct EngineDiagnosticSettings {
|
|||||||
#[serde(default, deny_unknown_fields)]
|
#[serde(default, deny_unknown_fields)]
|
||||||
pub(crate) struct RuntimePreferences {
|
pub(crate) struct RuntimePreferences {
|
||||||
pub(crate) execution: ExecutionPreferences,
|
pub(crate) execution: ExecutionPreferences,
|
||||||
|
#[serde(skip)]
|
||||||
pub(crate) speculative: SpeculativePreferences,
|
pub(crate) speculative: SpeculativePreferences,
|
||||||
|
#[serde(skip)]
|
||||||
pub(crate) ssd: SsdPreferences,
|
pub(crate) ssd: SsdPreferences,
|
||||||
pub(crate) steering: SteeringPreferences,
|
pub(crate) steering: SteeringPreferences,
|
||||||
pub(crate) diagnostics: DiagnosticPreferences,
|
pub(crate) diagnostics: DiagnosticPreferences,
|
||||||
@@ -505,7 +507,7 @@ pub(crate) struct EngineExecutionSettings {
|
|||||||
pub(crate) warm_weights: bool,
|
pub(crate) warm_weights: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub(crate) enum ReasoningMode {
|
pub(crate) enum ReasoningMode {
|
||||||
#[serde(rename = "none")]
|
#[serde(rename = "none")]
|
||||||
@@ -530,22 +532,28 @@ impl fmt::Display for ReasoningMode {
|
|||||||
pub(crate) struct GenerationPreferences {
|
pub(crate) struct GenerationPreferences {
|
||||||
pub(crate) context_tokens: i32,
|
pub(crate) context_tokens: i32,
|
||||||
pub(crate) max_generated_tokens: i32,
|
pub(crate) max_generated_tokens: i32,
|
||||||
|
#[serde(skip_serializing, default = "default_system_prompt")]
|
||||||
pub(crate) system_prompt: String,
|
pub(crate) system_prompt: String,
|
||||||
pub(crate) temperature: Option<f32>,
|
pub(crate) temperature: Option<f32>,
|
||||||
pub(crate) top_p: Option<f32>,
|
pub(crate) top_p: Option<f32>,
|
||||||
pub(crate) min_p: Option<f32>,
|
pub(crate) min_p: Option<f32>,
|
||||||
pub(crate) seed: Option<u64>,
|
pub(crate) seed: Option<u64>,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub(crate) reasoning_mode: ReasoningMode,
|
pub(crate) reasoning_mode: ReasoningMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const DEFAULT_SYSTEM_PROMPT: &str = "You are an expert coding assistant operating inside DS4Server, a local coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nGuidelines:\n- Be concise in your responses\n- Show file paths clearly when working with files";
|
pub(crate) const DEFAULT_SYSTEM_PROMPT: &str = "You are an expert coding assistant operating inside DS4Server, a local coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nGuidelines:\n- Be concise in your responses\n- Show file paths clearly when working with files";
|
||||||
|
|
||||||
|
fn default_system_prompt() -> String {
|
||||||
|
DEFAULT_SYSTEM_PROMPT.into()
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for GenerationPreferences {
|
impl Default for GenerationPreferences {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
context_tokens: 32_768,
|
context_tokens: 32_768,
|
||||||
max_generated_tokens: 50_000,
|
max_generated_tokens: 50_000,
|
||||||
system_prompt: DEFAULT_SYSTEM_PROMPT.into(),
|
system_prompt: default_system_prompt(),
|
||||||
temperature: None,
|
temperature: None,
|
||||||
top_p: None,
|
top_p: None,
|
||||||
min_p: None,
|
min_p: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user