1use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Deserialize)]
19pub struct ChatCompletionRequest {
20 pub model: ModelField,
22 pub messages: Vec<ChatCompletionMessage>,
24 #[serde(default)]
26 pub stream: bool,
27 #[serde(default)]
29 pub temperature: Option<f32>,
30 #[serde(default)]
32 pub max_tokens: Option<u32>,
33}
34
35#[derive(Debug, Clone, Deserialize)]
37#[serde(untagged)]
38pub enum ModelField {
39 Single(String),
41 Multiple(Vec<String>),
43}
44
45#[derive(Debug, Clone, Deserialize)]
47pub struct ChatCompletionMessage {
48 pub role: String,
50 pub content: String,
52}
53
54#[derive(Debug, Serialize)]
60pub struct ChatCompletionResponse {
61 pub id: String,
63 pub object: &'static str,
65 pub created: u64,
67 pub model: String,
69 pub choices: Vec<Choice>,
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub usage: Option<Usage>,
74}
75
76#[derive(Debug, Serialize)]
78pub struct Choice {
79 pub index: u32,
81 pub message: ResponseMessage,
83 pub finish_reason: Option<String>,
85}
86
87#[derive(Debug, Serialize)]
89pub struct ResponseMessage {
90 pub role: &'static str,
92 pub content: String,
94}
95
96#[derive(Debug, Serialize)]
98pub struct Usage {
99 #[serde(rename = "prompt_tokens")]
101 pub prompt: u32,
102 #[serde(rename = "completion_tokens")]
104 pub completion: u32,
105 #[serde(rename = "total_tokens")]
107 pub total: u32,
108}
109
110#[derive(Debug, Serialize)]
116pub struct ChatCompletionChunk {
117 pub id: String,
119 pub object: &'static str,
121 pub created: u64,
123 pub model: String,
125 pub choices: Vec<ChunkChoice>,
127}
128
129#[derive(Debug, Serialize)]
131pub struct ChunkChoice {
132 pub index: u32,
134 pub delta: Delta,
136 pub finish_reason: Option<String>,
138}
139
140#[derive(Debug, Serialize)]
142pub struct Delta {
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub role: Option<&'static str>,
146 #[serde(skip_serializing_if = "Option::is_none")]
148 pub content: Option<String>,
149}
150
151#[derive(Debug, Serialize)]
157pub struct MultiplexResponse {
158 pub id: String,
160 pub object: &'static str,
162 pub created: u64,
164 pub results: Vec<MultiplexProviderResult>,
166 pub summary: String,
168}
169
170#[derive(Debug, Serialize)]
172pub struct MultiplexProviderResult {
173 pub provider: String,
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub model: Option<String>,
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub content: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub error: Option<String>,
184 pub duration_ms: u64,
186}
187
188#[derive(Debug, Serialize)]
194pub struct ModelsResponse {
195 pub object: &'static str,
197 pub data: Vec<ModelObject>,
199}
200
201#[derive(Debug, Serialize)]
203pub struct ModelObject {
204 pub id: String,
206 pub object: &'static str,
208 pub owned_by: String,
210}
211
212#[derive(Debug, Serialize)]
218pub struct HealthResponse {
219 pub status: &'static str,
221 pub providers: std::collections::HashMap<String, String>,
223}
224
225#[derive(Debug, Serialize)]
231pub struct ErrorResponse {
232 pub error: ErrorDetail,
234}
235
236#[derive(Debug, Serialize)]
238pub struct ErrorDetail {
239 pub message: String,
241 #[serde(rename = "type")]
243 pub error_type: String,
244 #[serde(skip_serializing_if = "Option::is_none")]
246 pub param: Option<String>,
247 #[serde(skip_serializing_if = "Option::is_none")]
249 pub code: Option<String>,
250}
251
252impl ErrorResponse {
253 pub fn new(error_type: impl Into<String>, message: impl Into<String>) -> Self {
255 Self {
256 error: ErrorDetail {
257 message: message.into(),
258 error_type: error_type.into(),
259 param: None,
260 code: None,
261 },
262 }
263 }
264}
265
266#[cfg(test)]
267mod tests {
268 use super::*;
269
270 #[test]
271 fn deserialize_single_model() {
272 let json = r#"{"model":"copilot:gpt-4o","messages":[{"role":"user","content":"hi"}]}"#;
273 let req: ChatCompletionRequest = serde_json::from_str(json).expect("deserialize");
274 match req.model {
275 ModelField::Single(m) => assert_eq!(m, "copilot:gpt-4o"),
276 ModelField::Multiple(_) => panic!("expected single"),
277 }
278 assert!(!req.stream);
279 }
280
281 #[test]
282 fn deserialize_multiple_models() {
283 let json = r#"{"model":["copilot:gpt-4o","claude:opus"],"messages":[{"role":"user","content":"hi"}]}"#;
284 let req: ChatCompletionRequest = serde_json::from_str(json).expect("deserialize");
285 match req.model {
286 ModelField::Multiple(models) => {
287 assert_eq!(models.len(), 2);
288 assert_eq!(models[0], "copilot:gpt-4o");
289 assert_eq!(models[1], "claude:opus");
290 }
291 ModelField::Single(_) => panic!("expected multiple"),
292 }
293 }
294
295 #[test]
296 fn deserialize_with_stream_flag() {
297 let json =
298 r#"{"model":"copilot","messages":[{"role":"user","content":"hi"}],"stream":true}"#;
299 let req: ChatCompletionRequest = serde_json::from_str(json).expect("deserialize");
300 assert!(req.stream);
301 }
302
303 #[test]
304 fn serialize_completion_response() {
305 let resp = ChatCompletionResponse {
306 id: "chatcmpl-test".to_owned(),
307 object: "chat.completion",
308 created: 1_700_000_000,
309 model: "copilot:gpt-4o".to_owned(),
310 choices: vec![Choice {
311 index: 0,
312 message: ResponseMessage {
313 role: "assistant",
314 content: "Hello!".to_owned(),
315 },
316 finish_reason: Some("stop".to_owned()),
317 }],
318 usage: None,
319 };
320 let json = serde_json::to_string(&resp).expect("serialize");
321 assert!(json.contains("chat.completion"));
322 assert!(json.contains("Hello!"));
323 }
324
325 #[test]
326 fn serialize_error_response() {
327 let resp = ErrorResponse::new("invalid_request_error", "Unknown model");
328 let json = serde_json::to_string(&resp).expect("serialize");
329 assert!(json.contains("invalid_request_error"));
330 assert!(json.contains("Unknown model"));
331 }
332
333 #[test]
334 fn serialize_chunk_response() {
335 let chunk = ChatCompletionChunk {
336 id: "chatcmpl-test".to_owned(),
337 object: "chat.completion.chunk",
338 created: 1_700_000_000,
339 model: "copilot".to_owned(),
340 choices: vec![ChunkChoice {
341 index: 0,
342 delta: Delta {
343 role: None,
344 content: Some("token".to_owned()),
345 },
346 finish_reason: None,
347 }],
348 };
349 let json = serde_json::to_string(&chunk).expect("serialize");
350 assert!(json.contains("chat.completion.chunk"));
351 assert!(json.contains("token"));
352 }
353
354 #[test]
355 fn serialize_models_response() {
356 let resp = ModelsResponse {
357 object: "list",
358 data: vec![ModelObject {
359 id: "copilot:gpt-4o".to_owned(),
360 object: "model",
361 owned_by: "copilot".to_owned(),
362 }],
363 };
364 let json = serde_json::to_string(&resp).expect("serialize");
365 assert!(json.contains("copilot:gpt-4o"));
366 }
367}