1use crate::types::CacheControl;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct WebSearchTool {
22 #[serde(rename = "type")]
24 pub tool_type: String,
25
26 pub name: String,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub allowed_domains: Option<Vec<String>>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub blocked_domains: Option<Vec<String>>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub max_uses: Option<u32>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub user_location: Option<UserLocation>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub cache_control: Option<CacheControl>,
48}
49
50impl WebSearchTool {
51 pub fn new() -> Self {
53 Self {
54 tool_type: "web_search_20260209".into(),
55 name: "web_search".into(),
56 allowed_domains: None,
57 blocked_domains: None,
58 max_uses: None,
59 user_location: None,
60 cache_control: None,
61 }
62 }
63
64 pub fn with_allowed_domains(mut self, domains: Vec<String>) -> Self {
66 self.allowed_domains = Some(domains);
67 self
68 }
69
70 pub fn with_blocked_domains(mut self, domains: Vec<String>) -> Self {
72 self.blocked_domains = Some(domains);
73 self
74 }
75
76 pub fn with_max_uses(mut self, max: u32) -> Self {
78 self.max_uses = Some(max);
79 self
80 }
81
82 pub fn with_user_location(mut self, location: UserLocation) -> Self {
84 self.user_location = Some(location);
85 self
86 }
87
88 pub fn with_cache_control(mut self, cache_control: CacheControl) -> Self {
90 self.cache_control = Some(cache_control);
91 self
92 }
93}
94
95impl Default for WebSearchTool {
96 fn default() -> Self {
97 Self::new()
98 }
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct UserLocation {
104 #[serde(rename = "type")]
106 pub location_type: String,
107
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub city: Option<String>,
111
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub country: Option<String>,
115
116 #[serde(skip_serializing_if = "Option::is_none")]
118 pub region: Option<String>,
119
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub timezone: Option<String>,
123}
124
125impl UserLocation {
126 pub fn new() -> Self {
128 Self {
129 location_type: "approximate".into(),
130 city: None,
131 country: None,
132 region: None,
133 timezone: None,
134 }
135 }
136}
137
138impl Default for UserLocation {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct WebFetchTool {
155 #[serde(rename = "type")]
157 pub tool_type: String,
158
159 pub name: String,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub allowed_domains: Option<Vec<String>>,
165
166 #[serde(skip_serializing_if = "Option::is_none")]
168 pub blocked_domains: Option<Vec<String>>,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub max_uses: Option<u32>,
173
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub max_content_tokens: Option<u32>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub cache_control: Option<CacheControl>,
181}
182
183impl WebFetchTool {
184 pub fn new() -> Self {
186 Self {
187 tool_type: "web_fetch_20260309".into(),
188 name: "web_fetch".into(),
189 allowed_domains: None,
190 blocked_domains: None,
191 max_uses: None,
192 max_content_tokens: None,
193 cache_control: None,
194 }
195 }
196}
197
198impl Default for WebFetchTool {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct CodeExecutionTool {
215 #[serde(rename = "type")]
217 pub tool_type: String,
218
219 pub name: String,
221
222 #[serde(skip_serializing_if = "Option::is_none")]
224 pub cache_control: Option<CacheControl>,
225}
226
227impl CodeExecutionTool {
228 pub fn new() -> Self {
230 Self {
231 tool_type: "code_execution_20260120".into(),
232 name: "code_execution".into(),
233 cache_control: None,
234 }
235 }
236}
237
238impl Default for CodeExecutionTool {
239 fn default() -> Self {
240 Self::new()
241 }
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct BashTool {
255 #[serde(rename = "type")]
257 pub tool_type: String,
258
259 pub name: String,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub cache_control: Option<CacheControl>,
265}
266
267impl BashTool {
268 pub fn new() -> Self {
270 Self {
271 tool_type: "bash_20250124".into(),
272 name: "bash".into(),
273 cache_control: None,
274 }
275 }
276}
277
278impl Default for BashTool {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct TextEditorTool {
295 #[serde(rename = "type")]
297 pub tool_type: String,
298
299 pub name: String,
301
302 #[serde(skip_serializing_if = "Option::is_none")]
304 pub max_characters: Option<u32>,
305
306 #[serde(skip_serializing_if = "Option::is_none")]
308 pub cache_control: Option<CacheControl>,
309}
310
311impl TextEditorTool {
312 pub fn new() -> Self {
314 Self {
315 tool_type: "text_editor_20250728".into(),
316 name: "str_replace_editor".into(),
317 max_characters: None,
318 cache_control: None,
319 }
320 }
321}
322
323impl Default for TextEditorTool {
324 fn default() -> Self {
325 Self::new()
326 }
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
339pub struct MemoryTool {
340 #[serde(rename = "type")]
342 pub tool_type: String,
343
344 pub name: String,
346
347 #[serde(skip_serializing_if = "Option::is_none")]
348 pub cache_control: Option<CacheControl>,
349}
350
351impl MemoryTool {
352 pub fn new() -> Self {
354 Self {
355 tool_type: "memory_20250818".into(),
356 name: "memory".into(),
357 cache_control: None,
358 }
359 }
360}
361
362impl Default for MemoryTool {
363 fn default() -> Self {
364 Self::new()
365 }
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
378pub struct ToolSearchBm25 {
379 #[serde(rename = "type")]
381 pub tool_type: String,
382
383 pub name: String,
385
386 #[serde(skip_serializing_if = "Option::is_none")]
387 pub cache_control: Option<CacheControl>,
388}
389
390impl ToolSearchBm25 {
391 pub fn new() -> Self {
393 Self {
394 tool_type: "tool_search_tool_bm25_20251119".into(),
395 name: "tool_search_tool_bm25".into(),
396 cache_control: None,
397 }
398 }
399}
400
401impl Default for ToolSearchBm25 {
402 fn default() -> Self {
403 Self::new()
404 }
405}
406
407#[derive(Debug, Clone, Serialize, Deserialize)]
417pub struct ToolSearchRegex {
418 #[serde(rename = "type")]
420 pub tool_type: String,
421
422 pub name: String,
424
425 #[serde(skip_serializing_if = "Option::is_none")]
426 pub cache_control: Option<CacheControl>,
427}
428
429impl ToolSearchRegex {
430 pub fn new() -> Self {
432 Self {
433 tool_type: "tool_search_tool_regex_20251119".into(),
434 name: "tool_search_tool_regex".into(),
435 cache_control: None,
436 }
437 }
438}
439
440impl Default for ToolSearchRegex {
441 fn default() -> Self {
442 Self::new()
443 }
444}
445
446impl From<WebSearchTool> for crate::types::ToolDefinition {
449 fn from(tool: WebSearchTool) -> crate::types::ToolDefinition {
450 crate::types::ToolDefinition::Server(
451 serde_json::to_value(tool).expect("Failed to serialize WebSearchTool"),
452 )
453 }
454}
455
456impl From<WebFetchTool> for crate::types::ToolDefinition {
457 fn from(tool: WebFetchTool) -> crate::types::ToolDefinition {
458 crate::types::ToolDefinition::Server(
459 serde_json::to_value(tool).expect("Failed to serialize WebFetchTool"),
460 )
461 }
462}
463
464impl From<CodeExecutionTool> for crate::types::ToolDefinition {
465 fn from(tool: CodeExecutionTool) -> crate::types::ToolDefinition {
466 crate::types::ToolDefinition::Server(
467 serde_json::to_value(tool).expect("Failed to serialize CodeExecutionTool"),
468 )
469 }
470}
471
472impl From<BashTool> for crate::types::ToolDefinition {
473 fn from(tool: BashTool) -> crate::types::ToolDefinition {
474 crate::types::ToolDefinition::Server(
475 serde_json::to_value(tool).expect("Failed to serialize BashTool"),
476 )
477 }
478}
479
480impl From<TextEditorTool> for crate::types::ToolDefinition {
481 fn from(tool: TextEditorTool) -> crate::types::ToolDefinition {
482 crate::types::ToolDefinition::Server(
483 serde_json::to_value(tool).expect("Failed to serialize TextEditorTool"),
484 )
485 }
486}
487
488impl From<MemoryTool> for crate::types::ToolDefinition {
489 fn from(tool: MemoryTool) -> crate::types::ToolDefinition {
490 crate::types::ToolDefinition::Server(
491 serde_json::to_value(tool).expect("Failed to serialize MemoryTool"),
492 )
493 }
494}
495
496impl From<ToolSearchBm25> for crate::types::ToolDefinition {
497 fn from(tool: ToolSearchBm25) -> crate::types::ToolDefinition {
498 crate::types::ToolDefinition::Server(
499 serde_json::to_value(tool).expect("Failed to serialize ToolSearchBm25"),
500 )
501 }
502}
503
504impl From<ToolSearchRegex> for crate::types::ToolDefinition {
505 fn from(tool: ToolSearchRegex) -> crate::types::ToolDefinition {
506 crate::types::ToolDefinition::Server(
507 serde_json::to_value(tool).expect("Failed to serialize ToolSearchRegex"),
508 )
509 }
510}
511
512#[cfg(test)]
513mod tests {
514 use super::*;
515 use crate::types::CacheControl;
516
517 #[test]
520 fn test_web_search_tool_new_type_and_name() {
521 let tool = WebSearchTool::new();
522 assert_eq!(tool.tool_type, "web_search_20260209");
523 assert_eq!(tool.name, "web_search");
524 }
525
526 #[test]
527 fn test_web_search_tool_serialization() {
528 let tool = WebSearchTool::new();
529 let json = serde_json::to_value(&tool).unwrap();
530 assert_eq!(json["type"], "web_search_20260209");
531 assert_eq!(json["name"], "web_search");
532 assert!(json.get("allowed_domains").is_none());
533 assert!(json.get("blocked_domains").is_none());
534 assert!(json.get("max_uses").is_none());
535 assert!(json.get("user_location").is_none());
536 assert!(json.get("cache_control").is_none());
537 }
538
539 #[test]
540 fn test_web_search_tool_builder_allowed_domains() {
541 let tool =
542 WebSearchTool::new().with_allowed_domains(vec!["example.com".into(), "docs.rs".into()]);
543 let json = serde_json::to_value(&tool).unwrap();
544 let domains = json["allowed_domains"].as_array().unwrap();
545 assert_eq!(domains.len(), 2);
546 assert_eq!(domains[0], "example.com");
547 assert_eq!(domains[1], "docs.rs");
548 }
549
550 #[test]
551 fn test_web_search_tool_builder_blocked_domains() {
552 let tool = WebSearchTool::new().with_blocked_domains(vec!["spam.example.com".into()]);
553 let json = serde_json::to_value(&tool).unwrap();
554 let domains = json["blocked_domains"].as_array().unwrap();
555 assert_eq!(domains[0], "spam.example.com");
556 }
557
558 #[test]
559 fn test_web_search_tool_builder_max_uses() {
560 let tool = WebSearchTool::new().with_max_uses(10);
561 let json = serde_json::to_value(&tool).unwrap();
562 assert_eq!(json["max_uses"], 10);
563 }
564
565 #[test]
566 fn test_web_search_tool_builder_user_location() {
567 let location = UserLocation {
568 location_type: "approximate".into(),
569 city: Some("San Francisco".into()),
570 country: Some("US".into()),
571 region: Some("California".into()),
572 timezone: Some("America/Los_Angeles".into()),
573 };
574 let tool = WebSearchTool::new().with_user_location(location);
575 let json = serde_json::to_value(&tool).unwrap();
576 assert_eq!(json["user_location"]["type"], "approximate");
577 assert_eq!(json["user_location"]["city"], "San Francisco");
578 assert_eq!(json["user_location"]["country"], "US");
579 assert_eq!(json["user_location"]["region"], "California");
580 assert_eq!(json["user_location"]["timezone"], "America/Los_Angeles");
581 }
582
583 #[test]
584 fn test_web_search_tool_builder_cache_control() {
585 let tool = WebSearchTool::new().with_cache_control(CacheControl::ephemeral());
586 let json = serde_json::to_value(&tool).unwrap();
587 assert_eq!(json["cache_control"]["type"], "ephemeral");
588 }
589
590 #[test]
591 fn test_web_search_tool_roundtrip() {
592 let tool = WebSearchTool::new()
593 .with_allowed_domains(vec!["example.com".into()])
594 .with_max_uses(5);
595 let json = serde_json::to_string(&tool).unwrap();
596 let deserialized: WebSearchTool = serde_json::from_str(&json).unwrap();
597 assert_eq!(deserialized.tool_type, "web_search_20260209");
598 assert_eq!(deserialized.name, "web_search");
599 assert_eq!(deserialized.max_uses, Some(5));
600 let domains = deserialized.allowed_domains.unwrap();
601 assert_eq!(domains[0], "example.com");
602 }
603
604 #[test]
607 fn test_user_location_new() {
608 let loc = UserLocation::new();
609 assert_eq!(loc.location_type, "approximate");
610 assert!(loc.city.is_none());
611 assert!(loc.country.is_none());
612 assert!(loc.region.is_none());
613 assert!(loc.timezone.is_none());
614 }
615
616 #[test]
617 fn test_user_location_serialization_skips_none() {
618 let loc = UserLocation::new();
619 let json = serde_json::to_value(&loc).unwrap();
620 assert_eq!(json["type"], "approximate");
621 assert!(json.get("city").is_none());
622 }
623
624 #[test]
627 fn test_web_fetch_tool_new_type_and_name() {
628 let tool = WebFetchTool::new();
629 assert_eq!(tool.tool_type, "web_fetch_20260309");
630 assert_eq!(tool.name, "web_fetch");
631 }
632
633 #[test]
634 fn test_web_fetch_tool_serialization() {
635 let tool = WebFetchTool::new();
636 let json = serde_json::to_value(&tool).unwrap();
637 assert_eq!(json["type"], "web_fetch_20260309");
638 assert_eq!(json["name"], "web_fetch");
639 assert!(json.get("max_uses").is_none());
640 assert!(json.get("max_content_tokens").is_none());
641 }
642
643 #[test]
644 fn test_web_fetch_tool_roundtrip() {
645 let tool = WebFetchTool::new();
646 let json = serde_json::to_string(&tool).unwrap();
647 let deserialized: WebFetchTool = serde_json::from_str(&json).unwrap();
648 assert_eq!(deserialized.tool_type, "web_fetch_20260309");
649 assert_eq!(deserialized.name, "web_fetch");
650 }
651
652 #[test]
655 fn test_code_execution_tool_new_type_and_name() {
656 let tool = CodeExecutionTool::new();
657 assert_eq!(tool.tool_type, "code_execution_20260120");
658 assert_eq!(tool.name, "code_execution");
659 }
660
661 #[test]
662 fn test_code_execution_tool_serialization() {
663 let tool = CodeExecutionTool::new();
664 let json = serde_json::to_value(&tool).unwrap();
665 assert_eq!(json["type"], "code_execution_20260120");
666 assert_eq!(json["name"], "code_execution");
667 assert!(json.get("cache_control").is_none());
668 }
669
670 #[test]
671 fn test_code_execution_tool_roundtrip() {
672 let tool = CodeExecutionTool::new();
673 let json = serde_json::to_string(&tool).unwrap();
674 let deserialized: CodeExecutionTool = serde_json::from_str(&json).unwrap();
675 assert_eq!(deserialized.tool_type, "code_execution_20260120");
676 assert_eq!(deserialized.name, "code_execution");
677 }
678
679 #[test]
682 fn test_bash_tool_new_type_and_name() {
683 let tool = BashTool::new();
684 assert_eq!(tool.tool_type, "bash_20250124");
685 assert_eq!(tool.name, "bash");
686 }
687
688 #[test]
689 fn test_bash_tool_serialization() {
690 let tool = BashTool::new();
691 let json = serde_json::to_value(&tool).unwrap();
692 assert_eq!(json["type"], "bash_20250124");
693 assert_eq!(json["name"], "bash");
694 assert!(json.get("cache_control").is_none());
695 }
696
697 #[test]
698 fn test_bash_tool_roundtrip() {
699 let tool = BashTool::new();
700 let json = serde_json::to_string(&tool).unwrap();
701 let deserialized: BashTool = serde_json::from_str(&json).unwrap();
702 assert_eq!(deserialized.tool_type, "bash_20250124");
703 assert_eq!(deserialized.name, "bash");
704 }
705
706 #[test]
709 fn test_text_editor_tool_new_type_and_name() {
710 let tool = TextEditorTool::new();
711 assert_eq!(tool.tool_type, "text_editor_20250728");
712 assert_eq!(tool.name, "str_replace_editor");
713 }
714
715 #[test]
716 fn test_text_editor_tool_serialization() {
717 let tool = TextEditorTool::new();
718 let json = serde_json::to_value(&tool).unwrap();
719 assert_eq!(json["type"], "text_editor_20250728");
720 assert_eq!(json["name"], "str_replace_editor");
721 assert!(json.get("max_characters").is_none());
722 assert!(json.get("cache_control").is_none());
723 }
724
725 #[test]
726 fn test_text_editor_tool_roundtrip() {
727 let tool = TextEditorTool::new();
728 let json = serde_json::to_string(&tool).unwrap();
729 let deserialized: TextEditorTool = serde_json::from_str(&json).unwrap();
730 assert_eq!(deserialized.tool_type, "text_editor_20250728");
731 assert_eq!(deserialized.name, "str_replace_editor");
732 }
733
734 #[test]
737 fn test_memory_tool_serialization() {
738 let tool = MemoryTool::new();
739 let json = serde_json::to_value(&tool).unwrap();
740 assert_eq!(json["type"], "memory_20250818");
741 assert_eq!(json["name"], "memory");
742 }
743
744 #[test]
747 fn test_tool_search_bm25_serialization() {
748 let tool = ToolSearchBm25::new();
749 let json = serde_json::to_value(&tool).unwrap();
750 assert_eq!(json["type"], "tool_search_tool_bm25_20251119");
751 assert_eq!(json["name"], "tool_search_tool_bm25");
752 }
753
754 #[test]
757 fn test_tool_search_regex_serialization() {
758 let tool = ToolSearchRegex::new();
759 let json = serde_json::to_value(&tool).unwrap();
760 assert_eq!(json["type"], "tool_search_tool_regex_20251119");
761 assert_eq!(json["name"], "tool_search_tool_regex");
762 }
763}