add fast mode toggle (#13212)

- add a local Fast mode setting in codex-core (similar to how model id
is currently stored on disk locally)
- send `service_tier=priority` on requests when Fast is enabled
- add `/fast` in the TUI and persist it locally
- feature flag
This commit is contained in:
pash-openai
2026-03-02 20:29:33 -08:00
committed by GitHub
parent 56cc2c71f4
commit 2f5b01abd6
69 changed files with 929 additions and 127 deletions

View File

@@ -81,6 +81,7 @@ use codex_protocol::config_types::CollaborationMode;
use codex_protocol::config_types::CollaborationModeMask;
use codex_protocol::config_types::ModeKind;
use codex_protocol::config_types::Personality;
use codex_protocol::config_types::ServiceTier;
use codex_protocol::config_types::Settings;
#[cfg(target_os = "windows")]
use codex_protocol::config_types::WindowsSandboxLevel;
@@ -1156,6 +1157,7 @@ impl ChatWidget {
mask.reasoning_effort = Some(event.reasoning_effort);
}
self.refresh_model_display();
self.sync_fast_command_enabled();
self.sync_personality_command_enabled();
let startup_tooltip_override = self.startup_tooltip_override.take();
let session_info_cell = history_cell::new_session_info(
@@ -2960,6 +2962,7 @@ impl ChatWidget {
.bottom_pane
.set_status_line_enabled(!widget.configured_status_line_items().is_empty());
widget.bottom_pane.set_collaboration_modes_enabled(true);
widget.sync_fast_command_enabled();
widget.sync_personality_command_enabled();
widget
.bottom_pane
@@ -3139,6 +3142,7 @@ impl ChatWidget {
.bottom_pane
.set_status_line_enabled(!widget.configured_status_line_items().is_empty());
widget.bottom_pane.set_collaboration_modes_enabled(true);
widget.sync_fast_command_enabled();
widget.sync_personality_command_enabled();
widget
.bottom_pane
@@ -3307,6 +3311,7 @@ impl ChatWidget {
.bottom_pane
.set_status_line_enabled(!widget.configured_status_line_items().is_empty());
widget.bottom_pane.set_collaboration_modes_enabled(true);
widget.sync_fast_command_enabled();
widget.sync_personality_command_enabled();
widget
.bottom_pane
@@ -3605,6 +3610,14 @@ impl ChatWidget {
SlashCommand::Model => {
self.open_model_popup();
}
SlashCommand::Fast => {
let next_tier = if self.config.service_tier.is_some() {
None
} else {
Some(ServiceTier::Fast)
};
self.set_service_tier_selection(next_tier);
}
SlashCommand::Realtime => {
if !self.realtime_conversation_enabled() {
return;
@@ -3884,6 +3897,27 @@ impl ChatWidget {
let trimmed = args.trim();
match cmd {
SlashCommand::Fast => {
if trimmed.is_empty() {
self.dispatch_command(cmd);
return;
}
match trimmed.to_ascii_lowercase().as_str() {
"on" => self.set_service_tier_selection(Some(ServiceTier::Fast)),
"off" => self.set_service_tier_selection(None),
"status" => {
let status = if self.config.service_tier.is_some() {
"on"
} else {
"off"
};
self.add_info_message(format!("Fast mode is {status}."), None);
}
_ => {
self.add_error_message("Usage: /fast [on|off|status]".to_string());
}
}
}
SlashCommand::Rename if !trimmed.is_empty() => {
self.otel_manager.counter("codex.thread.rename", 1, &[]);
let Some((prepared_args, _prepared_elements)) =
@@ -4222,6 +4256,7 @@ impl ChatWidget {
.personality
.filter(|_| self.config.features.enabled(Feature::Personality))
.filter(|_| self.current_model_supports_personality());
let service_tier = self.fast_mode_enabled().then_some(self.config.service_tier);
let op = Op::UserTurn {
items,
cwd: self.config.cwd.clone(),
@@ -4230,6 +4265,7 @@ impl ChatWidget {
model: effective_mode.model().to_string(),
effort: effective_mode.reasoning_effort(),
summary: None,
service_tier,
final_output_json_schema: None,
collaboration_mode,
personality,
@@ -5210,6 +5246,7 @@ impl ChatWidget {
model: Some(switch_model_for_events.clone()),
effort: Some(Some(default_effort)),
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
}));
@@ -5329,6 +5366,7 @@ impl ChatWidget {
model: None,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
windows_sandbox_level: None,
personality: Some(personality),
@@ -6242,6 +6280,7 @@ impl ChatWidget {
model: None,
effort: None,
summary: None,
service_tier: None,
collaboration_mode: None,
personality: None,
}));
@@ -6777,6 +6816,9 @@ impl ChatWidget {
self.reset_realtime_conversation_state();
}
}
if feature == Feature::FastMode {
self.sync_fast_command_enabled();
}
if feature == Feature::Personality {
self.sync_personality_command_enabled();
}
@@ -6859,6 +6901,19 @@ impl ChatWidget {
self.config.personality = Some(personality);
}
/// Set Fast mode in the widget's config copy.
pub(crate) fn set_service_tier(&mut self, service_tier: Option<ServiceTier>) {
self.config.service_tier = service_tier;
}
pub(crate) fn current_service_tier(&self) -> Option<ServiceTier> {
self.config.service_tier
}
fn fast_mode_enabled(&self) -> bool {
self.config.features.enabled(Feature::FastMode)
}
pub(crate) fn set_realtime_audio_device(
&mut self,
kind: RealtimeAudioDeviceKind,
@@ -6888,6 +6943,25 @@ impl ChatWidget {
self.refresh_model_display();
}
fn set_service_tier_selection(&mut self, service_tier: Option<ServiceTier>) {
self.set_service_tier(service_tier);
self.app_event_tx
.send(AppEvent::CodexOp(Op::OverrideTurnContext {
cwd: None,
approval_policy: None,
sandbox_policy: None,
windows_sandbox_level: None,
model: None,
effort: None,
summary: None,
service_tier: Some(service_tier),
collaboration_mode: None,
personality: None,
}));
self.app_event_tx
.send(AppEvent::PersistServiceTierSelection { service_tier });
}
pub(crate) fn current_model(&self) -> &str {
if !self.collaboration_modes_enabled() {
return self.current_collaboration_mode.model();
@@ -6914,6 +6988,11 @@ impl ChatWidget {
.unwrap_or_else(|| "System default".to_string())
}
fn sync_fast_command_enabled(&mut self) {
self.bottom_pane
.set_fast_command_enabled(self.fast_mode_enabled());
}
fn sync_personality_command_enabled(&mut self) {
self.bottom_pane
.set_personality_command_enabled(self.config.features.enabled(Feature::Personality));