assistant tools: Add `Tool::icon` method instead of matching on name (#27444)

Agus Zubiaga created

Release Notes:

- N/A

Change summary

Cargo.lock                                           |  2 +
crates/assistant2/src/active_thread.rs               | 19 -------------
crates/assistant2/src/tool_use.rs                    |  9 ++++++
crates/assistant_tool/Cargo.toml                     |  1 
crates/assistant_tool/src/assistant_tool.rs          |  4 ++
crates/assistant_tools/src/bash_tool.rs              |  5 +++
crates/assistant_tools/src/copy_path_tool.rs         |  5 +++
crates/assistant_tools/src/create_file_tool.rs       |  5 +++
crates/assistant_tools/src/delete_path_tool.rs       |  5 +++
crates/assistant_tools/src/diagnostics_tool.rs       |  5 +++
crates/assistant_tools/src/edit_files_tool.rs        |  5 +++
crates/assistant_tools/src/fetch_tool.rs             |  5 +++
crates/assistant_tools/src/find_replace_file_tool.rs |  5 +++
crates/assistant_tools/src/list_directory_tool.rs    |  5 +++
crates/assistant_tools/src/move_path_tool.rs         |  5 +++
crates/assistant_tools/src/now_tool.rs               |  5 +++
crates/assistant_tools/src/path_search_tool.rs       |  5 +++
crates/assistant_tools/src/read_file_tool.rs         |  5 +++
crates/assistant_tools/src/regex_search_tool.rs      |  5 +++
crates/assistant_tools/src/thinking_tool.rs          |  5 +++
crates/context_server/Cargo.toml                     |  1 
crates/context_server/src/context_server_tool.rs     |  5 +++
22 files changed, 98 insertions(+), 18 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -704,6 +704,7 @@ dependencies = [
  "project",
  "serde",
  "serde_json",
+ "ui",
 ]
 
 [[package]]
@@ -3206,6 +3207,7 @@ dependencies = [
  "serde_json",
  "settings",
  "smol",
+ "ui",
  "url",
  "util",
 ]

crates/assistant2/src/active_thread.rs 🔗

@@ -1251,23 +1251,6 @@ impl ActiveThread {
 
         let lighter_border = cx.theme().colors().border.opacity(0.5);
 
-        let tool_icon = match tool_use.name.as_ref() {
-            "bash" => IconName::Terminal,
-            "copy-path" => IconName::Clipboard,
-            "delete-path" => IconName::Trash,
-            "diagnostics" => IconName::Warning,
-            "edit-files" | "find-replace-file" => IconName::Pencil,
-            "fetch" => IconName::Globe,
-            "list-directory" => IconName::Folder,
-            "move-path" => IconName::ArrowRightLeft,
-            "now" => IconName::Info,
-            "path-search" => IconName::SearchCode,
-            "read-file" => IconName::Eye,
-            "regex-search" => IconName::Regex,
-            "thinking" => IconName::Brain,
-            _ => IconName::Cog,
-        };
-
         div().py_2().child(
             v_flex()
                 .rounded_lg()
@@ -1293,7 +1276,7 @@ impl ActiveThread {
                             h_flex()
                                 .gap_1p5()
                                 .child(
-                                    Icon::new(tool_icon)
+                                    Icon::new(tool_use.icon)
                                         .size(IconSize::XSmall)
                                         .color(Color::Muted),
                                 )

crates/assistant2/src/tool_use.rs 🔗

@@ -10,6 +10,7 @@ use language_model::{
     LanguageModelRequestMessage, LanguageModelToolResult, LanguageModelToolUse,
     LanguageModelToolUseId, MessageContent, Role,
 };
+use ui::IconName;
 
 use crate::thread::MessageId;
 use crate::thread_store::SerializedMessage;
@@ -21,6 +22,7 @@ pub struct ToolUse {
     pub ui_text: SharedString,
     pub status: ToolUseStatus,
     pub input: serde_json::Value,
+    pub icon: ui::IconName,
 }
 
 #[derive(Debug, Clone)]
@@ -179,12 +181,19 @@ impl ToolUseState {
                 }
             })();
 
+            let icon = if let Some(tool) = self.tools.tool(&tool_use.name, cx) {
+                tool.icon()
+            } else {
+                IconName::Cog
+            };
+
             tool_uses.push(ToolUse {
                 id: tool_use.id.clone(),
                 name: tool_use.name.clone().into(),
                 ui_text: self.tool_ui_label(&tool_use.name, &tool_use.input, cx),
                 input: tool_use.input.clone(),
                 status,
+                icon,
             })
         }
 

crates/assistant_tool/Cargo.toml 🔗

@@ -23,3 +23,4 @@ parking_lot.workspace = true
 project.workspace = true
 serde.workspace = true
 serde_json.workspace = true
+ui.workspace = true

crates/assistant_tool/src/assistant_tool.rs 🔗

@@ -9,6 +9,7 @@ use language_model::LanguageModelRequestMessage;
 use project::Project;
 use std::fmt::{self, Debug, Formatter};
 use std::sync::Arc;
+use ui::IconName;
 
 pub use crate::tool_registry::*;
 pub use crate::tool_working_set::*;
@@ -33,6 +34,9 @@ pub trait Tool: 'static + Send + Sync {
     /// Returns the description of the tool.
     fn description(&self) -> String;
 
+    /// Returns the icon for the tool.
+    fn icon(&self) -> IconName;
+
     /// Returns the source of the tool.
     fn source(&self) -> ToolSource {
         ToolSource::Native

crates/assistant_tools/src/bash_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
+use ui::IconName;
 use util::command::new_smol_command;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
@@ -31,6 +32,10 @@ impl Tool for BashTool {
         include_str!("./bash_tool/description.md").to_string()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Terminal
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(BashToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/copy_path_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct CopyPathToolInput {
@@ -47,6 +48,10 @@ impl Tool for CopyPathTool {
         include_str!("./copy_path_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Clipboard
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(CopyPathToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/create_file_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct CreateFileToolInput {
@@ -44,6 +45,10 @@ impl Tool for CreateFileTool {
         include_str!("./create_file_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::File
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(CreateFileToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/delete_path_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct DeletePathToolInput {
@@ -38,6 +39,10 @@ impl Tool for DeletePathTool {
         include_str!("./delete_path_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Trash
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(DeletePathToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/diagnostics_tool.rs 🔗

@@ -11,6 +11,7 @@ use std::{
     path::{Path, PathBuf},
     sync::Arc,
 };
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct DiagnosticsToolInput {
@@ -45,6 +46,10 @@ impl Tool for DiagnosticsTool {
         include_str!("./diagnostics_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Warning
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(DiagnosticsToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/edit_files_tool.rs 🔗

@@ -17,6 +17,7 @@ use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::fmt::Write;
 use std::sync::Arc;
+use ui::IconName;
 use util::ResultExt;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
@@ -86,6 +87,10 @@ impl Tool for EditFilesTool {
         include_str!("./edit_files_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Pencil
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(EditFilesToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/fetch_tool.rs 🔗

@@ -12,6 +12,7 @@ use language_model::LanguageModelRequestMessage;
 use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use ui::IconName;
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
 enum ContentType {
@@ -121,6 +122,10 @@ impl Tool for FetchTool {
         include_str!("./fetch_tool/description.md").to_string()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Globe
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(FetchToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/find_replace_file_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::{collections::HashSet, path::PathBuf, sync::Arc};
+use ui::IconName;
 
 use crate::replace::replace_exact;
 
@@ -135,6 +136,10 @@ impl Tool for FindReplaceFileTool {
         include_str!("find_replace_tool/description.md").to_string()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Pencil
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(FindReplaceFileToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/list_directory_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::{fmt::Write, path::Path, sync::Arc};
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct ListDirectoryToolInput {
@@ -49,6 +50,10 @@ impl Tool for ListDirectoryTool {
         include_str!("./list_directory_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Folder
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(ListDirectoryToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/move_path_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::{path::Path, sync::Arc};
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct MovePathToolInput {
@@ -47,6 +48,10 @@ impl Tool for MovePathTool {
         include_str!("./move_path_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::ArrowRightLeft
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(MovePathToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/now_tool.rs 🔗

@@ -8,6 +8,7 @@ use language_model::LanguageModelRequestMessage;
 use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 #[serde(rename_all = "snake_case")]
@@ -39,6 +40,10 @@ impl Tool for NowTool {
         "Returns the current datetime in RFC 3339 format. Only use this tool when the user specifically asks for it or the current task would benefit from knowing the current datetime.".into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Info
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(NowToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/path_search_tool.rs 🔗

@@ -6,6 +6,7 @@ use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::{path::PathBuf, sync::Arc};
+use ui::IconName;
 use util::paths::PathMatcher;
 use worktree::Snapshot;
 
@@ -47,6 +48,10 @@ impl Tool for PathSearchTool {
         include_str!("./path_search_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::SearchCode
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(PathSearchToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/read_file_tool.rs 🔗

@@ -9,6 +9,7 @@ use language_model::LanguageModelRequestMessage;
 use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct ReadFileToolInput {
@@ -52,6 +53,10 @@ impl Tool for ReadFileTool {
         include_str!("./read_file_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Eye
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(ReadFileToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/regex_search_tool.rs 🔗

@@ -11,6 +11,7 @@ use project::{
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
 use std::{cmp, fmt::Write, sync::Arc};
+use ui::IconName;
 use util::paths::PathMatcher;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
@@ -49,6 +50,10 @@ impl Tool for RegexSearchTool {
         include_str!("./regex_search_tool/description.md").into()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Regex
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(RegexSearchToolInput);
         serde_json::to_value(&schema).unwrap()

crates/assistant_tools/src/thinking_tool.rs 🔗

@@ -7,6 +7,7 @@ use language_model::LanguageModelRequestMessage;
 use project::Project;
 use schemars::JsonSchema;
 use serde::{Deserialize, Serialize};
+use ui::IconName;
 
 #[derive(Debug, Serialize, Deserialize, JsonSchema)]
 pub struct ThinkingToolInput {
@@ -30,6 +31,10 @@ impl Tool for ThinkingTool {
         include_str!("./thinking_tool/description.md").to_string()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Brain
+    }
+
     fn input_schema(&self) -> serde_json::Value {
         let schema = schemars::schema_for!(ThinkingToolInput);
         serde_json::to_value(&schema).unwrap()

crates/context_server/Cargo.toml 🔗

@@ -30,5 +30,6 @@ serde.workspace = true
 serde_json.workspace = true
 settings.workspace = true
 smol.workspace = true
+ui.workspace = true
 url = { workspace = true, features = ["serde"] }
 util.workspace = true

crates/context_server/src/context_server_tool.rs 🔗

@@ -5,6 +5,7 @@ use assistant_tool::{ActionLog, Tool, ToolSource};
 use gpui::{App, Entity, Task};
 use language_model::LanguageModelRequestMessage;
 use project::Project;
+use ui::IconName;
 
 use crate::manager::ContextServerManager;
 use crate::types;
@@ -38,6 +39,10 @@ impl Tool for ContextServerTool {
         self.tool.description.clone().unwrap_or_default()
     }
 
+    fn icon(&self) -> IconName {
+        IconName::Cog
+    }
+
     fn source(&self) -> ToolSource {
         ToolSource::ContextServer {
             id: self.server_id.clone().into(),