tui/src/chatwidget/service_tiers.rs
158 lines
//! Service-tier selection and model-catalog helpers for `ChatWidget`.use super::ChatWidget;use crate::app_command::AppCommand;use crate::app_event::AppEvent;use crate::bottom_pane::slash_commands::ServiceTierCommand;use crate::service_tier_resolution;use codex_features::Feature;use codex_protocol::config_types::SERVICE_TIER_DEFAULT_REQUEST_VALUE;use codex_protocol::config_types::ServiceTier;use codex_protocol::openai_models::SPEED_TIER_FAST;impl ChatWidget { pub(crate) fn set_service_tier(&mut self, service_tier: Option<String>) { self.config.service_tier = service_tier; self.refresh_effective_service_tier(); self.refresh_model_dependent_surfaces(); } pub(crate) fn current_service_tier(&self) -> Option<&str> { self.effective_service_tier.as_deref() } pub(crate) fn configured_service_tier(&self) -> Option<String> { self.config.service_tier.clone() } pub(crate) fn service_tier_update_for_core(&self) -> Option<Option<String>> { service_tier_resolution::service_tier_update_for_core( &self.config, self.current_model(), &self.model_catalog.try_list_models().unwrap_or_default(), ) } pub(crate) fn should_show_fast_status(&self, model: &str, service_tier: Option<&str>) -> bool { service_tier.is_some_and(|service_tier| { service_tier == ServiceTier::Fast.request_value() && self.model_supports_service_tier(model, service_tier) }) && self.has_chatgpt_account } pub(super) fn fast_mode_enabled(&self) -> bool { self.config.features.enabled(Feature::FastMode) } pub(crate) fn can_toggle_fast_mode_from_keybinding(&self) -> bool { self.fast_mode_enabled() && self.current_model_fast_service_tier().is_some() && !self.is_user_turn_pending_or_running() && self.bottom_pane.no_modal_or_popup_active() } pub(crate) fn toggle_fast_mode_from_ui(&mut self) { let Some(fast_tier) = self.current_model_fast_service_tier() else { return; }; let next_tier = if self.current_service_tier() == Some(fast_tier.id.as_str()) { Some(SERVICE_TIER_DEFAULT_REQUEST_VALUE.to_string()) } else { Some(fast_tier.id) }; self.set_service_tier_selection(next_tier); } pub(crate) fn toggle_service_tier_from_ui(&mut self, command: ServiceTierCommand) { let next_tier = if self.current_service_tier() == Some(command.id.as_str()) { Some(SERVICE_TIER_DEFAULT_REQUEST_VALUE.to_string()) } else { Some(command.id) }; self.set_service_tier_selection(next_tier); } pub(super) fn sync_service_tier_commands(&mut self) { self.bottom_pane .set_service_tier_commands_enabled(self.fast_mode_enabled()); self.bottom_pane .set_service_tier_commands(self.current_model_service_tier_commands()); } pub(super) fn current_model_service_tier_commands(&self) -> Vec<ServiceTierCommand> { let model = self.current_model(); self.model_catalog .try_list_models() .ok() .and_then(|models| { models .into_iter() .find(|preset| preset.model == model) .map(|preset| { preset .service_tiers .into_iter() .map(|tier| ServiceTierCommand { id: tier.id, name: tier.name.to_lowercase(), description: tier.description, }) .collect() }) }) .unwrap_or_default() } fn set_service_tier_selection(&mut self, service_tier: Option<String>) { self.set_service_tier(service_tier.clone()); self.app_event_tx .send(AppEvent::CodexOp(AppCommand::override_turn_context( /*cwd*/ None, /*approval_policy*/ None, /*approvals_reviewer*/ None, /*permission_profile*/ None, /*active_permission_profile*/ None, /*windows_sandbox_level*/ None, /*model*/ None, /*effort*/ None, /*summary*/ None, Some(service_tier.clone()), /*collaboration_mode*/ None, /*personality*/ None, ))); self.app_event_tx .send(AppEvent::PersistServiceTierSelection { service_tier }); } fn model_supports_service_tier(&self, model: &str, service_tier: &str) -> bool { self.model_catalog .try_list_models() .ok() .and_then(|models| { models .into_iter() .find(|preset| preset.model == model) .map(|preset| { preset .service_tiers .iter() .any(|tier| tier.id == service_tier) }) }) .unwrap_or(false) } fn current_model_fast_service_tier(&self) -> Option<ServiceTierCommand> { self.current_model_service_tier_commands() .into_iter() .find(|tier| tier.name.eq_ignore_ascii_case(SPEED_TIER_FAST)) } pub(super) fn refresh_effective_service_tier(&mut self) { self.effective_service_tier = service_tier_resolution::effective_service_tier( &self.config, self.current_model(), &self.model_catalog.try_list_models().unwrap_or_default(), ); }}