mirror of
https://github.com/openai/codex.git
synced 2026-08-23 13:09:46 +00:00
Warn when configured service tiers are unsupported (#31284)
## Why Codex currently omits a configured `service_tier` when the selected model's catalog entry does not advertise support for it. That fallback is silent, so users can unknowingly send requests at the default tier instead. This makes cases such as #26604 difficult to diagnose. ## What changed - Emit the shared core `Warning` event during session startup when a configured service tier will be omitted because the initial model does not advertise support for it. - Do not warn on later model or service-tier changes, which keeps warning emission stateless and avoids client-specific handling. - Keep the existing request filtering behavior unchanged. ## Validation - `just test -p codex-core unsupported_service_tier` - `just test -p codex-core unsupported_configured_service_tier_warns_at_session_start` ### Manual validation - Launched the real TUI with the bundled catalog, where `gpt-5.5` advertises the `priority` tier but not `flex`, and configured `service_tier = "flex"`. - Confirmed the unsupported-tier warning appeared exactly once during startup. - Submitted two turns through a local Responses API SSE stub; both received mock replies and neither emitted another warning. - Inspected both captured `/v1/responses` request bodies and confirmed that neither contained a `service_tier` field. - Repeated the two-turn TUI flow against the live Responses API; both turns completed and the warning was not repeated.
This commit is contained in:
@@ -629,11 +629,14 @@ impl Codex {
|
||||
developer_instructions: None,
|
||||
},
|
||||
};
|
||||
let service_tier = get_service_tier(
|
||||
config.service_tier.clone(),
|
||||
config.features.enabled(Feature::FastMode),
|
||||
let fast_mode_enabled = config.features.enabled(Feature::FastMode);
|
||||
let initial_service_tier_warning = unsupported_service_tier_warning(
|
||||
config.service_tier.as_deref(),
|
||||
fast_mode_enabled,
|
||||
&model_info,
|
||||
);
|
||||
let service_tier =
|
||||
get_service_tier(config.service_tier.clone(), fast_mode_enabled, &model_info);
|
||||
let session_configuration = SessionConfiguration {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
@@ -706,6 +709,14 @@ impl Codex {
|
||||
error!("Failed to create session: {e:#}");
|
||||
map_session_init_error(&e, &config.codex_home)
|
||||
})?;
|
||||
if let Some(message) = initial_service_tier_warning {
|
||||
session
|
||||
.send_event_raw(Event {
|
||||
id: INITIAL_SUBMIT_ID.to_owned(),
|
||||
msg: EventMsg::Warning(WarningEvent { message }),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
let thread_id = session.thread_id;
|
||||
|
||||
// This task will run until Op::Shutdown is received.
|
||||
@@ -908,6 +919,22 @@ fn get_service_tier(
|
||||
})
|
||||
}
|
||||
|
||||
fn unsupported_service_tier_warning(
|
||||
configured_service_tier: Option<&str>,
|
||||
fast_mode_enabled: bool,
|
||||
model_info: &ModelInfo,
|
||||
) -> Option<String> {
|
||||
let service_tier = configured_service_tier.filter(|service_tier| {
|
||||
fast_mode_enabled
|
||||
&& *service_tier != SERVICE_TIER_DEFAULT_REQUEST_VALUE
|
||||
&& !model_info.supports_service_tier(service_tier)
|
||||
})?;
|
||||
Some(format!(
|
||||
"Configured service tier `{service_tier}` is not advertised as supported for model `{}` and will be omitted from requests.",
|
||||
model_info.slug
|
||||
))
|
||||
}
|
||||
|
||||
fn session_permission_profile_state_from_config(
|
||||
config: &Config,
|
||||
) -> CodexResult<PermissionProfileState> {
|
||||
|
||||
@@ -387,6 +387,46 @@ async fn unsupported_service_tier_is_omitted_from_http_turn() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn unsupported_configured_service_tier_warns_at_session_start() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let model_slug = "test-no-tier-model";
|
||||
let model = test_model_info(
|
||||
model_slug,
|
||||
model_slug,
|
||||
"no service tiers",
|
||||
default_input_modalities(),
|
||||
);
|
||||
let mut builder = test_codex()
|
||||
.with_model(model_slug)
|
||||
.with_config(move |config| {
|
||||
config.service_tier = Some(ServiceTier::Flex.request_value().to_string());
|
||||
config.model_catalog = Some(ModelsResponse {
|
||||
models: vec![model],
|
||||
});
|
||||
});
|
||||
let test = builder.build(&server).await?;
|
||||
|
||||
let warning = wait_for_event(&test.codex, |event| {
|
||||
matches!(
|
||||
event,
|
||||
EventMsg::Warning(warning)
|
||||
if warning.message.contains("will be omitted from requests")
|
||||
)
|
||||
})
|
||||
.await;
|
||||
let EventMsg::Warning(warning) = warning else {
|
||||
unreachable!("wait_for_event matched a warning")
|
||||
};
|
||||
assert_eq!(
|
||||
warning.message,
|
||||
"Configured service tier `flex` is not advertised as supported for model `test-no-tier-model` and will be omitted from requests."
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn default_service_tier_override_is_omitted_from_http_turn() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
Reference in New Issue
Block a user