From 9d579813bbcafde0abd4010f57ac93eea0b1be2b Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 5 May 2026 09:51:18 +0300 Subject: [PATCH] 1- Add model service tiers metadata (#20969) ## Why The model list needs to carry display-ready service tier metadata so clients can render tier choices with stable IDs, names, and descriptions. A raw speed-tier string list is not enough for richer UI copy or future tier labels. ## What changed - Added `ModelServiceTier` to shared model metadata with string `id`, `name`, and `description` fields. - Added `service_tiers` to `ModelInfo` and `ModelPreset`, preserving empty defaults for older cached model payloads. - Exposed `serviceTiers` on app-server v2 `Model` responses and threaded it through TUI app-server model conversion. - Marked legacy `additional_speed_tiers` / `additionalSpeedTiers` metadata as deprecated in source and generated schema output. - Regenerated app-server protocol JSON schema and TypeScript fixtures, including `ModelServiceTier.ts`. ## Verification - Ran `just write-app-server-schema`. - Did not run local tests per repo instruction; relying on PR CI. --------- Co-authored-by: Codex --- .../codex_app_server_protocol.schemas.json | 27 +++++++++++++ .../codex_app_server_protocol.v2.schemas.json | 27 +++++++++++++ .../schema/json/v2/ModelListResponse.json | 27 +++++++++++++ .../schema/typescript/v2/Model.ts | 7 +++- .../schema/typescript/v2/ModelServiceTier.ts | 5 +++ .../schema/typescript/v2/index.ts | 1 + .../app-server-protocol/src/protocol/v2.rs | 12 ++++++ codex-rs/app-server/src/models.rs | 10 +++++ .../app-server/tests/common/models_cache.rs | 1 + .../app-server/tests/suite/v2/model_list.rs | 10 +++++ .../codex-api/tests/models_integration.rs | 1 + codex-rs/core/tests/suite/model_switching.rs | 2 + codex-rs/core/tests/suite/models_cache_ttl.rs | 1 + codex-rs/core/tests/suite/personality.rs | 2 + codex-rs/core/tests/suite/remote_models.rs | 3 ++ codex-rs/core/tests/suite/rmcp_client.rs | 1 + .../tests/suite/spawn_agent_description.rs | 1 + codex-rs/core/tests/suite/view_image.rs | 1 + .../src/amazon_bedrock/catalog.rs | 2 + codex-rs/models-manager/src/model_info.rs | 1 + codex-rs/protocol/src/openai_models.rs | 39 +++++++++++++++++-- codex-rs/tools/src/agent_tool_tests.rs | 1 + codex-rs/tui/src/app_server_session.rs | 10 +++++ .../chatwidget/tests/popups_and_settings.rs | 2 + 24 files changed, 190 insertions(+), 4 deletions(-) create mode 100644 codex-rs/app-server-protocol/schema/typescript/v2/ModelServiceTier.ts diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 00278cccb..bce9dc3bd 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -11266,6 +11266,7 @@ "properties": { "additionalSpeedTiers": { "default": [], + "description": "Deprecated: use `serviceTiers` instead.", "items": { "type": "string" }, @@ -11312,6 +11313,13 @@ "model": { "type": "string" }, + "serviceTiers": { + "default": [], + "items": { + "$ref": "#/definitions/v2/ModelServiceTier" + }, + "type": "array" + }, "supportedReasoningEfforts": { "items": { "$ref": "#/definitions/v2/ReasoningEffortOption" @@ -11476,6 +11484,25 @@ "title": "ModelReroutedNotification", "type": "object" }, + "ModelServiceTier": { + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "description", + "id", + "name" + ], + "type": "object" + }, "ModelUpgradeInfo": { "properties": { "migrationMarkdown": { diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json index 597fde945..a6eb5aec8 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.v2.schemas.json @@ -7877,6 +7877,7 @@ "properties": { "additionalSpeedTiers": { "default": [], + "description": "Deprecated: use `serviceTiers` instead.", "items": { "type": "string" }, @@ -7923,6 +7924,13 @@ "model": { "type": "string" }, + "serviceTiers": { + "default": [], + "items": { + "$ref": "#/definitions/ModelServiceTier" + }, + "type": "array" + }, "supportedReasoningEfforts": { "items": { "$ref": "#/definitions/ReasoningEffortOption" @@ -8087,6 +8095,25 @@ "title": "ModelReroutedNotification", "type": "object" }, + "ModelServiceTier": { + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "description", + "id", + "name" + ], + "type": "object" + }, "ModelUpgradeInfo": { "properties": { "migrationMarkdown": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json index dc60c5b77..c0221805e 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ModelListResponse.json @@ -24,6 +24,7 @@ "properties": { "additionalSpeedTiers": { "default": [], + "description": "Deprecated: use `serviceTiers` instead.", "items": { "type": "string" }, @@ -70,6 +71,13 @@ "model": { "type": "string" }, + "serviceTiers": { + "default": [], + "items": { + "$ref": "#/definitions/ModelServiceTier" + }, + "type": "array" + }, "supportedReasoningEfforts": { "items": { "$ref": "#/definitions/ReasoningEffortOption" @@ -120,6 +128,25 @@ ], "type": "object" }, + "ModelServiceTier": { + "properties": { + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + } + }, + "required": [ + "description", + "id", + "name" + ], + "type": "object" + }, "ModelUpgradeInfo": { "properties": { "migrationMarkdown": { diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts b/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts index f4cf5a946..2354ffbf9 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/Model.ts @@ -4,7 +4,12 @@ import type { InputModality } from "../InputModality"; import type { ReasoningEffort } from "../ReasoningEffort"; import type { ModelAvailabilityNux } from "./ModelAvailabilityNux"; +import type { ModelServiceTier } from "./ModelServiceTier"; import type { ModelUpgradeInfo } from "./ModelUpgradeInfo"; import type { ReasoningEffortOption } from "./ReasoningEffortOption"; -export type Model = { id: string, model: string, upgrade: string | null, upgradeInfo: ModelUpgradeInfo | null, availabilityNux: ModelAvailabilityNux | null, displayName: string, description: string, hidden: boolean, supportedReasoningEfforts: Array, defaultReasoningEffort: ReasoningEffort, inputModalities: Array, supportsPersonality: boolean, additionalSpeedTiers: Array, isDefault: boolean, }; +export type Model = { id: string, model: string, upgrade: string | null, upgradeInfo: ModelUpgradeInfo | null, availabilityNux: ModelAvailabilityNux | null, displayName: string, description: string, hidden: boolean, supportedReasoningEfforts: Array, defaultReasoningEffort: ReasoningEffort, inputModalities: Array, supportsPersonality: boolean, +/** + * Deprecated: use `serviceTiers` instead. + */ +additionalSpeedTiers: Array, serviceTiers: Array, isDefault: boolean, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ModelServiceTier.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ModelServiceTier.ts new file mode 100644 index 000000000..09693d078 --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ModelServiceTier.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ModelServiceTier = { id: string, name: string, description: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts index 037534a22..a949da895 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/index.ts @@ -245,6 +245,7 @@ export type { ModelProviderCapabilitiesReadParams } from "./ModelProviderCapabil export type { ModelProviderCapabilitiesReadResponse } from "./ModelProviderCapabilitiesReadResponse"; export type { ModelRerouteReason } from "./ModelRerouteReason"; export type { ModelReroutedNotification } from "./ModelReroutedNotification"; +export type { ModelServiceTier } from "./ModelServiceTier"; export type { ModelUpgradeInfo } from "./ModelUpgradeInfo"; export type { ModelVerification } from "./ModelVerification"; export type { ModelVerificationNotification } from "./ModelVerificationNotification"; diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index 7ab25a943..92da9551f 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -2511,6 +2511,15 @@ impl From for ModelAvailabilityNux { } } +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export_to = "v2/")] +pub struct ModelServiceTier { + pub id: String, + pub name: String, + pub description: String, +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] @@ -2529,8 +2538,11 @@ pub struct Model { pub input_modalities: Vec, #[serde(default)] pub supports_personality: bool, + /// Deprecated: use `serviceTiers` instead. #[serde(default)] pub additional_speed_tiers: Vec, + #[serde(default)] + pub service_tiers: Vec, // Only one model should be marked as default. pub is_default: bool, } diff --git a/codex-rs/app-server/src/models.rs b/codex-rs/app-server/src/models.rs index fd08098f7..4d75a2058 100644 --- a/codex-rs/app-server/src/models.rs +++ b/codex-rs/app-server/src/models.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use codex_app_server_protocol::Model; +use codex_app_server_protocol::ModelServiceTier; use codex_app_server_protocol::ModelUpgradeInfo; use codex_app_server_protocol::ReasoningEffortOption; use codex_core::ThreadManager; @@ -43,6 +44,15 @@ fn model_from_preset(preset: ModelPreset) -> Model { input_modalities: preset.input_modalities, supports_personality: preset.supports_personality, additional_speed_tiers: preset.additional_speed_tiers, + service_tiers: preset + .service_tiers + .into_iter() + .map(|service_tier| ModelServiceTier { + id: service_tier.id, + name: service_tier.name, + description: service_tier.description, + }) + .collect(), is_default: preset.is_default, } } diff --git a/codex-rs/app-server/tests/common/models_cache.rs b/codex-rs/app-server/tests/common/models_cache.rs index 3b4a58a7a..be7d5d047 100644 --- a/codex-rs/app-server/tests/common/models_cache.rs +++ b/codex-rs/app-server/tests/common/models_cache.rs @@ -29,6 +29,7 @@ fn preset_to_info(preset: &ModelPreset, priority: i32) -> ModelInfo { supported_in_api: preset.supported_in_api, priority, additional_speed_tiers: preset.additional_speed_tiers.clone(), + service_tiers: preset.service_tiers.clone(), upgrade: preset.upgrade.as_ref().map(Into::into), base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/app-server/tests/suite/v2/model_list.rs b/codex-rs/app-server/tests/suite/v2/model_list.rs index 830ab0f78..e2039d333 100644 --- a/codex-rs/app-server/tests/suite/v2/model_list.rs +++ b/codex-rs/app-server/tests/suite/v2/model_list.rs @@ -9,6 +9,7 @@ use codex_app_server_protocol::JSONRPCResponse; use codex_app_server_protocol::Model; use codex_app_server_protocol::ModelListParams; use codex_app_server_protocol::ModelListResponse; +use codex_app_server_protocol::ModelServiceTier; use codex_app_server_protocol::ModelUpgradeInfo; use codex_app_server_protocol::ReasoningEffortOption; use codex_app_server_protocol::RequestId; @@ -51,6 +52,15 @@ fn model_from_preset(preset: &ModelPreset) -> Model { // todo(sayan): fix, maybe make roundtrip use ModelInfo only supports_personality: false, additional_speed_tiers: preset.additional_speed_tiers.clone(), + service_tiers: preset + .service_tiers + .iter() + .map(|service_tier| ModelServiceTier { + id: service_tier.id.clone(), + name: service_tier.name.clone(), + description: service_tier.description.clone(), + }) + .collect(), is_default: preset.is_default, } } diff --git a/codex-rs/codex-api/tests/models_integration.rs b/codex-rs/codex-api/tests/models_integration.rs index 9f95c9441..d2b31180b 100644 --- a/codex-rs/codex-api/tests/models_integration.rs +++ b/codex-rs/codex-api/tests/models_integration.rs @@ -75,6 +75,7 @@ async fn models_client_hits_models_endpoint() { supported_in_api: true, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/core/tests/suite/model_switching.rs b/codex-rs/core/tests/suite/model_switching.rs index 43ec50746..b0df5050c 100644 --- a/codex-rs/core/tests/suite/model_switching.rs +++ b/codex-rs/core/tests/suite/model_switching.rs @@ -106,6 +106,7 @@ fn test_model_info( supports_search_tool: false, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, @@ -818,6 +819,7 @@ async fn model_switch_to_smaller_model_updates_token_context_window() -> Result< supports_search_tool: false, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/core/tests/suite/models_cache_ttl.rs b/codex-rs/core/tests/suite/models_cache_ttl.rs index e2688afc9..8463ee1cf 100644 --- a/codex-rs/core/tests/suite/models_cache_ttl.rs +++ b/codex-rs/core/tests/suite/models_cache_ttl.rs @@ -341,6 +341,7 @@ fn test_remote_model(slug: &str, priority: i32) -> ModelInfo { supported_in_api: true, priority, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/core/tests/suite/personality.rs b/codex-rs/core/tests/suite/personality.rs index dde6d2ca5..09eb61fa6 100644 --- a/codex-rs/core/tests/suite/personality.rs +++ b/codex-rs/core/tests/suite/personality.rs @@ -579,6 +579,7 @@ async fn remote_model_friendly_personality_instructions_with_feature() -> anyhow supported_in_api: true, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: Some(ModelMessages { @@ -687,6 +688,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() - supported_in_api: true, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: Some(ModelMessages { diff --git a/codex-rs/core/tests/suite/remote_models.rs b/codex-rs/core/tests/suite/remote_models.rs index 49218c78d..d1caf2483 100644 --- a/codex-rs/core/tests/suite/remote_models.rs +++ b/codex-rs/core/tests/suite/remote_models.rs @@ -533,6 +533,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> { supports_search_tool: false, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, @@ -789,6 +790,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> { supports_search_tool: false, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: remote_base.to_string(), model_messages: None, @@ -1279,6 +1281,7 @@ fn test_remote_model_with_policy( supports_search_tool: false, priority, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/core/tests/suite/rmcp_client.rs b/codex-rs/core/tests/suite/rmcp_client.rs index 0947f4fba..8b7b4211c 100644 --- a/codex-rs/core/tests/suite/rmcp_client.rs +++ b/codex-rs/core/tests/suite/rmcp_client.rs @@ -1283,6 +1283,7 @@ async fn stdio_image_responses_are_sanitized_for_text_only_model() -> anyhow::Re supported_in_api: true, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/core/tests/suite/spawn_agent_description.rs b/codex-rs/core/tests/suite/spawn_agent_description.rs index 031c3135e..cc5a9952e 100644 --- a/codex-rs/core/tests/suite/spawn_agent_description.rs +++ b/codex-rs/core/tests/suite/spawn_agent_description.rs @@ -67,6 +67,7 @@ fn test_model_info( supports_search_tool: false, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/core/tests/suite/view_image.rs b/codex-rs/core/tests/suite/view_image.rs index 29c660d3a..19c2e6673 100644 --- a/codex-rs/core/tests/suite/view_image.rs +++ b/codex-rs/core/tests/suite/view_image.rs @@ -1085,6 +1085,7 @@ async fn view_image_tool_returns_unsupported_message_for_text_only_model() -> an supports_search_tool: false, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), upgrade: None, base_instructions: "base instructions".to_string(), model_messages: None, diff --git a/codex-rs/model-provider/src/amazon_bedrock/catalog.rs b/codex-rs/model-provider/src/amazon_bedrock/catalog.rs index 4ca2cb891..60fa17d36 100644 --- a/codex-rs/model-provider/src/amazon_bedrock/catalog.rs +++ b/codex-rs/model-provider/src/amazon_bedrock/catalog.rs @@ -47,6 +47,7 @@ fn gpt_5_4_cmb_bedrock_model(priority: i32) -> ModelInfo { supported_in_api: true, priority, additional_speed_tiers: vec!["fast".to_string()], + service_tiers: Vec::new(), availability_nux: None, upgrade: None, base_instructions: BASE_INSTRUCTIONS.to_string(), @@ -87,6 +88,7 @@ fn bedrock_oss_model(slug: &str, display_name: &str, priority: i32) -> ModelInfo supported_in_api: true, priority, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), availability_nux: None, upgrade: None, base_instructions: BASE_INSTRUCTIONS.to_string(), diff --git a/codex-rs/models-manager/src/model_info.rs b/codex-rs/models-manager/src/model_info.rs index 8e8abae54..774dd3eac 100644 --- a/codex-rs/models-manager/src/model_info.rs +++ b/codex-rs/models-manager/src/model_info.rs @@ -76,6 +76,7 @@ pub fn model_info_from_slug(slug: &str) -> ModelInfo { supported_in_api: true, priority: 99, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), availability_nux: None, upgrade: None, base_instructions: BASE_INSTRUCTIONS.to_string(), diff --git a/codex-rs/protocol/src/openai_models.rs b/codex-rs/protocol/src/openai_models.rs index 41275e6a6..70d241677 100644 --- a/codex-rs/protocol/src/openai_models.rs +++ b/codex-rs/protocol/src/openai_models.rs @@ -115,6 +115,13 @@ pub struct ModelAvailabilityNux { pub message: String, } +#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq, Eq)] +pub struct ModelServiceTier { + pub id: String, + pub name: String, + pub description: String, +} + /// Metadata describing a Codex-supported model. #[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq)] pub struct ModelPreset { @@ -133,9 +140,12 @@ pub struct ModelPreset { /// Whether this model supports personality-specific instructions. #[serde(default)] pub supports_personality: bool, - /// Additional speed tiers this model can run with beyond the standard path. + /// Deprecated: use `service_tiers` instead. #[serde(default)] pub additional_speed_tiers: Vec, + /// Service tiers this model can run with. + #[serde(default)] + pub service_tiers: Vec, /// Whether this is the default model for new users. pub is_default: bool, /// recommended upgrade model @@ -258,6 +268,8 @@ pub struct ModelInfo { pub priority: i32, #[serde(default)] pub additional_speed_tiers: Vec, + #[serde(default)] + pub service_tiers: Vec, pub availability_nux: Option, pub upgrade: Option, pub base_instructions: String, @@ -442,6 +454,7 @@ impl From for ModelPreset { supported_reasoning_efforts: info.supported_reasoning_levels.clone(), supports_personality, additional_speed_tiers: info.additional_speed_tiers, + service_tiers: info.service_tiers, is_default: false, // default is the highest priority available model upgrade: info.upgrade.as_ref().map(|upgrade| ModelUpgrade { id: upgrade.model.clone(), @@ -464,9 +477,13 @@ impl From for ModelPreset { impl ModelPreset { pub fn supports_fast_mode(&self) -> bool { - self.additional_speed_tiers + self.service_tiers .iter() - .any(|tier| tier == SPEED_TIER_FAST) + .any(|tier| tier.id == SPEED_TIER_FAST) + || self + .additional_speed_tiers + .iter() + .any(|tier| tier == SPEED_TIER_FAST) } /// Filter models based on authentication mode. @@ -548,6 +565,7 @@ mod tests { supported_in_api: true, priority: 1, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), availability_nux: None, upgrade: None, base_instructions: "base".to_string(), @@ -818,6 +836,7 @@ mod tests { message: "Try Spark.".to_string(), }), additional_speed_tiers: vec![SPEED_TIER_FAST.to_string()], + service_tiers: Vec::new(), ..test_model(/*spec*/ None) }); @@ -829,4 +848,18 @@ mod tests { ); assert!(preset.supports_fast_mode()); } + + #[test] + fn model_preset_supports_fast_mode_from_service_tiers() { + let preset = ModelPreset::from(ModelInfo { + service_tiers: vec![ModelServiceTier { + id: SPEED_TIER_FAST.to_string(), + name: "Fast".to_string(), + description: "Priority processing.".to_string(), + }], + ..test_model(/*spec*/ None) + }); + + assert!(preset.supports_fast_mode()); + } } diff --git a/codex-rs/tools/src/agent_tool_tests.rs b/codex-rs/tools/src/agent_tool_tests.rs index 3157cfc54..38391cdbb 100644 --- a/codex-rs/tools/src/agent_tool_tests.rs +++ b/codex-rs/tools/src/agent_tool_tests.rs @@ -20,6 +20,7 @@ fn model_preset(id: &str, show_in_picker: bool) -> ModelPreset { }], supports_personality: false, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), is_default: false, upgrade: None, show_in_picker, diff --git a/codex-rs/tui/src/app_server_session.rs b/codex-rs/tui/src/app_server_session.rs index 449da8e21..d3cad503d 100644 --- a/codex-rs/tui/src/app_server_session.rs +++ b/codex-rs/tui/src/app_server_session.rs @@ -111,6 +111,7 @@ use codex_protocol::models::PermissionProfile; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ModelAvailabilityNux; use codex_protocol::openai_models::ModelPreset; +use codex_protocol::openai_models::ModelServiceTier; use codex_protocol::openai_models::ModelUpgrade; use codex_protocol::openai_models::ReasoningEffortPreset; use codex_utils_absolute_path::AbsolutePathBuf; @@ -1046,6 +1047,15 @@ fn model_preset_from_api_model(model: ApiModel) -> ModelPreset { .collect(), supports_personality: model.supports_personality, additional_speed_tiers: model.additional_speed_tiers, + service_tiers: model + .service_tiers + .into_iter() + .map(|service_tier| ModelServiceTier { + id: service_tier.id, + name: service_tier.name, + description: service_tier.description, + }) + .collect(), is_default: model.is_default, upgrade, show_in_picker: !model.hidden, diff --git a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs index a2c4f11d5..00f1b3044 100644 --- a/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs +++ b/codex-rs/tui/src/chatwidget/tests/popups_and_settings.rs @@ -2453,6 +2453,7 @@ async fn model_picker_hides_show_in_picker_false_models_from_cache() { }], supports_personality: false, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), is_default: false, upgrade: None, show_in_picker, @@ -2673,6 +2674,7 @@ async fn single_reasoning_option_skips_selection() { supported_reasoning_efforts: single_effort, supports_personality: false, additional_speed_tiers: Vec::new(), + service_tiers: Vec::new(), is_default: false, upgrade: None, show_in_picker: true,