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));
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!" });
fake_model.send_last_completion_stream_event(LanguageModelCompletionEvent::ToolUse(
LanguageModelToolUse {
@ -665,12 +678,12 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
tool_call,
acp::ToolCall {
id: acp::ToolCallId("1".into()),
title: "Thinking".into(),
title: "thinking".into(),
kind: acp::ToolKind::Think,
status: acp::ToolCallStatus::Pending,
content: vec![],
locations: vec![],
raw_input: Some(json!({ "content": "Thinking hard!" })),
raw_input: Some(json!({})),
raw_output: None,
}
);
@ -680,7 +693,20 @@ async fn test_tool_updates_to_completion(cx: &mut TestAppContext) {
acp::ToolCallUpdate {
id: acp::ToolCallId("1".into()),
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()
},
}

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