systemprompt_models/schema/
capabilities.rs1use serde_json::Value;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub struct SchemaComposition {
18 pub allof: bool,
19 pub anyof: bool,
20 pub oneof: bool,
21 pub if_then_else: bool,
22 pub not: bool,
23}
24
25#[expect(
26 clippy::struct_excessive_bools,
27 reason = "schema feature matrix: each bool is an independent JSON-Schema construct the \
28 provider does or does not accept, not state"
29)]
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31pub struct SchemaFeatures {
32 pub references: bool,
33 pub definitions: bool,
34 pub additional_properties: bool,
35 pub const_values: bool,
36 pub exclusive_bounds: bool,
37 pub property_names: bool,
38 pub tuple_items: bool,
39 pub loose_items: bool,
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub struct ProviderCapabilities {
44 pub composition: SchemaComposition,
45 pub features: SchemaFeatures,
46}
47
48impl ProviderCapabilities {
49 pub const fn anthropic() -> Self {
50 Self {
51 composition: SchemaComposition {
52 allof: true,
53 anyof: true,
54 oneof: true,
55 if_then_else: true,
56 not: true,
57 },
58 features: SchemaFeatures {
59 references: true,
60 definitions: true,
61 additional_properties: true,
62 const_values: true,
63 exclusive_bounds: true,
64 property_names: true,
65 tuple_items: true,
66 loose_items: true,
67 },
68 }
69 }
70
71 pub const fn openai() -> Self {
72 Self {
73 composition: SchemaComposition {
74 allof: true,
75 anyof: true,
76 oneof: true,
77 if_then_else: false,
78 not: false,
79 },
80 features: SchemaFeatures {
81 references: true,
82 definitions: true,
83 additional_properties: true,
84 const_values: true,
85 exclusive_bounds: true,
86 property_names: true,
87 tuple_items: true,
88 loose_items: true,
89 },
90 }
91 }
92
93 pub const fn gemini() -> Self {
94 Self {
95 composition: SchemaComposition {
96 allof: false,
97 anyof: true,
98 oneof: false,
99 if_then_else: false,
100 not: false,
101 },
102 features: SchemaFeatures {
103 references: false,
104 definitions: false,
105 additional_properties: false,
106 const_values: false,
107 exclusive_bounds: false,
108 property_names: false,
109 tuple_items: false,
110 loose_items: false,
111 },
112 }
113 }
114
115 pub fn requires_transformation(&self, schema: &Value) -> bool {
117 if let Some(obj) = schema.as_object() {
118 if obj.contains_key("allOf") && !self.composition.allof {
119 return true;
120 }
121 if obj.contains_key("anyOf") && !self.composition.anyof {
122 return true;
123 }
124 if obj.contains_key("oneOf") && !self.composition.oneof {
125 return true;
126 }
127 if obj.contains_key("if") && !self.composition.if_then_else {
128 return true;
129 }
130 if obj.contains_key("$ref") && !self.features.references {
131 return true;
132 }
133 if (obj.contains_key("definitions") || obj.contains_key("$defs"))
134 && !self.features.definitions
135 {
136 return true;
137 }
138 if obj.contains_key("not") && !self.composition.not {
139 return true;
140 }
141 }
142 false
143 }
144}