[codex] Support sequential cutoff reasoning summaries (#31306)

## Summary

When enabled for the OpenAI provider, Codex sends
`stream_options.reasoning_summary_delivery = "sequential_cutoff"` on
HTTP and
WebSocket requests, including prewarm, and renders completed summary
sections
from `reasoning_summary_text.done`. Flag-off and non-OpenAI behavior is
unchanged.

## Expected rollout

```text
reasoning 0 added
summary 0 done
summary 1 done
summary 2 starts
summary 2 cancelled / incomplete
reasoning 0 done  <-- cancel summary 2 work and mark it incomplete
message 1 added
message 1 text streams
message 1 completed
```

Depends on
[openai/openai#1096660](https://github.com/openai/openai/pull/1096660).
This commit is contained in:
ashwinnathan-openai authored and GitHub committed 2026-07-06 23:23:56 -04:00
1 parent 831c14fc39
commit 775ef7dcc7
20 files changed
+352 -6

No files matched your search

+15 -1
View File
@@ -56,6 +56,7 @@ use codex_api::ResponsesWebsocketConnection as ApiWebSocketConnection;
use codex_api::ResponsesWsRequest;
use codex_api::SharedAuthProvider;
use codex_api::SseTelemetry;
use codex_api::StreamOptions;
use codex_api::TransportError;
use codex_api::WebsocketTelemetry;
use codex_api::auth_header_telemetry;
@@ -203,6 +204,7 @@ struct ModelClientState {
include_timing_metrics: bool,
beta_features_header: Option<String>,
item_ids_enabled: bool,
concurrent_reasoning_summaries_enabled: bool,
include_attestation: bool,
attestation_provider: Option<Arc<dyn AttestationProvider>>,
disable_websockets: AtomicBool,
@@ -311,6 +313,7 @@ fn responses_request_properties_match(
reasoning: previous_reasoning,
store: previous_store,
stream: previous_stream,
stream_options: _,
include: previous_include,
service_tier: previous_service_tier,
prompt_cache_key: previous_prompt_cache_key,
@@ -327,6 +330,7 @@ fn responses_request_properties_match(
reasoning: current_reasoning,
store: current_store,
stream: current_stream,
stream_options: _,
include: current_include,
service_tier: current_service_tier,
prompt_cache_key: current_prompt_cache_key,
@@ -342,6 +346,8 @@ fn responses_request_properties_match(
&& previous_reasoning == current_reasoning
&& previous_store == current_store
&& previous_stream == current_stream
// Stream options control delivery for this response, not the context
// referenced by `previous_response_id`.
&& previous_include == current_include
&& previous_service_tier == current_service_tier
&& previous_prompt_cache_key == current_prompt_cache_key
@@ -409,6 +415,7 @@ impl ModelClient {
include_timing_metrics: bool,
beta_features_header: Option<String>,
item_ids_enabled: bool,
concurrent_reasoning_summaries_enabled: bool,
attestation_provider: Option<Arc<dyn AttestationProvider>>,
) -> Self {
let model_provider = create_model_provider(provider_info, auth_manager);
@@ -431,6 +438,7 @@ impl ModelClient {
include_timing_metrics,
beta_features_header,
item_ids_enabled,
concurrent_reasoning_summaries_enabled,
include_attestation,
attestation_provider,
disable_websockets: AtomicBool::new(false),
@@ -820,7 +828,8 @@ impl ModelClient {
responses_metadata: &CodexResponsesMetadata,
) -> Result<ResponsesApiRequest> {
let mut input = prompt.get_formatted_input_for_request(model_info.use_responses_lite);
if !self.state.provider.info().is_openai() {
let is_openai = self.state.provider.info().is_openai();
if !is_openai {
input
.iter_mut()
.for_each(ResponseItem::clear_internal_chat_message_metadata_passthrough);
@@ -848,6 +857,10 @@ impl ModelClient {
} else {
(prompt.base_instructions.text.clone(), Some(tools))
};
let stream_options = (self.state.concurrent_reasoning_summaries_enabled && is_openai)
.then_some(StreamOptions {
reasoning_summary_delivery: codex_api::ReasoningSummaryDelivery::SequentialCutoff,
});
let reasoning = Self::build_reasoning(model_info, effort, summary);
let include = if reasoning.is_some() {
vec!["reasoning.encrypted_content".to_string()]
@@ -882,6 +895,7 @@ impl ModelClient {
reasoning,
store: provider.is_azure_responses_endpoint(),
stream: true,
stream_options,
include,
service_tier,
prompt_cache_key,