Add tool calling support for Gemini models (#27772)

Release Notes:

- N/A
This commit is contained in:
Bennet Bo Fenner
2025-03-31 17:46:42 +02:00
committed by GitHub
parent f6d58f76e4
commit c8a9a74e6a
32 changed files with 735 additions and 251 deletions
+4 -1
View File
@@ -1787,12 +1787,13 @@ impl ActiveThread {
fn handle_deny_tool(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_name: Arc<str>,
_: &ClickEvent,
_window: &mut Window,
cx: &mut Context<Self>,
) {
self.thread.update(cx, |thread, cx| {
thread.deny_tool_use(tool_use_id, cx);
thread.deny_tool_use(tool_use_id, tool_name, cx);
});
}
@@ -1865,10 +1866,12 @@ impl ActiveThread {
})
.child({
let tool_id = tool.id.clone();
let tool_name = tool.name.clone();
Button::new("deny-tool", "Deny").on_click(cx.listener(
move |this, event, window, cx| {
this.handle_deny_tool(
tool_id.clone(),
tool_name.clone(),
event,
window,
cx,
+29 -14
View File
@@ -777,7 +777,7 @@ impl Thread {
LanguageModelRequestTool {
name: tool.name(),
description: tool.description(),
input_schema: tool.input_schema(),
input_schema: tool.input_schema(model.tool_input_format()),
}
}));
@@ -1030,17 +1030,23 @@ impl Thread {
}
}
LanguageModelCompletionEvent::ToolUse(tool_use) => {
if let Some(last_assistant_message) = thread
let last_assistant_message_id = thread
.messages
.iter()
.rfind(|message| message.role == Role::Assistant)
{
thread.tool_use.request_tool_use(
last_assistant_message.id,
tool_use,
cx,
);
}
.map(|message| message.id)
.unwrap_or_else(|| {
thread.insert_message(
Role::Assistant,
vec![MessageSegment::Text("Using tool...".to_string())],
cx,
)
});
thread.tool_use.request_tool_use(
last_assistant_message_id,
tool_use,
cx,
);
}
}
@@ -1257,6 +1263,7 @@ impl Thread {
tool: Arc<dyn Tool>,
cx: &mut Context<Thread>,
) -> Task<()> {
let tool_name: Arc<str> = tool.name().into();
let run_tool = tool.run(
input,
messages,
@@ -1271,9 +1278,11 @@ impl Thread {
thread
.update(cx, |thread, cx| {
let pending_tool_use = thread
.tool_use
.insert_tool_output(tool_use_id.clone(), output);
let pending_tool_use = thread.tool_use.insert_tool_output(
tool_use_id.clone(),
tool_name,
output,
);
cx.emit(ThreadEvent::ToolFinished {
tool_use_id,
@@ -1561,12 +1570,18 @@ impl Thread {
self.cumulative_token_usage.clone()
}
pub fn deny_tool_use(&mut self, tool_use_id: LanguageModelToolUseId, cx: &mut Context<Self>) {
pub fn deny_tool_use(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_name: Arc<str>,
cx: &mut Context<Self>,
) {
let err = Err(anyhow::anyhow!(
"Permission to run tool action denied by user"
));
self.tool_use.insert_tool_output(tool_use_id.clone(), err);
self.tool_use
.insert_tool_output(tool_use_id.clone(), tool_name, err);
cx.emit(ThreadEvent::ToolFinished {
tool_use_id,
+6
View File
@@ -113,6 +113,7 @@ impl ToolUseState {
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id,
tool_name: tool_use.clone(),
is_error: tool_result.is_error,
content: tool_result.content.clone(),
},
@@ -134,6 +135,7 @@ impl ToolUseState {
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id,
tool_name: tool_use.name.clone(),
content: "Tool canceled by user".into(),
is_error: true,
},
@@ -313,6 +315,7 @@ impl ToolUseState {
pub fn insert_tool_output(
&mut self,
tool_use_id: LanguageModelToolUseId,
tool_name: Arc<str>,
output: Result<String>,
) -> Option<PendingToolUse> {
match output {
@@ -321,6 +324,7 @@ impl ToolUseState {
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content: tool_result.into(),
is_error: false,
},
@@ -332,6 +336,7 @@ impl ToolUseState {
tool_use_id.clone(),
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name,
content: err.to_string().into(),
is_error: true,
},
@@ -379,6 +384,7 @@ impl ToolUseState {
request_message.content.push(MessageContent::ToolResult(
LanguageModelToolResult {
tool_use_id: tool_use_id.clone(),
tool_name: tool_result.tool_name.clone(),
is_error: tool_result.is_error,
content: if tool_result.content.is_empty() {
// Surprisingly, the API fails if we return an empty string here.