From afc61b9527165292f1cf0fba608dc22379dd12d3 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Wed, 26 Feb 2025 19:37:44 -0500 Subject: [PATCH] assistant2: Automatically respond to the model with tool results (#25706) This PR updates the tool use flow in Assistant 2 to automatically respond to the model with tool results when the tools have finished running. Release Notes: - N/A --- crates/assistant2/src/active_thread.rs | 33 +++++++++++++++++++++++--- crates/assistant2/src/thread.rs | 12 ++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/crates/assistant2/src/active_thread.rs b/crates/assistant2/src/active_thread.rs index 0d4f7c062a41266e491e17e6ca8ca9144c4e6416..63b6fe64d8afd0a1af627c7d33384900ad0cd14d 100644 --- a/crates/assistant2/src/active_thread.rs +++ b/crates/assistant2/src/active_thread.rs @@ -8,14 +8,16 @@ use gpui::{ UnderlineStyle, WeakEntity, }; use language::LanguageRegistry; -use language_model::{LanguageModelToolUseId, Role}; +use language_model::{LanguageModelRegistry, LanguageModelToolUseId, Role}; use markdown::{Markdown, MarkdownStyle}; use settings::Settings as _; use theme::ThemeSettings; use ui::{prelude::*, Disclosure}; use workspace::Workspace; -use crate::thread::{MessageId, Thread, ThreadError, ThreadEvent, ToolUse, ToolUseStatus}; +use crate::thread::{ + MessageId, RequestKind, Thread, ThreadError, ThreadEvent, ToolUse, ToolUseStatus, +}; use crate::thread_store::ThreadStore; use crate::ui::ContextPill; @@ -263,7 +265,24 @@ impl ActiveThread { } } } - ThreadEvent::ToolFinished { .. } => {} + ThreadEvent::ToolFinished { .. } => { + let all_tools_finished = self + .thread + .read(cx) + .pending_tool_uses() + .into_iter() + .all(|tool_use| tool_use.status.is_error()); + if all_tools_finished { + let model_registry = LanguageModelRegistry::read_global(cx); + if let Some(model) = model_registry.active_model() { + self.thread.update(cx, |thread, cx| { + // Insert an empty user message to contain the tool results. + thread.insert_user_message("", Vec::new(), cx); + thread.send_to_model(model, RequestKind::Chat, true, cx); + }); + } + } + } } } @@ -281,6 +300,14 @@ impl ActiveThread { let tool_uses = self.thread.read(cx).tool_uses_for_message(message_id); let colors = cx.theme().colors(); + // Don't render user messages that are just there for returning tool results. + if message.role == Role::User + && message.text.is_empty() + && self.thread.read(cx).message_has_tool_results(message_id) + { + return Empty.into_any(); + } + let message_content = v_flex() .child(div().p_2p5().text_ui(cx).child(markdown.clone())) .when_some(context, |parent, context| { diff --git a/crates/assistant2/src/thread.rs b/crates/assistant2/src/thread.rs index 2b519194540fcfe5ff48911cd37b01fee2de10db..d9af2a4c1ddb4c4bca5bee57db81f751a66eb90c 100644 --- a/crates/assistant2/src/thread.rs +++ b/crates/assistant2/src/thread.rs @@ -263,6 +263,12 @@ impl Thread { tool_uses } + pub fn message_has_tool_results(&self, message_id: MessageId) -> bool { + self.tool_results_by_message + .get(&message_id) + .map_or(false, |results| !results.is_empty()) + } + pub fn insert_user_message( &mut self, text: impl Into, @@ -649,6 +655,8 @@ impl Thread { { tool_use.status = PendingToolUseStatus::Error(err.to_string()); } + + cx.emit(ThreadEvent::ToolFinished { tool_use_id }); } } }) @@ -724,4 +732,8 @@ impl PendingToolUseStatus { pub fn is_idle(&self) -> bool { matches!(self, PendingToolUseStatus::Idle) } + + pub fn is_error(&self) -> bool { + matches!(self, PendingToolUseStatus::Error(_)) + } }