openai_schemars/
lib.rs

1//! A Rust utility to generate JSON Schemas compatible with OpenAI's function calling API, using [schemars](https://docs.rs/schemars/) and [serde_json](https://docs.rs/serde_json/).
2//!
3//! ## Example
4//!
5//! ```rust
6//! use openai_schemars::{Schema, schemars};
7//! use schemars::JsonSchema;
8//!
9//! #[derive(JsonSchema)]
10//! struct MyStruct {
11//!     /// The name of the user
12//!     name: String,
13//! }
14//!
15//! let schema = Schema::new::<MyStruct>().expect("Failed to generate schema");
16//! println!("{}", serde_json::to_string_pretty(&schema.value).expect("Failed to serialize schema"));
17//! ```
18
19pub use schemars;
20
21use schemars::schema_for;
22
23/// A JSON Schema that is compatible with OpenAI's function calling API.
24pub struct Schema {
25    pub value: serde_json::Value,
26}
27
28impl Schema {
29    pub fn new<T: schemars::JsonSchema>() -> Result<Self, serde_json::Error> {
30        let schema = serde_json::to_value(schema_for!(T))?;
31        let mut json_schema = Self { value: schema };
32        json_schema.enforce_openai_subset();
33        Ok(json_schema)
34    }
35
36    fn enforce_openai_subset(&mut self) {
37        Self::remove_property_format_value_from_json(&mut self.value);
38        Self::replace_one_of_by_any_of(&mut self.value);
39        Self::set_additional_properties_to_false(&mut self.value);
40        Self::enforce_all_required_properties(&mut self.value);
41    }
42
43    fn set_additional_properties_to_false(object: &mut serde_json::Value) {
44        match object {
45            serde_json::Value::Object(object) => {
46                if object.get("type") == Some(&serde_json::Value::String("object".into())) {
47                    object.insert("additionalProperties".into(), serde_json::Value::Bool(false));
48                }
49                for value in object.values_mut() {
50                    Self::set_additional_properties_to_false(value);
51                }
52            }
53            serde_json::Value::Array(array) => {
54                for value in array.iter_mut() {
55                    Self::set_additional_properties_to_false(value);
56                }
57            }
58            _ => {}
59        }
60    }
61    
62    fn enforce_all_required_properties(object: &mut serde_json::Value) {
63        match object {
64            serde_json::Value::Object(object) => {
65                let properties = object
66                    .get_mut("properties")
67                    .and_then(|properties| properties.as_object())
68                    .map(|properties|
69                        properties
70                            .keys()
71                            .map(|key| serde_json::Value::String(key.to_string()))
72                            .collect::<Vec<_>>()
73                    );
74                if let (Some(required), Some(properties)) = (object.get_mut("required"), properties) {
75                    if let Some(required) = required.as_array_mut() {
76                        for property in properties {
77                            if !required.contains(&property) {
78                                required.push(property);
79                            }
80                        }
81                    }
82                }
83                for value in object.values_mut() {
84                    Self::enforce_all_required_properties(value);
85                }
86            },
87            serde_json::Value::Array(array) => {
88                for value in array.iter_mut() {
89                    Self::enforce_all_required_properties(value);
90                }
91            }
92            _ => {}
93        }
94    }
95    
96    fn replace_one_of_by_any_of(object: &mut serde_json::Value) {
97        match object {
98            serde_json::Value::Object(object) => {
99                for key in ["oneOf", "allOf"] {
100                    if object.contains_key(key) {
101                        if let Some(value) = object.remove(key) {
102                            object.insert("anyOf".into(), value);
103                        }
104                    }    
105                }
106                for value in object.values_mut() {
107                    Self::replace_one_of_by_any_of(value);
108                }
109            }
110            serde_json::Value::Array(array) => {
111                for value in array.iter_mut() {
112                    Self::replace_one_of_by_any_of(value);
113                }
114            }
115            _ => {}
116        }
117    }
118    
119    fn remove_property_format_value_from_json(object: &mut serde_json::Value) {
120        match object {
121            serde_json::Value::Object(object) => {
122                for key in ["minLength", "maxLength", "pattern", "format", "minimum", "maximum", "multipleOf", "patternProperties", "unevaluatedProperties", "propertyNames", "minProperties", "maxProperties", "unevaluatedItems", "contains", "minContains", "maxContains", "minItems", "maxItems", "uniqueItems"] {
123                    object.remove(key);
124                }
125                for value in object.values_mut() {
126                    Self::remove_property_format_value_from_json(value);
127                }
128            },
129            serde_json::Value::Array(array) => {
130                for value in array.iter_mut() {
131                    Self::remove_property_format_value_from_json(value);
132                }
133            },
134            _ => {}
135        }
136    }
137}