gemini_rust/
tools.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Tool that can be used by the model
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(untagged)]
7pub enum Tool {
8    /// Function-based tool
9    Function {
10        /// The function declaration for the tool
11        function_declarations: Vec<FunctionDeclaration>,
12    },
13    /// Google Search tool
14    GoogleSearch {
15        /// The Google Search configuration
16        google_search: GoogleSearchConfig,
17    },
18}
19
20/// Empty configuration for Google Search tool
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct GoogleSearchConfig {}
23
24impl Tool {
25    /// Create a new tool with a single function declaration
26    pub fn new(function_declaration: FunctionDeclaration) -> Self {
27        Self::Function {
28            function_declarations: vec![function_declaration],
29        }
30    }
31
32    /// Create a new tool with multiple function declarations
33    pub fn with_functions(function_declarations: Vec<FunctionDeclaration>) -> Self {
34        Self::Function {
35            function_declarations,
36        }
37    }
38
39    /// Create a new Google Search tool
40    pub fn google_search() -> Self {
41        Self::GoogleSearch {
42            google_search: GoogleSearchConfig {},
43        }
44    }
45}
46
47/// Declaration of a function that can be called by the model
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct FunctionDeclaration {
50    /// The name of the function
51    pub name: String,
52    /// The description of the function
53    pub description: String,
54    /// The parameters for the function
55    pub parameters: FunctionParameters,
56}
57
58impl FunctionDeclaration {
59    /// Create a new function declaration
60    pub fn new(
61        name: impl Into<String>,
62        description: impl Into<String>,
63        parameters: FunctionParameters,
64    ) -> Self {
65        Self {
66            name: name.into(),
67            description: description.into(),
68            parameters,
69        }
70    }
71}
72
73/// Parameters for a function
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct FunctionParameters {
76    /// The type of the parameters
77    #[serde(rename = "type")]
78    pub param_type: String,
79    /// The properties of the parameters
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub properties: Option<HashMap<String, PropertyDetails>>,
82    /// The required properties
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub required: Option<Vec<String>>,
85}
86
87impl FunctionParameters {
88    /// Create a new object parameter set
89    pub fn object() -> Self {
90        Self {
91            param_type: "OBJECT".to_string(),
92            properties: Some(HashMap::new()),
93            required: Some(Vec::new()),
94        }
95    }
96
97    /// Add a property to the parameters
98    pub fn with_property(
99        mut self,
100        name: impl Into<String>,
101        details: PropertyDetails,
102        required: bool,
103    ) -> Self {
104        let name = name.into();
105        if let Some(props) = &mut self.properties {
106            props.insert(name.clone(), details);
107        }
108        if required {
109            if let Some(req) = &mut self.required {
110                req.push(name);
111            }
112        }
113        self
114    }
115}
116
117/// Details about a property
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct PropertyDetails {
120    /// The type of the property
121    #[serde(rename = "type")]
122    pub property_type: String,
123    /// The description of the property
124    pub description: String,
125    /// The enum values if the property is an enum
126    #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
127    pub enum_values: Option<Vec<String>>,
128    /// The items if the property is an array
129    #[serde(skip_serializing_if = "Option::is_none")]
130    pub items: Option<Box<PropertyDetails>>,
131}
132
133impl PropertyDetails {
134    /// Create a new string property
135    pub fn string(description: impl Into<String>) -> Self {
136        Self {
137            property_type: "STRING".to_string(),
138            description: description.into(),
139            enum_values: None,
140            items: None,
141        }
142    }
143
144    /// Create a new number property
145    pub fn number(description: impl Into<String>) -> Self {
146        Self {
147            property_type: "NUMBER".to_string(),
148            description: description.into(),
149            enum_values: None,
150            items: None,
151        }
152    }
153
154    /// Create a new integer property
155    pub fn integer(description: impl Into<String>) -> Self {
156        Self {
157            property_type: "INTEGER".to_string(),
158            description: description.into(),
159            enum_values: None,
160            items: None,
161        }
162    }
163
164    /// Create a new boolean property
165    pub fn boolean(description: impl Into<String>) -> Self {
166        Self {
167            property_type: "BOOLEAN".to_string(),
168            description: description.into(),
169            enum_values: None,
170            items: None,
171        }
172    }
173
174    /// Create a new array property
175    pub fn array(description: impl Into<String>, items: PropertyDetails) -> Self {
176        Self {
177            property_type: "ARRAY".to_string(),
178            description: description.into(),
179            enum_values: None,
180            items: Some(Box::new(items)),
181        }
182    }
183
184    /// Create a new enum property
185    pub fn enum_type(
186        description: impl Into<String>,
187        enum_values: impl IntoIterator<Item = impl Into<String>>,
188    ) -> Self {
189        Self {
190            property_type: "STRING".to_string(),
191            description: description.into(),
192            enum_values: Some(enum_values.into_iter().map(|s| s.into()).collect()),
193            items: None,
194        }
195    }
196}
197
198/// A function call made by the model
199#[derive(Debug, Clone, Serialize, Deserialize)]
200pub struct FunctionCall {
201    /// The name of the function
202    pub name: String,
203    /// The arguments for the function
204    pub args: serde_json::Value,
205}
206
207impl FunctionCall {
208    /// Create a new function call
209    pub fn new(name: impl Into<String>, args: serde_json::Value) -> Self {
210        Self {
211            name: name.into(),
212            args,
213        }
214    }
215
216    /// Get a parameter from the arguments
217    pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> crate::Result<T> {
218        match &self.args {
219            serde_json::Value::Object(obj) => {
220                if let Some(value) = obj.get(key) {
221                    serde_json::from_value(value.clone()).map_err(|e| {
222                        crate::Error::FunctionCallError(format!(
223                            "Error deserializing parameter {}: {}",
224                            key, e
225                        ))
226                    })
227                } else {
228                    Err(crate::Error::FunctionCallError(format!(
229                        "Missing parameter: {}",
230                        key
231                    )))
232                }
233            }
234            _ => Err(crate::Error::FunctionCallError(
235                "Arguments are not an object".to_string(),
236            )),
237        }
238    }
239}
240
241/// A response from a function
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct FunctionResponse {
244    /// The name of the function
245    pub name: String,
246    /// The response from the function
247    /// This must be a valid JSON object
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub response: Option<serde_json::Value>,
250}
251
252impl FunctionResponse {
253    /// Create a new function response with a JSON value
254    pub fn new(name: impl Into<String>, response: serde_json::Value) -> Self {
255        Self {
256            name: name.into(),
257            response: Some(response),
258        }
259    }
260
261    /// Create a new function response with a string that will be parsed as JSON
262    pub fn from_str(
263        name: impl Into<String>,
264        response: impl Into<String>,
265    ) -> Result<Self, serde_json::Error> {
266        let json = serde_json::from_str(&response.into())?;
267        Ok(Self {
268            name: name.into(),
269            response: Some(json),
270        })
271    }
272}