From 7094fa467ea3ae596dae32519c3b90ab5390c9cc Mon Sep 17 00:00:00 2001 From: Francis Chalissery Date: Mon, 6 Jul 2026 10:04:26 -0700 Subject: [PATCH] [codex] Read retry model from buffering events (#31262) ## Summary - deserialize `retry_model` from streamed `safety_buffering` payloads - preserve the existing downstream faster-model API and legacy header fallback - update SSE, WebSocket, and end-to-end safety-buffering coverage ## Root cause Follow-up to #31064. The Responses API emits the retry target as `retry_model`, but the client was looking for `faster_model`, so the payload value was ignored in favor of the compatibility fallback. ## Behavior A non-null `retry_model` from the buffering payload takes precedence. An explicit null leaves the retry target unset, while an omitted field continues to fall back to the existing response header. ## Validation - `just test -p codex-api` (135 tests) - `just test -p codex-core safety_buffering` (2 tests) - `just fix -p codex-api` - `just fmt` - `git diff --check` --- codex-rs/codex-api/src/common.rs | 1 + .../codex-api/src/endpoint/responses_websocket.rs | 2 +- codex-rs/codex-api/src/sse/responses.rs | 14 +++++++------- codex-rs/core/tests/suite/safety_buffering.rs | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/codex-rs/codex-api/src/common.rs b/codex-rs/codex-api/src/common.rs index cbd93caf5d..7319d13a79 100644 --- a/codex-rs/codex-api/src/common.rs +++ b/codex-rs/codex-api/src/common.rs @@ -123,6 +123,7 @@ pub struct SafetyBuffering { pub reasons: Vec, #[serde(skip)] pub show_buffering_ui: bool, + #[serde(rename = "retry_model")] pub faster_model: Option, } diff --git a/codex-rs/codex-api/src/endpoint/responses_websocket.rs b/codex-rs/codex-api/src/endpoint/responses_websocket.rs index c4c617ac99..7f261bba36 100644 --- a/codex-rs/codex-api/src/endpoint/responses_websocket.rs +++ b/codex-rs/codex-api/src/endpoint/responses_websocket.rs @@ -1061,7 +1061,7 @@ mod tests { "safety_buffering": { "use_cases": ["cyber"], "reasons": ["user_risk"], - "faster_model": "gpt-fast-wire" + "retry_model": "gpt-fast-wire" } })) .expect("deserialize safety buffering event"); diff --git a/codex-rs/codex-api/src/sse/responses.rs b/codex-rs/codex-api/src/sse/responses.rs index bc32fc41c5..626f460dac 100644 --- a/codex-rs/codex-api/src/sse/responses.rs +++ b/codex-rs/codex-api/src/sse/responses.rs @@ -236,10 +236,10 @@ impl ResponsesStreamEvent { treatment: &SafetyBufferingTreatment, ) -> Option { let value = self.safety_buffering.as_ref()?; - let faster_model_present = value.as_object()?.contains_key("faster_model"); + let retry_model_present = value.as_object()?.contains_key("retry_model"); let mut buffering: SafetyBuffering = serde_json::from_value(value.clone()).ok()?; buffering.show_buffering_ui = true; - if !faster_model_present { + if !retry_model_present { buffering.faster_model.clone_from(&treatment.faster_model); } Some(buffering) @@ -1405,7 +1405,7 @@ mod tests { "safety_buffering": { "use_cases": ["cyber"], "reasons": ["user_risk"], - "faster_model": "gpt-fast-wire" + "retry_model": "gpt-fast-wire" } }), json!({ @@ -1453,12 +1453,12 @@ mod tests { } #[test] - fn safety_buffering_prefers_wire_faster_model_and_only_falls_back_when_omitted() { + fn safety_buffering_prefers_wire_retry_model_and_only_falls_back_when_omitted() { let treatment = SafetyBufferingTreatment { faster_model: Some("gpt-fast-header".to_string()), }; - for (faster_model, expected_faster_model) in [ + for (retry_model, expected_faster_model) in [ (None, Some("gpt-fast-header")), (Some(Value::Null), None), (Some(json!("gpt-fast-wire")), Some("gpt-fast-wire")), @@ -1470,8 +1470,8 @@ mod tests { "reasons": ["user_risk"] } }); - if let Some(faster_model) = faster_model { - event["safety_buffering"]["faster_model"] = faster_model; + if let Some(retry_model) = retry_model { + event["safety_buffering"]["retry_model"] = retry_model; } let event: ResponsesStreamEvent = serde_json::from_value(event).expect("deserialize safety buffering event"); diff --git a/codex-rs/core/tests/suite/safety_buffering.rs b/codex-rs/core/tests/suite/safety_buffering.rs index 5a8393f864..473cc18ced 100644 --- a/codex-rs/core/tests/suite/safety_buffering.rs +++ b/codex-rs/core/tests/suite/safety_buffering.rs @@ -83,7 +83,7 @@ async fn emits_safety_buffering_with_the_responses_api_model_without_header_gati created["safety_buffering"] = json!({ "use_cases": ["cyber"], "reasons": ["policy-check"], - "faster_model": FASTER_MODEL, + "retry_model": FASTER_MODEL, }); mount_response_once( &server,