[codex] Send Responses Lite transport header (#26542)

## Summary

- send `X-OpenAI-Internal-Codex-Responses-Lite: true` on HTTP Responses
requests and WebSocket upgrade requests when model metadata enables
Responses Lite
- use client metadata when sending it over the websocket

This PR is stacked on #26490.

## Why

The Responses Lite marker is request-scoped for HTTP but
connection-scoped for Responses-over-WebSocket because it is carried on
the upgrade request. Reusing a cached socket opened for the opposite
mode would therefore send the wrong transport contract.

## Validation

- `just test -p codex-core responses_lite`
- `just test -p codex-core
responses_websocket_reconnects_when_responses_lite_mode_changes`
- `just fix -p codex-core`
- `just fmt`
This commit is contained in:
rka-oai
2026-06-05 18:01:20 -07:00
committed by GitHub
Unverified
parent df7818c7d1
commit 954e2878bb
4 changed files with 218 additions and 4 deletions
+37 -3
View File
@@ -143,7 +143,11 @@ pub const X_RESPONSESAPI_INCLUDE_TIMING_METRICS_HEADER: &str =
"x-responsesapi-include-timing-metrics";
const X_CODEX_WS_STREAM_REQUEST_START_MS_CLIENT_METADATA_KEY: &str =
"x-codex-ws-stream-request-start-ms";
const WS_REQUEST_HEADER_RESPONSES_LITE_CLIENT_METADATA_KEY: &str =
"ws_request_header_x_openai_internal_codex_responses_lite";
const RESPONSES_WEBSOCKETS_V2_BETA_HEADER_VALUE: &str = "responses_websockets=2026-02-06";
const X_OPENAI_INTERNAL_CODEX_RESPONSES_LITE_HEADER: &str =
"x-openai-internal-codex-responses-lite";
const RESPONSES_ENDPOINT: &str = "/responses";
const RESPONSES_COMPACT_ENDPOINT: &str = "/responses/compact";
// `/responses/compact` is unary, so the timeout covers the full response rather than one idle
@@ -526,6 +530,7 @@ impl ModelClient {
if let Some(header_value) = self.generate_attestation_header_for().await {
extra_headers.insert(X_OAI_ATTESTATION_HEADER, header_value);
}
add_responses_lite_header(&mut extra_headers, model_info.use_responses_lite);
let compact_request_timeout = client_setup
.api_provider
.stream_idle_timeout
@@ -655,6 +660,7 @@ impl ModelClient {
fn build_ws_client_metadata(
&self,
turn_metadata_header: Option<&str>,
use_responses_lite: bool,
) -> HashMap<String, String> {
let mut client_metadata = HashMap::new();
client_metadata.insert(
@@ -682,6 +688,12 @@ impl ModelClient {
turn_metadata.to_string(),
);
}
if use_responses_lite {
client_metadata.insert(
WS_REQUEST_HEADER_RESPONSES_LITE_CLIENT_METADATA_KEY.to_string(),
"true".to_string(),
);
}
client_metadata
}
@@ -982,6 +994,7 @@ impl ModelClientSession {
&self,
turn_metadata_header: Option<&str>,
compression: Compression,
use_responses_lite: bool,
) -> ApiResponsesOptions {
let turn_metadata_header = parse_turn_metadata_header(turn_metadata_header);
let session_id = self.client.state.session_id.to_string();
@@ -1000,6 +1013,7 @@ impl ModelClientSession {
if let Some(header_value) = self.client.generate_attestation_header_for().await {
headers.insert(X_OAI_ATTESTATION_HEADER, header_value);
}
add_responses_lite_header(&mut headers, use_responses_lite);
headers
},
compression,
@@ -1267,7 +1281,11 @@ impl ModelClientSession {
);
let compression = self.responses_request_compression(client_setup.auth.as_ref());
let mut options = self
.build_responses_options(turn_metadata_header, compression)
.build_responses_options(
turn_metadata_header,
compression,
model_info.use_responses_lite,
)
.await;
let request = self.client.build_responses_request(
@@ -1377,7 +1395,11 @@ impl ModelClientSession {
let compression = self.responses_request_compression(client_setup.auth.as_ref());
let options = self
.build_responses_options(turn_metadata_header, compression)
.build_responses_options(
turn_metadata_header,
compression,
model_info.use_responses_lite,
)
.await;
let request = self.client.build_responses_request(
&client_setup.api_provider,
@@ -1389,7 +1411,10 @@ impl ModelClientSession {
)?;
let mut ws_payload = ResponseCreateWsRequest {
client_metadata: response_create_client_metadata(
Some(self.client.build_ws_client_metadata(turn_metadata_header)),
Some(self.client.build_ws_client_metadata(
turn_metadata_header,
model_info.use_responses_lite,
)),
request_trace.as_ref(),
),
..ResponseCreateWsRequest::from(&request)
@@ -1707,6 +1732,15 @@ fn build_responses_headers(
headers
}
fn add_responses_lite_header(headers: &mut ApiHeaderMap, use_responses_lite: bool) {
if use_responses_lite {
headers.insert(
X_OPENAI_INTERNAL_CODEX_RESPONSES_LITE_HEADER,
HeaderValue::from_static("true"),
);
}
}
fn subagent_header_value(session_source: &SessionSource) -> Option<String> {
match session_source {
SessionSource::SubAgent(subagent_source) => match subagent_source {
+4 -1
View File
@@ -293,7 +293,10 @@ fn build_ws_client_metadata_includes_window_lineage_and_turn_metadata() {
client.advance_window_generation();
let client_metadata = client.build_ws_client_metadata(Some(r#"{"turn_id":"turn-123"}"#));
let client_metadata = client.build_ws_client_metadata(
Some(r#"{"turn_id":"turn-123"}"#),
/*use_responses_lite*/ false,
);
let thread_id = client.state.thread_id;
assert_eq!(
client_metadata,