[codex] Read buffering metadata from response events (#31064)

## Summary

- read optional faster-model metadata from streamed buffering payloads
- use the buffering payload itself to determine whether buffering UI
should be shown
- retain the existing header value as a compatibility fallback when the
payload omits the field

## Behavior

An object-valued buffering signal now enables the buffering UI. The
response event's faster-model field takes precedence when present, while
omitted fields fall back to existing response metadata. An explicit null
leaves the retry target unset.

## Validation

- `just test -p codex-api`
- `just fix -p codex-api`
- `cargo fmt --all -- --check`
- `git diff --check`
This commit is contained in:
Francis Chalissery
2026-07-05 00:07:53 +00:00
committed by GitHub
parent 98d28aab54
commit be33f80bc6
5 changed files with 231 additions and 44 deletions
+56 -1
View File
@@ -19,7 +19,7 @@ use serde_json::json;
const FASTER_MODEL: &str = "faster-model";
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn emits_safety_buffering_with_the_requested_model() -> anyhow::Result<()> {
async fn emits_safety_buffering_with_the_header_fallback_model() -> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
@@ -72,3 +72,58 @@ async fn emits_safety_buffering_with_the_requested_model() -> anyhow::Result<()>
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn emits_safety_buffering_with_the_responses_api_model_without_header_gating()
-> anyhow::Result<()> {
skip_if_no_network!(Ok(()));
let server = start_mock_server().await;
let mut created = ev_response_created("resp-1");
created["safety_buffering"] = json!({
"use_cases": ["cyber"],
"reasons": ["policy-check"],
"faster_model": FASTER_MODEL,
});
mount_response_once(
&server,
sse_response(sse(vec![created, ev_completed("resp-1")])),
)
.await;
let test = test_codex().build(&server).await?;
test.codex
.submit(Op::UserInput {
items: vec![UserInput::Text {
text: "Check this request".into(),
text_elements: Vec::new(),
}],
final_output_json_schema: None,
responsesapi_client_metadata: None,
additional_context: Default::default(),
thread_settings: Default::default(),
})
.await?;
let event = wait_for_event_match(&test.codex, |event| match event {
EventMsg::SafetyBuffering(event) => Some(event.clone()),
_ => None,
})
.await;
assert_eq!(
event,
SafetyBufferingEvent {
model: test.session_configured.model.clone(),
use_cases: vec!["cyber".to_string()],
reasons: vec!["policy-check".to_string()],
show_buffering_ui: true,
faster_model: Some(FASTER_MODEL.to_string()),
}
);
wait_for_event(&test.codex, |event| {
matches!(event, EventMsg::TurnComplete(_))
})
.await;
Ok(())
}