From b85ae3ad8ad00b34965f39b3b36d64803f6e524d Mon Sep 17 00:00:00 2001 From: James Pine Date: Thu, 12 Mar 2026 05:04:00 -0700 Subject: [PATCH] fix: preserve whitespace-only content segments in streaming responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Streaming providers (e.g. Kimi) sometimes send content chunks that are just whitespace. The trim().is_empty() guard in collect_openai_text_content and extract_text_content_from_responses_output_item dropped these segments, causing missing spaces in reconstructed output — especially at sentence boundaries and around numbers in lists. Change to is_empty() so only truly empty strings are filtered, preserving legitimate whitespace tokens from streaming deltas. --- src/llm/model.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/llm/model.rs b/src/llm/model.rs index 3cc0b56c4..a40a1d313 100644 --- a/src/llm/model.rs +++ b/src/llm/model.rs @@ -2568,7 +2568,10 @@ fn parse_openai_reasoning_fallback(message: &serde_json::Value) -> Option) { match value { serde_json::Value::String(text) => { - if !text.trim().is_empty() { + // Use is_empty() instead of trim().is_empty() to preserve whitespace-only + // segments. Streaming providers (e.g. Kimi) sometimes send content chunks + // that are just spaces; dropping those causes missing spaces in output. + if !text.is_empty() { text_parts.push(text.to_string()); } } @@ -2579,17 +2582,17 @@ fn collect_openai_text_content(value: &serde_json::Value, text_parts: &mut Vec { if let Some(text) = map.get("text").and_then(serde_json::Value::as_str) - && !text.trim().is_empty() + && !text.is_empty() { text_parts.push(text.to_string()); } if let Some(summary) = map.get("summary").and_then(serde_json::Value::as_str) - && !summary.trim().is_empty() + && !summary.is_empty() { text_parts.push(summary.to_string()); } if let Some(refusal) = map.get("refusal").and_then(serde_json::Value::as_str) - && !refusal.trim().is_empty() + && !refusal.is_empty() { text_parts.push(refusal.to_string()); } @@ -2663,7 +2666,7 @@ fn extract_text_content_from_responses_output_item( } if let Some(text) = map.get("text").and_then(serde_json::Value::as_str) - && !text.trim().is_empty() + && !text.is_empty() { text_parts.push(text.to_string()); }