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(®istry, 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}