Persist fast mode across new threads (#53356)

When toggling fast mode, the setting is now written to `settings.json`
under `agent.default_model.speed`, so new threads start with the same
speed. This follows the same pattern as `enable_thinking` and `effort`.

The `speed` field uses the existing `Speed` enum (`"fast"` /
`"standard"`) rather than a boolean, to leave room for future speed
tiers.

Example settings:
```json
{
  "agent": {
    "default_model": {
      "provider": "zed.dev",
      "model": "claude-sonnet-4",
      "speed": "fast"
    }
  }
}
```

cc @benbrandt

Release Notes:

- N/A

---------

Co-authored-by: Ben Brandt <benjamin.j.brandt@gmail.com>
This commit is contained in:
Nathan Sobo 2026-04-08 11:59:33 -07:00 committed by GitHub
parent 08e43aed1f
commit 4a02367db2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 31 additions and 9 deletions

View file

@ -1355,6 +1355,7 @@ impl acp_thread::AgentModelSelector for NativeAgentModelSelector {
let provider = model.provider_id().0.to_string();
let model = model.id().0.to_string();
let enable_thinking = thread.read(cx).thinking_enabled();
let speed = thread.read(cx).speed();
settings
.agent
.get_or_insert_default()
@ -1363,6 +1364,7 @@ impl acp_thread::AgentModelSelector for NativeAgentModelSelector {
model,
enable_thinking,
effort,
speed,
});
},
);

View file

@ -97,6 +97,7 @@ fn model_id_to_selection(model_id: &acp::ModelId) -> LanguageModelSelection {
model: model.to_owned(),
enable_thinking: false,
effort: None,
speed: None,
}
}

View file

@ -1041,6 +1041,10 @@ impl Thread {
.default_model
.as_ref()
.and_then(|model| model.effort.clone());
let speed = settings
.default_model
.as_ref()
.and_then(|model| model.speed);
let (prompt_capabilities_tx, prompt_capabilities_rx) =
watch::channel(Self::prompt_capabilities(model.as_deref()));
Self {
@ -1072,7 +1076,7 @@ impl Thread {
model,
summarization_model: None,
thinking_enabled: enable_thinking,
speed: None,
speed,
thinking_effort,
prompt_capabilities_tx,
prompt_capabilities_rx,

View file

@ -267,6 +267,7 @@ impl ManageProfilesModal {
effort: model
.default_effort_level()
.map(|effort| effort.value.to_string()),
speed: None,
});
}
}

View file

@ -1861,6 +1861,7 @@ impl AgentPanel {
model,
enable_thinking,
effort,
speed: None,
})
});
}

View file

@ -8467,13 +8467,20 @@ impl ThreadView {
return;
};
thread.update(cx, |thread, cx| {
thread.set_speed(
thread
.speed()
.map(|speed| speed.toggle())
.unwrap_or(Speed::Fast),
cx,
);
let new_speed = thread
.speed()
.map(|speed| speed.toggle())
.unwrap_or(Speed::Fast);
thread.set_speed(new_speed, cx);
let fs = thread.project().read(cx).fs().clone();
update_settings_file(fs, cx, move |settings, _| {
if let Some(agent) = settings.agent.as_mut()
&& let Some(default_model) = agent.default_model.as_mut()
{
default_model.speed = Some(new_speed);
}
});
});
}

View file

@ -11,6 +11,7 @@ fn language_model_to_selection(model: &Arc<dyn LanguageModel>) -> LanguageModelS
model: model.id().0.to_string(),
enable_thinking: false,
effort: None,
speed: None,
}
}

View file

@ -333,7 +333,9 @@ pub struct LanguageModelRequest {
pub speed: Option<Speed>,
}
#[derive(Clone, Copy, Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[derive(
Clone, Copy, Default, Debug, Serialize, Deserialize, PartialEq, Eq, schemars::JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum Speed {
#[default]

View file

@ -256,6 +256,7 @@ impl AgentSettingsContent {
model,
enable_thinking: false,
effort: None,
speed: None,
});
}
@ -397,6 +398,7 @@ pub struct LanguageModelSelection {
#[serde(default)]
pub enable_thinking: bool,
pub effort: Option<String>,
pub speed: Option<language_model_core::Speed>,
}
#[with_fallible_options]

View file

@ -56,6 +56,7 @@ merge_from_overwrites!(
std::sync::Arc<str>,
std::path::PathBuf,
std::sync::Arc<std::path::Path>,
language_model_core::Speed,
);
impl<T: Clone + MergeFrom> MergeFrom for Option<T> {