assistant_tools.rs

  1mod copy_path_tool;
  2mod create_directory_tool;
  3mod delete_path_tool;
  4mod diagnostics_tool;
  5mod edit_agent;
  6mod edit_file_tool;
  7mod fetch_tool;
  8mod find_path_tool;
  9mod grep_tool;
 10mod list_directory_tool;
 11mod move_path_tool;
 12mod now_tool;
 13mod open_tool;
 14mod project_notifications_tool;
 15mod read_file_tool;
 16mod schema;
 17mod templates;
 18mod terminal_tool;
 19mod thinking_tool;
 20mod ui;
 21mod web_search_tool;
 22
 23use std::sync::Arc;
 24
 25use assistant_tool::ToolRegistry;
 26use copy_path_tool::CopyPathTool;
 27use gpui::{App, Entity};
 28use http_client::HttpClientWithUrl;
 29use language_model::LanguageModelRegistry;
 30use move_path_tool::MovePathTool;
 31use web_search_tool::WebSearchTool;
 32
 33pub(crate) use templates::*;
 34
 35use crate::create_directory_tool::CreateDirectoryTool;
 36use crate::delete_path_tool::DeletePathTool;
 37use crate::diagnostics_tool::DiagnosticsTool;
 38use crate::edit_file_tool::EditFileTool;
 39use crate::fetch_tool::FetchTool;
 40use crate::find_path_tool::FindPathTool;
 41use crate::list_directory_tool::ListDirectoryTool;
 42use crate::now_tool::NowTool;
 43use crate::thinking_tool::ThinkingTool;
 44
 45pub use edit_file_tool::{EditFileMode, EditFileToolInput};
 46pub use find_path_tool::FindPathToolInput;
 47pub use grep_tool::{GrepTool, GrepToolInput};
 48pub use open_tool::OpenTool;
 49pub use project_notifications_tool::ProjectNotificationsTool;
 50pub use read_file_tool::{ReadFileTool, ReadFileToolInput};
 51pub use terminal_tool::TerminalTool;
 52
 53pub fn init(http_client: Arc<HttpClientWithUrl>, cx: &mut App) {
 54    assistant_tool::init(cx);
 55
 56    let registry = ToolRegistry::global(cx);
 57    registry.register_tool(TerminalTool::new(cx));
 58    registry.register_tool(CreateDirectoryTool);
 59    registry.register_tool(CopyPathTool);
 60    registry.register_tool(DeletePathTool);
 61    registry.register_tool(MovePathTool);
 62    registry.register_tool(DiagnosticsTool);
 63    registry.register_tool(ListDirectoryTool);
 64    registry.register_tool(NowTool);
 65    registry.register_tool(OpenTool);
 66    registry.register_tool(ProjectNotificationsTool);
 67    registry.register_tool(FindPathTool);
 68    registry.register_tool(ReadFileTool);
 69    registry.register_tool(GrepTool);
 70    registry.register_tool(ThinkingTool);
 71    registry.register_tool(FetchTool::new(http_client));
 72    registry.register_tool(EditFileTool);
 73
 74    register_web_search_tool(&LanguageModelRegistry::global(cx), cx);
 75    cx.subscribe(
 76        &LanguageModelRegistry::global(cx),
 77        move |registry, event, cx| match event {
 78            language_model::Event::DefaultModelChanged => {
 79                register_web_search_tool(&registry, cx);
 80            }
 81            _ => {}
 82        },
 83    )
 84    .detach();
 85}
 86
 87fn register_web_search_tool(registry: &Entity<LanguageModelRegistry>, cx: &mut App) {
 88    let using_zed_provider = registry
 89        .read(cx)
 90        .default_model()
 91        .map_or(false, |default| default.is_provided_by_zed());
 92    if using_zed_provider {
 93        ToolRegistry::global(cx).register_tool(WebSearchTool);
 94    } else {
 95        ToolRegistry::global(cx).unregister_tool(WebSearchTool);
 96    }
 97}
 98
 99#[cfg(test)]
100mod tests {
101    use super::*;
102    use agent_settings::AgentSettings;
103    use client::Client;
104    use clock::FakeSystemClock;
105    use http_client::FakeHttpClient;
106    use schemars::JsonSchema;
107    use serde::Serialize;
108    use settings::Settings;
109
110    #[test]
111    fn test_json_schema() {
112        #[derive(Serialize, JsonSchema)]
113        struct GetWeatherTool {
114            location: String,
115        }
116
117        let schema = schema::json_schema_for::<GetWeatherTool>(
118            language_model::LanguageModelToolSchemaFormat::JsonSchema,
119        )
120        .unwrap();
121
122        assert_eq!(
123            schema,
124            serde_json::json!({
125                "type": "object",
126                "properties": {
127                    "location": {
128                        "type": "string"
129                    }
130                },
131                "required": ["location"],
132                "additionalProperties": false
133            })
134        );
135    }
136
137    #[gpui::test]
138    fn test_builtin_tool_schema_compatibility(cx: &mut App) {
139        settings::init(cx);
140        AgentSettings::register(cx);
141
142        let client = Client::new(
143            Arc::new(FakeSystemClock::new()),
144            FakeHttpClient::with_200_response(),
145            cx,
146        );
147        language_model::init(client.clone(), cx);
148        crate::init(client.http_client(), cx);
149
150        for tool in ToolRegistry::global(cx).tools() {
151            let actual_schema = tool
152                .input_schema(language_model::LanguageModelToolSchemaFormat::JsonSchemaSubset)
153                .unwrap();
154            let mut expected_schema = actual_schema.clone();
155            assistant_tool::adapt_schema_to_format(
156                &mut expected_schema,
157                language_model::LanguageModelToolSchemaFormat::JsonSchemaSubset,
158            )
159            .unwrap();
160
161            let error_message = format!(
162                "Tool schema for `{}` is not compatible with `language_model::LanguageModelToolSchemaFormat::JsonSchemaSubset` (Gemini Models).\n\
163                Are you using `schema::json_schema_for<T>(format)` to generate the schema?",
164                tool.name(),
165            );
166
167            assert_eq!(actual_schema, expected_schema, "{}", error_message)
168        }
169    }
170}