Cargo.lock 🔗
@@ -9040,6 +9040,7 @@ dependencies = [
"editor",
"extension",
"extension_host",
+ "feature_flags",
"fs",
"futures 0.3.31",
"google_ai",
Marshall Bowers and Neel created
This PR adds a thinking toggle for controlling whether to use thinking
for a model in the Zed provider:
<img width="645" height="142" alt="Screenshot 2026-01-22 at 12 34 01 PM"
src="https://github.com/user-attachments/assets/9aa543fe-e708-4840-8b38-1a6fbcb78388"
/>
Previously we would create separate "Thinking" variants of the models
that supported thinking in the model selector.
This only applies to Anthropic models in the Zed provider, currently.
This is gated behind the `cloud-thinking-toggle` feature flag.
Release Notes:
- N/A
---------
Co-authored-by: Neel <neel@zed.dev>
Cargo.lock | 1
crates/agent/src/thread.rs | 16 ++++++++
crates/agent_ui/src/acp/thread_view.rs | 41 ++++++++++++++++++++-
crates/feature_flags/src/flags.rs | 11 +++++
crates/language_model/src/language_model.rs | 5 ++
crates/language_models/Cargo.toml | 1
crates/language_models/src/provider/cloud.rs | 37 +++++++++++++++----
7 files changed, 100 insertions(+), 12 deletions(-)
@@ -9040,6 +9040,7 @@ dependencies = [
"editor",
"extension",
"extension_host",
+ "feature_flags",
"fs",
"futures 0.3.31",
"google_ai",
@@ -749,6 +749,7 @@ pub struct Thread {
templates: Arc<Templates>,
model: Option<Arc<dyn LanguageModel>>,
summarization_model: Option<Arc<dyn LanguageModel>>,
+ thinking_enabled: bool,
prompt_capabilities_tx: watch::Sender<acp::PromptCapabilities>,
pub(crate) prompt_capabilities_rx: watch::Receiver<acp::PromptCapabilities>,
pub(crate) project: Entity<Project>,
@@ -811,6 +812,7 @@ impl Thread {
templates,
model,
summarization_model: None,
+ thinking_enabled: true,
prompt_capabilities_tx,
prompt_capabilities_rx,
project,
@@ -872,6 +874,7 @@ impl Thread {
templates,
model: Some(model),
summarization_model: None,
+ thinking_enabled: true,
prompt_capabilities_tx,
prompt_capabilities_rx,
project,
@@ -1069,6 +1072,8 @@ impl Thread {
templates,
model,
summarization_model: None,
+ // TODO: Persist this on the `DbThread`.
+ thinking_enabled: true,
project,
action_log,
updated_at: db_thread.updated_at,
@@ -1167,6 +1172,15 @@ impl Thread {
cx.notify()
}
+ pub fn thinking_enabled(&self) -> bool {
+ self.thinking_enabled
+ }
+
+ pub fn set_thinking_enabled(&mut self, enabled: bool, cx: &mut Context<Self>) {
+ self.thinking_enabled = enabled;
+ cx.notify();
+ }
+
pub fn last_message(&self) -> Option<Message> {
if let Some(message) = self.pending_message.clone() {
Some(Message::Agent(message))
@@ -2292,7 +2306,7 @@ impl Thread {
tool_choice: None,
stop: Vec::new(),
temperature: AgentSettings::temperature_for_model(model, cx),
- thinking_allowed: true,
+ thinking_allowed: self.thinking_enabled,
bypass_rate_limit: false,
};
@@ -21,8 +21,8 @@ use editor::{
Editor, EditorEvent, EditorMode, MultiBuffer, PathKey, SelectionEffects, SizingBehavior,
};
use feature_flags::{
- AgentSharingFeatureFlag, AgentV2FeatureFlag, FeatureFlagAppExt as _,
- UserSlashCommandsFeatureFlag,
+ AgentSharingFeatureFlag, AgentV2FeatureFlag, CloudThinkingToggleFeatureFlag,
+ FeatureFlagAppExt as _, UserSlashCommandsFeatureFlag,
};
use file_icons::FileIcons;
use fs::Fs;
@@ -6216,6 +6216,7 @@ impl AcpThreadView {
h_flex()
.gap_1()
.children(self.render_token_usage(cx))
+ .children(self.render_thinking_toggle(cx))
.children(self.profile_selector.clone())
// Either config_options_view OR (mode_selector + model_selector)
.children(self.config_options_view.clone())
@@ -6486,6 +6487,42 @@ impl AcpThreadView {
}
}
+ fn render_thinking_toggle(&self, cx: &mut Context<Self>) -> Option<IconButton> {
+ if !cx.has_flag::<CloudThinkingToggleFeatureFlag>() {
+ return None;
+ }
+
+ let thread = self.as_native_thread(cx)?.read(cx);
+
+ let supports_thinking = thread.model()?.supports_thinking();
+ if !supports_thinking {
+ return None;
+ }
+
+ let thinking = thread.thinking_enabled();
+
+ let tooltip_label = if thinking {
+ "Disable Thinking Mode".to_string()
+ } else {
+ "Enable Thinking Mode".to_string()
+ };
+
+ Some(
+ IconButton::new("thinking-mode", IconName::ToolThink)
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Muted)
+ .toggle_state(thinking)
+ .tooltip(Tooltip::text(tooltip_label))
+ .on_click(cx.listener(move |this, _, _window, cx| {
+ if let Some(thread) = this.as_native_thread(cx) {
+ thread.update(cx, |thread, cx| {
+ thread.set_thinking_enabled(!thread.thinking_enabled(), cx);
+ });
+ }
+ })),
+ )
+ }
+
fn keep_all(&mut self, _: &KeepAll, _window: &mut Window, cx: &mut Context<Self>) {
let Some(thread) = self.thread() else {
return;
@@ -65,3 +65,14 @@ impl FeatureFlag for DiffReviewFeatureFlag {
false
}
}
+
+/// Controls whether we show the new thinking toggle in the Agent Panel when using models through the Zed provider (Cloud).
+pub struct CloudThinkingToggleFeatureFlag;
+
+impl FeatureFlag for CloudThinkingToggleFeatureFlag {
+ const NAME: &'static str = "cloud-thinking-toggle";
+
+ fn enabled_for_staff() -> bool {
+ false
+ }
+}
@@ -591,6 +591,11 @@ pub trait LanguageModel: Send + Sync {
None
}
+ /// Whether this model supports extended thinking.
+ fn supports_thinking(&self) -> bool {
+ false
+ }
+
/// Whether this model supports images
fn supports_images(&self) -> bool;
@@ -32,6 +32,7 @@ credentials_provider.workspace = true
deepseek = { workspace = true, features = ["schemars"] }
extension.workspace = true
extension_host.workspace = true
+feature_flags.workspace = true
fs.workspace = true
futures.workspace = true
google_ai = { workspace = true, features = ["schemars"] }
@@ -9,6 +9,7 @@ use cloud_llm_client::{
ListModelsResponse, Plan, PlanV2, SERVER_SUPPORTS_STATUS_MESSAGES_HEADER_NAME,
ZED_VERSION_HEADER_NAME,
};
+use feature_flags::{CloudThinkingToggleFeatureFlag, FeatureFlagAppExt as _};
use futures::{
AsyncBufReadExt, FutureExt, Stream, StreamExt, future::BoxFuture, stream::BoxStream,
};
@@ -164,20 +165,27 @@ impl State {
state.update(cx, |_, cx| cx.notify())
})
}
+
fn update_models(&mut self, response: ListModelsResponse, cx: &mut Context<Self>) {
+ let is_thinking_toggle_enabled = cx.has_flag::<CloudThinkingToggleFeatureFlag>();
+
let mut models = Vec::new();
for model in response.models {
models.push(Arc::new(model.clone()));
- // Right now we represent thinking variants of models as separate models on the client,
- // so we need to insert variants for any model that supports thinking.
- if model.supports_thinking {
- models.push(Arc::new(cloud_llm_client::LanguageModel {
- id: cloud_llm_client::LanguageModelId(format!("{}-thinking", model.id).into()),
- display_name: format!("{} Thinking", model.display_name),
- ..model
- }));
+ if !is_thinking_toggle_enabled {
+ // Right now we represent thinking variants of models as separate models on the client,
+ // so we need to insert variants for any model that supports thinking.
+ if model.supports_thinking {
+ models.push(Arc::new(cloud_llm_client::LanguageModel {
+ id: cloud_llm_client::LanguageModelId(
+ format!("{}-thinking", model.id).into(),
+ ),
+ display_name: format!("{} Thinking", model.display_name),
+ ..model
+ }));
+ }
}
}
@@ -570,6 +578,10 @@ impl LanguageModel for CloudLanguageModel {
self.model.supports_images
}
+ fn supports_thinking(&self) -> bool {
+ self.model.supports_thinking
+ }
+
fn supports_streaming_tools(&self) -> bool {
self.model.supports_streaming_tools
}
@@ -721,6 +733,13 @@ impl LanguageModel for CloudLanguageModel {
let bypass_rate_limit = request.bypass_rate_limit;
let app_version = Some(cx.update(|cx| AppVersion::global(cx)));
let thinking_allowed = request.thinking_allowed;
+ let is_thinking_toggle_enabled =
+ cx.update(|cx| cx.has_flag::<CloudThinkingToggleFeatureFlag>());
+ let enable_thinking = if is_thinking_toggle_enabled {
+ thinking_allowed && self.model.supports_thinking
+ } else {
+ thinking_allowed && self.model.id.0.ends_with("-thinking")
+ };
let provider_name = provider_name(&self.model.provider);
match self.model.provider {
cloud_llm_client::LanguageModelProvider::Anthropic => {
@@ -729,7 +748,7 @@ impl LanguageModel for CloudLanguageModel {
self.model.id.to_string(),
1.0,
self.model.max_output_tokens as u64,
- if thinking_allowed && self.model.id.0.ends_with("-thinking") {
+ if enable_thinking {
AnthropicModelMode::Thinking {
budget_tokens: Some(4_096),
}