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