chore: warn when Code Mode lacks model metadata (#29490)

This commit is contained in:
Celia Chen
2026-06-22 18:15:11 -07:00
committed by GitHub
Unverified
parent b16d2858f5
commit 3310fc8ae5
10 changed files with 275 additions and 12 deletions
@@ -0,0 +1,26 @@
use codex_features::Feature;
use codex_features::Features;
use codex_protocol::openai_models::ModelInfo;
pub(super) fn unsupported_code_mode_warning(
model_info: &ModelInfo,
features: &Features,
) -> Option<String> {
let code_mode_enabled =
features.enabled(Feature::CodeMode) || features.enabled(Feature::CodeModeOnly);
if !code_mode_enabled
|| model_info.tool_mode.is_some()
|| model_info.used_fallback_model_metadata
{
return None;
}
let model = &model_info.slug;
Some(format!(
"Code Mode is enabled in configuration, but model `{model}` does not advertise Code Mode support. This may degrade model performance. Disable `features.code_mode` and `features.code_mode_only`, or select a model whose metadata enables Code Mode."
))
}
#[cfg(test)]
#[path = "code_mode_warning_tests.rs"]
mod tests;
@@ -0,0 +1,70 @@
use super::unsupported_code_mode_warning;
use codex_features::Feature;
use codex_features::Features;
use codex_models_manager::model_info::model_info_from_slug;
use codex_protocol::openai_models::ModelInfo;
use codex_protocol::openai_models::ToolMode;
use pretty_assertions::assert_eq;
const MODEL_SLUG: &str = "test-model";
fn known_model_info() -> ModelInfo {
ModelInfo {
used_fallback_model_metadata: false,
..model_info_from_slug(MODEL_SLUG)
}
}
#[test]
fn warns_when_code_mode_is_enabled_without_model_selector() {
let mut features = Features::with_defaults();
features.enable(Feature::CodeMode);
assert_eq!(
unsupported_code_mode_warning(&known_model_info(), &features),
Some(format!(
"Code Mode is enabled in configuration, but model `{MODEL_SLUG}` does not advertise Code Mode support. This may degrade model performance. Disable `features.code_mode` and `features.code_mode_only`, or select a model whose metadata enables Code Mode."
))
);
}
#[test]
fn warns_when_code_mode_only_is_enabled_without_model_selector() {
let mut features = Features::with_defaults();
features.enable(Feature::CodeModeOnly);
assert!(unsupported_code_mode_warning(&known_model_info(), &features).is_some());
}
#[test]
fn does_not_warn_when_code_mode_is_disabled() {
assert_eq!(
unsupported_code_mode_warning(&known_model_info(), &Features::with_defaults()),
None
);
}
#[test]
fn does_not_warn_when_model_has_tool_mode_selector() {
let mut features = Features::with_defaults();
features.enable(Feature::CodeModeOnly);
for tool_mode in [ToolMode::Direct, ToolMode::CodeMode, ToolMode::CodeModeOnly] {
let model_info = ModelInfo {
tool_mode: Some(tool_mode),
..known_model_info()
};
assert_eq!(unsupported_code_mode_warning(&model_info, &features), None);
}
}
#[test]
fn fallback_metadata_only_uses_existing_warning() {
let mut features = Features::with_defaults();
features.enable(Feature::CodeMode);
assert_eq!(
unsupported_code_mode_warning(&model_info_from_slug(MODEL_SLUG), &features),
None
);
}
+2 -2
View File
@@ -218,7 +218,7 @@ pub(super) async fn user_input_or_turn_inner(
})
.await;
}
sess.maybe_emit_unknown_model_warning_for_turn(current_context.as_ref())
sess.maybe_emit_model_warnings_for_turn(current_context.as_ref())
.await;
match sess
.steer_input(
@@ -673,7 +673,7 @@ pub async fn review(
review_request: ReviewRequest,
) {
let turn_context = sess.new_default_turn_with_sub_id(sub_id.clone()).await;
sess.maybe_emit_unknown_model_warning_for_turn(turn_context.as_ref())
sess.maybe_emit_model_warnings_for_turn(turn_context.as_ref())
.await;
sess.refresh_mcp_servers_if_requested(&turn_context, Some(sess.mcp_elicitation_reviewer()))
.await;
+1 -1
View File
@@ -94,7 +94,7 @@ impl Session {
input,
));
}
self.maybe_emit_unknown_model_warning_for_turn(turn_context.as_ref())
self.maybe_emit_model_warnings_for_turn(turn_context.as_ref())
.await;
if self.input_queue.has_trigger_turn_mailbox_items().await {
self.clear_reserved_idle_turn(&turn_state).await;
+2
View File
@@ -208,6 +208,7 @@ use codex_protocol::error::Result as CodexResult;
#[cfg(test)]
use codex_protocol::exec_output::StreamOutput;
mod code_mode_warning;
mod config_lock;
mod handlers;
mod inject;
@@ -225,6 +226,7 @@ mod token_budget;
pub(crate) mod turn;
pub(crate) mod turn_context;
mod world_state;
use self::code_mode_warning::unsupported_code_mode_warning;
use self::config_lock::export_config_lock_if_configured;
use self::config_lock::validate_config_lock_if_configured;
#[cfg(test)]
+8 -1
View File
@@ -797,7 +797,7 @@ impl Session {
turn_context
}
pub(crate) async fn maybe_emit_unknown_model_warning_for_turn(&self, tc: &TurnContext) {
pub(crate) async fn maybe_emit_model_warnings_for_turn(&self, tc: &TurnContext) {
if tc.model_info.used_fallback_model_metadata {
self.send_event(
tc,
@@ -810,6 +810,13 @@ impl Session {
)
.await;
}
if let Some(message) =
unsupported_code_mode_warning(&tc.model_info, tc.config.features.get())
{
self.send_event(tc, EventMsg::Warning(WarningEvent { message }))
.await;
}
}
pub(crate) async fn new_default_turn(&self) -> Arc<TurnContext> {