Skip to main content

adk_tool/builtin/
gemini_extra.rs

1use crate::builtin::bypass::BypassMultiToolsLimit;
2use adk_core::{Result, Tool, ToolContext};
3use async_trait::async_trait;
4use serde_json::{Value, json};
5use std::sync::Arc;
6
7/// Contextual Google Maps location used by Gemini retrieval config.
8#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct GoogleMapsContext {
10    latitude: f64,
11    longitude: f64,
12}
13
14impl GoogleMapsContext {
15    /// Create a new contextual location.
16    pub fn new(latitude: f64, longitude: f64) -> Self {
17        Self { latitude, longitude }
18    }
19
20    fn to_json(self) -> Value {
21        json!({
22            "retrievalConfig": {
23                "latLng": {
24                    "latitude": self.latitude,
25                    "longitude": self.longitude,
26                }
27            }
28        })
29    }
30}
31
32/// Gemini built-in Google Maps grounding tool.
33#[derive(Debug, Clone, Default)]
34pub struct GoogleMapsTool {
35    enable_widget: bool,
36    context: Option<GoogleMapsContext>,
37}
38
39impl GoogleMapsTool {
40    /// Create a new `GoogleMapsTool` with default settings.
41    pub fn new() -> Self {
42        Self::default()
43    }
44
45    /// Enable or disable the interactive widget in the response.
46    pub fn with_widget(mut self, enable_widget: bool) -> Self {
47        self.enable_widget = enable_widget;
48        self
49    }
50
51    /// Set the contextual location for map queries.
52    pub fn with_context(mut self, context: GoogleMapsContext) -> Self {
53        self.context = Some(context);
54        self
55    }
56}
57
58#[async_trait]
59impl Tool for GoogleMapsTool {
60    fn name(&self) -> &str {
61        "google_maps"
62    }
63
64    fn description(&self) -> &str {
65        "Grounds responses with Google Maps data for places, routes, and local context."
66    }
67
68    fn is_builtin(&self) -> bool {
69        true
70    }
71
72    fn declaration(&self) -> Value {
73        json!({
74            "name": self.name(),
75            "description": self.description(),
76            "x-adk-gemini-tool": {
77                "google_maps": {
78                    "enable_widget": self.enable_widget.then_some(true),
79                }
80            },
81            "x-adk-gemini-tool-config": self.context.map(GoogleMapsContext::to_json),
82        })
83    }
84
85    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
86        Err(adk_core::AdkError::tool("GoogleMaps is handled internally by Gemini"))
87    }
88}
89
90/// Gemini built-in code execution tool.
91#[derive(Debug, Clone, Default)]
92pub struct GeminiCodeExecutionTool;
93
94impl GeminiCodeExecutionTool {
95    /// Create a new `GeminiCodeExecutionTool`.
96    pub fn new() -> Self {
97        Self
98    }
99}
100
101#[async_trait]
102impl Tool for GeminiCodeExecutionTool {
103    fn name(&self) -> &str {
104        "gemini_code_execution"
105    }
106
107    fn description(&self) -> &str {
108        "Allows Gemini to write and execute Python code server-side."
109    }
110
111    fn is_builtin(&self) -> bool {
112        true
113    }
114
115    fn declaration(&self) -> Value {
116        json!({
117            "name": self.name(),
118            "description": self.description(),
119            "x-adk-gemini-tool": {
120                "code_execution": {}
121            }
122        })
123    }
124
125    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
126        Err(adk_core::AdkError::tool("Gemini code execution is handled internally by Gemini"))
127    }
128}
129
130/// Gemini built-in file search tool.
131#[derive(Debug, Clone)]
132pub struct GeminiFileSearchTool {
133    file_search_store_names: Vec<String>,
134}
135
136impl GeminiFileSearchTool {
137    /// Create a new `GeminiFileSearchTool` with the given store names.
138    pub fn new(file_search_store_names: impl IntoIterator<Item = impl Into<String>>) -> Self {
139        Self {
140            file_search_store_names: file_search_store_names.into_iter().map(Into::into).collect(),
141        }
142    }
143}
144
145#[async_trait]
146impl Tool for GeminiFileSearchTool {
147    fn name(&self) -> &str {
148        "gemini_file_search"
149    }
150
151    fn description(&self) -> &str {
152        "Searches Gemini File Search stores for relevant documents."
153    }
154
155    fn is_builtin(&self) -> bool {
156        true
157    }
158
159    fn declaration(&self) -> Value {
160        json!({
161            "name": self.name(),
162            "description": self.description(),
163            "x-adk-gemini-tool": {
164                "file_search": {
165                    "file_search_store_names": self.file_search_store_names
166                }
167            }
168        })
169    }
170
171    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
172        Err(adk_core::AdkError::tool("Gemini file search is handled internally by Gemini"))
173    }
174}
175
176/// Bypass support: convert the built-in Gemini File Search tool into a
177/// function-calling tool so it can be used alongside custom function tools
178/// under the Gemini Interactions API.
179///
180/// The converted tool declares a `query: string` function schema and performs
181/// the document search by delegating to an internal single-turn agent (an
182/// `LlmAgent` configured with [`GeminiFileSearchTool`] and a Gemini model).
183///
184/// # Example
185///
186/// ```rust,ignore
187/// use adk_tool::{BypassMultiToolsLimit, GeminiFileSearchTool};
188/// use std::sync::Arc;
189///
190/// // `file_search_agent` is an LlmAgent with GeminiFileSearchTool + a Gemini model.
191/// let tool = GeminiFileSearchTool::new(["my-store"])
192///     .with_bypass_multi_tools_limit(Arc::new(file_search_agent));
193/// assert!(!tool.is_builtin());
194/// ```
195impl BypassMultiToolsLimit for GeminiFileSearchTool {
196    fn bypass_name(&self) -> String {
197        self.name().to_string()
198    }
199
200    fn bypass_description(&self) -> String {
201        "Searches Gemini File Search stores for documents relevant to a query.".to_string()
202    }
203
204    fn bypass_parameters_schema(&self) -> Value {
205        json!({
206            "type": "object",
207            "properties": {
208                "query": {
209                    "type": "string",
210                    "description": "The query used to search the File Search stores."
211                }
212            },
213            "required": ["query"]
214        })
215    }
216
217    fn bypass_query_field(&self) -> String {
218        "query".to_string()
219    }
220}
221
222/// Target environment for Gemini computer use.
223#[derive(Debug, Clone, Copy, PartialEq, Eq)]
224pub enum GeminiComputerEnvironment {
225    /// Browser-based environment.
226    Browser,
227}
228
229impl GeminiComputerEnvironment {
230    fn as_wire(self) -> &'static str {
231        match self {
232            Self::Browser => "ENVIRONMENT_BROWSER",
233        }
234    }
235}
236
237/// Gemini built-in computer use tool declaration.
238#[derive(Debug, Clone)]
239pub struct GeminiComputerUseTool {
240    environment: GeminiComputerEnvironment,
241    excluded_predefined_functions: Vec<String>,
242}
243
244impl Default for GeminiComputerUseTool {
245    fn default() -> Self {
246        Self {
247            environment: GeminiComputerEnvironment::Browser,
248            excluded_predefined_functions: Vec::new(),
249        }
250    }
251}
252
253impl GeminiComputerUseTool {
254    /// Create a new `GeminiComputerUseTool` for the given environment.
255    pub fn new(environment: GeminiComputerEnvironment) -> Self {
256        Self { environment, ..Default::default() }
257    }
258
259    /// Exclude specific predefined functions from computer use.
260    pub fn with_excluded_functions(
261        mut self,
262        excluded_predefined_functions: impl IntoIterator<Item = impl Into<String>>,
263    ) -> Self {
264        self.excluded_predefined_functions =
265            excluded_predefined_functions.into_iter().map(Into::into).collect();
266        self
267    }
268}
269
270#[async_trait]
271impl Tool for GeminiComputerUseTool {
272    fn name(&self) -> &str {
273        "gemini_computer_use"
274    }
275
276    fn description(&self) -> &str {
277        "Enables Gemini computer use, which emits predefined UI action function calls."
278    }
279
280    fn is_builtin(&self) -> bool {
281        true
282    }
283
284    fn declaration(&self) -> Value {
285        json!({
286            "name": self.name(),
287            "description": self.description(),
288            "x-adk-gemini-tool": {
289                "computer_use": {
290                    "environment": self.environment.as_wire(),
291                    "excluded_predefined_functions": (!self.excluded_predefined_functions.is_empty())
292                        .then_some(self.excluded_predefined_functions.clone()),
293                }
294            }
295        })
296    }
297
298    async fn execute(&self, _ctx: Arc<dyn ToolContext>, _args: Value) -> Result<Value> {
299        Err(adk_core::AdkError::tool("Gemini computer use actions must be executed client-side"))
300    }
301}