From 03f4bfaa9932a86539c2db4aee5dc66d1c9ec9e7 Mon Sep 17 00:00:00 2001 From: "g.sunilkumar" Date: Wed, 22 Apr 2026 14:36:45 +0530 Subject: [PATCH] fix(providers): handle missing delta field in streaming chunks (#8700) Signed-off-by: sunilkumarvalmiki Co-authored-by: Lifei Zhou --- crates/goose/src/providers/formats/openai.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/crates/goose/src/providers/formats/openai.rs b/crates/goose/src/providers/formats/openai.rs index ce632d4ea7..7633b85790 100644 --- a/crates/goose/src/providers/formats/openai.rs +++ b/crates/goose/src/providers/formats/openai.rs @@ -65,7 +65,7 @@ struct ContentPart { thought_signature: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Default)] struct Delta { #[serde(default)] content: Option, @@ -89,6 +89,7 @@ impl Delta { #[derive(Serialize, Deserialize, Debug)] struct StreamingChoice { + #[serde(default)] delta: Delta, index: Option, finish_reason: Option, @@ -2012,6 +2013,23 @@ data: [DONE] Ok(()) } + #[tokio::test] + async fn test_azure_annotation_chunk_without_delta_does_not_fail() -> anyhow::Result<()> { + let response_lines = r#" +data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":1234567890,"model":"gpt-5.4","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}],"usage":null} +data: {"choices":[{"content_filter_offsets":{"check_offset":5,"start_offset":5,"end_offset":5},"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"finish_reason":null,"index":0}],"created":0,"id":"","model":"","object":""} +data: {"id":"chatcmpl-test","object":"chat.completion.chunk","created":1234567891,"model":"gpt-5.4","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":10,"completion_tokens":1,"total_tokens":11}} +data: [DONE] +"#; + + let result = run_streaming_test(response_lines).await?; + + assert!(result.has_text_content, "Expected text content in response"); + assert_usage_yielded_once(&result, 10, 1, 11); + + Ok(()) + } + #[test] fn test_response_to_message_with_nested_extra_content() -> anyhow::Result<()> { let response = json!({