Skip to main content

va_ai_api_bridge/universal/
model.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::WireProtocol;
7
8pub type Extensions = BTreeMap<String, Value>;
9
10#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct UniversalRequest {
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    pub id: Option<String>,
15    #[serde(default, skip_serializing_if = "Option::is_none")]
16    pub model: Option<String>,
17    #[serde(default, skip_serializing_if = "Vec::is_empty")]
18    pub instructions: Vec<ContentBlock>,
19    #[serde(default, skip_serializing_if = "Vec::is_empty")]
20    pub input: Vec<UniversalItem>,
21    #[serde(default, skip_serializing_if = "Vec::is_empty")]
22    pub tools: Vec<UniversalTool>,
23    #[serde(default, skip_serializing_if = "Vec::is_empty")]
24    pub server_tools: Vec<ServerToolDeclaration>,
25    #[serde(default, skip_serializing_if = "Option::is_none")]
26    pub tool_choice: Option<ToolChoice>,
27    #[serde(default, skip_serializing_if = "is_false")]
28    pub stream: bool,
29    #[serde(default, skip_serializing_if = "GenerationConfig::is_empty")]
30    pub generation: GenerationConfig,
31    #[serde(default, skip_serializing_if = "Option::is_none")]
32    pub reasoning: Option<ReasoningConfig>,
33    #[serde(default, skip_serializing_if = "Option::is_none")]
34    pub source: Option<SourcePayload>,
35    #[serde(default, skip_serializing_if = "Extensions::is_empty")]
36    pub extensions: Extensions,
37}
38
39#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct UniversalResponse {
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub id: Option<String>,
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub model: Option<String>,
46    #[serde(default, skip_serializing_if = "Vec::is_empty")]
47    pub output: Vec<UniversalItem>,
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub usage: Option<Usage>,
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub finish_reason: Option<FinishReason>,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub status: Option<String>,
54    #[serde(default, skip_serializing_if = "Option::is_none")]
55    pub source: Option<SourcePayload>,
56    #[serde(default, skip_serializing_if = "Extensions::is_empty")]
57    pub extensions: Extensions,
58}
59#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
60#[serde(
61    tag = "type",
62    rename_all = "snake_case",
63    rename_all_fields = "camelCase"
64)]
65pub enum UniversalItem {
66    Message {
67        role: Role,
68        #[serde(default, skip_serializing_if = "Option::is_none")]
69        id: Option<String>,
70        #[serde(default, skip_serializing_if = "Vec::is_empty")]
71        content: Vec<ContentBlock>,
72        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
73        extensions: Extensions,
74    },
75    ToolCall {
76        id: String,
77        name: String,
78        arguments: Value,
79        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
80        extensions: Extensions,
81    },
82    ToolResult {
83        tool_call_id: String,
84        #[serde(default, skip_serializing_if = "Vec::is_empty")]
85        content: Vec<ContentBlock>,
86        #[serde(default, skip_serializing_if = "is_false")]
87        is_error: bool,
88        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
89        extensions: Extensions,
90    },
91    Reasoning {
92        #[serde(default, skip_serializing_if = "Option::is_none")]
93        id: Option<String>,
94        #[serde(default, skip_serializing_if = "Option::is_none")]
95        text: Option<String>,
96        #[serde(default, skip_serializing_if = "Option::is_none")]
97        encrypted: Option<String>,
98        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
99        extensions: Extensions,
100    },
101    Unknown {
102        raw: Value,
103    },
104}
105
106#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
107#[serde(
108    tag = "type",
109    rename_all = "snake_case",
110    rename_all_fields = "camelCase"
111)]
112pub enum ContentBlock {
113    Text {
114        text: String,
115    },
116    Image {
117        #[serde(default, skip_serializing_if = "Option::is_none")]
118        media_type: Option<String>,
119        #[serde(default, skip_serializing_if = "Option::is_none")]
120        url: Option<String>,
121        #[serde(default, skip_serializing_if = "Option::is_none")]
122        data: Option<String>,
123        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
124        extensions: Extensions,
125    },
126    File {
127        #[serde(default, skip_serializing_if = "Option::is_none")]
128        media_type: Option<String>,
129        #[serde(default, skip_serializing_if = "Option::is_none")]
130        filename: Option<String>,
131        #[serde(default, skip_serializing_if = "Option::is_none")]
132        url: Option<String>,
133        #[serde(default, skip_serializing_if = "Option::is_none")]
134        data: Option<String>,
135        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
136        extensions: Extensions,
137    },
138    ToolCall {
139        id: String,
140        name: String,
141        arguments: Value,
142        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
143        extensions: Extensions,
144    },
145    ToolResult {
146        tool_call_id: String,
147        #[serde(default, skip_serializing_if = "Vec::is_empty")]
148        content: Vec<ContentBlock>,
149        #[serde(default, skip_serializing_if = "is_false")]
150        is_error: bool,
151        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
152        extensions: Extensions,
153    },
154    Reasoning {
155        #[serde(default, skip_serializing_if = "Option::is_none")]
156        text: Option<String>,
157        #[serde(default, skip_serializing_if = "Option::is_none")]
158        encrypted: Option<String>,
159        #[serde(default, skip_serializing_if = "Extensions::is_empty")]
160        extensions: Extensions,
161    },
162    Unknown {
163        raw: Value,
164    },
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
168#[serde(rename_all = "snake_case")]
169pub enum Role {
170    Developer,
171    System,
172    User,
173    Assistant,
174    Tool,
175}
176
177#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
178#[serde(
179    tag = "type",
180    rename_all = "snake_case",
181    rename_all_fields = "camelCase"
182)]
183pub enum ToolChoice {
184    Auto,
185    None,
186    Required,
187    Tool { name: String },
188    ServerTool { kind: ServerToolKind },
189}
190
191#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
192#[serde(rename_all = "camelCase")]
193pub struct UniversalTool {
194    pub name: String,
195    #[serde(default, skip_serializing_if = "Option::is_none")]
196    pub description: Option<String>,
197    #[serde(default, skip_serializing_if = "Option::is_none")]
198    pub input_schema: Option<Value>,
199    #[serde(default, skip_serializing_if = "Option::is_none")]
200    pub strict: Option<bool>,
201    #[serde(default, skip_serializing_if = "Extensions::is_empty")]
202    pub extensions: Extensions,
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
206#[serde(rename_all = "snake_case")]
207pub enum ServerToolKind {
208    WebSearch,
209    XSearch,
210    FileSearch,
211    CodeInterpreter,
212    CodeExecution,
213    Unknown,
214}
215
216#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
217#[serde(rename_all = "camelCase")]
218pub struct ServerToolDeclaration {
219    pub kind: ServerToolKind,
220    pub wire_type: String,
221    pub source_protocol: WireProtocol,
222    #[serde(default, skip_serializing_if = "Option::is_none")]
223    pub name: Option<String>,
224    #[serde(default, skip_serializing_if = "Value::is_null")]
225    pub config: Value,
226    pub raw: Value,
227    #[serde(default, skip_serializing_if = "Extensions::is_empty")]
228    pub extensions: Extensions,
229}
230
231#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
232#[serde(rename_all = "camelCase")]
233pub struct GenerationConfig {
234    #[serde(default, skip_serializing_if = "Option::is_none")]
235    pub temperature: Option<f64>,
236    #[serde(default, skip_serializing_if = "Option::is_none")]
237    pub top_p: Option<f64>,
238    #[serde(default, skip_serializing_if = "Option::is_none")]
239    pub max_output_tokens: Option<u64>,
240    #[serde(default, skip_serializing_if = "Extensions::is_empty")]
241    pub extensions: Extensions,
242}
243
244impl GenerationConfig {
245    pub fn is_empty(&self) -> bool {
246        self.temperature.is_none()
247            && self.top_p.is_none()
248            && self.max_output_tokens.is_none()
249            && self.extensions.is_empty()
250    }
251}
252
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254#[serde(rename_all = "camelCase")]
255pub struct ReasoningConfig {
256    #[serde(default, skip_serializing_if = "Option::is_none")]
257    pub effort: Option<String>,
258    #[serde(default, skip_serializing_if = "Option::is_none")]
259    pub budget_tokens: Option<u64>,
260    #[serde(default, skip_serializing_if = "Option::is_none")]
261    pub visible: Option<bool>,
262    #[serde(default, skip_serializing_if = "Extensions::is_empty")]
263    pub extensions: Extensions,
264}
265
266#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
267#[serde(rename_all = "camelCase")]
268pub struct Usage {
269    #[serde(default, skip_serializing_if = "Option::is_none")]
270    pub input_tokens: Option<u64>,
271    #[serde(default, skip_serializing_if = "Option::is_none")]
272    pub output_tokens: Option<u64>,
273    #[serde(default, skip_serializing_if = "Option::is_none")]
274    pub total_tokens: Option<u64>,
275}
276
277#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(rename_all = "snake_case")]
279pub enum FinishReason {
280    Stop,
281    Length,
282    ToolCall,
283    ContentFilter,
284    Error,
285    Unknown,
286}
287
288#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
289#[serde(rename_all = "camelCase")]
290pub struct SourcePayload {
291    pub protocol: WireProtocol,
292    #[serde(default, skip_serializing_if = "Option::is_none")]
293    pub raw: Option<Value>,
294}
295
296fn is_false(value: &bool) -> bool {
297    !*value
298}