openai_compat/types/responses/
tools.rs1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct FunctionTool {
11 pub name: String,
12 #[serde(default, skip_serializing_if = "Option::is_none")]
13 pub description: Option<String>,
14 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub parameters: Option<serde_json::Value>,
16 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub strict: Option<bool>,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(tag = "type", rename_all = "snake_case")]
26pub enum KnownTool {
27 Function(FunctionTool),
28 WebSearch {
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 search_context_size: Option<String>,
31 },
32 FileSearch {
33 vector_store_ids: Vec<String>,
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 max_num_results: Option<u32>,
36 },
37 CodeInterpreter {
38 container: serde_json::Value,
42 },
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(untagged)]
52pub enum Tool {
53 Known(KnownTool),
54 Other(serde_json::Value),
55}
56
57impl Tool {
58 pub fn function(
60 name: impl Into<String>,
61 description: impl Into<String>,
62 parameters: serde_json::Value,
63 ) -> Self {
64 Self::Known(KnownTool::Function(FunctionTool {
65 name: name.into(),
66 description: Some(description.into()),
67 parameters: Some(parameters),
68 strict: None,
69 }))
70 }
71
72 pub fn web_search() -> Self {
74 Self::Known(KnownTool::WebSearch {
75 search_context_size: None,
76 })
77 }
78
79 pub fn file_search(vector_store_ids: Vec<String>) -> Self {
81 Self::Known(KnownTool::FileSearch {
82 vector_store_ids,
83 max_num_results: None,
84 })
85 }
86}
87
88#[derive(Debug, Clone, Serialize, Deserialize)]
91#[serde(untagged)]
92pub enum ToolChoice {
93 Mode(String),
94 Typed(serde_json::Value),
95}
96
97impl ToolChoice {
98 pub const NONE: &'static str = "none";
99 pub const AUTO: &'static str = "auto";
100 pub const REQUIRED: &'static str = "required";
101
102 pub fn mode(mode: impl Into<String>) -> Self {
103 Self::Mode(mode.into())
104 }
105
106 pub fn function(name: impl Into<String>) -> Self {
108 Self::Typed(serde_json::json!({
109 "type": "function",
110 "name": name.into(),
111 }))
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn function_tool_serializes() {
121 let tool = Tool::function(
122 "get_weather",
123 "Get current weather",
124 serde_json::json!({"type": "object"}),
125 );
126 assert_eq!(
127 serde_json::to_value(&tool).unwrap(),
128 serde_json::json!({
129 "type": "function",
130 "name": "get_weather",
131 "description": "Get current weather",
132 "parameters": {"type": "object"}
133 })
134 );
135 }
136
137 #[test]
138 fn unknown_tool_preserved() {
139 let json = serde_json::json!({"type": "mcp", "server_label": "s", "server_url": "u"});
140 let tool: Tool = serde_json::from_value(json.clone()).unwrap();
141 assert!(matches!(tool, Tool::Other(_)));
142 assert_eq!(serde_json::to_value(&tool).unwrap(), json);
143 }
144
145 #[test]
146 fn tool_choice_mode_serializes_as_plain_string() {
147 assert_eq!(
148 serde_json::to_value(ToolChoice::mode("auto")).unwrap(),
149 "auto"
150 );
151 }
152}