gproxy_protocol/protocol/openai/common/tools/
definitions.rs1use serde::ser::SerializeStruct;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4use super::super::{CustomToolGrammarSyntax, Extra, JsonSchema};
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
7#[non_exhaustive]
8pub struct FunctionDefinition {
9 pub name: String,
10 #[serde(skip_serializing_if = "Option::is_none")]
11 pub description: Option<String>,
12 #[serde(skip_serializing_if = "Option::is_none")]
13 pub parameters: Option<JsonSchema>,
14 #[serde(skip_serializing_if = "Option::is_none")]
15 pub strict: Option<bool>,
16 #[serde(
17 default,
18 flatten,
19 skip_serializing_if = "std::collections::BTreeMap::is_empty"
20 )]
21 pub extra: Extra,
22}
23
24#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
25#[non_exhaustive]
26pub struct FunctionCall {
27 pub arguments: String,
28 pub name: String,
29 #[serde(
30 default,
31 flatten,
32 skip_serializing_if = "std::collections::BTreeMap::is_empty"
33 )]
34 pub extra: Extra,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
38#[non_exhaustive]
39pub struct CustomToolDefinition {
40 pub name: String,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub description: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub format: Option<CustomToolInputFormat>,
45 #[serde(
46 default,
47 flatten,
48 skip_serializing_if = "std::collections::BTreeMap::is_empty"
49 )]
50 pub extra: Extra,
51}
52
53#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54#[serde(untagged)]
55#[non_exhaustive]
56pub enum CustomToolInputFormat {
57 Text(CustomToolTextFormat),
58 Grammar(CustomToolGrammarFormat),
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
62#[serde(deny_unknown_fields)]
63#[derive(gproxy_protocol_macros::WireBuilder)]
64#[non_exhaustive]
65pub struct CustomToolTextFormat {
66 #[serde(rename = "type")]
67 pub type_: CustomToolTextFormatType,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
71#[non_exhaustive]
72pub enum CustomToolTextFormatType {
73 #[serde(rename = "text")]
74 Text,
75}
76
77#[derive(Debug, Clone, PartialEq, gproxy_protocol_macros::WireBuilder)]
78#[non_exhaustive]
79pub struct CustomToolGrammarFormat {
80 pub type_: CustomToolGrammarFormatType,
81 pub grammar: CustomToolGrammar,
82}
83
84impl Serialize for CustomToolGrammarFormat {
85 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86 where
87 S: Serializer,
88 {
89 let mut state = serializer.serialize_struct("CustomToolGrammarFormat", 3)?;
93 state.serialize_field("type", &self.type_)?;
94 state.serialize_field("definition", &self.grammar.definition)?;
95 state.serialize_field("syntax", &self.grammar.syntax)?;
96 state.end()
97 }
98}
99
100impl<'de> Deserialize<'de> for CustomToolGrammarFormat {
101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102 where
103 D: Deserializer<'de>,
104 {
105 #[derive(Deserialize)]
106 #[serde(deny_unknown_fields)]
107 struct Wire {
108 #[serde(rename = "type")]
109 type_: CustomToolGrammarFormatType,
110 definition: String,
111 syntax: CustomToolGrammarSyntax,
112 }
113
114 let wire = Wire::deserialize(deserializer)?;
115 Ok(Self {
116 type_: wire.type_,
117 grammar: CustomToolGrammar {
118 definition: wire.definition,
119 syntax: wire.syntax,
120 },
121 })
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126#[non_exhaustive]
127pub enum CustomToolGrammarFormatType {
128 #[serde(rename = "grammar")]
129 Grammar,
130}
131
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
133#[serde(deny_unknown_fields)]
134#[derive(gproxy_protocol_macros::WireBuilder)]
135#[non_exhaustive]
136pub struct CustomToolGrammar {
137 pub definition: String,
138 pub syntax: CustomToolGrammarSyntax,
139}
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, gproxy_protocol_macros::WireBuilder)]
142#[non_exhaustive]
143pub struct NamedTool {
144 pub name: String,
145 #[serde(
146 default,
147 flatten,
148 skip_serializing_if = "std::collections::BTreeMap::is_empty"
149 )]
150 pub extra: Extra,
151}
152
153#[cfg(test)]
154mod tests {
155 use serde_json::json;
156
157 use super::*;
158
159 #[test]
160 fn custom_tool_grammar_format_matches_flat_openai_wire_shape() {
161 let wire = json!({
162 "type": "grammar",
163 "definition": "start: /[a-z]+/",
164 "syntax": "lark"
165 });
166
167 let format: CustomToolInputFormat = serde_json::from_value(wire.clone()).unwrap();
168 let CustomToolInputFormat::Grammar(format) = &format else {
169 panic!("expected grammar format");
170 };
171 assert_eq!(format.type_, CustomToolGrammarFormatType::Grammar);
172 assert_eq!(format.grammar.definition, "start: /[a-z]+/");
173 assert_eq!(format.grammar.syntax, CustomToolGrammarSyntax::Lark);
174 assert_eq!(serde_json::to_value(format).unwrap(), wire);
175 }
176}