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