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