1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4
5use crate::enums::{
6 ApiSpec, AuthType, Behavior, DynamicRetrievalConfigMode, Environment, FunctionCallingMode,
7 HttpElementLocation, PhishBlockThreshold, Type,
8};
9
10#[derive(Debug, Clone, Serialize, Deserialize, Default)]
12#[serde(rename_all = "camelCase")]
13pub struct Tool {
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub retrieval: Option<Retrieval>,
16 #[serde(skip_serializing_if = "Option::is_none")]
17 pub computer_use: Option<ComputerUse>,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub file_search: Option<FileSearch>,
20 #[serde(skip_serializing_if = "Option::is_none")]
21 pub code_execution: Option<CodeExecution>,
22 #[serde(skip_serializing_if = "Option::is_none")]
23 pub enterprise_web_search: Option<EnterpriseWebSearch>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 pub function_declarations: Option<Vec<FunctionDeclaration>>,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub google_maps: Option<GoogleMaps>,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub google_search: Option<GoogleSearch>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub google_search_retrieval: Option<GoogleSearchRetrieval>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 pub url_context: Option<UrlContext>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(rename_all = "camelCase")]
39pub struct FunctionDeclaration {
40 pub name: String,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub description: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub parameters: Option<Schema>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub parameters_json_schema: Option<Value>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub response: Option<Schema>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub response_json_schema: Option<Value>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub behavior: Option<Behavior>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize, Default)]
57#[serde(rename_all = "camelCase")]
58pub struct GoogleSearch {
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub exclude_domains: Option<Vec<String>>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub blocking_confidence: Option<PhishBlockThreshold>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub time_range_filter: Option<Interval>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, Default)]
69#[serde(rename_all = "camelCase")]
70pub struct EnterpriseWebSearch {
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub exclude_domains: Option<Vec<String>>,
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub blocking_confidence: Option<PhishBlockThreshold>,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize, Default)]
79#[serde(rename_all = "camelCase")]
80pub struct CodeExecution {}
81
82#[derive(Debug, Clone, Serialize, Deserialize, Default)]
84#[serde(rename_all = "camelCase")]
85pub struct UrlContext {}
86
87#[derive(Debug, Clone, Serialize, Deserialize, Default)]
89#[serde(rename_all = "camelCase")]
90pub struct ComputerUse {
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub environment: Option<Environment>,
93 #[serde(skip_serializing_if = "Option::is_none")]
94 pub excluded_predefined_functions: Option<Vec<String>>,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, Default)]
99#[serde(rename_all = "camelCase")]
100pub struct GoogleMaps {
101 #[serde(skip_serializing_if = "Option::is_none")]
102 pub auth_config: Option<AuthConfig>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 pub enable_widget: Option<bool>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize, Default)]
109#[serde(rename_all = "camelCase")]
110pub struct GoogleSearchRetrieval {
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub dynamic_retrieval_config: Option<DynamicRetrievalConfig>,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, Default)]
117#[serde(rename_all = "camelCase")]
118pub struct FileSearch {
119 #[serde(skip_serializing_if = "Option::is_none")]
120 pub file_search_store_names: Option<Vec<String>>,
121 #[serde(skip_serializing_if = "Option::is_none")]
122 pub top_k: Option<i32>,
123 #[serde(skip_serializing_if = "Option::is_none")]
124 pub metadata_filter: Option<String>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize, Default)]
129#[serde(rename_all = "camelCase")]
130pub struct Interval {
131 #[serde(skip_serializing_if = "Option::is_none")]
132 pub end_time: Option<String>,
133 #[serde(skip_serializing_if = "Option::is_none")]
134 pub start_time: Option<String>,
135}
136
137#[derive(Debug, Clone, Serialize, Deserialize, Default)]
139#[serde(rename_all = "camelCase")]
140pub struct ApiKeyConfig {
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub api_key_secret: Option<String>,
143 #[serde(skip_serializing_if = "Option::is_none")]
144 pub api_key_string: Option<String>,
145 #[serde(skip_serializing_if = "Option::is_none")]
146 pub http_element_location: Option<HttpElementLocation>,
147 #[serde(skip_serializing_if = "Option::is_none")]
148 pub name: Option<String>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize, Default)]
153#[serde(rename_all = "camelCase")]
154pub struct ApiAuthApiKeyConfig {
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub api_key_secret_version: Option<String>,
157 #[serde(skip_serializing_if = "Option::is_none")]
158 pub api_key_string: Option<String>,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize, Default)]
163#[serde(rename_all = "camelCase")]
164pub struct ApiAuth {
165 #[serde(skip_serializing_if = "Option::is_none")]
166 pub api_key_config: Option<ApiAuthApiKeyConfig>,
167}
168
169#[derive(Debug, Clone, Serialize, Deserialize, Default)]
171#[serde(rename_all = "camelCase")]
172pub struct AuthConfigGoogleServiceAccountConfig {
173 #[serde(skip_serializing_if = "Option::is_none")]
174 pub service_account: Option<String>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize, Default)]
179#[serde(rename_all = "camelCase")]
180pub struct AuthConfigHttpBasicAuthConfig {
181 #[serde(skip_serializing_if = "Option::is_none")]
182 pub credential_secret: Option<String>,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, Default)]
187#[serde(rename_all = "camelCase")]
188pub struct AuthConfigOauthConfig {
189 #[serde(skip_serializing_if = "Option::is_none")]
190 pub access_token: Option<String>,
191 #[serde(skip_serializing_if = "Option::is_none")]
192 pub service_account: Option<String>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, Default)]
197#[serde(rename_all = "camelCase")]
198pub struct AuthConfigOidcConfig {
199 #[serde(skip_serializing_if = "Option::is_none")]
200 pub id_token: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 pub service_account: Option<String>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, Default)]
207#[serde(rename_all = "camelCase")]
208pub struct AuthConfig {
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub api_key_config: Option<ApiKeyConfig>,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub auth_type: Option<AuthType>,
213 #[serde(skip_serializing_if = "Option::is_none")]
214 pub google_service_account_config: Option<AuthConfigGoogleServiceAccountConfig>,
215 #[serde(skip_serializing_if = "Option::is_none")]
216 pub http_basic_auth_config: Option<AuthConfigHttpBasicAuthConfig>,
217 #[serde(skip_serializing_if = "Option::is_none")]
218 pub oauth_config: Option<AuthConfigOauthConfig>,
219 #[serde(skip_serializing_if = "Option::is_none")]
220 pub oidc_config: Option<AuthConfigOidcConfig>,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize, Default)]
225#[serde(rename_all = "camelCase")]
226pub struct ExternalApiElasticSearchParams {
227 #[serde(skip_serializing_if = "Option::is_none")]
228 pub index: Option<String>,
229 #[serde(skip_serializing_if = "Option::is_none")]
230 pub num_hits: Option<i32>,
231 #[serde(skip_serializing_if = "Option::is_none")]
232 pub search_template: Option<String>,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize, Default)]
237#[serde(rename_all = "camelCase")]
238pub struct ExternalApiSimpleSearchParams {}
239
240#[derive(Debug, Clone, Serialize, Deserialize, Default)]
242#[serde(rename_all = "camelCase")]
243pub struct ExternalApi {
244 #[serde(skip_serializing_if = "Option::is_none")]
245 pub api_auth: Option<ApiAuth>,
246 #[serde(skip_serializing_if = "Option::is_none")]
247 pub api_spec: Option<ApiSpec>,
248 #[serde(skip_serializing_if = "Option::is_none")]
249 pub auth_config: Option<AuthConfig>,
250 #[serde(skip_serializing_if = "Option::is_none")]
251 pub elastic_search_params: Option<ExternalApiElasticSearchParams>,
252 #[serde(skip_serializing_if = "Option::is_none")]
253 pub endpoint: Option<String>,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 pub simple_search_params: Option<ExternalApiSimpleSearchParams>,
256}
257
258#[derive(Debug, Clone, Serialize, Deserialize, Default)]
260#[serde(rename_all = "camelCase")]
261pub struct VertexAiSearchDataStoreSpec {
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub data_store: Option<String>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub filter: Option<String>,
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize, Default)]
270#[serde(rename_all = "camelCase")]
271pub struct VertexAiSearch {
272 #[serde(skip_serializing_if = "Option::is_none")]
273 pub data_store_specs: Option<Vec<VertexAiSearchDataStoreSpec>>,
274 #[serde(skip_serializing_if = "Option::is_none")]
275 pub datastore: Option<String>,
276 #[serde(skip_serializing_if = "Option::is_none")]
277 pub engine: Option<String>,
278 #[serde(skip_serializing_if = "Option::is_none")]
279 pub filter: Option<String>,
280 #[serde(skip_serializing_if = "Option::is_none")]
281 pub max_results: Option<i32>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize, Default)]
286#[serde(rename_all = "camelCase")]
287pub struct VertexRagStoreRagResource {
288 #[serde(skip_serializing_if = "Option::is_none")]
289 pub rag_corpus: Option<String>,
290 #[serde(skip_serializing_if = "Option::is_none")]
291 pub rag_file_ids: Option<Vec<String>>,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize, Default)]
296#[serde(rename_all = "camelCase")]
297pub struct RagRetrievalConfigFilter {
298 #[serde(skip_serializing_if = "Option::is_none")]
299 pub metadata_filter: Option<String>,
300 #[serde(skip_serializing_if = "Option::is_none")]
301 pub vector_distance_threshold: Option<f64>,
302 #[serde(skip_serializing_if = "Option::is_none")]
303 pub vector_similarity_threshold: Option<f64>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, Default)]
308#[serde(rename_all = "camelCase")]
309pub struct RagRetrievalConfigHybridSearch {
310 #[serde(skip_serializing_if = "Option::is_none")]
311 pub alpha: Option<f32>,
312}
313
314#[derive(Debug, Clone, Serialize, Deserialize, Default)]
316#[serde(rename_all = "camelCase")]
317pub struct RagRetrievalConfigRankingLlmRanker {
318 #[serde(skip_serializing_if = "Option::is_none")]
319 pub model_name: Option<String>,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize, Default)]
324#[serde(rename_all = "camelCase")]
325pub struct RagRetrievalConfigRankingRankService {
326 #[serde(skip_serializing_if = "Option::is_none")]
327 pub model_name: Option<String>,
328}
329
330#[derive(Debug, Clone, Serialize, Deserialize, Default)]
332#[serde(rename_all = "camelCase")]
333pub struct RagRetrievalConfigRanking {
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub llm_ranker: Option<RagRetrievalConfigRankingLlmRanker>,
336 #[serde(skip_serializing_if = "Option::is_none")]
337 pub rank_service: Option<RagRetrievalConfigRankingRankService>,
338}
339
340#[derive(Debug, Clone, Serialize, Deserialize, Default)]
342#[serde(rename_all = "camelCase")]
343pub struct RagRetrievalConfig {
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub filter: Option<RagRetrievalConfigFilter>,
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub hybrid_search: Option<RagRetrievalConfigHybridSearch>,
348 #[serde(skip_serializing_if = "Option::is_none")]
349 pub ranking: Option<RagRetrievalConfigRanking>,
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub top_k: Option<i32>,
352}
353
354#[derive(Debug, Clone, Serialize, Deserialize, Default)]
356#[serde(rename_all = "camelCase")]
357pub struct VertexRagStore {
358 #[serde(skip_serializing_if = "Option::is_none")]
359 pub rag_corpora: Option<Vec<String>>,
360 #[serde(skip_serializing_if = "Option::is_none")]
361 pub rag_resources: Option<Vec<VertexRagStoreRagResource>>,
362 #[serde(skip_serializing_if = "Option::is_none")]
363 pub rag_retrieval_config: Option<RagRetrievalConfig>,
364 #[serde(skip_serializing_if = "Option::is_none")]
365 pub similarity_top_k: Option<i32>,
366 #[serde(skip_serializing_if = "Option::is_none")]
367 pub store_context: Option<bool>,
368 #[serde(skip_serializing_if = "Option::is_none")]
369 pub vector_distance_threshold: Option<f64>,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize, Default)]
374#[serde(rename_all = "camelCase")]
375pub struct Retrieval {
376 #[serde(skip_serializing_if = "Option::is_none")]
377 pub disable_attribution: Option<bool>,
378 #[serde(skip_serializing_if = "Option::is_none")]
379 pub external_api: Option<ExternalApi>,
380 #[serde(skip_serializing_if = "Option::is_none")]
381 pub vertex_ai_search: Option<VertexAiSearch>,
382 #[serde(skip_serializing_if = "Option::is_none")]
383 pub vertex_rag_store: Option<VertexRagStore>,
384}
385
386#[derive(Debug, Clone, Serialize, Deserialize, Default)]
388#[serde(rename_all = "camelCase")]
389pub struct DynamicRetrievalConfig {
390 #[serde(skip_serializing_if = "Option::is_none")]
391 pub dynamic_threshold: Option<f32>,
392 #[serde(skip_serializing_if = "Option::is_none")]
393 pub mode: Option<DynamicRetrievalConfigMode>,
394}
395
396#[derive(Debug, Clone, Serialize, Deserialize, Default)]
398#[serde(rename_all = "camelCase")]
399pub struct ToolConfig {
400 #[serde(skip_serializing_if = "Option::is_none")]
401 pub function_calling_config: Option<FunctionCallingConfig>,
402 #[serde(skip_serializing_if = "Option::is_none")]
403 pub retrieval_config: Option<RetrievalConfig>,
404}
405
406#[derive(Debug, Clone, Serialize, Deserialize, Default)]
408#[serde(rename_all = "camelCase")]
409pub struct FunctionCallingConfig {
410 #[serde(skip_serializing_if = "Option::is_none")]
411 pub allowed_function_names: Option<Vec<String>>,
412 #[serde(skip_serializing_if = "Option::is_none")]
413 pub mode: Option<FunctionCallingMode>,
414 #[serde(skip_serializing_if = "Option::is_none")]
415 pub stream_function_call_arguments: Option<bool>,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize, Default)]
420#[serde(rename_all = "camelCase")]
421pub struct LatLng {
422 #[serde(skip_serializing_if = "Option::is_none")]
423 pub latitude: Option<f64>,
424 #[serde(skip_serializing_if = "Option::is_none")]
425 pub longitude: Option<f64>,
426}
427
428#[derive(Debug, Clone, Serialize, Deserialize, Default)]
430#[serde(rename_all = "camelCase")]
431pub struct RetrievalConfig {
432 #[serde(skip_serializing_if = "Option::is_none")]
433 pub lat_lng: Option<LatLng>,
434 #[serde(skip_serializing_if = "Option::is_none")]
435 pub language_code: Option<String>,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize, Default)]
440#[serde(rename_all = "camelCase")]
441pub struct Schema {
442 #[serde(skip_serializing_if = "Option::is_none")]
443 pub any_of: Option<Vec<Schema>>,
444 #[serde(skip_serializing_if = "Option::is_none")]
445 pub default: Option<Value>,
446 #[serde(skip_serializing_if = "Option::is_none")]
447 pub description: Option<String>,
448 #[serde(skip_serializing_if = "Option::is_none", rename = "enum")]
449 pub enum_values: Option<Vec<String>>,
450 #[serde(skip_serializing_if = "Option::is_none")]
451 pub example: Option<Value>,
452 #[serde(skip_serializing_if = "Option::is_none")]
453 pub format: Option<String>,
454 #[serde(skip_serializing_if = "Option::is_none")]
455 pub items: Option<Box<Schema>>,
456 #[serde(skip_serializing_if = "Option::is_none")]
457 pub max_items: Option<i64>,
458 #[serde(skip_serializing_if = "Option::is_none")]
459 pub max_length: Option<i64>,
460 #[serde(skip_serializing_if = "Option::is_none")]
461 pub max_properties: Option<i64>,
462 #[serde(skip_serializing_if = "Option::is_none")]
463 pub maximum: Option<f64>,
464 #[serde(skip_serializing_if = "Option::is_none")]
465 pub min_items: Option<i64>,
466 #[serde(skip_serializing_if = "Option::is_none")]
467 pub min_length: Option<i64>,
468 #[serde(skip_serializing_if = "Option::is_none")]
469 pub min_properties: Option<i64>,
470 #[serde(skip_serializing_if = "Option::is_none")]
471 pub minimum: Option<f64>,
472 #[serde(skip_serializing_if = "Option::is_none")]
473 pub nullable: Option<bool>,
474 #[serde(skip_serializing_if = "Option::is_none")]
475 pub pattern: Option<String>,
476 #[serde(skip_serializing_if = "Option::is_none")]
477 pub properties: Option<HashMap<String, Box<Schema>>>,
478 #[serde(skip_serializing_if = "Option::is_none")]
479 pub property_ordering: Option<Vec<String>>,
480 #[serde(skip_serializing_if = "Option::is_none")]
481 pub required: Option<Vec<String>>,
482 #[serde(skip_serializing_if = "Option::is_none")]
483 pub title: Option<String>,
484 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
485 pub ty: Option<Type>,
486}
487
488#[cfg(test)]
489mod schema_builder_tests {
490 use super::*;
491
492 #[test]
493 fn test_tool_serialization() {
494 let tool = Tool {
495 google_maps: Some(GoogleMaps {
496 enable_widget: Some(true),
497 ..GoogleMaps::default()
498 }),
499 ..Tool::default()
500 };
501
502 let json = serde_json::to_value(&tool).unwrap();
503 assert_eq!(json["googleMaps"]["enableWidget"].as_bool(), Some(true));
504 }
505}
506
507impl Schema {
508 #[must_use]
510 pub fn object() -> SchemaBuilder {
511 SchemaBuilder::new(Type::Object)
512 }
513
514 #[must_use]
516 pub fn array() -> SchemaBuilder {
517 SchemaBuilder::new(Type::Array)
518 }
519
520 #[must_use]
522 pub fn string() -> Self {
523 Self {
524 ty: Some(Type::String),
525 ..Default::default()
526 }
527 }
528
529 #[must_use]
531 pub fn integer() -> Self {
532 Self {
533 ty: Some(Type::Integer),
534 ..Default::default()
535 }
536 }
537
538 #[must_use]
540 pub fn number() -> Self {
541 Self {
542 ty: Some(Type::Number),
543 ..Default::default()
544 }
545 }
546
547 #[must_use]
549 pub fn boolean() -> Self {
550 Self {
551 ty: Some(Type::Boolean),
552 ..Default::default()
553 }
554 }
555}
556
557pub struct SchemaBuilder {
558 schema: Schema,
559}
560
561impl SchemaBuilder {
562 #[must_use]
564 pub fn new(ty: Type) -> Self {
565 Self {
566 schema: Schema {
567 ty: Some(ty),
568 ..Default::default()
569 },
570 }
571 }
572
573 #[must_use]
575 pub fn description(mut self, description: impl Into<String>) -> Self {
576 self.schema.description = Some(description.into());
577 self
578 }
579
580 #[must_use]
582 pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
583 let properties = self.schema.properties.get_or_insert_with(HashMap::new);
584 properties.insert(name.into(), Box::new(schema));
585 self
586 }
587
588 #[must_use]
590 pub fn required(mut self, name: impl Into<String>) -> Self {
591 let required = self.schema.required.get_or_insert_with(Vec::new);
592 required.push(name.into());
593 self
594 }
595
596 #[must_use]
598 pub fn items(mut self, schema: Schema) -> Self {
599 self.schema.items = Some(Box::new(schema));
600 self
601 }
602
603 #[must_use]
605 pub fn enum_values(mut self, values: Vec<String>) -> Self {
606 self.schema.enum_values = Some(values);
607 self
608 }
609
610 #[must_use]
612 pub fn build(self) -> Schema {
613 self.schema
614 }
615}
616
617#[cfg(test)]
618mod tests {
619 use super::*;
620
621 #[test]
622 fn schema_builder_object() {
623 let schema = Schema::object()
624 .property("name", Schema::string())
625 .required("name")
626 .build();
627 assert_eq!(schema.ty, Some(Type::Object));
628 assert!(schema.properties.unwrap().contains_key("name"));
629 }
630
631 #[test]
632 fn schema_builder_array_and_enum() {
633 let schema = Schema::array()
634 .items(Schema::string())
635 .enum_values(vec!["a".into(), "b".into()])
636 .build();
637 assert_eq!(schema.ty, Some(Type::Array));
638 assert_eq!(schema.items.unwrap().ty, Some(Type::String));
639 assert_eq!(
640 schema.enum_values.unwrap(),
641 vec!["a".to_string(), "b".to_string()]
642 );
643 }
644
645 #[test]
646 fn tool_function_declaration_serialization() {
647 let declaration = FunctionDeclaration {
648 name: "lookup".to_string(),
649 description: Some("search".to_string()),
650 parameters: Some(Schema::object().property("q", Schema::string()).build()),
651 parameters_json_schema: None,
652 response: Some(Schema::string()),
653 response_json_schema: None,
654 behavior: None,
655 };
656 let tool = Tool {
657 function_declarations: Some(vec![declaration]),
658 ..Default::default()
659 };
660 let json = serde_json::to_value(&tool).unwrap();
661 assert!(json["functionDeclarations"].is_array());
662 assert_eq!(
663 json["functionDeclarations"][0]["name"].as_str(),
664 Some("lookup")
665 );
666 }
667}