gemini_rust/tools/
model.rs

1use serde::{Deserialize, Serialize};
2use snafu::{ResultExt, Snafu};
3use std::collections::HashMap;
4
5/// Tool that can be used by the model
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
7#[serde(untagged)]
8pub enum Tool {
9    /// Function-based tool
10    Function {
11        /// The function declaration for the tool
12        function_declarations: Vec<FunctionDeclaration>,
13    },
14    /// Google Search tool
15    GoogleSearch {
16        /// The Google Search configuration
17        google_search: GoogleSearchConfig,
18    },
19}
20
21/// Empty configuration for Google Search tool
22#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23pub struct GoogleSearchConfig {}
24
25impl Tool {
26    /// Create a new tool with a single function declaration
27    pub fn new(function_declaration: FunctionDeclaration) -> Self {
28        Self::Function {
29            function_declarations: vec![function_declaration],
30        }
31    }
32
33    /// Create a new tool with multiple function declarations
34    pub fn with_functions(function_declarations: Vec<FunctionDeclaration>) -> Self {
35        Self::Function {
36            function_declarations,
37        }
38    }
39
40    /// Create a new Google Search tool
41    pub fn google_search() -> Self {
42        Self::GoogleSearch {
43            google_search: GoogleSearchConfig {},
44        }
45    }
46}
47
48/// Declaration of a function that can be called by the model
49#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
50pub struct FunctionDeclaration {
51    /// The name of the function
52    pub name: String,
53    /// The description of the function
54    pub description: String,
55    /// The parameters for the function
56    pub parameters: FunctionParameters,
57}
58
59impl FunctionDeclaration {
60    /// Create a new function declaration
61    pub fn new(
62        name: impl Into<String>,
63        description: impl Into<String>,
64        parameters: FunctionParameters,
65    ) -> Self {
66        Self {
67            name: name.into(),
68            description: description.into(),
69            parameters,
70        }
71    }
72}
73
74/// Parameters for a function
75#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
76pub struct FunctionParameters {
77    /// The type of the parameters
78    #[serde(rename = "type")]
79    pub param_type: String,
80    /// The properties of the parameters
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub properties: Option<HashMap<String, PropertyDetails>>,
83    /// The required properties
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub required: Option<Vec<String>>,
86}
87
88impl FunctionParameters {
89    /// Create a new object parameter set
90    pub fn object() -> Self {
91        Self {
92            param_type: "object".to_string(),
93            properties: Some(HashMap::new()),
94            required: Some(Vec::new()),
95        }
96    }
97
98    /// Add a property to the parameters
99    pub fn with_property(
100        mut self,
101        name: impl Into<String>,
102        details: PropertyDetails,
103        required: bool,
104    ) -> Self {
105        let name = name.into();
106        if let Some(props) = &mut self.properties {
107            props.insert(name.clone(), details);
108        }
109        if required {
110            if let Some(req) = &mut self.required {
111                req.push(name);
112            }
113        }
114        self
115    }
116}
117
118/// Details about a property
119#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
120pub struct PropertyDetails {
121    /// The type of the property
122    #[serde(rename = "type")]
123    pub property_type: String,
124    /// The description of the property
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub description: Option<String>,
127    /// The enum values if the property is an enum
128    #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
129    pub enum_values: Option<Vec<String>>,
130    /// The items if the property is an array
131    #[serde(skip_serializing_if = "Option::is_none")]
132    pub items: Option<Box<PropertyDetails>>,
133}
134
135impl PropertyDetails {
136    /// Create a new string property
137    pub fn string(description: impl Into<String>) -> Self {
138        Self {
139            property_type: "string".to_string(),
140            description: Some(description.into()),
141            enum_values: None,
142            items: None,
143        }
144    }
145
146    /// Create a new number property
147    pub fn number(description: impl Into<String>) -> Self {
148        Self {
149            property_type: "number".to_string(),
150            description: Some(description.into()),
151            enum_values: None,
152            items: None,
153        }
154    }
155
156    /// Create a new integer property
157    pub fn integer(description: impl Into<String>) -> Self {
158        Self {
159            property_type: "integer".to_string(),
160            description: Some(description.into()),
161            enum_values: None,
162            items: None,
163        }
164    }
165
166    /// Create a new boolean property
167    pub fn boolean(description: impl Into<String>) -> Self {
168        Self {
169            property_type: "boolean".to_string(),
170            description: Some(description.into()),
171            enum_values: None,
172            items: None,
173        }
174    }
175
176    /// Create a new array property
177    pub fn array(description: impl Into<String>, items: PropertyDetails) -> Self {
178        Self {
179            property_type: "array".to_string(),
180            description: Some(description.into()),
181            enum_values: None,
182            items: Some(Box::new(items)),
183        }
184    }
185
186    /// Create a new enum property
187    pub fn enum_type(
188        description: impl Into<String>,
189        enum_values: impl IntoIterator<Item = impl Into<String>>,
190    ) -> Self {
191        Self {
192            property_type: "string".to_string(),
193            description: Some(description.into()),
194            enum_values: Some(enum_values.into_iter().map(|s| s.into()).collect()),
195            items: None,
196        }
197    }
198}
199/// A function call made by the model
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
201pub struct FunctionCall {
202    /// The name of the function
203    pub name: String,
204    /// The arguments for the function
205    pub args: serde_json::Value,
206    /// The thought signature for the function call (Gemini 2.5 series only)
207    #[serde(skip_serializing_if = "Option::is_none")]
208    pub thought_signature: Option<String>,
209}
210
211#[derive(Debug, Snafu)]
212pub enum FunctionCallError {
213    #[snafu(display("failed to deserialize parameter '{key}'"))]
214    Deserialization {
215        source: serde_json::Error,
216        key: String,
217    },
218
219    #[snafu(display("parameter '{key}' is missing in arguments '{args}'"))]
220    MissingParameter {
221        key: String,
222        args: serde_json::Value,
223    },
224
225    #[snafu(display("arguments should be an object; actual: {actual}"))]
226    ArgumentTypeMismatch { actual: String },
227}
228
229impl FunctionCall {
230    /// Create a new function call
231    pub fn new(name: impl Into<String>, args: serde_json::Value) -> Self {
232        Self {
233            name: name.into(),
234            args,
235            thought_signature: None,
236        }
237    }
238
239    /// Create a new function call with thought signature
240    pub fn with_thought_signature(
241        name: impl Into<String>,
242        args: serde_json::Value,
243        thought_signature: impl Into<String>,
244    ) -> Self {
245        Self {
246            name: name.into(),
247            args,
248            thought_signature: Some(thought_signature.into()),
249        }
250    }
251
252    /// Get a parameter from the arguments
253    pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T, FunctionCallError> {
254        match &self.args {
255            serde_json::Value::Object(obj) => {
256                if let Some(value) = obj.get(key) {
257                    serde_json::from_value(value.clone()).with_context(|_| DeserializationSnafu {
258                        key: key.to_string(),
259                    })
260                } else {
261                    Err(MissingParameterSnafu {
262                        key: key.to_string(),
263                        args: self.args.clone(),
264                    }
265                    .build())
266                }
267            }
268            _ => Err(ArgumentTypeMismatchSnafu {
269                actual: self.args.to_string(),
270            }
271            .build()),
272        }
273    }
274}
275
276/// A response from a function
277#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
278pub struct FunctionResponse {
279    /// The name of the function
280    pub name: String,
281    /// The response from the function
282    /// This must be a valid JSON object
283    #[serde(skip_serializing_if = "Option::is_none")]
284    pub response: Option<serde_json::Value>,
285}
286
287impl FunctionResponse {
288    /// Create a new function response with a JSON value
289    pub fn new(name: impl Into<String>, response: serde_json::Value) -> Self {
290        Self {
291            name: name.into(),
292            response: Some(response),
293        }
294    }
295
296    /// Create a new function response with a string that will be parsed as JSON
297    pub fn from_str(
298        name: impl Into<String>,
299        response: impl Into<String>,
300    ) -> Result<Self, serde_json::Error> {
301        let json = serde_json::from_str(&response.into())?;
302        Ok(Self {
303            name: name.into(),
304            response: Some(json),
305        })
306    }
307}
308
309/// Configuration for tools
310#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
311pub struct ToolConfig {
312    /// The function calling config
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub function_calling_config: Option<FunctionCallingConfig>,
315}
316
317/// Configuration for function calling
318#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
319pub struct FunctionCallingConfig {
320    /// The mode for function calling
321    pub mode: FunctionCallingMode,
322}
323
324/// Mode for function calling
325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
326#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
327pub enum FunctionCallingMode {
328    /// The model may use function calling
329    Auto,
330    /// The model must use function calling
331    Any,
332    /// The model must not use function calling
333    None,
334}