From 93538a70e32ef7e4a68a70c2c414915e09434eff Mon Sep 17 00:00:00 2001 From: Xiangjun Ma Date: Wed, 18 Feb 2026 00:01:19 -0800 Subject: [PATCH] fix(agent): relay final response as progressive chunks via on_delta Previously on_delta sent the entire completed response as a single message, defeating the purpose of the streaming draft updates. Now the text is split into ~80-char chunks on whitespace boundaries (UTF-8 safe via split_inclusive) and sent progressively through the channel, so Telegram draft edits show text arriving incrementally. The consumer in process_channel_message already accumulates chunks and calls update_draft with the full text so far, and Telegram's rate-limiting (draft_update_interval_ms) throttles editMessageText calls to avoid API spam. --- src/agent/loop_.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/agent/loop_.rs b/src/agent/loop_.rs index cb0fe17..bce5ce0 100644 --- a/src/agent/loop_.rs +++ b/src/agent/loop_.rs @@ -941,9 +941,22 @@ pub(crate) async fn run_tool_call_loop( if tool_calls.is_empty() { // No tool calls — this is the final response. - // If a streaming sender is provided, send the final text through it. + // If a streaming sender is provided, relay the text in small chunks + // so the channel can progressively update the draft message. if let Some(ref tx) = on_delta { - let _ = tx.send(display_text.clone()).await; + // Split on whitespace boundaries, accumulating ~80-char chunks. + let mut chunk = String::new(); + for word in display_text.split_inclusive(char::is_whitespace) { + chunk.push_str(word); + if chunk.len() >= 80 { + if tx.send(std::mem::take(&mut chunk)).await.is_err() { + break; // receiver dropped + } + } + } + if !chunk.is_empty() { + let _ = tx.send(chunk).await; + } } history.push(ChatMessage::assistant(response_text.clone())); return Ok(display_text);