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 function_declarations: Vec<FunctionDeclaration>,
14 },
15 GoogleSearch {
17 google_search: GoogleSearchConfig,
19 },
20 GoogleMaps {
22 google_maps: Value,
24 },
25 CodeExecution {
27 code_execution: Value,
29 },
30 URLContext {
32 url_context: URLContextConfig,
34 },
35 FileSearch {
37 file_search: Value,
39 },
40 ComputerUse {
42 computer_use: Value,
44 },
45 McpServer {
47 #[serde(rename = "mcp_server")]
49 mcp_server: Value,
50 },
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
55pub struct GoogleSearchConfig {}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
59pub struct URLContextConfig {}
60
61impl Tool {
62 pub fn new(function_declaration: FunctionDeclaration) -> Self {
64 Self::Function { function_declarations: vec![function_declaration] }
65 }
66
67 pub fn with_functions(function_declarations: Vec<FunctionDeclaration>) -> Self {
69 Self::Function { function_declarations }
70 }
71
72 pub fn google_search() -> Self {
74 Self::GoogleSearch { google_search: GoogleSearchConfig {} }
75 }
76
77 pub fn url_context() -> Self {
79 Self::URLContext { url_context: URLContextConfig {} }
80 }
81
82 pub fn google_maps(config: Value) -> Self {
84 Self::GoogleMaps { google_maps: config }
85 }
86
87 pub fn code_execution() -> Self {
89 Self::CodeExecution { code_execution: Value::Object(Default::default()) }
90 }
91
92 pub fn file_search(config: Value) -> Self {
94 Self::FileSearch { file_search: config }
95 }
96
97 pub fn computer_use(config: Value) -> Self {
99 Self::ComputerUse { computer_use: config }
100 }
101
102 pub fn mcp_server(config: Value) -> Self {
104 Self::McpServer { mcp_server: config }
105 }
106
107 pub fn is_server_side(&self) -> bool {
114 !matches!(self, Self::Function { .. })
115 }
116}
117
118#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
120#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
121pub enum Behavior {
122 #[default]
125 Blocking,
126 NonBlocking,
130}
131
132#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
134pub struct FunctionDeclaration {
135 pub name: String,
137 pub description: String,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub behavior: Option<Behavior>,
142 #[serde(skip_serializing_if = "Option::is_none")]
144 pub(crate) parameters: Option<Value>,
145 #[serde(skip_serializing_if = "Option::is_none")]
149 pub(crate) response: Option<Value>,
150}
151
152fn generate_parameters_schema<Parameters>() -> Value
154where
155 Parameters: JsonSchema + Serialize,
156{
157 let schema_generator = SchemaGenerator::new(SchemaSettings::openapi3().with(|s| {
159 s.inline_subschemas = true;
160 s.meta_schema = None;
161 }));
162
163 let mut schema = schema_generator.into_root_schema_for::<Parameters>();
164
165 schema.remove("title");
167 schema.to_value()
168}
169
170impl FunctionDeclaration {
171 pub fn new(
173 name: impl Into<String>,
174 description: impl Into<String>,
175 behavior: Option<Behavior>,
176 ) -> Self {
177 Self { name: name.into(), description: description.into(), behavior, ..Default::default() }
178 }
179
180 pub fn with_parameters<Parameters>(mut self) -> Self
182 where
183 Parameters: JsonSchema + Serialize,
184 {
185 self.parameters = Some(generate_parameters_schema::<Parameters>());
186 self
187 }
188
189 pub fn with_response<Response>(mut self) -> Self
191 where
192 Response: JsonSchema + Serialize,
193 {
194 self.response = Some(generate_parameters_schema::<Response>());
195 self
196 }
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
201pub struct FunctionCall {
202 pub name: String,
204 pub args: serde_json::Value,
206 #[serde(skip_serializing_if = "Option::is_none", default)]
211 pub id: Option<String>,
212 #[serde(
218 skip_serializing_if = "Option::is_none",
219 default,
220 rename = "thoughtSignature",
221 alias = "thought_signature"
222 )]
223 pub thought_signature: Option<String>,
224}
225
226#[derive(Debug, Snafu)]
228pub enum FunctionCallError {
229 #[snafu(display("failed to deserialize parameter '{key}'"))]
231 Deserialization {
232 source: serde_json::Error,
234 key: String,
236 },
237
238 #[snafu(display("parameter '{key}' is missing in arguments '{args}'"))]
240 MissingParameter {
241 key: String,
243 args: serde_json::Value,
245 },
246
247 #[snafu(display("arguments should be an object; actual: {actual}"))]
249 ArgumentTypeMismatch {
250 actual: String,
252 },
253}
254
255impl FunctionCall {
256 pub fn new(name: impl Into<String>, args: serde_json::Value) -> Self {
258 Self { name: name.into(), args, id: None, thought_signature: None }
259 }
260
261 pub fn with_thought_signature(
263 name: impl Into<String>,
264 args: serde_json::Value,
265 thought_signature: impl Into<String>,
266 ) -> Self {
267 Self {
268 name: name.into(),
269 args,
270 id: None,
271 thought_signature: Some(thought_signature.into()),
272 }
273 }
274
275 pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T, FunctionCallError> {
277 match &self.args {
278 serde_json::Value::Object(obj) => {
279 if let Some(value) = obj.get(key) {
280 serde_json::from_value(value.clone())
281 .with_context(|_| DeserializationSnafu { key: key.to_string() })
282 } else {
283 Err(MissingParameterSnafu { key: key.to_string(), args: self.args.clone() }
284 .build())
285 }
286 }
287 _ => Err(ArgumentTypeMismatchSnafu { actual: self.args.to_string() }.build()),
288 }
289 }
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
294pub struct FunctionResponse {
295 pub name: String,
297 #[serde(skip_serializing_if = "Option::is_none", default)]
304 pub id: Option<String>,
305 #[serde(skip_serializing_if = "Option::is_none")]
308 pub response: Option<serde_json::Value>,
309 #[serde(default, skip_serializing_if = "Vec::is_empty")]
313 pub parts: Vec<FunctionResponsePart>,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
321#[serde(untagged)]
322pub enum FunctionResponsePart {
323 InlineData {
325 #[serde(rename = "inlineData")]
327 inline_data: crate::Blob,
328 },
329 FileData {
331 #[serde(rename = "fileData")]
333 file_data: crate::FileDataRef,
334 },
335}
336
337impl FunctionResponse {
338 pub fn new(name: impl Into<String>, response: serde_json::Value) -> Self {
340 let response = match response {
341 serde_json::Value::Object(_) => response,
342 other => serde_json::json!({ "result": other }),
343 };
344 Self { name: name.into(), id: None, response: Some(response), parts: Vec::new() }
345 }
346
347 pub fn with_id(mut self, id: impl Into<String>) -> Self {
352 self.id = Some(id.into());
353 self
354 }
355
356 pub fn with_inline_data(
358 name: impl Into<String>,
359 response: serde_json::Value,
360 inline_data: Vec<crate::Blob>,
361 ) -> Self {
362 let response = match response {
363 serde_json::Value::Object(_) => response,
364 other => serde_json::json!({ "result": other }),
365 };
366 let parts = inline_data
367 .into_iter()
368 .map(|blob| FunctionResponsePart::InlineData { inline_data: blob })
369 .collect();
370 Self { name: name.into(), id: None, response: Some(response), parts }
371 }
372
373 pub fn with_file_data(
375 name: impl Into<String>,
376 response: serde_json::Value,
377 file_data: Vec<crate::FileDataRef>,
378 ) -> Self {
379 let response = match response {
380 serde_json::Value::Object(_) => response,
381 other => serde_json::json!({ "result": other }),
382 };
383 let parts = file_data
384 .into_iter()
385 .map(|fdr| FunctionResponsePart::FileData { file_data: fdr })
386 .collect();
387 Self { name: name.into(), id: None, response: Some(response), parts }
388 }
389
390 pub fn inline_data_only(name: impl Into<String>, inline_data: Vec<crate::Blob>) -> Self {
392 let parts = inline_data
393 .into_iter()
394 .map(|blob| FunctionResponsePart::InlineData { inline_data: blob })
395 .collect();
396 Self { name: name.into(), id: None, response: None, parts }
397 }
398
399 pub fn from_schema<Response>(
401 name: impl Into<String>,
402 response: Response,
403 ) -> Result<Self, serde_json::Error>
404 where
405 Response: JsonSchema + Serialize,
406 {
407 let json = serde_json::to_value(&response)?;
408 Ok(Self::new(name, json))
409 }
410
411 pub fn from_str(
413 name: impl Into<String>,
414 response: impl Into<String>,
415 ) -> Result<Self, serde_json::Error> {
416 let json = serde_json::from_str(&response.into())?;
417 Ok(Self::new(name, json))
418 }
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
423pub struct ToolConfig {
424 #[serde(skip_serializing_if = "Option::is_none")]
426 pub function_calling_config: Option<FunctionCallingConfig>,
427 #[serde(skip_serializing_if = "Option::is_none", rename = "includeServerSideToolInvocations")]
430 pub include_server_side_tool_invocations: Option<bool>,
431 #[serde(skip_serializing_if = "Option::is_none", rename = "retrievalConfig")]
433 pub retrieval_config: Option<Value>,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
438pub struct FunctionCallingConfig {
439 pub mode: FunctionCallingMode,
441 #[serde(skip_serializing_if = "Option::is_none")]
445 pub allowed_function_names: Option<Vec<String>>,
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
450#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
451pub enum FunctionCallingMode {
452 Auto,
454 Any,
456 None,
458 Validated,
461}
462
463#[cfg(test)]
464mod tests {
465 use super::*;
466
467 #[test]
468 fn tool_config_include_server_side_tool_invocations_serde_round_trip() {
469 let config = ToolConfig {
470 function_calling_config: None,
471 include_server_side_tool_invocations: Some(true),
472 retrieval_config: None,
473 };
474
475 let json = serde_json::to_value(&config).unwrap();
476 assert_eq!(json["includeServerSideToolInvocations"], true);
477 assert!(json.get("include_server_side_tool_invocations").is_none());
479
480 let deserialized: ToolConfig = serde_json::from_value(json).unwrap();
481 assert_eq!(deserialized, config);
482 }
483
484 #[test]
485 fn tool_config_default_omits_server_side_flag() {
486 let config = ToolConfig::default();
487 assert_eq!(config.include_server_side_tool_invocations, None);
488 assert_eq!(config.retrieval_config, None);
489
490 let json = serde_json::to_value(&config).unwrap();
491 assert!(json.get("includeServerSideToolInvocations").is_none());
492 }
493
494 #[test]
495 fn function_calling_mode_validated_serde_round_trip() {
496 let config = FunctionCallingConfig {
497 mode: FunctionCallingMode::Validated,
498 allowed_function_names: None,
499 };
500 let json = serde_json::to_value(&config).unwrap();
501 assert_eq!(json["mode"], "VALIDATED");
502 let deserialized: FunctionCallingConfig = serde_json::from_value(json).unwrap();
503 assert_eq!(deserialized.mode, FunctionCallingMode::Validated);
504 }
505
506 #[test]
507 fn function_calling_config_with_allowed_names() {
508 let config = FunctionCallingConfig {
509 mode: FunctionCallingMode::Any,
510 allowed_function_names: Some(vec!["get_weather".to_string(), "search".to_string()]),
511 };
512 let json = serde_json::to_value(&config).unwrap();
513 assert_eq!(json["mode"], "ANY");
514 assert_eq!(json["allowed_function_names"], serde_json::json!(["get_weather", "search"]));
515
516 let deserialized: FunctionCallingConfig = serde_json::from_value(json).unwrap();
517 assert_eq!(deserialized, config);
518 }
519
520 #[test]
521 fn function_calling_config_omits_none_allowed_names() {
522 let config =
523 FunctionCallingConfig { mode: FunctionCallingMode::Auto, allowed_function_names: None };
524 let json = serde_json::to_value(&config).unwrap();
525 assert!(json.get("allowed_function_names").is_none());
526 }
527
528 #[test]
529 fn function_call_with_id_serde_round_trip() {
530 let call = FunctionCall {
531 name: "get_weather".to_string(),
532 args: serde_json::json!({"city": "Tokyo"}),
533 id: Some("fc_001".to_string()),
534 thought_signature: None,
535 };
536 let json = serde_json::to_value(&call).unwrap();
537 assert_eq!(json["id"], "fc_001");
538
539 let deserialized: FunctionCall = serde_json::from_value(json).unwrap();
540 assert_eq!(deserialized.id, Some("fc_001".to_string()));
541 }
542
543 #[test]
544 fn function_call_without_id_omits_field() {
545 let call = FunctionCall::new("get_weather", serde_json::json!({"city": "Tokyo"}));
546 let json = serde_json::to_value(&call).unwrap();
547 assert!(json.get("id").is_none());
548 }
549
550 #[test]
551 fn function_call_deserializes_without_id() {
552 let json = serde_json::json!({
553 "name": "get_weather",
554 "args": {"city": "Tokyo"}
555 });
556 let call: FunctionCall = serde_json::from_value(json).unwrap();
557 assert_eq!(call.id, None);
558 assert_eq!(call.name, "get_weather");
559 }
560}