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
@@ -23,6 +23,7 @@ use codex_protocol::protocol::AskForApproval;
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct ConfigProfile {
|
||||
pub model: Option<String>,
|
||||
/// Optional explicit service tier preference for new turns (`fast` or `flex`).
|
||||
pub service_tier: Option<ServiceTier>,
|
||||
/// The key in the `model_providers` map identifying the
|
||||
/// [`ModelProviderInfo`] to use.
|
||||
|
||||
@@ -615,6 +615,8 @@ pub struct Notice {
|
||||
pub hide_full_access_warning: Option<bool>,
|
||||
/// Tracks whether the user has acknowledged the Windows world-writable directories warning.
|
||||
pub hide_world_writable_warning: Option<bool>,
|
||||
/// Tracks whether the user opted out of Codex-managed fast defaults.
|
||||
pub fast_default_opt_out: Option<bool>,
|
||||
/// Tracks whether the user opted out of the rate limit model switch reminder.
|
||||
pub hide_rate_limit_model_nudge: Option<bool>,
|
||||
/// Tracks whether the user has seen the model migration prompt
|
||||
|
||||
@@ -631,7 +631,12 @@
|
||||
"$ref": "#/definitions/SandboxMode"
|
||||
},
|
||||
"service_tier": {
|
||||
"$ref": "#/definitions/ServiceTier"
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ServiceTier"
|
||||
}
|
||||
],
|
||||
"description": "Optional explicit service tier preference for new turns (`fast` or `flex`)."
|
||||
},
|
||||
"tools": {
|
||||
"$ref": "#/definitions/ToolsToml"
|
||||
@@ -1397,6 +1402,10 @@
|
||||
},
|
||||
"description": "Tracks scopes where external config migration prompts should be suppressed."
|
||||
},
|
||||
"fast_default_opt_out": {
|
||||
"description": "Tracks whether the user opted out of Codex-managed fast defaults.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"hide_full_access_warning": {
|
||||
"description": "Tracks whether the user has acknowledged the full access warning prompt.",
|
||||
"type": "boolean"
|
||||
|
||||
@@ -39,6 +39,7 @@ use codex_config::types::McpServerTransportConfig;
|
||||
use codex_config::types::MemoriesConfig;
|
||||
use codex_config::types::MemoriesToml;
|
||||
use codex_config::types::ModelAvailabilityNuxConfig;
|
||||
use codex_config::types::Notice;
|
||||
use codex_config::types::NotificationCondition;
|
||||
use codex_config::types::NotificationMethod;
|
||||
use codex_config::types::Notifications;
|
||||
@@ -5298,6 +5299,50 @@ async fn metrics_exporter_defaults_to_statsig_when_missing() -> std::io::Result<
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn explicit_null_service_tier_override_sets_fast_default_opt_out() -> std::io::Result<()> {
|
||||
let fixture = create_test_fixture()?;
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
fixture.cfg.clone(),
|
||||
ConfigOverrides {
|
||||
cwd: Some(fixture.cwd_path()),
|
||||
service_tier: Some(None),
|
||||
..Default::default()
|
||||
},
|
||||
fixture.codex_home(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.service_tier, None);
|
||||
assert_eq!(config.notices.fast_default_opt_out, Some(true));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fast_default_opt_out_notice_config_is_respected() -> std::io::Result<()> {
|
||||
let fixture = create_test_fixture()?;
|
||||
let mut cfg = fixture.cfg.clone();
|
||||
cfg.notice = Some(Notice {
|
||||
fast_default_opt_out: Some(true),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let config = Config::load_from_base_config_with_overrides(
|
||||
cfg,
|
||||
ConfigOverrides {
|
||||
cwd: Some(fixture.cwd_path()),
|
||||
..Default::default()
|
||||
},
|
||||
fixture.codex_home(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
assert_eq!(config.service_tier, None);
|
||||
assert_eq!(config.notices.fast_default_opt_out, Some(true));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
|
||||
let fixture = create_test_fixture()?;
|
||||
|
||||
@@ -37,6 +37,8 @@ pub enum ConfigEdit {
|
||||
SetNoticeHideFullAccessWarning(bool),
|
||||
/// Toggle the Windows world-writable directories warning acknowledgement flag.
|
||||
SetNoticeHideWorldWritableWarning(bool),
|
||||
/// Toggle the opt-out marker for Codex-managed fast defaults.
|
||||
SetNoticeFastDefaultOptOut(bool),
|
||||
/// Toggle the rate limit model nudge acknowledgement flag.
|
||||
SetNoticeHideRateLimitModelNudge(bool),
|
||||
/// Toggle the Windows onboarding acknowledgement flag.
|
||||
@@ -436,6 +438,11 @@ impl ConfigDocument {
|
||||
&[NOTICE_TABLE_KEY, "hide_world_writable_warning"],
|
||||
value(*acknowledged),
|
||||
)),
|
||||
ConfigEdit::SetNoticeFastDefaultOptOut(opted_out) => Ok(self.write_value(
|
||||
Scope::Global,
|
||||
&[NOTICE_TABLE_KEY, "fast_default_opt_out"],
|
||||
value(*opted_out),
|
||||
)),
|
||||
ConfigEdit::SetNoticeHideRateLimitModelNudge(acknowledged) => Ok(self.write_value(
|
||||
Scope::Global,
|
||||
&[NOTICE_TABLE_KEY, "hide_rate_limit_model_nudge"],
|
||||
@@ -978,6 +985,12 @@ impl ConfigEditsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_fast_default_opt_out(mut self, opted_out: bool) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::SetNoticeFastDefaultOptOut(opted_out));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_hide_rate_limit_model_nudge(mut self, acknowledged: bool) -> Self {
|
||||
self.edits
|
||||
.push(ConfigEdit::SetNoticeHideRateLimitModelNudge(acknowledged));
|
||||
|
||||
@@ -2031,14 +2031,24 @@ impl Config {
|
||||
let forced_login_method = cfg.forced_login_method;
|
||||
|
||||
let model = model.or(config_profile.model).or(cfg.model);
|
||||
let service_tier = service_tier_override
|
||||
.unwrap_or_else(|| config_profile.service_tier.or(cfg.service_tier));
|
||||
let mut notices = cfg.notice.unwrap_or_default();
|
||||
let service_tier = match service_tier_override {
|
||||
Some(Some(service_tier)) => Some(service_tier),
|
||||
Some(None) => {
|
||||
// Preserve explicit standard/clear intent after the nested override
|
||||
// collapses into `Config.service_tier = None`.
|
||||
notices.fast_default_opt_out = Some(true);
|
||||
None
|
||||
}
|
||||
None => config_profile.service_tier.or(cfg.service_tier),
|
||||
};
|
||||
let service_tier = match service_tier {
|
||||
Some(ServiceTier::Fast) if features.enabled(Feature::FastMode) => {
|
||||
Some(ServiceTier::Fast)
|
||||
}
|
||||
Some(ServiceTier::Fast) => None,
|
||||
Some(ServiceTier::Flex) => Some(ServiceTier::Flex),
|
||||
_ => None,
|
||||
None => None,
|
||||
};
|
||||
|
||||
let compact_prompt = compact_prompt.or(cfg.compact_prompt).and_then(|value| {
|
||||
@@ -2414,7 +2424,7 @@ impl Config {
|
||||
active_profile: active_profile_name,
|
||||
active_project,
|
||||
windows_wsl_setup_acknowledged: cfg.windows_wsl_setup_acknowledged.unwrap_or(false),
|
||||
notices: cfg.notice.unwrap_or_default(),
|
||||
notices,
|
||||
check_for_update_on_startup,
|
||||
disable_paste_burst: cfg.disable_paste_burst.unwrap_or(false),
|
||||
analytics_enabled: config_profile
|
||||
|
||||
@@ -77,6 +77,7 @@ use codex_otel::current_span_w3c_trace_context;
|
||||
use codex_otel::set_parent_from_w3c_trace_context;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::ToolName;
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
use codex_protocol::approvals::ElicitationRequestEvent;
|
||||
use codex_protocol::approvals::ExecPolicyAmendment;
|
||||
use codex_protocol::approvals::NetworkPolicyAmendment;
|
||||
@@ -600,11 +601,20 @@ impl Codex {
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let account_plan_type = auth_manager
|
||||
.auth_cached()
|
||||
.and_then(|auth| auth.account_plan_type());
|
||||
let service_tier = get_service_tier(
|
||||
config.service_tier,
|
||||
config.notices.fast_default_opt_out.unwrap_or(false),
|
||||
account_plan_type,
|
||||
config.features.enabled(Feature::FastMode),
|
||||
);
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
service_tier: config.service_tier,
|
||||
service_tier,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
user_instructions,
|
||||
personality: config.personality,
|
||||
@@ -785,6 +795,27 @@ impl Codex {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_service_tier(
|
||||
configured_service_tier: Option<ServiceTier>,
|
||||
fast_default_opt_out: bool,
|
||||
account_plan_type: Option<AccountPlanType>,
|
||||
fast_mode_enabled: bool,
|
||||
) -> Option<ServiceTier> {
|
||||
if configured_service_tier.is_some() || fast_default_opt_out || !fast_mode_enabled {
|
||||
return configured_service_tier;
|
||||
}
|
||||
|
||||
account_plan_type
|
||||
.is_some_and(is_enterprise_default_service_tier_plan)
|
||||
.then_some(ServiceTier::Fast)
|
||||
}
|
||||
|
||||
fn is_enterprise_default_service_tier_plan(plan_type: AccountPlanType) -> bool {
|
||||
plan_type == AccountPlanType::Enterprise
|
||||
|| plan_type.is_business_like()
|
||||
|| plan_type.is_team_like()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn completed_session_loop_termination() -> SessionLoopTermination {
|
||||
futures::future::ready(()).boxed().shared()
|
||||
|
||||
@@ -26,6 +26,8 @@ use codex_models_manager::bundled_models_response;
|
||||
use codex_models_manager::model_info;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::account::PlanType as AccountPlanType;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::exec_output::ExecToolCallOutput;
|
||||
use codex_protocol::models::FileSystemPermissions;
|
||||
@@ -2643,6 +2645,104 @@ fn session_telemetry(
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_service_tier_defaults_enterprise_accounts_to_fast() {
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::Enterprise),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
Some(ServiceTier::Fast)
|
||||
);
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::EnterpriseCbpUsageBased),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
Some(ServiceTier::Fast)
|
||||
);
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::Business),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
Some(ServiceTier::Fast)
|
||||
);
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::Team),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
Some(ServiceTier::Fast)
|
||||
);
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::SelfServeBusinessUsageBased),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
Some(ServiceTier::Fast)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_service_tier_respects_fast_default_opt_out() {
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ true,
|
||||
Some(AccountPlanType::Enterprise),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_service_tier_does_not_default_non_enterprise_or_disabled_fast_mode() {
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::Pro),
|
||||
/*fast_mode_enabled*/ true,
|
||||
),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
get_service_tier(
|
||||
/*configured_service_tier*/ None,
|
||||
/*fast_default_opt_out*/ false,
|
||||
Some(AccountPlanType::Enterprise),
|
||||
/*fast_mode_enabled*/ false,
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn session_settings_null_service_tier_update_clears_service_tier() {
|
||||
let session_configuration = make_session_configuration_for_tests().await;
|
||||
|
||||
let updated = session_configuration
|
||||
.apply(&SessionSettingsUpdate {
|
||||
service_tier: Some(None),
|
||||
..Default::default()
|
||||
})
|
||||
.expect("null service tier update should apply");
|
||||
|
||||
assert_eq!(updated.service_tier, None);
|
||||
}
|
||||
|
||||
pub(crate) async fn make_session_configuration_for_tests() -> SessionConfiguration {
|
||||
let codex_home = tempfile::tempdir().expect("create temp dir");
|
||||
let config = build_test_config(codex_home.path()).await;
|
||||
|
||||
@@ -1029,14 +1029,24 @@ impl App {
|
||||
AppEvent::PersistServiceTierSelection { service_tier } => {
|
||||
self.refresh_status_line();
|
||||
let profile = self.active_profile.as_deref();
|
||||
match ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
self.config.service_tier = service_tier;
|
||||
let mut edits = ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.with_profile(profile)
|
||||
.set_service_tier(service_tier)
|
||||
.apply()
|
||||
.await
|
||||
{
|
||||
.set_service_tier(service_tier);
|
||||
if service_tier.is_none() {
|
||||
self.config.notices.fast_default_opt_out = Some(true);
|
||||
edits = edits.set_fast_default_opt_out(/*opted_out*/ true);
|
||||
}
|
||||
match edits.apply().await {
|
||||
Ok(()) => {
|
||||
let status = if service_tier.is_some() { "on" } else { "off" };
|
||||
let status = if matches!(
|
||||
service_tier,
|
||||
Some(codex_protocol::config_types::ServiceTier::Fast)
|
||||
) {
|
||||
"on"
|
||||
} else {
|
||||
"off"
|
||||
};
|
||||
let mut message = format!("Fast mode set to {status}");
|
||||
if let Some(profile) = profile {
|
||||
message.push_str(" for ");
|
||||
|
||||
@@ -622,7 +622,8 @@ impl App {
|
||||
|
||||
pub(super) fn fresh_session_config(&self) -> Config {
|
||||
let mut config = self.config.clone();
|
||||
config.service_tier = self.chat_widget.current_service_tier();
|
||||
config.service_tier = self.chat_widget.configured_service_tier();
|
||||
config.notices.fast_default_opt_out = self.chat_widget.fast_default_opt_out();
|
||||
config
|
||||
}
|
||||
pub(super) async fn resume_target_session(
|
||||
|
||||
@@ -776,6 +776,8 @@ pub(crate) struct ChatWidget {
|
||||
/// where the overlay may briefly treat new tail content as already cached.
|
||||
active_cell_revision: u64,
|
||||
config: Config,
|
||||
/// Runtime value resolved by core. `config.service_tier` remains the explicit user choice.
|
||||
effective_service_tier: Option<ServiceTier>,
|
||||
/// The unmasked collaboration mode settings (always Default mode).
|
||||
///
|
||||
/// Masks are applied on top of this base mode to derive the effective mode.
|
||||
@@ -2120,6 +2122,7 @@ impl ChatWidget {
|
||||
self.current_rollout_path = event.rollout_path.clone();
|
||||
self.current_cwd = Some(event.cwd.to_path_buf());
|
||||
self.config.cwd = event.cwd.clone();
|
||||
self.effective_service_tier = event.service_tier;
|
||||
if let Err(err) = self
|
||||
.config
|
||||
.permissions
|
||||
@@ -5106,6 +5109,7 @@ impl ChatWidget {
|
||||
let active_cell = Some(Self::placeholder_session_header_cell(&config));
|
||||
|
||||
let current_cwd = Some(config.cwd.to_path_buf());
|
||||
let effective_service_tier = config.service_tier;
|
||||
let queued_message_edit_binding = queued_message_edit_binding_for_terminal(terminal_info());
|
||||
let mut widget = Self {
|
||||
app_event_tx: app_event_tx.clone(),
|
||||
@@ -5124,6 +5128,7 @@ impl ChatWidget {
|
||||
active_cell,
|
||||
active_cell_revision: 0,
|
||||
config,
|
||||
effective_service_tier,
|
||||
skills_all: Vec::new(),
|
||||
skills_initial_state: None,
|
||||
current_collaboration_mode,
|
||||
@@ -5942,7 +5947,11 @@ impl ChatWidget {
|
||||
.personality
|
||||
.filter(|_| self.config.features.enabled(Feature::Personality))
|
||||
.filter(|_| self.current_model_supports_personality());
|
||||
let service_tier = Some(self.config.service_tier);
|
||||
let service_tier = match self.config.service_tier {
|
||||
Some(service_tier) => Some(Some(service_tier)),
|
||||
None if self.config.notices.fast_default_opt_out == Some(true) => Some(None),
|
||||
None => None,
|
||||
};
|
||||
let op = AppCommand::user_turn(
|
||||
items,
|
||||
self.config.cwd.to_path_buf(),
|
||||
@@ -9850,12 +9859,21 @@ impl ChatWidget {
|
||||
/// 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;
|
||||
self.effective_service_tier = service_tier;
|
||||
}
|
||||
|
||||
pub(crate) fn current_service_tier(&self) -> Option<ServiceTier> {
|
||||
self.effective_service_tier
|
||||
}
|
||||
|
||||
pub(crate) fn configured_service_tier(&self) -> Option<ServiceTier> {
|
||||
self.config.service_tier
|
||||
}
|
||||
|
||||
pub(crate) fn fast_default_opt_out(&self) -> Option<bool> {
|
||||
self.config.notices.fast_default_opt_out
|
||||
}
|
||||
|
||||
pub(crate) fn status_account_display(&self) -> Option<&StatusAccountDisplay> {
|
||||
self.status_account_display.as_ref()
|
||||
}
|
||||
@@ -9932,6 +9950,9 @@ impl ChatWidget {
|
||||
}
|
||||
|
||||
fn set_service_tier_selection(&mut self, service_tier: Option<ServiceTier>) {
|
||||
if service_tier.is_none() {
|
||||
self.config.notices.fast_default_opt_out = Some(true);
|
||||
}
|
||||
self.set_service_tier(service_tier);
|
||||
self.app_event_tx.send(AppEvent::CodexOp(
|
||||
AppCommand::override_turn_context(
|
||||
|
||||
@@ -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:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ use codex_config::types::McpServerTransportConfig;
|
||||
use codex_mcp::qualified_mcp_tool_name_prefix;
|
||||
use codex_otel::RuntimeMetricsSummary;
|
||||
use codex_protocol::account::PlanType;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::mcp::Resource;
|
||||
#[cfg(test)]
|
||||
@@ -1241,12 +1240,7 @@ pub(crate) fn new_session_info(
|
||||
} else {
|
||||
if config.show_tooltips
|
||||
&& let Some(tooltips) = tooltip_override
|
||||
.or_else(|| {
|
||||
tooltips::get_tooltip(
|
||||
auth_plan,
|
||||
matches!(config.service_tier, Some(ServiceTier::Fast)),
|
||||
)
|
||||
})
|
||||
.or_else(|| tooltips::get_tooltip(auth_plan, show_fast_status))
|
||||
.map(|tip| TooltipHistoryCell::new(tip, &config.cwd))
|
||||
{
|
||||
parts.push(Box::new(tooltips));
|
||||
|
||||
Reference in New Issue
Block a user