mirror of
https://github.com/openai/codex.git
synced 2026-11-08 20:40:33 +00:00
[codex] Simplify concurrent reasoning summary rollout
This commit is contained in:
@@ -131,6 +131,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
/*item_ids_enabled*/ false,
|
||||
/*parallel_reasoning_summaries_enabled*/ false,
|
||||
/*attestation_provider*/ None,
|
||||
);
|
||||
let responses_metadata = test_turn_responses_metadata(&client, thread_id, &session_source);
|
||||
@@ -265,6 +266,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
/*item_ids_enabled*/ false,
|
||||
/*parallel_reasoning_summaries_enabled*/ false,
|
||||
/*attestation_provider*/ None,
|
||||
);
|
||||
let responses_metadata = test_turn_responses_metadata(&client, thread_id, &session_source);
|
||||
@@ -385,6 +387,7 @@ async fn responses_respects_model_info_overrides_from_config() {
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
/*item_ids_enabled*/ false,
|
||||
/*parallel_reasoning_summaries_enabled*/ false,
|
||||
/*attestation_provider*/ None,
|
||||
);
|
||||
let responses_metadata = test_turn_responses_metadata(&client, thread_id, &session_source);
|
||||
|
||||
@@ -1334,6 +1334,8 @@ async fn send_provider_auth_request(server: &MockServer, auth: ModelProviderAuth
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
/*item_ids_enabled*/ config.features.enabled(Feature::ItemIds),
|
||||
/*parallel_reasoning_summaries_enabled*/
|
||||
config.features.enabled(Feature::ParallelReasoningSummaries),
|
||||
/*attestation_provider*/ None,
|
||||
);
|
||||
let responses_metadata = test_turn_responses_metadata(&client, thread_id);
|
||||
@@ -2376,6 +2378,7 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> {
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.model_reasoning_summary = Some(ReasoningSummary::Concise);
|
||||
let _ = config.features.enable(Feature::ParallelReasoningSummaries);
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
@@ -2956,6 +2959,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
|
||||
/*include_timing_metrics*/ false,
|
||||
/*beta_features_header*/ None,
|
||||
/*item_ids_enabled*/ false,
|
||||
/*parallel_reasoning_summaries_enabled*/ false,
|
||||
/*attestation_provider*/ None,
|
||||
);
|
||||
let responses_metadata = test_turn_responses_metadata(&client, thread_id);
|
||||
|
||||
@@ -388,10 +388,11 @@ async fn responses_websocket_request_prewarm_reuses_connection() {
|
||||
|
||||
let mut provider = websocket_provider(&server);
|
||||
provider.name = "OpenAI".to_string();
|
||||
let mut harness =
|
||||
websocket_harness_with_provider_options(provider, /*runtime_metrics_enabled*/ true).await;
|
||||
harness.model_info.supports_reasoning_summaries = true;
|
||||
harness.summary = ReasoningSummary::Concise;
|
||||
let harness = websocket_harness_with_provider_options(
|
||||
provider, /*runtime_metrics_enabled*/ true,
|
||||
/*parallel_reasoning_summaries_enabled*/ true,
|
||||
)
|
||||
.await;
|
||||
let mut client_session = harness.client.new_session();
|
||||
let prompt = prompt_with_input(vec![message_item("hello")]);
|
||||
let responses_metadata = prewarm_metadata(&harness, /*turn_id*/ None);
|
||||
@@ -446,7 +447,6 @@ async fn responses_websocket_request_prewarm_reuses_connection() {
|
||||
follow_up["stream_options"]["reasoning_summary_delivery"].as_str(),
|
||||
Some("concurrent_cutoff")
|
||||
);
|
||||
|
||||
server.shutdown().await;
|
||||
}
|
||||
|
||||
@@ -1000,8 +1000,11 @@ async fn responses_websocket_v2_incremental_requests_are_reused_across_turns() {
|
||||
// use OpenAI provider to check metadata logic
|
||||
let mut provider = websocket_provider(&server);
|
||||
provider.name = ModelProviderInfo::create_openai_provider(/*base_url*/ None).name;
|
||||
let harness =
|
||||
websocket_harness_with_provider_options(provider, /*runtime_metrics_enabled*/ false).await;
|
||||
let harness = websocket_harness_with_provider_options(
|
||||
provider, /*runtime_metrics_enabled*/ false,
|
||||
/*parallel_reasoning_summaries_enabled*/ false,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Turn one: initiate
|
||||
let prompt_one = prompt_with_input(vec![message_item("hello")]);
|
||||
@@ -2205,13 +2208,18 @@ async fn websocket_harness_with_options(
|
||||
server: &WebSocketTestServer,
|
||||
runtime_metrics_enabled: bool,
|
||||
) -> WebsocketTestHarness {
|
||||
websocket_harness_with_provider_options(websocket_provider(server), runtime_metrics_enabled)
|
||||
.await
|
||||
websocket_harness_with_provider_options(
|
||||
websocket_provider(server),
|
||||
runtime_metrics_enabled,
|
||||
/*parallel_reasoning_summaries_enabled*/ false,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn websocket_harness_with_provider_options(
|
||||
provider: ModelProviderInfo,
|
||||
runtime_metrics_enabled: bool,
|
||||
parallel_reasoning_summaries_enabled: bool,
|
||||
) -> WebsocketTestHarness {
|
||||
let codex_home = TempDir::new().unwrap();
|
||||
let mut config = load_default_config_for_test(&codex_home).await;
|
||||
@@ -2222,6 +2230,12 @@ async fn websocket_harness_with_provider_options(
|
||||
.enable(Feature::RuntimeMetrics)
|
||||
.expect("test config should allow feature update");
|
||||
}
|
||||
if parallel_reasoning_summaries_enabled {
|
||||
config
|
||||
.features
|
||||
.enable(Feature::ParallelReasoningSummaries)
|
||||
.expect("test config should allow feature update");
|
||||
}
|
||||
let config = Arc::new(config);
|
||||
let model_info = codex_core::test_support::construct_model_info_offline(MODEL, &config);
|
||||
let thread_id = ThreadId::new();
|
||||
@@ -2261,6 +2275,8 @@ async fn websocket_harness_with_provider_options(
|
||||
runtime_metrics_enabled,
|
||||
/*beta_features_header*/ None,
|
||||
/*item_ids_enabled*/ config.features.enabled(Feature::ItemIds),
|
||||
/*parallel_reasoning_summaries_enabled*/
|
||||
config.features.enabled(Feature::ParallelReasoningSummaries),
|
||||
/*attestation_provider*/ None,
|
||||
);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![cfg(not(target_os = "windows"))]
|
||||
|
||||
use anyhow::Ok;
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
@@ -1085,6 +1086,63 @@ async fn plan_mode_handles_missing_plan_close_tag() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn reasoning_content_delta_has_item_metadata() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = start_mock_server().await;
|
||||
|
||||
let TestCodex { codex, .. } = test_codex().build(&server).await?;
|
||||
|
||||
let stream = sse(vec![
|
||||
ev_response_created("resp-1"),
|
||||
ev_reasoning_item_added("reasoning-1", &[""]),
|
||||
ev_reasoning_summary_text_delta("step one"),
|
||||
ev_reasoning_item("reasoning-1", &["step one"], &[]),
|
||||
ev_completed("resp-1"),
|
||||
]);
|
||||
let response_mock = mount_sse_once(&server, stream).await;
|
||||
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "reason through it".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
responsesapi_client_metadata: None,
|
||||
additional_context: Default::default(),
|
||||
thread_settings: Default::default(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
let reasoning_item = wait_for_event_match(&codex, |ev| match ev {
|
||||
EventMsg::ItemStarted(ItemStartedEvent {
|
||||
item: TurnItem::Reasoning(item),
|
||||
..
|
||||
}) => Some(item.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
|
||||
let delta_event = wait_for_event_match(&codex, |ev| match ev {
|
||||
EventMsg::ReasoningContentDelta(event) => Some(event.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.await;
|
||||
assert_eq!(delta_event.item_id, reasoning_item.id);
|
||||
assert_eq!(delta_event.delta, "step one");
|
||||
assert_eq!(
|
||||
response_mock
|
||||
.single_request()
|
||||
.body_json()
|
||||
.get("stream_options"),
|
||||
None
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn concurrent_cutoff_emits_only_current_reasoning_summary_done() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -1097,6 +1155,7 @@ async fn concurrent_cutoff_emits_only_current_reasoning_summary_done() -> anyhow
|
||||
})
|
||||
.with_config(|config| {
|
||||
config.model_reasoning_summary = Some(ReasoningSummary::Auto);
|
||||
let _ = config.features.enable(Feature::ParallelReasoningSummaries);
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
@@ -1124,14 +1183,14 @@ async fn concurrent_cutoff_emits_only_current_reasoning_summary_done() -> anyhow
|
||||
// Once a later output item starts, summaries for the earlier item are stale.
|
||||
summary_done("reasoning-1", 1, "late step"),
|
||||
ev_output_text_delta("Done"),
|
||||
with_output_index(
|
||||
ev_reasoning_item("reasoning-1", &["step one", "late step"], &[]),
|
||||
/*output_index*/ 0,
|
||||
),
|
||||
with_output_index(
|
||||
ev_assistant_message("message-1", "Done"),
|
||||
/*output_index*/ 1,
|
||||
),
|
||||
with_output_index(
|
||||
ev_reasoning_item("reasoning-1", &["step one", "late step"], &[]),
|
||||
/*output_index*/ 0,
|
||||
),
|
||||
ev_completed("resp-1"),
|
||||
]),
|
||||
)
|
||||
@@ -1159,10 +1218,22 @@ async fn concurrent_cutoff_emits_only_current_reasoning_summary_done() -> anyhow
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut started_item_ids = vec![reasoning_item.id.clone()];
|
||||
let mut completed_item_ids = Vec::new();
|
||||
let mut summary_deltas = Vec::new();
|
||||
let mut summary_sections = Vec::new();
|
||||
loop {
|
||||
match wait_for_event(&codex, |_| true).await {
|
||||
EventMsg::ItemStarted(ItemStartedEvent { item, .. })
|
||||
if matches!(&item, TurnItem::Reasoning(_) | TurnItem::AgentMessage(_)) =>
|
||||
{
|
||||
started_item_ids.push(item.id());
|
||||
}
|
||||
EventMsg::ItemCompleted(ItemCompletedEvent { item, .. })
|
||||
if matches!(&item, TurnItem::Reasoning(_) | TurnItem::AgentMessage(_)) =>
|
||||
{
|
||||
completed_item_ids.push(item.id());
|
||||
}
|
||||
EventMsg::ReasoningContentDelta(event) => {
|
||||
summary_deltas.push((event.item_id, event.delta, event.summary_index));
|
||||
}
|
||||
@@ -1178,6 +1249,8 @@ async fn concurrent_cutoff_emits_only_current_reasoning_summary_done() -> anyhow
|
||||
summary_deltas,
|
||||
vec![(reasoning_item.id, "step one".to_string(), 0)]
|
||||
);
|
||||
assert_eq!(started_item_ids, vec!["reasoning-1", "message-1"]);
|
||||
assert_eq!(completed_item_ids, vec!["reasoning-1", "message-1"]);
|
||||
assert_eq!(summary_sections, Vec::new());
|
||||
assert_eq!(
|
||||
response_mock.single_request().body_json()["stream_options"]["reasoning_summary_delivery"]
|
||||
|
||||
Reference in New Issue
Block a user