mirror of
https://github.com/openai/codex.git
synced 2026-10-17 17:08:55 +00:00
Allow history and notes without experimental context capability (#47625)
## Why Enabling `features.token_budget.use_history_notes_extension`, explicitly or through model defaults, rejected session startup when the model lacked `supports_experimental_context`, even with experimental mode disabled. ## What changed Remove that startup restriction so token-budget history and notes can be used independently of the model's experimental context capability. ## Testing Extend core tests to cover explicit configuration and model defaults on startup and cold resume, checking `new_context`, token-budget contexts, and `history_ingest_requested` metadata. Add app-server coverage for native history and notes tools and context-window hints without experimental context capability. GitOrigin-RevId: 6157479aeb75ffd785b8d4b2f48b11c6f46e8f35
This commit is contained in:
@@ -30,13 +30,15 @@ const THREAD_HINT: &str =
|
||||
"Recent notes (up to 5, most-recent first):\n- /root/notes/latest.md (2 lines, 14 UTF-8 bytes)";
|
||||
const BRIDGE_HINT: &str = "unstructured notes/thread_hint fixture result";
|
||||
|
||||
#[test_case(true, 200, THREAD_HINT; "native_hint")]
|
||||
#[test_case(true, 200, ""; "no_notes")]
|
||||
#[test_case(true, 503, THREAD_HINT; "native_failure_does_not_use_bridge")]
|
||||
#[test_case(false, 200, THREAD_HINT; "bridge_hint")]
|
||||
#[test_case(true, true, 200, THREAD_HINT; "native_hint")]
|
||||
#[test_case(true, false, 200, THREAD_HINT; "native_hint_without_experimental_capability")]
|
||||
#[test_case(true, true, 200, ""; "no_notes")]
|
||||
#[test_case(true, true, 503, THREAD_HINT; "native_failure_does_not_use_bridge")]
|
||||
#[test_case(false, true, 200, THREAD_HINT; "bridge_hint")]
|
||||
#[tokio::test]
|
||||
async fn app_server_uses_configured_notes_backend_for_context_window_hints(
|
||||
use_history_notes_extension: bool,
|
||||
supports_experimental_context: bool,
|
||||
hint_status: u16,
|
||||
hint_text: &str,
|
||||
) -> Result<()> {
|
||||
@@ -115,7 +117,7 @@ async fn app_server_uses_configured_notes_backend_for_context_window_hints(
|
||||
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.supports_experimental_context = supports_experimental_context;
|
||||
let catalog_path = codex_home.path().join("models.json");
|
||||
std::fs::write(
|
||||
&catalog_path,
|
||||
@@ -132,7 +134,7 @@ async fn app_server_uses_configured_notes_backend_for_context_window_hints(
|
||||
.with_provider_base_url(&format!("{}/backend-api/codex", server.uri()))
|
||||
.with_provider_config("supports_websockets = false\nrequires_openai_auth = true")
|
||||
.with_extra_config(&format!(
|
||||
"[features.token_budget]\nenabled = true\nuse_history_notes_extension = {use_history_notes_extension}\n\n[mcp_servers.notes]\nurl = \"{}/mcp\"\nstartup_timeout_sec = 10\n",
|
||||
"[features.context_management]\nexperimental_mode = false\n\n[features.token_budget]\nenabled = true\nuse_history_notes_extension = {use_history_notes_extension}\n\n[mcp_servers.notes]\nurl = \"{}/mcp\"\nstartup_timeout_sec = 10\n",
|
||||
server.uri(),
|
||||
))
|
||||
.write(codex_home.path())?;
|
||||
|
||||
@@ -726,16 +726,6 @@ 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(|| {
|
||||
|
||||
@@ -364,14 +364,14 @@ 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, true, false; "explicit_history_notes_without_experimental_capability")]
|
||||
#[test_case(false, true, true; "explicit_history_notes_with_experimental_capability")]
|
||||
#[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, true, false; "model_default_history_notes_without_experimental_capability")]
|
||||
#[test_case(true, true, true; "model_default_history_notes_with_experimental_capability")]
|
||||
#[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(
|
||||
async fn token_budget_history_notes_can_be_enabled_without_experimental_context(
|
||||
use_model_defaults: bool,
|
||||
use_history_notes: bool,
|
||||
supports_context: bool,
|
||||
@@ -380,7 +380,7 @@ async fn token_budget_history_notes_requires_capable_starting_model(
|
||||
|
||||
let server = start_mock_server().await;
|
||||
let backend_url = format!("{}/backend-api/codex", server.uri());
|
||||
let result = test_codex()
|
||||
let mut builder = test_codex()
|
||||
.with_auth(CodexAuth::from_external_chatgpt_tokens(
|
||||
"header.e30.signature",
|
||||
"account-123",
|
||||
@@ -411,30 +411,50 @@ async fn token_budget_history_notes_requires_capable_starting_model(
|
||||
.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;
|
||||
});
|
||||
let test = builder.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;
|
||||
let response = mount_sse_sequence(
|
||||
&server,
|
||||
vec![sse_completed("resp-1"), sse_completed("resp-2")],
|
||||
)
|
||||
.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);
|
||||
// Builder overrides are consumed by the initial build. Reapply the model and
|
||||
// provider settings so the cold resume exercises the same activation defaults.
|
||||
let initial_config = test.config.clone();
|
||||
let resumed = builder
|
||||
.with_config(move |config| {
|
||||
config.model = initial_config.model;
|
||||
config.model_catalog = initial_config.model_catalog;
|
||||
config.model_provider = initial_config.model_provider;
|
||||
config.model_context_window = initial_config.model_context_window;
|
||||
})
|
||||
.restart(&server, &test)
|
||||
.await?;
|
||||
resumed
|
||||
.submit_turn("inspect token-budget activation after restart")
|
||||
.await?;
|
||||
|
||||
let requests = response.requests();
|
||||
assert_eq!(requests.len(), 2);
|
||||
for request in requests {
|
||||
assert!(
|
||||
tool_names(&request)
|
||||
.iter()
|
||||
.any(|name| name == "new_context")
|
||||
);
|
||||
assert_eq!(token_budget_contexts(&request).len(), 1);
|
||||
let turn_metadata: Value = serde_json::from_str(
|
||||
&request
|
||||
.header("x-codex-turn-metadata")
|
||||
.expect("request should include turn metadata"),
|
||||
)?;
|
||||
assert_eq!(
|
||||
turn_metadata["history_ingest_requested"].as_bool(),
|
||||
use_history_notes.then_some(true)
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user