1use schemars::{JsonSchema, SchemaGenerator, generate::SchemaSettings};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use snafu::{ResultExt, Snafu};
5
6#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
8#[serde(untagged)]
9pub enum Tool {
10 Function {
12 #[serde(rename = "functionDeclarations")]
14 function_declarations: Vec<FunctionDeclaration>,
15 },
16 GoogleSearch {
18 google_search: GoogleSearchConfig,
20 },
21 GoogleMaps {
23 google_maps: Value,
25 },
26 CodeExecution {
28 code_execution: Value,
30 },
31 URLContext {
33 url_context: URLContextConfig,
35 },
36 FileSearch {
38 file_search: Value,
40 },
41 ComputerUse {
43 computer_use: Value,
45 },
46 McpServer {
48 #[serde(rename = "mcp_server")]
50 mcp_server: Value,
51 },
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
56pub struct GoogleSearchConfig {}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct URLContextConfig {}
61
62impl Tool {
63 pub fn new(function_declaration: FunctionDeclaration) -> Self {
65 Self::Function { function_declarations: vec![function_declaration] }
66 }
67
68 pub fn with_functions(function_declarations: Vec<FunctionDeclaration>) -> Self {
70 Self::Function { function_declarations }
71 }
72
73 pub fn google_search() -> Self {
75 Self::GoogleSearch { google_search: GoogleSearchConfig {} }
76 }
77
78 pub fn url_context() -> Self {
80 Self::URLContext { url_context: URLContextConfig {} }
81 }
82
83 pub fn google_maps(config: Value) -> Self {
85 Self::GoogleMaps { google_maps: config }
86 }
87
88 pub fn code_execution() -> Self {
90 Self::CodeExecution { code_execution: Value::Object(Default::default()) }
91 }
92
93 pub fn file_search(config: Value) -> Self {
95 Self::FileSearch { file_search: config }
96 }
97
98 pub fn computer_use(config: Value) -> Self {
100 Self::ComputerUse { computer_use: config }
101 }
102
103 pub fn mcp_server(config: Value) -> Self {
105 Self::McpServer { mcp_server: config }
106 }
107
108 pub fn is_server_side(&self) -> bool {
115 !matches!(self, Self::Function { .. })
116 }
117}
118
119#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
121#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
122pub enum Behavior {
123 #[default]
126 Blocking,
127 NonBlocking,
131}
132
133#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
135pub struct FunctionDeclaration {
136 pub name: String,
138 pub description: String,
140 #[serde(skip_serializing_if = "Option::is_none")]
142 pub behavior: Option<Behavior>,
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub(crate) parameters: Option<Value>,
146 #[serde(skip_serializing_if = "Option::is_none")]
150 pub(crate) response: Option<Value>,
151}
152
153fn generate_parameters_schema<Parameters>() -> Value
155where
156 Parameters: JsonSchema + Serialize,
157{
158 let schema_generator = SchemaGenerator::new(SchemaSettings::openapi3().with(|s| {
160 s.inline_subschemas = true;
161 s.meta_schema = None;
162 }));
163
164 let mut schema = schema_generator.into_root_schema_for::<Parameters>();
165
166 schema.remove("title");
168 schema.to_value()
169}
170
171impl FunctionDeclaration {
172 pub fn new(
174 name: impl Into<String>,
175 description: impl Into<String>,
176 behavior: Option<Behavior>,
177 ) -> Self {
178 Self { name: name.into(), description: description.into(), behavior, ..Default::default() }
179 }
180
181 pub fn with_parameters<Parameters>(mut self) -> Self
183 where
184 Parameters: JsonSchema + Serialize,
185 {
186 self.parameters = Some(generate_parameters_schema::<Parameters>());
187 self
188 }
189
190 pub fn with_response<Response>(mut self) -> Self
192 where
193 Response: JsonSchema + Serialize,
194 {
195 self.response = Some(generate_parameters_schema::<Response>());
196 self
197 }
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
202pub struct FunctionCall {
203 pub name: String,
205 pub args: serde_json::Value,
207 #[serde(skip_serializing_if = "Option::is_none", default)]
212 pub id: Option<String>,
213 #[serde(
219 skip_serializing_if = "Option::is_none",
220 default,
221 rename = "thoughtSignature",
222 alias = "thought_signature"
223 )]
224 pub thought_signature: Option<String>,
225}
226
227#[derive(Debug, Snafu)]
229pub enum FunctionCallError {
230 #[snafu(display("failed to deserialize parameter '{key}'"))]
232 Deserialization {
233 source: serde_json::Error,
235 key: String,
237 },
238
239 #[snafu(display("parameter '{key}' is missing in arguments '{args}'"))]
241 MissingParameter {
242 key: String,
244 args: serde_json::Value,
246 },
247
248 #[snafu(display("arguments should be an object; actual: {actual}"))]
250 ArgumentTypeMismatch {
251 actual: String,
253 },
254}
255
256impl FunctionCall {
257 pub fn new(name: impl Into<String>, args: serde_json::Value) -> Self {
259 Self { name: name.into(), args, id: None, thought_signature: None }
260 }
261
262 pub fn with_thought_signature(
264 name: impl Into<String>,
265 args: serde_json::Value,
266 thought_signature: impl Into<String>,
267 ) -> Self {
268 Self {
269 name: name.into(),
270 args,
271 id: None,
272 thought_signature: Some(thought_signature.into()),
273 }
274 }
275
276 pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T, FunctionCallError> {
278 match &self.args {
279 serde_json::Value::Object(obj) => {
280 if let Some(value) = obj.get(key) {
281 serde_json::from_value(value.clone())
282 .with_context(|_| DeserializationSnafu { key: key.to_string() })
283 } else {
284 Err(MissingParameterSnafu { key: key.to_string(), args: self.args.clone() }
285 .build())
286 }
287 }
288 _ => Err(ArgumentTypeMismatchSnafu { actual: self.args.to_string() }.build()),
289 }
290 }
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
295pub struct FunctionResponse {
296 pub name: String,
298 #[serde(skip_serializing_if = "Option::is_none", default)]
305 pub id: Option<String>,
306 #[serde(skip_serializing_if = "Option::is_none")]
309 pub response: Option<serde_json::Value>,
310 #[serde(default, skip_serializing_if = "Vec::is_empty")]
314 pub parts: Vec<FunctionResponsePart>,
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
322#[serde(untagged)]
323pub enum FunctionResponsePart {
324 InlineData {
326 #[serde(rename = "inlineData")]
328 inline_data: crate::Blob,
329 },
330 FileData {
332 #[serde(rename = "fileData")]
334 file_data: crate::FileDataRef,
335 },
336}
337
338impl FunctionResponse {
339 pub fn new(name: impl Into<String>, response: serde_json::Value) -> Self {
341 let response = match response {
342 serde_json::Value::Object(_) => response,
343 other => serde_json::json!({ "result": other }),
344 };
345 Self { name: name.into(), id: None, response: Some(response), parts: Vec::new() }
346 }
347
348 pub fn with_id(mut self, id: impl Into<String>) -> Self {
353 self.id = Some(id.into());
354 self
355 }
356
357 pub fn with_inline_data(
359 name: impl Into<String>,
360 response: serde_json::Value,
361 inline_data: Vec<crate::Blob>,
362 ) -> Self {
363 let response = match response {
364 serde_json::Value::Object(_) => response,
365 other => serde_json::json!({ "result": other }),
366 };
367 let parts = inline_data
368 .into_iter()
369 .map(|blob| FunctionResponsePart::InlineData { inline_data: blob })
370 .collect();
371 Self { name: name.into(), id: None, response: Some(response), parts }
372 }
373
374 pub fn with_file_data(
376 name: impl Into<String>,
377 response: serde_json::Value,
378 file_data: Vec<crate::FileDataRef>,
379 ) -> Self {
380 let response = match response {
381 serde_json::Value::Object(_) => response,
382 other => serde_json::json!({ "result": other }),
383 };
384 let parts = file_data
385 .into_iter()
386 .map(|fdr| FunctionResponsePart::FileData { file_data: fdr })
387 .collect();
388 Self { name: name.into(), id: None, response: Some(response), parts }
389 }
390
391 pub fn inline_data_only(name: impl Into<String>, inline_data: Vec<crate::Blob>) -> Self {
393 let parts = inline_data
394 .into_iter()
395 .map(|blob| FunctionResponsePart::InlineData { inline_data: blob })
396 .collect();
397 Self { name: name.into(), id: None, response: None, parts }
398 }
399
400 pub fn from_schema<Response>(
402 name: impl Into<String>,
403 response: Response,
404 ) -> Result<Self, serde_json::Error>
405 where
406 Response: JsonSchema + Serialize,
407 {
408 let json = serde_json::to_value(&response)?;
409 Ok(Self::new(name, json))
410 }
411
412 pub fn from_str(
414 name: impl Into<String>,
415 response: impl Into<String>,
416 ) -> Result<Self, serde_json::Error> {
417 let json = serde_json::from_str(&response.into())?;
418 Ok(Self::new(name, json))
419 }
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
424pub struct ToolConfig {
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub function_calling_config: Option<FunctionCallingConfig>,
428 #[serde(skip_serializing_if = "Option::is_none", rename = "includeServerSideToolInvocations")]
431 pub include_server_side_tool_invocations: Option<bool>,
432 #[serde(skip_serializing_if = "Option::is_none", rename = "retrievalConfig")]
434 pub retrieval_config: Option<Value>,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
439pub struct FunctionCallingConfig {
440 pub mode: FunctionCallingMode,
442 #[serde(skip_serializing_if = "Option::is_none")]
446 pub allowed_function_names: Option<Vec<String>>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
451#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
452pub enum FunctionCallingMode {
453 Auto,
455 Any,
457 None,
459 Validated,
462}
463
464#[cfg(test)]
465mod tests {
466 use super::*;
467
468 #[test]
469 fn tool_function_declarations_uses_camel_case() {
470 let tool = Tool::Function {
471 function_declarations: vec![FunctionDeclaration::new("test_func", "desc", None)],
472 };
473
474 let json = serde_json::to_value(&tool).unwrap();
475 assert!(json.get("functionDeclarations").is_some());
476 assert!(json.get("function_declarations").is_none());
477 }
478
479 #[test]
480 fn tool_config_include_server_side_tool_invocations_serde_round_trip() {
481 let config = ToolConfig {
482 function_calling_config: None,
483 include_server_side_tool_invocations: Some(true),
484 retrieval_config: None,
485 };
486
487 let json = serde_json::to_value(&config).unwrap();
488 assert_eq!(json["includeServerSideToolInvocations"], true);
489 assert!(json.get("include_server_side_tool_invocations").is_none());
491
492 let deserialized: ToolConfig = serde_json::from_value(json).unwrap();
493 assert_eq!(deserialized, config);
494 }
495
496 #[test]
497 fn tool_config_default_omits_server_side_flag() {
498 let config = ToolConfig::default();
499 assert_eq!(config.include_server_side_tool_invocations, None);
500 assert_eq!(config.retrieval_config, None);
501
502 let json = serde_json::to_value(&config).unwrap();
503 assert!(json.get("includeServerSideToolInvocations").is_none());
504 }
505
506 #[test]
507 fn function_calling_mode_validated_serde_round_trip() {
508 let config = FunctionCallingConfig {
509 mode: FunctionCallingMode::Validated,
510 allowed_function_names: None,
511 };
512 let json = serde_json::to_value(&config).unwrap();
513 assert_eq!(json["mode"], "VALIDATED");
514 let deserialized: FunctionCallingConfig = serde_json::from_value(json).unwrap();
515 assert_eq!(deserialized.mode, FunctionCallingMode::Validated);
516 }
517
518 #[test]
519 fn function_calling_config_with_allowed_names() {
520 let config = FunctionCallingConfig {
521 mode: FunctionCallingMode::Any,
522 allowed_function_names: Some(vec!["get_weather".to_string(), "search".to_string()]),
523 };
524 let json = serde_json::to_value(&config).unwrap();
525 assert_eq!(json["mode"], "ANY");
526 assert_eq!(json["allowed_function_names"], serde_json::json!(["get_weather", "search"]));
527
528 let deserialized: FunctionCallingConfig = serde_json::from_value(json).unwrap();
529 assert_eq!(deserialized, config);
530 }
531
532 #[test]
533 fn function_calling_config_omits_none_allowed_names() {
534 let config =
535 FunctionCallingConfig { mode: FunctionCallingMode::Auto, allowed_function_names: None };
536 let json = serde_json::to_value(&config).unwrap();
537 assert!(json.get("allowed_function_names").is_none());
538 }
539
540 #[test]
541 fn function_call_with_id_serde_round_trip() {
542 let call = FunctionCall {
543 name: "get_weather".to_string(),
544 args: serde_json::json!({"city": "Tokyo"}),
545 id: Some("fc_001".to_string()),
546 thought_signature: None,
547 };
548 let json = serde_json::to_value(&call).unwrap();
549 assert_eq!(json["id"], "fc_001");
550
551 let deserialized: FunctionCall = serde_json::from_value(json).unwrap();
552 assert_eq!(deserialized.id, Some("fc_001".to_string()));
553 }
554
555 #[test]
556 fn function_call_without_id_omits_field() {
557 let call = FunctionCall::new("get_weather", serde_json::json!({"city": "Tokyo"}));
558 let json = serde_json::to_value(&call).unwrap();
559 assert!(json.get("id").is_none());
560 }
561
562 #[test]
563 fn function_call_deserializes_without_id() {
564 let json = serde_json::json!({
565 "name": "get_weather",
566 "args": {"city": "Tokyo"}
567 });
568 let call: FunctionCall = serde_json::from_value(json).unwrap();
569 assert_eq!(call.id, None);
570 assert_eq!(call.name, "get_weather");
571 }
572}