1use serde::{Deserialize, Deserializer, Serialize};
21use validator::Validate;
22
23fn de_opt_string_from_number_or_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
25where
26 D: Deserializer<'de>,
27{
28 let v = serde_json::Value::deserialize(deserializer)?;
29 match v {
30 serde_json::Value::Null => Ok(None),
31 serde_json::Value::String(s) => Ok(Some(s)),
32 serde_json::Value::Number(n) => Ok(Some(n.to_string())),
33 other => Err(serde::de::Error::custom(format!(
34 "expected string or number, got {}",
35 other
36 ))),
37 }
38}
39
40#[derive(Clone, Serialize, Deserialize, Validate, Default)]
49#[serde(default)]
50pub struct ChatCompletionResponse {
51 #[serde(
53 skip_serializing_if = "Option::is_none",
54 deserialize_with = "de_opt_string_from_number_or_string"
55 )]
56 pub id: Option<String>,
57
58 #[serde(
60 skip_serializing_if = "Option::is_none",
61 deserialize_with = "de_opt_string_from_number_or_string"
62 )]
63 pub request_id: Option<String>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub created: Option<u64>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub model: Option<String>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub choices: Option<Vec<Choice>>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub usage: Option<Usage>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub video_result: Option<Vec<VideoResultItem>>,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
88 pub web_search: Option<Vec<WebSearchInfo>>,
89
90 #[serde(skip_serializing_if = "Option::is_none")]
92 pub content_filter: Option<Vec<ContentFilterInfo>>,
93 #[serde(skip_serializing_if = "Option::is_none")]
97 pub task_status: Option<TaskStatus>,
98}
99
100impl std::fmt::Debug for ChatCompletionResponse {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 match serde_json::to_string_pretty(self) {
103 Ok(s) => f.write_str(&s),
104 Err(_) => f.debug_struct("ChatCompletionResponse").finish(),
105 }
106 }
107}
108#[derive(Debug, Clone, Serialize, Deserialize)]
111pub enum TaskStatus {
112 #[serde(rename = "PROCESSING", alias = "processing")]
113 Processing,
114 #[serde(rename = "SUCCESS", alias = "success")]
115 Success,
116 #[serde(rename = "FAIL", alias = "fail")]
117 Fail,
118}
119impl TaskStatus {
120 pub fn as_str(&self) -> &'static str {
121 match self {
122 TaskStatus::Processing => "PROCESSING",
123 TaskStatus::Success => "SUCCESS",
124 TaskStatus::Fail => "FAIL",
125 }
126 }
127}
128
129impl std::fmt::Display for TaskStatus {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.write_str(self.as_str())
132 }
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
137pub struct Choice {
138 pub index: i32,
140
141 pub message: Message,
143
144 #[serde(skip_serializing_if = "Option::is_none")]
147 pub finish_reason: Option<String>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
158pub struct Message {
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub role: Option<String>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
168 pub content: Option<serde_json::Value>,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
172 pub reasoning_content: Option<String>,
173
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub audio: Option<AudioContent>,
177
178 #[serde(skip_serializing_if = "Option::is_none")]
180 pub tool_calls: Option<Vec<ToolCallMessage>>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
190pub struct ToolCallMessage {
191 #[serde(
192 skip_serializing_if = "Option::is_none",
193 deserialize_with = "de_opt_string_from_number_or_string"
194 )]
195 pub id: Option<String>,
196 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
197 pub type_: Option<String>,
198 #[serde(skip_serializing_if = "Option::is_none")]
199 pub function: Option<ToolFunction>,
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub mcp: Option<MCPMessage>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
206pub struct ToolFunction {
207 #[serde(skip_serializing_if = "Option::is_none")]
208 pub name: Option<String>,
209 #[serde(skip_serializing_if = "Option::is_none")]
210 pub arguments: Option<String>,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
215pub struct MCPMessage {
216 #[serde(
218 skip_serializing_if = "Option::is_none",
219 deserialize_with = "de_opt_string_from_number_or_string"
220 )]
221 pub id: Option<String>,
222 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
224 pub type_: Option<MCPCallType>,
225 #[serde(skip_serializing_if = "Option::is_none")]
227 pub server_label: Option<String>,
228 #[serde(skip_serializing_if = "Option::is_none")]
230 pub error: Option<String>,
231
232 #[serde(skip_serializing_if = "Option::is_none")]
234 pub tools: Option<Vec<MCPTool>>,
235
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub arguments: Option<String>,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub name: Option<String>,
242 #[serde(skip_serializing_if = "Option::is_none")]
244 pub output: Option<serde_json::Value>,
245}
246
247#[derive(Debug, Clone, Serialize, Deserialize)]
248#[serde(rename_all = "snake_case")]
249pub enum MCPCallType {
250 McpListTools,
251 McpCall,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
255pub struct MCPTool {
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub name: Option<String>,
259 #[serde(skip_serializing_if = "Option::is_none")]
261 pub description: Option<String>,
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub annotations: Option<serde_json::Value>,
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub input_schema: Option<MCPInputSchema>,
268}
269#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
270pub struct MCPInputSchema {
271 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
273 pub type_: Option<MCPInputType>,
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub properties: Option<serde_json::Value>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub required: Option<Vec<String>>,
280 #[serde(skip_serializing_if = "Option::is_none")]
282 pub additional_properties: Option<bool>,
283}
284
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(rename_all = "lowercase")]
290pub enum MCPInputType {
291 Object,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
302pub struct AudioContent {
303 #[serde(
305 skip_serializing_if = "Option::is_none",
306 deserialize_with = "de_opt_string_from_number_or_string"
307 )]
308 pub id: Option<String>,
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub data: Option<String>,
312 #[serde(
314 skip_serializing_if = "Option::is_none",
315 deserialize_with = "de_opt_string_from_number_or_string"
316 )]
317 pub expires_at: Option<String>,
318}
319
320#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
329pub struct Usage {
330 #[serde(skip_serializing_if = "Option::is_none")]
331 pub prompt_tokens: Option<u32>,
332 #[serde(skip_serializing_if = "Option::is_none")]
333 pub completion_tokens: Option<u32>,
334 #[serde(skip_serializing_if = "Option::is_none")]
335 pub total_tokens: Option<u32>,
336 #[serde(skip_serializing_if = "Option::is_none")]
338 pub prompt_tokens_details: Option<PromptTokensDetails>,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
342pub struct PromptTokensDetails {
345 #[serde(skip_serializing_if = "Option::is_none")]
347 pub cached_tokens: Option<u32>,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
357pub struct WebSearchInfo {
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub icon: Option<String>,
361 #[serde(skip_serializing_if = "Option::is_none")]
363 pub title: Option<String>,
364 #[serde(skip_serializing_if = "Option::is_none")]
366 #[validate(url)]
367 pub link: Option<String>,
368 #[serde(skip_serializing_if = "Option::is_none")]
370 pub media: Option<String>,
371 #[serde(skip_serializing_if = "Option::is_none")]
373 pub publish_date: Option<String>,
374 #[serde(skip_serializing_if = "Option::is_none")]
376 pub content: Option<String>,
377 #[serde(skip_serializing_if = "Option::is_none")]
379 pub refer: Option<String>,
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
389pub struct VideoResultItem {
390 #[serde(skip_serializing_if = "Option::is_none")]
392 #[validate(url)]
393 pub url: Option<String>,
394 #[serde(skip_serializing_if = "Option::is_none")]
396 #[validate(url)]
397 pub cover_image_url: Option<String>,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
406pub struct ContentFilterInfo {
407 #[serde(skip_serializing_if = "Option::is_none")]
410 pub role: Option<String>,
411
412 #[serde(skip_serializing_if = "Option::is_none")]
414 #[validate(range(min = 0, max = 3))]
415 pub level: Option<i32>,
416}
417
418impl ChatCompletionResponse {
420 pub fn id(&self) -> Option<&str> {
421 self.id.as_deref()
422 }
423 pub fn request_id(&self) -> Option<&str> {
424 self.request_id.as_deref()
425 }
426 pub fn created(&self) -> Option<u64> {
427 self.created
428 }
429 pub fn model(&self) -> Option<&str> {
430 self.model.as_deref()
431 }
432 pub fn choices(&self) -> Option<&[Choice]> {
433 self.choices.as_deref()
434 }
435 pub fn usage(&self) -> Option<&Usage> {
436 self.usage.as_ref()
437 }
438 pub fn video_result(&self) -> Option<&[VideoResultItem]> {
439 self.video_result.as_deref()
440 }
441 pub fn web_search(&self) -> Option<&[WebSearchInfo]> {
442 self.web_search.as_deref()
443 }
444 pub fn content_filter(&self) -> Option<&[ContentFilterInfo]> {
445 self.content_filter.as_deref()
446 }
447 pub fn task_status(&self) -> Option<&TaskStatus> {
448 self.task_status.as_ref()
449 }
450}
451
452impl Choice {
453 pub fn index(&self) -> i32 {
454 self.index
455 }
456 pub fn message(&self) -> &Message {
457 &self.message
458 }
459 pub fn finish_reason(&self) -> Option<&str> {
460 self.finish_reason.as_deref()
461 }
462}
463
464impl Message {
465 pub fn role(&self) -> Option<&str> {
466 self.role.as_deref()
467 }
468 pub fn content(&self) -> Option<&serde_json::Value> {
469 self.content.as_ref()
470 }
471 pub fn reasoning_content(&self) -> Option<&str> {
472 self.reasoning_content.as_deref()
473 }
474 pub fn audio(&self) -> Option<&AudioContent> {
475 self.audio.as_ref()
476 }
477 pub fn tool_calls(&self) -> Option<&[ToolCallMessage]> {
478 self.tool_calls.as_deref()
479 }
480}
481
482impl ToolCallMessage {
483 pub fn id(&self) -> Option<&str> {
484 self.id.as_deref()
485 }
486 pub fn type_(&self) -> Option<&str> {
487 self.type_.as_deref()
488 }
489 pub fn function(&self) -> Option<&ToolFunction> {
490 self.function.as_ref()
491 }
492 pub fn mcp(&self) -> Option<&MCPMessage> {
493 self.mcp.as_ref()
494 }
495}
496
497impl ToolFunction {
498 pub fn name(&self) -> Option<&str> {
499 self.name.as_deref()
500 }
501 pub fn arguments(&self) -> Option<&str> {
502 self.arguments.as_deref()
503 }
504}
505
506impl MCPMessage {
507 pub fn id(&self) -> Option<&str> {
508 self.id.as_deref()
509 }
510 pub fn type_(&self) -> Option<&MCPCallType> {
511 self.type_.as_ref()
512 }
513 pub fn server_label(&self) -> Option<&str> {
514 self.server_label.as_deref()
515 }
516 pub fn error(&self) -> Option<&str> {
517 self.error.as_deref()
518 }
519 pub fn tools(&self) -> Option<&[MCPTool]> {
520 self.tools.as_deref()
521 }
522 pub fn arguments(&self) -> Option<&str> {
523 self.arguments.as_deref()
524 }
525 pub fn name(&self) -> Option<&str> {
526 self.name.as_deref()
527 }
528 pub fn output(&self) -> Option<&serde_json::Value> {
529 self.output.as_ref()
530 }
531}
532
533impl MCPTool {
534 pub fn name(&self) -> Option<&str> {
535 self.name.as_deref()
536 }
537 pub fn description(&self) -> Option<&str> {
538 self.description.as_deref()
539 }
540 pub fn annotations(&self) -> Option<&serde_json::Value> {
541 self.annotations.as_ref()
542 }
543 pub fn input_schema(&self) -> Option<&MCPInputSchema> {
544 self.input_schema.as_ref()
545 }
546}
547
548impl MCPInputSchema {
549 pub fn type_(&self) -> Option<&MCPInputType> {
550 self.type_.as_ref()
551 }
552 pub fn properties(&self) -> Option<&serde_json::Value> {
553 self.properties.as_ref()
554 }
555 pub fn required(&self) -> Option<&[String]> {
556 self.required.as_deref()
557 }
558 pub fn additional_properties(&self) -> Option<bool> {
559 self.additional_properties
560 }
561}
562
563impl AudioContent {
564 pub fn id(&self) -> Option<&str> {
565 self.id.as_deref()
566 }
567 pub fn data(&self) -> Option<&str> {
568 self.data.as_deref()
569 }
570 pub fn expires_at(&self) -> Option<&str> {
571 self.expires_at.as_deref()
572 }
573}
574
575impl Usage {
576 pub fn prompt_tokens(&self) -> Option<u32> {
577 self.prompt_tokens
578 }
579 pub fn completion_tokens(&self) -> Option<u32> {
580 self.completion_tokens
581 }
582 pub fn total_tokens(&self) -> Option<u32> {
583 self.total_tokens
584 }
585 pub fn prompt_tokens_details(&self) -> Option<&PromptTokensDetails> {
586 self.prompt_tokens_details.as_ref()
587 }
588}
589
590impl PromptTokensDetails {
591 pub fn cached_tokens(&self) -> Option<u32> {
592 self.cached_tokens
593 }
594}
595
596impl WebSearchInfo {
597 pub fn icon(&self) -> Option<&str> {
598 self.icon.as_deref()
599 }
600 pub fn title(&self) -> Option<&str> {
601 self.title.as_deref()
602 }
603 pub fn link(&self) -> Option<&str> {
604 self.link.as_deref()
605 }
606 pub fn media(&self) -> Option<&str> {
607 self.media.as_deref()
608 }
609 pub fn publish_date(&self) -> Option<&str> {
610 self.publish_date.as_deref()
611 }
612 pub fn content(&self) -> Option<&str> {
613 self.content.as_deref()
614 }
615 pub fn refer(&self) -> Option<&str> {
616 self.refer.as_deref()
617 }
618}
619
620impl VideoResultItem {
621 pub fn url(&self) -> Option<&str> {
622 self.url.as_deref()
623 }
624 pub fn cover_image_url(&self) -> Option<&str> {
625 self.cover_image_url.as_deref()
626 }
627}
628
629impl ContentFilterInfo {
630 pub fn role(&self) -> Option<&str> {
631 self.role.as_deref()
632 }
633 pub fn level(&self) -> Option<i32> {
634 self.level
635 }
636}