Codex Handbook
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(),        );    }}