mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
## Why Model catalog responses can now advertise a nullable `default_service_tier` for each model. Codex needs to preserve three distinct states all the way from config/app-server inputs to inference: - no explicit service tier, so the client may apply the current model catalog default when FastMode is enabled - explicit `default`, meaning the user intentionally wants standard routing - explicit catalog tier ids such as `priority`, `flex`, or future tiers Keeping those states distinct prevents the UI from showing one tier while core sends another, especially after model switches or app-server `thread/start` / `turn/start` updates. ## What Changed - Plumbed `default_service_tier` through model catalog protocol types, app-server model responses, generated schemas, model cache fixtures, and provider/model-manager conversions. - Added the request-only `default` service tier sentinel and normalized legacy config spelling so `fast` in `config.toml` still materializes as the runtime/request id `priority`. - Moved catalog default resolution to the TUI/client side, including recomputing the effective service tier when model/FastMode-dependent surfaces change. - Updated app-server thread lifecycle config construction so `serviceTier: null` preserves explicit standard-routing intent by mapping to `default` instead of internal `None`. - Kept core responsible for validating explicit tiers against the current model and stripping `default` before `/v1/responses`, without applying catalog defaults itself. ## Validation - `CARGO_INCREMENTAL=0 cargo build -p codex-cli` - `CARGO_INCREMENTAL=0 cargo test -p codex-app-server model_list` - `cargo test -p codex-tui service_tier` - `cargo test -p codex-protocol service_tier_for_request` - `cargo test -p codex-core get_service_tier` - `RUST_MIN_STACK=8388608 CARGO_INCREMENTAL=0 cargo test -p codex-core service_tier`
180 lines
6.9 KiB
Rust
180 lines
6.9 KiB
Rust
use codex_model_provider_info::AMAZON_BEDROCK_GPT_5_4_MODEL_ID;
|
|
use codex_models_manager::model_info::BASE_INSTRUCTIONS;
|
|
use codex_protocol::config_types::ReasoningSummary;
|
|
use codex_protocol::config_types::ServiceTier;
|
|
use codex_protocol::config_types::Verbosity;
|
|
use codex_protocol::openai_models::ApplyPatchToolType;
|
|
use codex_protocol::openai_models::ConfigShellToolType;
|
|
use codex_protocol::openai_models::InputModality;
|
|
use codex_protocol::openai_models::ModelInfo;
|
|
use codex_protocol::openai_models::ModelServiceTier;
|
|
use codex_protocol::openai_models::ModelVisibility;
|
|
use codex_protocol::openai_models::ModelsResponse;
|
|
use codex_protocol::openai_models::ReasoningEffort;
|
|
use codex_protocol::openai_models::ReasoningEffortPreset;
|
|
use codex_protocol::openai_models::SPEED_TIER_FAST;
|
|
use codex_protocol::openai_models::TruncationPolicyConfig;
|
|
use codex_protocol::openai_models::WebSearchToolType;
|
|
|
|
const GPT_OSS_CONTEXT_WINDOW: i64 = 128_000;
|
|
const GPT_5_4_CONTEXT_WINDOW: i64 = 272_000;
|
|
const GPT_5_4_MAX_CONTEXT_WINDOW: i64 = 1_000_000;
|
|
|
|
pub(crate) fn static_model_catalog() -> ModelsResponse {
|
|
ModelsResponse {
|
|
models: vec![
|
|
gpt_5_4_cmb_bedrock_model(/*priority*/ 0),
|
|
bedrock_oss_model(
|
|
"openai.gpt-oss-120b",
|
|
"GPT OSS 120B on Bedrock",
|
|
/*priority*/ 1,
|
|
),
|
|
bedrock_oss_model(
|
|
"openai.gpt-oss-20b",
|
|
"GPT OSS 20B on Bedrock",
|
|
/*priority*/ 2,
|
|
),
|
|
],
|
|
}
|
|
}
|
|
|
|
fn gpt_5_4_cmb_bedrock_model(priority: i32) -> ModelInfo {
|
|
ModelInfo {
|
|
slug: AMAZON_BEDROCK_GPT_5_4_MODEL_ID.to_string(),
|
|
display_name: "gpt-5.4".to_string(),
|
|
description: Some("Strong model for everyday coding.".to_string()),
|
|
default_reasoning_level: Some(ReasoningEffort::Medium),
|
|
supported_reasoning_levels: gpt_5_4_cmb_reasoning_levels(),
|
|
shell_type: ConfigShellToolType::ShellCommand,
|
|
visibility: ModelVisibility::List,
|
|
supported_in_api: true,
|
|
priority,
|
|
additional_speed_tiers: Vec::new(),
|
|
service_tiers: vec![ModelServiceTier {
|
|
id: ServiceTier::Fast.request_value().to_string(),
|
|
name: SPEED_TIER_FAST.to_string(),
|
|
description: "Fastest inference with increased plan usage".to_string(),
|
|
}],
|
|
default_service_tier: None,
|
|
availability_nux: None,
|
|
upgrade: None,
|
|
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
|
model_messages: None,
|
|
supports_reasoning_summaries: true,
|
|
default_reasoning_summary: ReasoningSummary::None,
|
|
support_verbosity: true,
|
|
default_verbosity: Some(Verbosity::Medium),
|
|
apply_patch_tool_type: Some(ApplyPatchToolType::Freeform),
|
|
web_search_tool_type: WebSearchToolType::TextAndImage,
|
|
truncation_policy: TruncationPolicyConfig::tokens(/*limit*/ 10_000),
|
|
supports_parallel_tool_calls: true,
|
|
supports_image_detail_original: true,
|
|
context_window: Some(GPT_5_4_CONTEXT_WINDOW),
|
|
max_context_window: Some(GPT_5_4_MAX_CONTEXT_WINDOW),
|
|
auto_compact_token_limit: None,
|
|
effective_context_window_percent: 95,
|
|
experimental_supported_tools: Vec::new(),
|
|
input_modalities: vec![InputModality::Text, InputModality::Image],
|
|
used_fallback_model_metadata: false,
|
|
supports_search_tool: true,
|
|
}
|
|
}
|
|
|
|
fn bedrock_oss_model(slug: &str, display_name: &str, priority: i32) -> ModelInfo {
|
|
ModelInfo {
|
|
slug: slug.to_string(),
|
|
display_name: display_name.to_string(),
|
|
description: Some(display_name.to_string()),
|
|
default_reasoning_level: Some(ReasoningEffort::Medium),
|
|
supported_reasoning_levels: vec![
|
|
reasoning_effort_preset(ReasoningEffort::Low),
|
|
reasoning_effort_preset(ReasoningEffort::Medium),
|
|
reasoning_effort_preset(ReasoningEffort::High),
|
|
],
|
|
shell_type: ConfigShellToolType::ShellCommand,
|
|
visibility: ModelVisibility::List,
|
|
supported_in_api: true,
|
|
priority,
|
|
additional_speed_tiers: Vec::new(),
|
|
service_tiers: Vec::new(),
|
|
default_service_tier: None,
|
|
availability_nux: None,
|
|
upgrade: None,
|
|
base_instructions: BASE_INSTRUCTIONS.to_string(),
|
|
model_messages: None,
|
|
supports_reasoning_summaries: true,
|
|
default_reasoning_summary: ReasoningSummary::None,
|
|
support_verbosity: false,
|
|
default_verbosity: None,
|
|
apply_patch_tool_type: None,
|
|
web_search_tool_type: WebSearchToolType::Text,
|
|
truncation_policy: TruncationPolicyConfig::tokens(/*limit*/ 10_000),
|
|
supports_parallel_tool_calls: true,
|
|
supports_image_detail_original: false,
|
|
context_window: Some(GPT_OSS_CONTEXT_WINDOW),
|
|
max_context_window: Some(GPT_OSS_CONTEXT_WINDOW),
|
|
auto_compact_token_limit: None,
|
|
effective_context_window_percent: 95,
|
|
experimental_supported_tools: Vec::new(),
|
|
input_modalities: vec![InputModality::Text],
|
|
used_fallback_model_metadata: false,
|
|
supports_search_tool: false,
|
|
}
|
|
}
|
|
|
|
fn gpt_5_4_cmb_reasoning_levels() -> Vec<ReasoningEffortPreset> {
|
|
vec![
|
|
reasoning_effort_preset(ReasoningEffort::Minimal),
|
|
reasoning_effort_preset(ReasoningEffort::Low),
|
|
reasoning_effort_preset(ReasoningEffort::Medium),
|
|
reasoning_effort_preset(ReasoningEffort::High),
|
|
]
|
|
}
|
|
|
|
fn reasoning_effort_preset(effort: ReasoningEffort) -> ReasoningEffortPreset {
|
|
ReasoningEffortPreset {
|
|
effort,
|
|
description: match effort {
|
|
ReasoningEffort::None => "No reasoning",
|
|
ReasoningEffort::Minimal => "Minimal reasoning",
|
|
ReasoningEffort::Low => "Fast responses with lighter reasoning",
|
|
ReasoningEffort::Medium => "Balances speed and reasoning depth for everyday tasks",
|
|
ReasoningEffort::High => "Greater reasoning depth for complex problems",
|
|
ReasoningEffort::XHigh => "Extra high reasoning depth for complex problems",
|
|
}
|
|
.to_string(),
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use pretty_assertions::assert_eq;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn catalog_uses_mantle_model_ids_as_slugs() {
|
|
let catalog = static_model_catalog();
|
|
|
|
assert_eq!(catalog.models.len(), 3);
|
|
assert_eq!(catalog.models[0].slug, AMAZON_BEDROCK_GPT_5_4_MODEL_ID);
|
|
assert_eq!(catalog.models[1].slug, "openai.gpt-oss-120b");
|
|
assert_eq!(catalog.models[2].slug, "openai.gpt-oss-20b");
|
|
}
|
|
|
|
#[test]
|
|
fn gpt_5_4_cmb_advertises_only_bedrock_supported_reasoning_levels() {
|
|
let catalog = static_model_catalog();
|
|
let cmb_model = catalog
|
|
.models
|
|
.iter()
|
|
.find(|model| model.slug == AMAZON_BEDROCK_GPT_5_4_MODEL_ID)
|
|
.expect("Bedrock catalog should include GPT-5.4 CMB");
|
|
|
|
assert_eq!(
|
|
cmb_model.supported_reasoning_levels,
|
|
gpt_5_4_cmb_reasoning_levels()
|
|
);
|
|
}
|
|
}
|