mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Honor client-resolved service tier defaults (#23537)
## 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`
This commit is contained in:
1 parent
0e9d222178
commit
370b13afc9
54 files changed
+682
-192
No files matched your search
@@ -17,6 +17,7 @@ use ts_rs::TS;
|
||||
|
||||
use crate::config_types::Personality;
|
||||
use crate::config_types::ReasoningSummary;
|
||||
use crate::config_types::SERVICE_TIER_DEFAULT_REQUEST_VALUE;
|
||||
use crate::config_types::ServiceTier;
|
||||
use crate::config_types::Verbosity;
|
||||
|
||||
@@ -147,6 +148,9 @@ pub struct ModelPreset {
|
||||
/// Service tiers this model can run with.
|
||||
#[serde(default)]
|
||||
pub service_tiers: Vec<ModelServiceTier>,
|
||||
/// Catalog default service tier id for this model.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default_service_tier: Option<String>,
|
||||
/// Whether this is the default model for new users.
|
||||
pub is_default: bool,
|
||||
/// recommended upgrade model
|
||||
@@ -270,6 +274,8 @@ pub struct ModelInfo {
|
||||
pub additional_speed_tiers: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub service_tiers: Vec<ModelServiceTier>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default_service_tier: Option<String>,
|
||||
pub availability_nux: Option<ModelAvailabilityNux>,
|
||||
pub upgrade: Option<ModelInfoUpgrade>,
|
||||
pub base_instructions: String,
|
||||
@@ -455,6 +461,7 @@ impl From<ModelInfo> for ModelPreset {
|
||||
supports_personality,
|
||||
additional_speed_tiers: info.additional_speed_tiers,
|
||||
service_tiers: info.service_tiers,
|
||||
default_service_tier: info.default_service_tier,
|
||||
is_default: false, // default is the highest priority available model
|
||||
upgrade: info.upgrade.as_ref().map(|upgrade| ModelUpgrade {
|
||||
id: upgrade.model.clone(),
|
||||
@@ -493,6 +500,13 @@ impl ModelInfo {
|
||||
.iter()
|
||||
.any(|tier| tier.id == service_tier)
|
||||
}
|
||||
|
||||
pub fn service_tier_for_request(&self, service_tier: Option<String>) -> Option<String> {
|
||||
service_tier.filter(|service_tier| {
|
||||
service_tier != SERVICE_TIER_DEFAULT_REQUEST_VALUE
|
||||
&& self.supports_service_tier(service_tier)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ModelPreset {
|
||||
@@ -576,6 +590,7 @@ mod tests {
|
||||
priority: 1,
|
||||
additional_speed_tiers: Vec::new(),
|
||||
service_tiers: Vec::new(),
|
||||
default_service_tier: None,
|
||||
availability_nux: None,
|
||||
upgrade: None,
|
||||
base_instructions: "base".to_string(),
|
||||
@@ -846,6 +861,7 @@ mod tests {
|
||||
message: "Try Spark.".to_string(),
|
||||
}),
|
||||
additional_speed_tiers: vec![SPEED_TIER_FAST.to_string()],
|
||||
default_service_tier: Some(ServiceTier::Fast.request_value().to_string()),
|
||||
service_tiers: Vec::new(),
|
||||
..test_model(/*spec*/ None)
|
||||
});
|
||||
@@ -857,6 +873,10 @@ mod tests {
|
||||
})
|
||||
);
|
||||
assert!(preset.supports_fast_mode());
|
||||
assert_eq!(
|
||||
preset.default_service_tier,
|
||||
Some(ServiceTier::Fast.request_value().to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -872,4 +892,60 @@ mod tests {
|
||||
|
||||
assert!(preset.supports_fast_mode());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_tier_for_request_omits_explicit_default_tier() {
|
||||
let model = ModelInfo {
|
||||
default_service_tier: Some(ServiceTier::Fast.request_value().to_string()),
|
||||
service_tiers: vec![ModelServiceTier {
|
||||
id: ServiceTier::Fast.request_value().to_string(),
|
||||
name: "Fast".to_string(),
|
||||
description: "Priority processing.".to_string(),
|
||||
}],
|
||||
..test_model(/*spec*/ None)
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
model.service_tier_for_request(Some(SERVICE_TIER_DEFAULT_REQUEST_VALUE.to_string())),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_tier_for_request_filters_unsupported_tiers() {
|
||||
let model = ModelInfo {
|
||||
default_service_tier: Some(ServiceTier::Fast.request_value().to_string()),
|
||||
service_tiers: vec![ModelServiceTier {
|
||||
id: ServiceTier::Fast.request_value().to_string(),
|
||||
name: "Fast".to_string(),
|
||||
description: "Priority processing.".to_string(),
|
||||
}],
|
||||
..test_model(/*spec*/ None)
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
model.service_tier_for_request(Some(ServiceTier::Fast.request_value().to_string())),
|
||||
Some(ServiceTier::Fast.request_value().to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
model.service_tier_for_request(Some("unsupported".to_string())),
|
||||
None
|
||||
);
|
||||
assert_eq!(model.service_tier_for_request(/*service_tier*/ None), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn service_tier_for_request_does_not_apply_catalog_default() {
|
||||
let model = ModelInfo {
|
||||
default_service_tier: Some(ServiceTier::Fast.request_value().to_string()),
|
||||
service_tiers: vec![ModelServiceTier {
|
||||
id: ServiceTier::Fast.request_value().to_string(),
|
||||
name: "Fast".to_string(),
|
||||
description: "Priority processing.".to_string(),
|
||||
}],
|
||||
..test_model(/*spec*/ None)
|
||||
};
|
||||
|
||||
assert_eq!(model.service_tier_for_request(/*service_tier*/ None), None);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user