mirror of
https://github.com/openai/codex.git
synced 2026-04-30 19:32:04 +03:00
turn metadata: per-turn non-blocking (#11677)
This commit is contained in:
@@ -375,6 +375,15 @@ async fn responses_stream_includes_turn_metadata_header_for_git_workspace_e2e()
|
||||
.expect("x-codex-turn-metadata header should be present");
|
||||
let initial_parsed: serde_json::Value =
|
||||
serde_json::from_str(&initial_header).expect("x-codex-turn-metadata should be valid JSON");
|
||||
let initial_turn_id = initial_parsed
|
||||
.get("turn_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.expect("turn_id should be present")
|
||||
.to_string();
|
||||
assert!(
|
||||
!initial_turn_id.is_empty(),
|
||||
"turn_id should not be empty in x-codex-turn-metadata"
|
||||
);
|
||||
assert_eq!(
|
||||
initial_parsed
|
||||
.get("sandbox")
|
||||
@@ -424,56 +433,95 @@ async fn responses_stream_includes_turn_metadata_header_for_git_workspace_e2e()
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
let deadline = tokio::time::Instant::now() + std::time::Duration::from_secs(5);
|
||||
loop {
|
||||
let request_recorder = responses::mount_sse_once(&server, response_body.clone()).await;
|
||||
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
|
||||
test.submit_turn("hello")
|
||||
.await
|
||||
.expect("submit post-git turn prompt");
|
||||
let first_response = responses::sse(vec![
|
||||
responses::ev_response_created("resp-2"),
|
||||
responses::ev_reasoning_item("rsn-1", &["thinking"], &[]),
|
||||
responses::ev_shell_command_call("call-1", "echo turn-metadata"),
|
||||
responses::ev_completed("resp-2"),
|
||||
]);
|
||||
let follow_up_response = responses::sse(vec![
|
||||
responses::ev_response_created("resp-3"),
|
||||
responses::ev_assistant_message("msg-1", "done"),
|
||||
responses::ev_completed("resp-3"),
|
||||
]);
|
||||
let request_log = responses::mount_response_sequence(
|
||||
&server,
|
||||
vec![
|
||||
responses::sse_response(first_response),
|
||||
responses::sse_response(follow_up_response),
|
||||
],
|
||||
)
|
||||
.await;
|
||||
|
||||
let maybe_metadata = request_recorder
|
||||
.single_request()
|
||||
test.submit_turn("hello")
|
||||
.await
|
||||
.expect("submit post-git turn prompt");
|
||||
|
||||
let requests = request_log.requests();
|
||||
assert_eq!(requests.len(), 2, "expected two requests in one turn");
|
||||
|
||||
let first_parsed: serde_json::Value = serde_json::from_str(
|
||||
&requests[0]
|
||||
.header("x-codex-turn-metadata")
|
||||
.and_then(|header_value| {
|
||||
let parsed: serde_json::Value = serde_json::from_str(&header_value).ok()?;
|
||||
let workspace = parsed
|
||||
.get("workspaces")
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.and_then(|workspaces| workspaces.values().next())
|
||||
.cloned()?;
|
||||
Some((parsed, workspace))
|
||||
});
|
||||
let Some((parsed, workspace)) = maybe_metadata else {
|
||||
if tokio::time::Instant::now() >= deadline {
|
||||
break;
|
||||
}
|
||||
tokio::time::sleep(std::time::Duration::from_millis(25)).await;
|
||||
continue;
|
||||
};
|
||||
.expect("first request should include turn metadata"),
|
||||
)
|
||||
.expect("first metadata should be valid json");
|
||||
let second_parsed: serde_json::Value = serde_json::from_str(
|
||||
&requests[1]
|
||||
.header("x-codex-turn-metadata")
|
||||
.expect("second request should include turn metadata"),
|
||||
)
|
||||
.expect("second metadata should be valid json");
|
||||
|
||||
assert_eq!(
|
||||
parsed.get("sandbox").and_then(serde_json::Value::as_str),
|
||||
Some("none")
|
||||
);
|
||||
assert_eq!(
|
||||
workspace
|
||||
.get("latest_git_commit_hash")
|
||||
.and_then(serde_json::Value::as_str),
|
||||
Some(expected_head.as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
workspace
|
||||
.get("associated_remote_urls")
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.and_then(|remotes| remotes.get("origin"))
|
||||
.and_then(serde_json::Value::as_str),
|
||||
Some(expected_origin.as_str())
|
||||
);
|
||||
return;
|
||||
}
|
||||
let first_turn_id = first_parsed
|
||||
.get("turn_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.expect("first turn_id should be present");
|
||||
let second_turn_id = second_parsed
|
||||
.get("turn_id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.expect("second turn_id should be present");
|
||||
assert_eq!(
|
||||
first_turn_id, second_turn_id,
|
||||
"requests should share turn_id"
|
||||
);
|
||||
assert_ne!(
|
||||
second_turn_id,
|
||||
initial_turn_id.as_str(),
|
||||
"post-git turn should have a new turn_id"
|
||||
);
|
||||
|
||||
panic!(
|
||||
"x-codex-turn-metadata with git workspace info was never observed within 5s after git setup"
|
||||
assert_eq!(
|
||||
second_parsed
|
||||
.get("sandbox")
|
||||
.and_then(serde_json::Value::as_str),
|
||||
Some("none")
|
||||
);
|
||||
|
||||
let workspace = second_parsed
|
||||
.get("workspaces")
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.and_then(|workspaces| workspaces.values().next())
|
||||
.cloned()
|
||||
.expect("second request should include git workspace metadata");
|
||||
assert_eq!(
|
||||
workspace
|
||||
.get("latest_git_commit_hash")
|
||||
.and_then(serde_json::Value::as_str),
|
||||
Some(expected_head.as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
workspace
|
||||
.get("associated_remote_urls")
|
||||
.and_then(serde_json::Value::as_object)
|
||||
.and_then(|remotes| remotes.get("origin"))
|
||||
.and_then(serde_json::Value::as_str),
|
||||
Some(expected_origin.as_str())
|
||||
);
|
||||
assert_eq!(
|
||||
workspace
|
||||
.get("has_changes")
|
||||
.and_then(serde_json::Value::as_bool),
|
||||
Some(false)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ async fn responses_websocket_preconnect_reuses_connection() {
|
||||
let harness = websocket_harness(&server).await;
|
||||
let mut client_session = harness.client.new_session();
|
||||
client_session
|
||||
.prewarm_websocket(&harness.otel_manager, &harness.model_info, None)
|
||||
.prewarm_websocket(&harness.otel_manager, &harness.model_info)
|
||||
.await
|
||||
.expect("websocket prewarm failed");
|
||||
let prompt = prompt_with_input(vec![message_item("hello")]);
|
||||
@@ -131,7 +131,7 @@ async fn responses_websocket_preconnect_is_reused_even_with_header_changes() {
|
||||
let harness = websocket_harness(&server).await;
|
||||
let mut client_session = harness.client.new_session();
|
||||
client_session
|
||||
.prewarm_websocket(&harness.otel_manager, &harness.model_info, None)
|
||||
.prewarm_websocket(&harness.otel_manager, &harness.model_info)
|
||||
.await
|
||||
.expect("websocket prewarm failed");
|
||||
let prompt = prompt_with_input(vec![message_item("hello")]);
|
||||
@@ -172,7 +172,7 @@ async fn responses_websocket_prewarm_uses_model_preference_when_feature_disabled
|
||||
let harness = websocket_harness_with_options(&server, false, false, false, true).await;
|
||||
let mut client_session = harness.client.new_session();
|
||||
client_session
|
||||
.prewarm_websocket(&harness.otel_manager, &harness.model_info, None)
|
||||
.prewarm_websocket(&harness.otel_manager, &harness.model_info)
|
||||
.await
|
||||
.expect("websocket prewarm failed");
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ use core_test_support::responses::start_websocket_server_with_headers;
|
||||
use core_test_support::skip_if_no_network;
|
||||
use core_test_support::test_codex::test_codex;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value;
|
||||
|
||||
const TURN_STATE_HEADER: &str = "x-codex-turn-state";
|
||||
|
||||
@@ -65,6 +66,25 @@ async fn responses_turn_state_persists_within_turn_and_resets_after() -> Result<
|
||||
);
|
||||
assert_eq!(requests[2].header(TURN_STATE_HEADER), None);
|
||||
|
||||
let parse_turn_id = |header: Option<String>| {
|
||||
let value = header?;
|
||||
let parsed: Value = serde_json::from_str(&value).ok()?;
|
||||
parsed
|
||||
.get("turn_id")
|
||||
.and_then(Value::as_str)
|
||||
.map(str::to_string)
|
||||
};
|
||||
|
||||
let first_turn_id = parse_turn_id(requests[0].header("x-codex-turn-metadata"))
|
||||
.expect("first request should include turn metadata turn_id");
|
||||
let second_turn_id = parse_turn_id(requests[1].header("x-codex-turn-metadata"))
|
||||
.expect("follow-up request should include turn metadata turn_id");
|
||||
let third_turn_id = parse_turn_id(requests[2].header("x-codex-turn-metadata"))
|
||||
.expect("new turn request should include turn metadata turn_id");
|
||||
|
||||
assert_eq!(first_turn_id, second_turn_id);
|
||||
assert_ne!(second_turn_id, third_turn_id);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user