openai_tools/common/
function.rs1use crate::common::{
30 errors::{OpenAIToolError, Result as OpenAIToolResult},
31 parameters::Parameters,
32};
33use serde::{ser::SerializeStruct, Deserialize, Serialize};
34use serde_json::Value;
35use std::collections::HashMap;
36
37#[derive(Debug, Clone, Default)]
42pub struct Function {
43 pub name: String,
45 pub description: Option<String>,
47 pub parameters: Option<Parameters>,
49 pub arguments: Option<HashMap<String, Value>>,
51 pub strict: bool,
53}
54
55impl Function {
56 pub fn new<T: AsRef<str>, U: AsRef<str>>(name: T, description: U, parameters: Parameters, strict: bool) -> Self {
69 Self {
70 name: name.as_ref().to_string(),
71 description: Some(description.as_ref().to_string()),
72 parameters: Some(parameters),
73 strict,
74 ..Default::default()
75 }
76 }
77
78 pub fn arguments_as_map(&self) -> OpenAIToolResult<HashMap<String, Value>> {
89 if let Some(args) = &self.arguments {
90 Ok(args.clone())
91 } else {
92 Err(OpenAIToolError::from(anyhow::anyhow!("Function arguments are not set")))
93 }
94 }
95}
96
97impl Serialize for Function {
102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103 where
104 S: serde::Serializer,
105 {
106 let mut state = serializer.serialize_struct("Function", 4)?;
107 state.serialize_field("name", &self.name)?;
108 if let Some(description) = &self.description {
109 state.serialize_field("description", description)?;
110 }
111 if let Some(parameters) = &self.parameters {
112 state.serialize_field("parameters", parameters)?;
113 }
114 state.serialize_field("strict", &self.strict)?;
115
116 if let Some(arguments) = &self.arguments {
120 if !arguments.is_empty() {
121 state.serialize_field("arguments", &serde_json::to_string(arguments).expect("Failed to serialize arguments in Function"))?;
122 }
123 }
124 state.end()
125 }
126}
127
128impl<'de> Deserialize<'de> for Function {
134 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135 where
136 D: serde::Deserializer<'de>,
137 {
138 let mut function = Function::default();
139 let map: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
140
141 if let Some(name) = map.get("name").and_then(Value::as_str) {
142 function.name = name.to_string();
143 } else {
144 return Err(serde::de::Error::missing_field("name"));
145 }
146
147 let arguments = map.get("arguments").and_then(Value::as_str);
148 if let Some(args) = arguments {
149 function.arguments = serde_json::from_str(args).ok();
150 } else {
151 function.arguments = None;
152 }
153
154 let parameters = map.get("parameters").and_then(Value::as_object);
155 if let Some(params) = parameters {
156 function.parameters = Some(Parameters::deserialize(Value::Object(params.clone())).map_err(serde::de::Error::custom)?);
157 } else {
158 function.parameters = None;
159 }
160
161 function.description = map.get("description").and_then(Value::as_str).map(String::from);
162 function.strict = map.get("strict").and_then(Value::as_bool).unwrap_or(false);
163
164 Ok(function)
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use serde_json::json;
172
173 fn function_with_arguments() -> Function {
174 let mut arguments = HashMap::new();
175 arguments.insert("location".to_string(), json!("Tokyo"));
176 arguments.insert("unit".to_string(), json!("celsius"));
177 Function { name: "get_weather".to_string(), arguments: Some(arguments), ..Default::default() }
178 }
179
180 #[test]
183 fn test_arguments_serialized_only_once() {
184 let json = serde_json::to_string(&function_with_arguments()).unwrap();
185
186 let occurrences = json.matches("\"arguments\"").count();
187 assert_eq!(occurrences, 1, "`arguments` must appear exactly once, got {} in {}", occurrences, json);
188 }
189
190 #[test]
193 fn test_arguments_serialized_as_json_string() {
194 let value = serde_json::to_value(function_with_arguments()).unwrap();
195
196 let arguments = value.get("arguments").expect("arguments field is missing");
197 let encoded = arguments.as_str().expect("arguments must serialize as a JSON string");
198
199 let decoded: HashMap<String, Value> = serde_json::from_str(encoded).unwrap();
200 assert_eq!(decoded.get("location"), Some(&json!("Tokyo")));
201 assert_eq!(decoded.get("unit"), Some(&json!("celsius")));
202 }
203
204 #[test]
207 fn test_tool_definition_omits_arguments() {
208 let function =
209 Function { name: "get_weather".to_string(), description: Some("Get the weather".to_string()), strict: true, ..Default::default() };
210
211 let value = serde_json::to_value(&function).unwrap();
212 assert!(value.get("arguments").is_none(), "arguments must be omitted when unset, got {}", value);
213 assert_eq!(value.get("name").and_then(Value::as_str), Some("get_weather"));
214 assert_eq!(value.get("strict").and_then(Value::as_bool), Some(true));
215 }
216
217 #[test]
219 fn test_arguments_roundtrip_through_wire_format() {
220 let json = serde_json::to_string(&function_with_arguments()).unwrap();
221 let parsed: Function = serde_json::from_str(&json).unwrap();
222
223 assert_eq!(parsed.name, "get_weather");
224 let arguments = parsed.arguments.expect("arguments should survive the roundtrip");
225 assert_eq!(arguments.get("location"), Some(&json!("Tokyo")));
226 assert_eq!(arguments.get("unit"), Some(&json!("celsius")));
227 }
228}