commit c10d594f62a33346c63d30925ed0a0eef76f2f67
parent e7763d873568f9d689b3a23b4806d2d0035dd5c7
Author: William Casarin <jb55@jb55.com>
Date: Mon, 26 Jan 2026 00:45:30 -0800
dave: use StreamEvent for incremental text display
Switch from receiving complete text blocks via Message::Assistant to
using Message::StreamEvent with content_block_delta events. This enables
text to appear incrementally as it streams in, matching the OpenAI
backend behavior.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat:
1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/crates/notedeck_dave/src/backend/claude.rs b/crates/notedeck_dave/src/backend/claude.rs
@@ -6,7 +6,7 @@ use crate::tools::Tool;
use crate::Message;
use claude_agent_sdk_rs::{
ClaudeAgentOptions, ClaudeClient, ContentBlock, Message as ClaudeMessage, PermissionMode,
- PermissionResult, PermissionResultAllow, PermissionResultDeny, TextBlock, ToolUseBlock,
+ PermissionResult, PermissionResultAllow, PermissionResultDeny, ToolUseBlock,
};
use futures::future::BoxFuture;
use futures::StreamExt;
@@ -210,12 +210,14 @@ impl AiBackend for ClaudeBackend {
.permission_mode(PermissionMode::Default)
.stderr_callback(stderr_callback.clone())
.can_use_tool(can_use_tool)
+ .include_partial_messages(true)
.build()
} else {
ClaudeAgentOptions::builder()
.permission_mode(PermissionMode::Default)
.stderr_callback(stderr_callback.clone())
.can_use_tool(can_use_tool)
+ .include_partial_messages(true)
.continue_conversation(true)
.build()
};
@@ -241,11 +243,29 @@ impl AiBackend for ClaudeBackend {
match result {
Ok(message) => match message {
ClaudeMessage::Assistant(assistant_msg) => {
+ // Text is handled by StreamEvent for incremental display
for block in &assistant_msg.message.content {
- match block {
- ContentBlock::Text(TextBlock { text }) => {
+ if let ContentBlock::ToolUse(ToolUseBlock { id, name, input }) =
+ block
+ {
+ // Store for later correlation with tool result
+ pending_tools.insert(id.clone(), (name.clone(), input.clone()));
+ }
+ }
+ }
+ ClaudeMessage::StreamEvent(event) => {
+ if let Some(event_type) =
+ event.event.get("type").and_then(|v| v.as_str())
+ {
+ if event_type == "content_block_delta" {
+ if let Some(text) = event
+ .event
+ .get("delta")
+ .and_then(|d| d.get("text"))
+ .and_then(|t| t.as_str())
+ {
if let Err(err) =
- tx.send(DaveApiResponse::Token(text.clone()))
+ tx.send(DaveApiResponse::Token(text.to_string()))
{
tracing::error!("Failed to send token to UI: {}", err);
drop(stream);
@@ -254,12 +274,6 @@ impl AiBackend for ClaudeBackend {
}
ctx.request_repaint();
}
- ContentBlock::ToolUse(ToolUseBlock { id, name, input }) => {
- // Store for later correlation with tool result
- pending_tools
- .insert(id.clone(), (name.clone(), input.clone()));
- }
- _ => {}
}
}
}