Handle streaming input in title updates

Co-authored-by: Antonio Scandurra <me@as-cii.com>
This commit is contained in:
Ben Brandt 2025-08-08 14:57:38 +02:00
parent ebc7df2c2e
commit 320312fa25
No known key found for this signature in database
GPG key ID: D4618C5D3B500571
2 changed files with 49 additions and 12 deletions

View file

@ -647,6 +647,19 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
let mut events = thread.update(cx, |thread, cx| thread.send(model.clone(), "Think", cx)); let mut events = thread.update(cx, |thread, cx| thread.send(model.clone(), "Think", cx));
cx.run_until_parked(); cx.run_until_parked();
// Simulate streaming partial input.
let input = json!({});
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
LanguageModelToolUse {
id: "1".into(),
name: ThinkingTool.name().into(),
raw_input: input.to_string(),
input,
is_input_complete: false,
},
));
// Input streaming completed
let input = json!({ "content": "Thinking hard!" }); let input = json!({ "content": "Thinking hard!" });
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse( fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
LanguageModelToolUse { LanguageModelToolUse {
@ -665,12 +678,12 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
tool_call, tool_call,
acp::ToolCall { acp::ToolCall {
id: acp::ToolCallId("1".into()), id: acp::ToolCallId("1".into()),
title: "Thinking".into(), title: "thinking".into(),
kind: acp::ToolKind::Think, kind: acp::ToolKind::Think,
status: acp::ToolCallStatus::Pending, status: acp::ToolCallStatus::Pending,
content: vec![], content: vec![],
locations: vec![], locations: vec![],
raw_input: Some(json!({ "content": "Thinking hard!" })), raw_input: Some(json!({})),
raw_output: None, raw_output: None,
} }
); );
@ -680,7 +693,20 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
acp::ToolCallUpdate { acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()), id: acp::ToolCallId("1".into()),
fields: acp::ToolCallUpdateFields { fields: acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::InProgress,), title: Some("Thinking".into()),
kind: Some(acp::ToolKind::Think),
raw_input: Some(json!({ "content": "Thinking hard!" })),
..Default::default()
},
}
);
let update = expect_tool_call_update(&mut events).await;
assert_eq!(
update,
acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()),
fields: acp::ToolCallUpdateFields {
status: Some(acp::ToolCallStatus::InProgress),
..Default::default() ..Default::default()
}, },
} }

View file

@ -474,8 +474,17 @@ impl Thread {
} }
}); });
let mut title = SharedString::from(&tool_use.name);
let mut kind = acp::ToolKind::Other;
if let Some(tool) = tool.as_ref() {
if let Ok(initial_title) = tool.initial_title(tool_use.input.clone()) {
title = initial_title;
}
kind = tool.kind();
}
if push_new_tool_use { if push_new_tool_use {
event_stream.send_tool_call(tool.as_ref(), &tool_use); event_stream.send_tool_call(&tool_use.id, title, kind, tool_use.input.clone());
last_message last_message
.content .content
.push(MessageContent::ToolUse(tool_use.clone())); .push(MessageContent::ToolUse(tool_use.clone()));
@ -483,6 +492,8 @@ impl Thread {
event_stream.send_tool_call_update( event_stream.send_tool_call_update(
&tool_use.id, &tool_use.id,
acp::ToolCallUpdateFields { acp::ToolCallUpdateFields {
title: Some(title.into()),
kind: Some(kind),
raw_input: Some(tool_use.input.clone()), raw_input: Some(tool_use.input.clone()),
..Default::default() ..Default::default()
}, },
@ -842,17 +853,17 @@ impl AgentResponseEventStream {
fn send_tool_call( fn send_tool_call(
&self, &self,
tool: Option<&Arc<dyn AnyAgentTool>>, id: &LanguageModelToolUseId,
tool_use: &LanguageModelToolUse, title: SharedString,
kind: acp::ToolKind,
input: serde_json::Value,
) { ) {
self.0 self.0
.unbounded_send(Ok(AgentResponseEvent::ToolCall(Self::initial_tool_call( .unbounded_send(Ok(AgentResponseEvent::ToolCall(Self::initial_tool_call(
&tool_use.id, id,
tool.and_then(|t| t.initial_title(tool_use.input.clone()).ok()) title.to_string(),
.map(|i| i.into()) kind,
.unwrap_or_else(|| tool_use.name.to_string()), input,
tool.map(|t| t.kind()).unwrap_or(acp::ToolKind::Other),
tool_use.input.clone(),
)))) ))))
.ok(); .ok();
} }