1use schemars::{generate::SchemaSettings, JsonSchema, SchemaGenerator};
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 URLContext {
21 url_context: URLContextConfig,
22 },
23 GoogleMaps {
25 google_maps: GoogleMapsConfig,
27 },
28 CodeExecution {
30 #[serde(rename = "codeExecution")]
31 code_execution: CodeExecutionConfig,
32 },
33 FileSearch {
35 file_search: FileSearchConfig,
37 },
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
42pub struct GoogleSearchConfig {}
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
46pub struct URLContextConfig {}
47
48#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
50#[serde(rename_all = "camelCase")]
51pub struct GoogleMapsConfig {
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub enable_widget: Option<bool>,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
59pub struct CodeExecutionConfig {}
60
61#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
65#[serde(rename_all = "camelCase")]
66pub struct ExecutableCode {
67 pub language: CodeLanguage,
69 pub code: String,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
75#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
76pub enum CodeLanguage {
77 Python,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
85#[serde(rename_all = "camelCase")]
86pub struct CodeExecutionResult {
87 pub outcome: CodeExecutionOutcome,
89 pub output: String,
91}
92
93#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
95#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
96pub enum CodeExecutionOutcome {
97 OutcomeOk,
99 OutcomeFailed,
101 OutcomeDeadlineExceeded,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
107#[serde(rename_all = "camelCase")]
108pub struct FileSearchConfig {
109 pub file_search_store_names: Vec<String>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub metadata_filter: Option<String>,
115}
116
117impl Tool {
118 pub fn new(function_declaration: FunctionDeclaration) -> Self {
120 Self::Function {
121 function_declarations: vec![function_declaration],
122 }
123 }
124
125 pub fn with_functions(function_declarations: Vec<FunctionDeclaration>) -> Self {
127 Self::Function {
128 function_declarations,
129 }
130 }
131
132 pub fn google_search() -> Self {
134 Self::GoogleSearch {
135 google_search: GoogleSearchConfig {},
136 }
137 }
138
139 pub fn url_context() -> Self {
141 Self::URLContext {
142 url_context: URLContextConfig {},
143 }
144 }
145
146 pub fn google_maps(enable_widget: Option<bool>) -> Self {
148 Self::GoogleMaps {
149 google_maps: GoogleMapsConfig { enable_widget },
150 }
151 }
152
153 pub fn code_execution() -> Self {
158 Self::CodeExecution {
159 code_execution: CodeExecutionConfig {},
160 }
161 }
162
163 pub fn file_search(store_names: Vec<String>, metadata_filter: Option<String>) -> Self {
165 Self::FileSearch {
166 file_search: FileSearchConfig {
167 file_search_store_names: store_names,
168 metadata_filter,
169 },
170 }
171 }
172}
173
174#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
176#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
177pub enum Behavior {
178 #[default]
181 Blocking,
182 NonBlocking,
186}
187
188#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
190pub struct FunctionDeclaration {
191 pub name: String,
193 pub description: String,
195 #[serde(skip_serializing_if = "Option::is_none")]
197 pub behavior: Option<Behavior>,
198 #[serde(skip_serializing_if = "Option::is_none")]
200 pub(crate) parameters: Option<Value>,
201 #[serde(
203 rename = "parametersJsonSchema",
204 skip_serializing_if = "Option::is_none"
205 )]
206 pub(crate) parameters_json_schema: Option<Value>,
207 #[serde(skip_serializing_if = "Option::is_none")]
211 pub(crate) response: Option<Value>,
212 #[serde(rename = "responseJsonSchema", skip_serializing_if = "Option::is_none")]
214 pub(crate) response_json_schema: Option<Value>,
215}
216
217fn generate_parameters_schema<Parameters>() -> Value
219where
220 Parameters: JsonSchema + Serialize,
221{
222 let schema_generator = SchemaGenerator::new(SchemaSettings::openapi3().with(|s| {
224 s.inline_subschemas = true;
225 s.meta_schema = None;
226 }));
227
228 let schema = schema_generator.into_root_schema_for::<Parameters>();
229 let mut value = serde_json::to_value(&schema).expect("schema should serialize to JSON value");
230 sanitize_json_schema_openapi3(&mut value);
231 value
232}
233
234fn sanitize_json_schema_openapi3(value: &mut Value) {
235 if let Value::Object(map) = value {
236 map.remove("title");
237 map.remove("components");
238 }
239}
240
241fn generate_parameters_json_schema<Parameters>() -> Value
243where
244 Parameters: JsonSchema + Serialize,
245{
246 let schema_generator = SchemaGenerator::new(SchemaSettings::draft07().with(|s| {
247 s.inline_subschemas = true;
248 s.meta_schema = None;
249 }));
250
251 let schema = schema_generator.into_root_schema_for::<Parameters>();
252 let mut value = serde_json::to_value(&schema).expect("schema should serialize to JSON value");
253 if let Value::Object(map) = &mut value {
254 map.remove("title");
255 map.remove("$schema");
256 map.remove("definitions");
257 map.remove("$defs");
258 }
259 value
260}
261
262impl FunctionDeclaration {
263 pub fn new(
265 name: impl Into<String>,
266 description: impl Into<String>,
267 behavior: Option<Behavior>,
268 ) -> Self {
269 Self {
270 name: name.into(),
271 description: description.into(),
272 behavior,
273 ..Default::default()
274 }
275 }
276
277 pub fn with_parameters<Parameters>(mut self) -> Self
279 where
280 Parameters: JsonSchema + Serialize,
281 {
282 self.parameters = Some(generate_parameters_schema::<Parameters>());
283 self.parameters_json_schema = None;
284 self
285 }
286
287 pub fn with_parameters_json_schema<Parameters>(mut self) -> Self
289 where
290 Parameters: JsonSchema + Serialize,
291 {
292 self.parameters_json_schema = Some(generate_parameters_json_schema::<Parameters>());
293 self.parameters = None;
294 self
295 }
296
297 pub fn with_parameters_value(mut self, mut value: Value) -> Self {
299 sanitize_json_schema_openapi3(&mut value);
300 self.parameters = Some(value);
301 self.parameters_json_schema = None;
302 self
303 }
304
305 pub fn with_response<Response>(mut self) -> Self
307 where
308 Response: JsonSchema + Serialize,
309 {
310 self.response = Some(generate_parameters_schema::<Response>());
311 self.response_json_schema = None;
312 self
313 }
314
315 pub fn with_response_json_schema<Response>(mut self) -> Self
317 where
318 Response: JsonSchema + Serialize,
319 {
320 self.response_json_schema = Some(generate_parameters_json_schema::<Response>());
321 self.response = None;
322 self
323 }
324
325 pub fn with_response_value(mut self, mut value: Value) -> Self {
327 sanitize_json_schema_openapi3(&mut value);
328 self.response = Some(value);
329 self.response_json_schema = None;
330 self
331 }
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
336pub struct FunctionCall {
337 pub name: String,
339 pub args: serde_json::Value,
341 #[serde(skip_serializing_if = "Option::is_none")]
343 pub thought_signature: Option<String>,
344}
345
346#[derive(Debug, Snafu)]
347pub enum FunctionCallError {
348 #[snafu(display("failed to deserialize parameter '{key}'"))]
349 Deserialization {
350 source: serde_json::Error,
351 key: String,
352 },
353
354 #[snafu(display("parameter '{key}' is missing in arguments '{args}'"))]
355 MissingParameter {
356 key: String,
357 args: serde_json::Value,
358 },
359
360 #[snafu(display("arguments should be an object; actual: {actual}"))]
361 ArgumentTypeMismatch { actual: String },
362}
363
364impl FunctionCall {
365 pub fn new(name: impl Into<String>, args: serde_json::Value) -> Self {
367 Self {
368 name: name.into(),
369 args,
370 thought_signature: None,
371 }
372 }
373
374 pub fn with_thought_signature(
376 name: impl Into<String>,
377 args: serde_json::Value,
378 thought_signature: impl Into<String>,
379 ) -> Self {
380 Self {
381 name: name.into(),
382 args,
383 thought_signature: Some(thought_signature.into()),
384 }
385 }
386
387 pub fn get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T, FunctionCallError> {
389 match &self.args {
390 serde_json::Value::Object(obj) => {
391 if let Some(value) = obj.get(key) {
392 serde_json::from_value(value.clone()).with_context(|_| DeserializationSnafu {
393 key: key.to_string(),
394 })
395 } else {
396 Err(MissingParameterSnafu {
397 key: key.to_string(),
398 args: self.args.clone(),
399 }
400 .build())
401 }
402 }
403 _ => Err(ArgumentTypeMismatchSnafu {
404 actual: self.args.to_string(),
405 }
406 .build()),
407 }
408 }
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
413pub struct FunctionResponse {
414 pub name: String,
416 #[serde(skip_serializing_if = "Option::is_none")]
419 pub response: Option<serde_json::Value>,
420}
421
422impl FunctionResponse {
423 pub fn new(name: impl Into<String>, response: serde_json::Value) -> Self {
425 Self {
426 name: name.into(),
427 response: Some(response),
428 }
429 }
430
431 pub fn from_schema<Response>(
433 name: impl Into<String>,
434 response: Response,
435 ) -> Result<Self, serde_json::Error>
436 where
437 Response: JsonSchema + Serialize,
438 {
439 let json = serde_json::to_value(&response)?;
440 Ok(Self {
441 name: name.into(),
442 response: Some(json),
443 })
444 }
445
446 pub fn from_str(
448 name: impl Into<String>,
449 response: impl Into<String>,
450 ) -> Result<Self, serde_json::Error> {
451 let json = serde_json::from_str(&response.into())?;
452 Ok(Self {
453 name: name.into(),
454 response: Some(json),
455 })
456 }
457}
458
459#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
461pub struct ToolConfig {
462 #[serde(skip_serializing_if = "Option::is_none")]
464 pub function_calling_config: Option<FunctionCallingConfig>,
465 #[serde(skip_serializing_if = "Option::is_none")]
467 pub include_server_side_tool_invocations: Option<bool>,
468 #[serde(skip_serializing_if = "Option::is_none")]
470 pub retrieval_config: Option<RetrievalConfig>,
471}
472
473#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
475pub struct FunctionCallingConfig {
476 pub mode: FunctionCallingMode,
478}
479
480#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
482#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
483pub enum FunctionCallingMode {
484 Auto,
486 Any,
488 None,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
494#[serde(rename_all = "camelCase")]
495pub struct RetrievalConfig {
496 #[serde(skip_serializing_if = "Option::is_none")]
498 pub lat_lng: Option<LatLng>,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
503pub struct LatLng {
504 pub latitude: f64,
506 pub longitude: f64,
508}
509
510impl LatLng {
511 pub fn new(latitude: f64, longitude: f64) -> Self {
513 Self {
514 latitude,
515 longitude,
516 }
517 }
518}