agent: Add max mode on text threads (#31361)

Related discussions #30240 #30596

Release Notes:

- Added the ability to use max mode on text threads.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
Alvaro Parker 2025-05-28 09:12:38 -04:00 committed by GitHub
parent 957e4adc3f
commit e314963f5b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 160 additions and 35 deletions

View file

@ -3,6 +3,7 @@ mod context_editor;
mod context_history;
mod context_store;
pub mod language_model_selector;
mod max_mode_tooltip;
mod slash_command;
mod slash_command_picker;

View file

@ -29,6 +29,7 @@ use paths::contexts_dir;
use project::Project;
use prompt_store::PromptBuilder;
use serde::{Deserialize, Serialize};
use settings::Settings;
use smallvec::SmallVec;
use std::{
cmp::{Ordering, max},
@ -682,6 +683,7 @@ pub struct AssistantContext {
language_registry: Arc<LanguageRegistry>,
project: Option<Entity<Project>>,
prompt_builder: Arc<PromptBuilder>,
completion_mode: agent_settings::CompletionMode,
}
trait ContextAnnotation {
@ -718,6 +720,14 @@ impl AssistantContext {
)
}
pub fn completion_mode(&self) -> agent_settings::CompletionMode {
self.completion_mode
}
pub fn set_completion_mode(&mut self, completion_mode: agent_settings::CompletionMode) {
self.completion_mode = completion_mode;
}
pub fn new(
id: ContextId,
replica_id: ReplicaId,
@ -764,6 +774,7 @@ impl AssistantContext {
pending_cache_warming_task: Task::ready(None),
_subscriptions: vec![cx.subscribe(&buffer, Self::handle_buffer_event)],
pending_save: Task::ready(Ok(())),
completion_mode: AgentSettings::get_global(cx).preferred_completion_mode,
path: None,
buffer,
telemetry,
@ -2321,7 +2332,15 @@ impl AssistantContext {
completion_request.messages.push(request_message);
}
}
let supports_max_mode = if let Some(model) = model {
model.supports_max_mode()
} else {
false
};
if supports_max_mode {
completion_request.mode = Some(self.completion_mode.into());
}
completion_request
}

View file

@ -1,7 +1,10 @@
use crate::language_model_selector::{
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
use crate::{
language_model_selector::{
LanguageModelSelector, LanguageModelSelectorPopoverMenu, ToggleModelSelector,
},
max_mode_tooltip::MaxModeTooltip,
};
use agent_settings::AgentSettings;
use agent_settings::{AgentSettings, CompletionMode};
use anyhow::Result;
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
use assistant_slash_commands::{
@ -2008,17 +2011,17 @@ impl ContextEditor {
None => (ButtonStyle::Filled, None),
};
ButtonLike::new("send_button")
Button::new("send_button", "Send")
.label_size(LabelSize::Small)
.disabled(self.sending_disabled(cx))
.style(style)
.when_some(tooltip, |button, tooltip| {
button.tooltip(move |_, _| tooltip.clone())
})
.layer(ElevationIndex::ModalSurface)
.child(Label::new("Send"))
.children(
.key_binding(
KeyBinding::for_action_in(&Assist, &focus_handle, window, cx)
.map(|binding| binding.into_any_element()),
.map(|kb| kb.size(rems_from_px(12.))),
)
.on_click(move |_event, window, cx| {
focus_handle.dispatch_action(&Assist, window, cx);
@ -2058,6 +2061,45 @@ impl ContextEditor {
)
}
fn render_max_mode_toggle(&self, cx: &mut Context<Self>) -> Option<AnyElement> {
let context = self.context().read(cx);
let active_model = LanguageModelRegistry::read_global(cx)
.default_model()
.map(|default| default.model)?;
if !active_model.supports_max_mode() {
return None;
}
let active_completion_mode = context.completion_mode();
let max_mode_enabled = active_completion_mode == CompletionMode::Max;
let icon = if max_mode_enabled {
IconName::ZedBurnModeOn
} else {
IconName::ZedBurnMode
};
Some(
IconButton::new("burn-mode", icon)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
.toggle_state(max_mode_enabled)
.selected_icon_color(Color::Error)
.on_click(cx.listener(move |this, _event, _window, cx| {
this.context().update(cx, |context, _cx| {
context.set_completion_mode(match active_completion_mode {
CompletionMode::Max => CompletionMode::Normal,
CompletionMode::Normal => CompletionMode::Max,
});
});
}))
.tooltip(move |_window, cx| {
cx.new(|_| MaxModeTooltip::new().selected(max_mode_enabled))
.into()
})
.into_any_element(),
)
}
fn render_language_model_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
let active_model = LanguageModelRegistry::read_global(cx)
.default_model()
@ -2503,6 +2545,7 @@ impl Render for ContextEditor {
let provider = LanguageModelRegistry::read_global(cx)
.default_model()
.map(|default| default.provider);
let accept_terms = if self.show_accept_terms {
provider.as_ref().and_then(|provider| {
provider.render_accept_terms(LanguageModelProviderTosView::PromptEditorPopup, cx)
@ -2512,6 +2555,8 @@ impl Render for ContextEditor {
};
let language_model_selector = self.language_model_selector_menu_handle.clone();
let max_mode_toggle = self.render_max_mode_toggle(cx);
v_flex()
.key_context("ContextEditor")
.capture_action(cx.listener(ContextEditor::cancel))
@ -2551,31 +2596,28 @@ impl Render for ContextEditor {
})
.children(self.render_last_error(cx))
.child(
h_flex().w_full().relative().child(
h_flex()
.p_2()
.w_full()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
.child(
h_flex()
.gap_1()
.child(self.render_inject_context_menu(cx))
.child(ui::Divider::vertical())
.child(
div()
.pl_0p5()
.child(self.render_language_model_selector(cx)),
),
)
.child(
h_flex()
.w_full()
.justify_end()
.child(self.render_send_button(window, cx)),
),
),
h_flex()
.relative()
.py_2()
.pl_1p5()
.pr_2()
.w_full()
.justify_between()
.border_t_1()
.border_color(cx.theme().colors().border_variant)
.bg(cx.theme().colors().editor_background)
.child(
h_flex()
.gap_0p5()
.child(self.render_inject_context_menu(cx))
.when_some(max_mode_toggle, |this, element| this.child(element)),
)
.child(
h_flex()
.gap_1()
.child(self.render_language_model_selector(cx))
.child(self.render_send_button(window, cx)),
),
)
}
}

View file

@ -0,0 +1,60 @@
use gpui::{Context, IntoElement, Render, Window};
use ui::{prelude::*, tooltip_container};
pub struct MaxModeTooltip {
selected: bool,
}
impl MaxModeTooltip {
pub fn new() -> Self {
Self { selected: false }
}
pub fn selected(mut self, selected: bool) -> Self {
self.selected = selected;
self
}
}
impl Render for MaxModeTooltip {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let icon = if self.selected {
IconName::ZedBurnModeOn
} else {
IconName::ZedBurnMode
};
let title = h_flex()
.gap_1()
.child(Icon::new(icon).size(IconSize::Small))
.child(Label::new("Burn Mode"));
tooltip_container(window, cx, |this, _, _| {
this.gap_0p5()
.map(|header| if self.selected {
header.child(
h_flex()
.justify_between()
.child(title)
.child(
h_flex()
.gap_0p5()
.child(Icon::new(IconName::Check).size(IconSize::XSmall).color(Color::Accent))
.child(Label::new("Turned On").size(LabelSize::XSmall).color(Color::Accent))
)
)
} else {
header.child(title)
})
.child(
div()
.max_w_72()
.child(
Label::new("Enables models to use large context windows, unlimited tool calls, and other capabilities for expanded reasoning, offering an unfettered agentic experience.")
.size(LabelSize::Small)
.color(Color::Muted)
)
)
})
}
}

View file

@ -24,11 +24,14 @@ Non-Max Mode usage will use up to 25 tool calls per one prompt. If your prompt e
In Max Mode, we enable models to use [large context windows](#context-windows), unlimited tool calls, and other capabilities for expanded reasoning, to allow an unfettered agentic experience.
Because of the increased cost to Zed, each subsequent request beyond the initial user prompt in Max Mode models is counted as a prompt for metering.
In addition, usage-based pricing per request is slightly more expensive for Max Mode models than usage-based pricing per prompt for regular models.
> Note that the Agent Panel using a Max Mode model may consume a good bit of your monthly prompt capacity, if many tool calls are used. We encourage you to think through what model is best for your needs before leaving the Agent Panel to work.
> Note that the Agent Panel using a Max Mode model may consume a good bit of your monthly prompt capacity, if many tool calls are used.
> We encourage you to think through what model is best for your needs before leaving the Agent Panel to work.
By default, all Agent threads start in normal mode, however you can use the agent setting `preferred_completion_mode` to start new Agent threads in Max Mode.
By default, all threads and [text threads](./text-threads.md) start in normal mode.
However, you can use the `agent.preferred_completion_mode` setting to have Max Mode activated by default.
## Context Windows {#context-windows}
@ -36,7 +39,7 @@ A context window is the maximum span of text and code an LLM can consider at onc
In [Max Mode](#max-mode), we increase context window size to allow models to have enhanced reasoning capabilities.
Each Agent thread in Zed maintains its own context window.
Each Agent thread and text thread in Zed maintains its own context window.
The more prompts, attached files, and responses included in a session, the larger the context window grows.
For best results, its recommended you take a purpose-based approach to Agent thread management, starting a new thread for each unique task.