From cec8960549998e868130fd5e3070f9d666b1fd6f Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 12 Oct 2026 16:58:28 +0100 Subject: [PATCH] fix(chatgpt): retry transient WebSocket response failures --- Cargo.lock | 4 +-- Cargo.toml | 2 +- src/provider/chatgpt/websocket_tests.rs | 33 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fb9d4e0b..9e6cb7fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -327,9 +327,9 @@ dependencies = [ [[package]] name = "agentkit-provider-openai" -version = "0.10.12" +version = "0.10.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59949238158745f1c53d3f31057dda3b7a643581f067b91b993075e6b3b09511" +checksum = "aea3cc16b94aa7bc6e55e403c8224d27eb6838af31626fd9404ab888e99f4ca6" dependencies = [ "agentkit-adapter-completions", "agentkit-core", diff --git a/Cargo.toml b/Cargo.toml index 1a89cd6e..22b30754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ agentkit-loop = { version = "=0.10.13", features = ["otel"] } agentkit-mcp = "=0.10.7" agentkit-http = "=0.10.7" agentkit-plugins = "=0.10.7" -agentkit-provider-openai = "=0.10.12" +agentkit-provider-openai = "=0.10.13" agentkit-provider-openrouter = "=0.10.8" agentkit-task-manager = "=0.10.7" agentkit-tool-compose = { version = "=0.10.12", default-features = false, features = ["runlet"] } diff --git a/src/provider/chatgpt/websocket_tests.rs b/src/provider/chatgpt/websocket_tests.rs index d568d500..b2a8b614 100644 --- a/src/provider/chatgpt/websocket_tests.rs +++ b/src/provider/chatgpt/websocket_tests.rs @@ -502,6 +502,39 @@ async fn subscription_retries_reach_the_session_retry_observer() { assert!(retries.load(std::sync::atomic::Ordering::SeqCst) > 0); } +#[tokio::test] +async fn subscription_retries_correlated_transient_response_failure() { + let (endpoint, peer) = server(|listener| { + let mut failed = tungstenite::accept(accept(&listener)).unwrap(); + receive(&mut failed, false); + failed + .send(Message::Text(response("failed", "")[0].to_string().into())) + .unwrap(); + failed + .send(Message::Text( + json!({ + "type": "response.failed", + "response": { + "id": "failed", + "error": {"type": "server_error", "code": "server_error", "status": 503} + } + }) + .to_string() + .into(), + )) + .unwrap(); + // Keep the failed socket open: recovery must follow the terminal event, + // not a transport disconnect. The replacement sends the full request. + let mut fresh = tungstenite::accept(accept(&listener)).unwrap(); + receive(&mut fresh, false); + success(&mut fresh, "retried", "first answer"); + }); + let mut session = session_with_retries(&endpoint, 1).await; + let mut turn = begin(&mut session, false).await; + finished(&mut turn, "retried", "first answer").await; + peer.join().unwrap(); +} + #[tokio::test] async fn subscription_auto_426_fallback_stays_http_for_future_turns() { let (endpoint, peer) = server(|listener| {