mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Default Fast service tier for eligible ChatGPT plans (#19053)
## Why Enterprise and business-like ChatGPT plans should get Codex's Fast service tier by default when the user or caller has not made an explicit service-tier choice. At the same time, callers need a durable way to choose standard routing without adding a new persisted `standard` service tier value. This keeps existing config compatibility while letting core own the managed default policy. ## What changed - Resolve the effective service tier in core at session creation: explicit `fast` or `flex` wins, explicit null/clear or `[notice].fast_default_opt_out = true` resolves to standard routing, and otherwise eligible ChatGPT plans resolve to Fast when FastMode is enabled. - Add `[notice].fast_default_opt_out` as the persisted opt-out marker for managed Fast defaults. - Treat app-server/TUI `service_tier: null` as an explicit standard/clear choice by preserving that intent through config loading. - Update TUI rendering to use core's effective service tier for startup and status surfaces while still keeping `config.service_tier` as the explicit configured choice. - Update `/fast off` to clear `service_tier`, persist the opt-out marker, and send explicit standard for subsequent turns. ## Verification - Added unit coverage for config override/notice handling, service-tier resolution, runtime null clearing, and `/fast off` turn propagation. - `cargo build -p codex-cli` Full test suite was not run locally per author request.
This commit is contained in:
committed by
GitHub
Unverified
parent
082fc4f632
commit
02170996e6
@@ -172,7 +172,7 @@ impl ChatWidget {
|
||||
self.open_model_popup();
|
||||
}
|
||||
SlashCommand::Fast => {
|
||||
let next_tier = if matches!(self.config.service_tier, Some(ServiceTier::Fast)) {
|
||||
let next_tier = if matches!(self.current_service_tier(), Some(ServiceTier::Fast)) {
|
||||
None
|
||||
} else {
|
||||
Some(ServiceTier::Fast)
|
||||
@@ -527,12 +527,12 @@ impl ChatWidget {
|
||||
"on" => self.set_service_tier_selection(Some(ServiceTier::Fast)),
|
||||
"off" => self.set_service_tier_selection(/*service_tier*/ None),
|
||||
"status" => {
|
||||
let status = if matches!(self.config.service_tier, Some(ServiceTier::Fast))
|
||||
{
|
||||
"on"
|
||||
} else {
|
||||
"off"
|
||||
};
|
||||
let status =
|
||||
if matches!(self.current_service_tier(), Some(ServiceTier::Fast)) {
|
||||
"on"
|
||||
} else {
|
||||
"off"
|
||||
};
|
||||
self.add_info_message(
|
||||
format!("Fast mode is {status}."),
|
||||
/*hint*/ None,
|
||||
|
||||
@@ -482,7 +482,7 @@ impl ChatWidget {
|
||||
)),
|
||||
StatusLineItem::SessionId => self.thread_id.map(|id| id.to_string()),
|
||||
StatusLineItem::FastMode => Some(
|
||||
if matches!(self.config.service_tier, Some(ServiceTier::Fast)) {
|
||||
if matches!(self.current_service_tier(), Some(ServiceTier::Fast)) {
|
||||
"Fast on".to_string()
|
||||
} else {
|
||||
"Fast off".to_string()
|
||||
@@ -603,7 +603,7 @@ impl ChatWidget {
|
||||
fn model_with_reasoning_display_name(&self) -> String {
|
||||
let label = Self::status_line_reasoning_effort_label(self.effective_reasoning_effort());
|
||||
let fast_label =
|
||||
if self.should_show_fast_status(self.current_model(), self.config.service_tier) {
|
||||
if self.should_show_fast_status(self.current_model(), self.current_service_tier()) {
|
||||
" fast"
|
||||
} else {
|
||||
""
|
||||
|
||||
@@ -181,6 +181,7 @@ pub(super) async fn make_chatwidget_manual(
|
||||
};
|
||||
let current_collaboration_mode = base_mode;
|
||||
let active_collaboration_mask = collaboration_modes::default_mask(model_catalog.as_ref());
|
||||
let effective_service_tier = cfg.service_tier;
|
||||
let mut widget = ChatWidget {
|
||||
app_event_tx,
|
||||
codex_op_target: super::CodexOpTarget::Direct(op_tx),
|
||||
@@ -188,6 +189,7 @@ pub(super) async fn make_chatwidget_manual(
|
||||
active_cell: None,
|
||||
active_cell_revision: 0,
|
||||
config: cfg,
|
||||
effective_service_tier,
|
||||
current_collaboration_mode,
|
||||
active_collaboration_mask,
|
||||
has_chatgpt_account: false,
|
||||
|
||||
@@ -1550,7 +1550,7 @@ async fn queued_fast_slash_applies_before_next_queued_message() {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn user_turn_clears_service_tier_after_fast_is_turned_off() {
|
||||
async fn user_turn_sends_standard_override_after_fast_is_turned_off() {
|
||||
let (mut chat, mut rx, mut op_rx) = make_chatwidget_manual(Some("gpt-5.3-codex")).await;
|
||||
chat.thread_id = Some(ThreadId::new());
|
||||
set_chatgpt_auth(&mut chat);
|
||||
@@ -1560,7 +1560,24 @@ async fn user_turn_clears_service_tier_after_fast_is_turned_off() {
|
||||
let _events = std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>();
|
||||
|
||||
chat.dispatch_command_with_args(SlashCommand::Fast, "off".to_string(), Vec::new());
|
||||
let _events = std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>();
|
||||
let events = std::iter::from_fn(|| rx.try_recv().ok()).collect::<Vec<_>>();
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::CodexOp(Op::OverrideTurnContext {
|
||||
service_tier: Some(None),
|
||||
..
|
||||
})
|
||||
)),
|
||||
"expected fast-mode off override app event; events: {events:?}"
|
||||
);
|
||||
assert!(
|
||||
events.iter().any(|event| matches!(
|
||||
event,
|
||||
AppEvent::PersistServiceTierSelection { service_tier: None }
|
||||
)),
|
||||
"expected fast-mode opt-out persistence app event; events: {events:?}"
|
||||
);
|
||||
|
||||
chat.bottom_pane
|
||||
.set_composer_text("hello".to_string(), Vec::new(), Vec::new());
|
||||
@@ -1571,7 +1588,7 @@ async fn user_turn_clears_service_tier_after_fast_is_turned_off() {
|
||||
service_tier: Some(None),
|
||||
..
|
||||
} => {}
|
||||
other => panic!("expected Op::UserTurn to clear service tier, got {other:?}"),
|
||||
other => panic!("expected Op::UserTurn with standard service tier override, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user