mirror of
https://github.com/openai/codex.git
synced 2026-09-14 11:57:03 +00:00
Reject token-budget history notes for unsupported starting models (#44883)
## What changed After resolving startup configuration and model defaults, reject `features.token_budget.use_history_notes_extension` when the starting model lacks `supports_experimental_context`. Return an error directing users to disable the option or select a compatible model. ## Testing Add startup coverage for explicit configuration and model defaults, verifying rejection for unsupported models and successful activation for supported models and standalone token budgets. Update history-notes test fixtures to declare experimental context support. GitOrigin-RevId: abf1a024efc5acf97cfc858fbb93821363769dc0
This commit is contained in:
@@ -112,8 +112,20 @@ async fn app_server_uses_configured_notes_backend_for_context_window_hints(
|
||||
.await;
|
||||
|
||||
let codex_home = TempDir::new()?;
|
||||
let config = load_default_config_for_test(&codex_home).await;
|
||||
let mut model = codex_core::test_support::construct_model_info_offline("mock-model", &config);
|
||||
model.supports_experimental_context = true;
|
||||
let catalog_path = codex_home.path().join("models.json");
|
||||
std::fs::write(
|
||||
&catalog_path,
|
||||
serde_json::to_vec(&json!({"models": [model]}))?,
|
||||
)?;
|
||||
MockResponsesConfig::new(&server.uri())
|
||||
.with_root_config(&format!("chatgpt_base_url = \"{}\"", server.uri()))
|
||||
.with_root_config(&format!(
|
||||
"model_catalog_json = {}",
|
||||
serde_json::to_string(&catalog_path)?
|
||||
))
|
||||
.with_model_provider("openai-custom")
|
||||
.with_provider_name("OpenAI")
|
||||
.with_provider_base_url(&format!("{}/backend-api/codex", server.uri()))
|
||||
@@ -321,6 +333,7 @@ async fn history_notes_and_async_message_emit_control_tool_analytics() -> Result
|
||||
let codex_home = TempDir::new()?;
|
||||
let config = load_default_config_for_test(&codex_home).await;
|
||||
let mut model = codex_core::test_support::construct_model_info_offline("mock-model", &config);
|
||||
model.supports_experimental_context = true;
|
||||
model
|
||||
.experimental_supported_tools
|
||||
.push("send_user_message_async".to_string());
|
||||
|
||||
@@ -707,6 +707,16 @@ impl Session {
|
||||
&model_info,
|
||||
)?;
|
||||
token_budget::apply_model_defaults(Arc::make_mut(&mut config), &model_info);
|
||||
if config
|
||||
.token_budget
|
||||
.as_ref()
|
||||
.is_some_and(|token_budget| token_budget.use_history_notes_extension)
|
||||
&& !model_info.supports_experimental_context
|
||||
{
|
||||
return Err(CodexErr::InvalidRequest(format!(
|
||||
"features.token_budget.use_history_notes_extension is not supported by model `{model}`; disable it or select a model that supports experimental context"
|
||||
)));
|
||||
}
|
||||
}
|
||||
let configured_config = Arc::clone(&config);
|
||||
let multi_agent_version = config.multi_agent_version_override().or_else(|| {
|
||||
|
||||
@@ -308,6 +308,7 @@ async fn guardian_review_compacts_with_summary_despite_parent_token_budget(
|
||||
let mut builder = test_codex()
|
||||
.with_model_info_override("gpt-5.5", |model| {
|
||||
model.auto_review_model_override = Some(model.slug.clone());
|
||||
model.supports_experimental_context = true;
|
||||
model
|
||||
.model_messages
|
||||
.as_mut()
|
||||
|
||||
@@ -364,6 +364,80 @@ async fn experimental_context_requires_capable_model_and_codex_backend(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(false, true, false; "explicit_history_notes_unsupported")]
|
||||
#[test_case(false, true, true; "explicit_history_notes_supported")]
|
||||
#[test_case(false, false, false; "explicit_standalone_token_budget")]
|
||||
#[test_case(true, true, false; "model_default_history_notes_unsupported")]
|
||||
#[test_case(true, true, true; "model_default_history_notes_supported")]
|
||||
#[test_case(true, false, false; "model_default_standalone_token_budget")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn token_budget_history_notes_requires_capable_starting_model(
|
||||
use_model_defaults: bool,
|
||||
use_history_notes: bool,
|
||||
supports_context: bool,
|
||||
) -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let backend_url = format!("{}/backend-api/codex", server.uri());
|
||||
let result = test_codex()
|
||||
.with_auth(CodexAuth::from_external_chatgpt_tokens(
|
||||
"header.e30.signature",
|
||||
"account-123",
|
||||
Some("plus"),
|
||||
)?)
|
||||
.with_pre_build_hook(move |home| {
|
||||
let config = if use_model_defaults {
|
||||
"[features.context_management]\nexperimental_mode = false\n".to_string()
|
||||
} else {
|
||||
format!(
|
||||
"[features.context_management]\nexperimental_mode = false\n[features.token_budget]\nenabled = true\nuse_history_notes_extension = {use_history_notes}\n"
|
||||
)
|
||||
};
|
||||
std::fs::write(home.join("config.toml"), config)
|
||||
.expect("write token-budget configuration");
|
||||
})
|
||||
.with_model_info_override("gpt-5.2", move |model_info| {
|
||||
model_info.supports_experimental_context = supports_context;
|
||||
let mut defaults = model_token_budget_config();
|
||||
defaults.enabled = use_model_defaults;
|
||||
defaults.use_history_notes_extension = use_history_notes;
|
||||
model_info
|
||||
.model_messages
|
||||
.as_mut()
|
||||
.expect("bundled model should have model messages")
|
||||
.token_budget = Some(defaults);
|
||||
})
|
||||
.with_config(move |config| {
|
||||
config.model_provider.base_url = Some(backend_url);
|
||||
config.model_context_window = Some(CONFIGURED_CONTEXT_WINDOW);
|
||||
})
|
||||
.build_with_auto_env(&server)
|
||||
.await;
|
||||
|
||||
if use_history_notes && !supports_context {
|
||||
let error = result
|
||||
.err()
|
||||
.expect("unsupported history/notes must fail at startup");
|
||||
assert!(error.to_string().contains(
|
||||
"features.token_budget.use_history_notes_extension is not supported by model `gpt-5.2`"
|
||||
));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let test = result?;
|
||||
let response = mount_sse_once(&server, sse_completed("resp-1")).await;
|
||||
test.submit_turn("inspect token-budget activation").await?;
|
||||
let request = response.single_request();
|
||||
assert!(
|
||||
tool_names(&request)
|
||||
.iter()
|
||||
.any(|name| name == "new_context")
|
||||
);
|
||||
assert_eq!(token_budget_contexts(&request).len(), 1);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn token_budget_uses_model_message_defaults() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -376,6 +450,7 @@ async fn token_budget_uses_model_message_defaults() -> Result<()> {
|
||||
let expected_guidance = model_defaults.guidance_message.clone();
|
||||
let test = test_codex()
|
||||
.with_model_info_override("gpt-5.2", move |model_info| {
|
||||
model_info.supports_experimental_context = true;
|
||||
model_info
|
||||
.model_messages
|
||||
.as_mut()
|
||||
|
||||
@@ -64,6 +64,9 @@ async fn history_images_reach_the_next_model_request() -> Result<(), Box<dyn std
|
||||
);
|
||||
let test = test_codex()
|
||||
.with_auth(auth)
|
||||
.with_model_info_override("gpt-5.5", |model_info| {
|
||||
model_info.supports_experimental_context = true;
|
||||
})
|
||||
.with_extensions(Arc::new(extensions.build()))
|
||||
.with_config(|config| {
|
||||
config.model_provider.name = "OpenAI".to_string();
|
||||
|
||||
Reference in New Issue
Block a user