1use std::{collections::HashMap, pin::Pin};
12
13use derive_builder::Builder;
14use futures::Stream;
15use serde::{Deserialize, Serialize};
16use utoipa::ToSchema;
17
18use url::Url;
19use uuid::{Uuid, uuid};
20
21use crate::error::OpenAIError;
22
23#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
24#[serde(untagged)]
25pub enum Prompt {
26 String(String),
27 StringArray(Vec<String>),
28 IntegerArray(Vec<u32>),
30 ArrayOfIntegerArray(Vec<Vec<u32>>),
31}
32
33#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
34#[serde(untagged)]
35pub enum Stop {
36 String(String), StringArray(Vec<String>), }
39
40#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
41pub struct Logprobs {
42 pub tokens: Vec<String>,
43 pub token_logprobs: Vec<Option<f32>>, pub top_logprobs: Vec<serde_json::Value>,
45 pub text_offset: Vec<u32>,
46}
47
48#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
49#[serde(rename_all = "snake_case")]
50pub enum CompletionFinishReason {
51 Stop,
52 Length,
53 ContentFilter,
54}
55
56#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
57pub struct Choice {
58 pub text: String,
59 pub index: u32,
60 #[serde(skip_serializing_if = "Option::is_none")]
61 pub logprobs: Option<Logprobs>,
62 #[serde(skip_serializing_if = "Option::is_none")]
63 pub finish_reason: Option<CompletionFinishReason>,
64}
65
66#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
67pub enum ChatCompletionFunctionCall {
68 #[serde(rename = "none")]
70 None,
71 #[serde(rename = "auto")]
73 Auto,
74
75 #[serde(untagged)]
80 Function { name: String },
81}
82
83#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialEq)]
84#[serde(rename_all = "lowercase")]
85pub enum Role {
86 System,
87 #[default]
88 User,
89 Assistant,
90 Tool,
91 Function,
92}
93
94#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
96pub struct FunctionCall {
97 pub name: String,
99 pub arguments: String,
101}
102
103#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
105pub struct CompletionUsage {
106 pub prompt_tokens: u32,
108 pub completion_tokens: u32,
110 pub total_tokens: u32,
112 #[serde(skip_serializing_if = "Option::is_none")]
114 pub prompt_tokens_details: Option<PromptTokensDetails>,
115 #[serde(skip_serializing_if = "Option::is_none")]
117 pub completion_tokens_details: Option<CompletionTokensDetails>,
118}
119
120#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
122pub struct PromptTokensDetails {
123 pub audio_tokens: Option<u32>,
125 pub cached_tokens: Option<u32>,
127}
128
129#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq, Default)]
131pub struct CompletionTokensDetails {
132 pub accepted_prediction_tokens: Option<u32>,
133 pub audio_tokens: Option<u32>,
135 pub reasoning_tokens: Option<u32>,
137 pub rejected_prediction_tokens: Option<u32>,
143}
144
145#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
146#[builder(name = "ChatCompletionRequestDeveloperMessageArgs")]
147#[builder(pattern = "mutable")]
148#[builder(setter(into, strip_option), default)]
149#[builder(derive(Debug))]
150#[builder(build_fn(error = "OpenAIError"))]
151pub struct ChatCompletionRequestDeveloperMessage {
152 pub content: ChatCompletionRequestDeveloperMessageContent,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub name: Option<String>,
158}
159
160#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
161#[serde(untagged)]
162pub enum ChatCompletionRequestDeveloperMessageContent {
163 Text(String),
164 Array(Vec<ChatCompletionRequestMessageContentPartText>),
165}
166
167#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
168#[builder(name = "ChatCompletionRequestSystemMessageArgs")]
169#[builder(pattern = "mutable")]
170#[builder(setter(into, strip_option), default)]
171#[builder(derive(Debug))]
172#[builder(build_fn(error = "OpenAIError"))]
173pub struct ChatCompletionRequestSystemMessage {
174 pub content: ChatCompletionRequestSystemMessageContent,
176 #[serde(skip_serializing_if = "Option::is_none")]
178 pub name: Option<String>,
179}
180
181#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
182#[builder(name = "ChatCompletionRequestMessageContentPartTextArgs")]
183#[builder(pattern = "mutable")]
184#[builder(setter(into, strip_option), default)]
185#[builder(derive(Debug))]
186#[builder(build_fn(error = "OpenAIError"))]
187pub struct ChatCompletionRequestMessageContentPartText {
188 pub text: String,
189}
190
191#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
192pub struct ChatCompletionRequestMessageContentPartRefusal {
193 pub refusal: String,
195}
196
197#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
198#[serde(rename_all = "lowercase")]
199pub enum ImageDetail {
200 #[default]
201 Auto,
202 Low,
203 High,
204}
205
206#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Builder, PartialEq)]
207#[builder(name = "ImageUrlArgs")]
208#[builder(pattern = "mutable")]
209#[builder(setter(into, strip_option))]
210#[builder(derive(Debug))]
211#[builder(build_fn(error = "OpenAIError"))]
212pub struct ImageUrl {
213 pub url: url::Url,
215 pub detail: Option<ImageDetail>,
217 #[serde(skip_serializing_if = "Option::is_none")]
219 pub uuid: Option<uuid::Uuid>,
220}
221
222#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Builder, PartialEq)]
223#[builder(name = "VideoUrlArgs")]
224#[builder(pattern = "mutable")]
225#[builder(setter(into, strip_option))]
226#[builder(derive(Debug))]
227#[builder(build_fn(error = "OpenAIError"))]
228pub struct VideoUrl {
229 pub url: url::Url,
231 pub detail: Option<ImageDetail>,
233 #[serde(skip_serializing_if = "Option::is_none")]
235 pub uuid: Option<uuid::Uuid>,
236}
237
238#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Builder, PartialEq)]
239#[builder(name = "ChatCompletionRequestMessageContentPartImageArgs")]
240#[builder(pattern = "mutable")]
241#[builder(setter(into, strip_option))]
242#[builder(derive(Debug))]
243#[builder(build_fn(error = "OpenAIError"))]
244pub struct ChatCompletionRequestMessageContentPartImage {
245 pub image_url: ImageUrl,
246}
247
248#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Builder, PartialEq)]
249#[builder(name = "ChatCompletionRequestMessageContentPartVideoArgs")]
250#[builder(pattern = "mutable")]
251#[builder(setter(into, strip_option))]
252#[builder(derive(Debug))]
253#[builder(build_fn(error = "OpenAIError"))]
254pub struct ChatCompletionRequestMessageContentPartVideo {
255 pub video_url: VideoUrl,
256}
257
258#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Builder, PartialEq)]
259#[builder(name = "AudioUrlArgs")]
260#[builder(pattern = "mutable")]
261#[builder(setter(into, strip_option))]
262#[builder(derive(Debug))]
263#[builder(build_fn(error = "OpenAIError"))]
264pub struct AudioUrl {
265 pub url: url::Url,
267 #[serde(skip_serializing_if = "Option::is_none")]
269 pub uuid: Option<uuid::Uuid>,
270}
271
272#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Builder, PartialEq)]
273#[builder(name = "ChatCompletionRequestMessageContentPartAudioUrlArgs")]
274#[builder(pattern = "mutable")]
275#[builder(setter(into, strip_option))]
276#[builder(derive(Debug))]
277#[builder(build_fn(error = "OpenAIError"))]
278pub struct ChatCompletionRequestMessageContentPartAudioUrl {
279 pub audio_url: AudioUrl,
280}
281
282#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
283#[serde(rename_all = "lowercase")]
284pub enum InputAudioFormat {
285 Wav,
286 #[default]
287 Mp3,
288}
289
290#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
291pub struct InputAudio {
292 pub data: String,
294 pub format: InputAudioFormat,
296}
297
298#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
300#[builder(name = "ChatCompletionRequestMessageContentPartAudioArgs")]
301#[builder(pattern = "mutable")]
302#[builder(setter(into, strip_option), default)]
303#[builder(derive(Debug))]
304#[builder(build_fn(error = "OpenAIError"))]
305pub struct ChatCompletionRequestMessageContentPartAudio {
306 pub input_audio: InputAudio,
307}
308
309#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
310#[serde(tag = "type")]
311#[serde(rename_all = "snake_case")]
312pub enum ChatCompletionRequestUserMessageContentPart {
313 Text(ChatCompletionRequestMessageContentPartText),
314 ImageUrl(ChatCompletionRequestMessageContentPartImage),
315 VideoUrl(ChatCompletionRequestMessageContentPartVideo),
316 AudioUrl(ChatCompletionRequestMessageContentPartAudioUrl),
317 InputAudio(ChatCompletionRequestMessageContentPartAudio),
318}
319
320#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
321#[serde(tag = "type")]
322#[serde(rename_all = "snake_case")]
323pub enum ChatCompletionRequestSystemMessageContentPart {
324 Text(ChatCompletionRequestMessageContentPartText),
325}
326
327#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
328#[serde(tag = "type")]
329#[serde(rename_all = "snake_case")]
330pub enum ChatCompletionRequestAssistantMessageContentPart {
331 Text(ChatCompletionRequestMessageContentPartText),
332 Refusal(ChatCompletionRequestMessageContentPartRefusal),
333}
334
335#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
336#[serde(tag = "type")]
337#[serde(rename_all = "snake_case")]
338pub enum ChatCompletionRequestToolMessageContentPart {
339 Text(ChatCompletionRequestMessageContentPartText),
340}
341
342#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
343#[serde(untagged)]
344pub enum ChatCompletionRequestSystemMessageContent {
345 Text(String),
347 Array(Vec<ChatCompletionRequestSystemMessageContentPart>),
349}
350
351#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
352#[serde(untagged)]
353pub enum ChatCompletionRequestUserMessageContent {
354 Text(String),
356 Array(Vec<ChatCompletionRequestUserMessageContentPart>),
358}
359
360#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
361#[serde(untagged)]
362pub enum ChatCompletionRequestAssistantMessageContent {
363 Text(String),
365 Array(Vec<ChatCompletionRequestAssistantMessageContentPart>),
367}
368
369#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
370#[serde(untagged)]
371pub enum ChatCompletionRequestToolMessageContent {
372 Text(String),
374 Array(Vec<ChatCompletionRequestToolMessageContentPart>),
376}
377
378#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
383pub struct ChatCompletionResponseContentPartText {
384 pub text: String,
385}
386
387#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
389pub struct ChatCompletionResponseContentPartImageUrl {
390 pub image_url: ImageUrlResponse,
391}
392
393#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
395pub struct ChatCompletionResponseContentPartVideoUrl {
396 pub video_url: VideoUrlResponse,
397}
398
399#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
401pub struct ChatCompletionResponseContentPartAudioUrl {
402 pub audio_url: AudioUrlResponse,
403}
404
405#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
407pub struct ImageUrlResponse {
408 pub url: String,
410 #[serde(skip_serializing_if = "Option::is_none")]
412 pub detail: Option<String>,
413}
414
415#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
417pub struct VideoUrlResponse {
418 pub url: String,
420}
421
422#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
424pub struct AudioUrlResponse {
425 pub url: String,
427}
428
429#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
431#[serde(tag = "type", rename_all = "snake_case")]
432pub enum ChatCompletionResponseContentPart {
433 Text(ChatCompletionResponseContentPartText),
434 ImageUrl(ChatCompletionResponseContentPartImageUrl),
435 VideoUrl(ChatCompletionResponseContentPartVideoUrl),
436 AudioUrl(ChatCompletionResponseContentPartAudioUrl),
437}
438
439#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
441#[serde(untagged)]
442pub enum ChatCompletionMessageContent {
443 Text(String),
445 Parts(Vec<ChatCompletionResponseContentPart>),
447}
448
449#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
450#[builder(name = "ChatCompletionRequestUserMessageArgs")]
451#[builder(pattern = "mutable")]
452#[builder(setter(into, strip_option), default)]
453#[builder(derive(Debug))]
454#[builder(build_fn(error = "OpenAIError"))]
455pub struct ChatCompletionRequestUserMessage {
456 pub content: ChatCompletionRequestUserMessageContent,
458 #[serde(skip_serializing_if = "Option::is_none")]
460 pub name: Option<String>,
461}
462
463#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
464pub struct ChatCompletionRequestAssistantMessageAudio {
465 pub id: String,
467}
468
469#[derive(ToSchema, Serialize, Deserialize, Clone, Debug, PartialEq)]
480#[serde(untagged)]
481pub enum ReasoningContent {
482 Text(String),
484 Segments(Vec<String>),
488}
489
490impl ReasoningContent {
491 pub fn to_flat_string(&self) -> String {
493 match self {
494 ReasoningContent::Text(s) => s.clone(),
495 ReasoningContent::Segments(segs) => segs
496 .iter()
497 .filter(|s| !s.is_empty())
498 .cloned()
499 .collect::<Vec<_>>()
500 .join("\n"),
501 }
502 }
503
504 pub fn segments(&self) -> Option<&[String]> {
506 match self {
507 ReasoningContent::Segments(segs) => Some(segs),
508 ReasoningContent::Text(_) => None,
509 }
510 }
511}
512
513#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
514#[builder(name = "ChatCompletionRequestAssistantMessageArgs")]
515#[builder(pattern = "mutable")]
516#[builder(setter(into, strip_option), default)]
517#[builder(derive(Debug))]
518#[builder(build_fn(error = "OpenAIError"))]
519pub struct ChatCompletionRequestAssistantMessage {
520 #[serde(skip_serializing_if = "Option::is_none")]
522 pub content: Option<ChatCompletionRequestAssistantMessageContent>,
523 #[serde(skip_serializing_if = "Option::is_none")]
529 pub reasoning_content: Option<ReasoningContent>,
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub refusal: Option<String>,
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub name: Option<String>,
536 #[serde(skip_serializing_if = "Option::is_none")]
539 pub audio: Option<ChatCompletionRequestAssistantMessageAudio>,
540 #[serde(skip_serializing_if = "Option::is_none")]
541 pub tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
542 #[deprecated]
544 #[serde(skip_serializing_if = "Option::is_none")]
545 pub function_call: Option<FunctionCall>,
546}
547
548#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
550#[builder(name = "ChatCompletionRequestToolMessageArgs")]
551#[builder(pattern = "mutable")]
552#[builder(setter(into, strip_option), default)]
553#[builder(derive(Debug))]
554#[builder(build_fn(error = "OpenAIError"))]
555pub struct ChatCompletionRequestToolMessage {
556 pub content: ChatCompletionRequestToolMessageContent,
558 pub tool_call_id: String,
559}
560
561#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, Builder, PartialEq)]
562#[builder(name = "ChatCompletionRequestFunctionMessageArgs")]
563#[builder(pattern = "mutable")]
564#[builder(setter(into, strip_option), default)]
565#[builder(derive(Debug))]
566#[builder(build_fn(error = "OpenAIError"))]
567pub struct ChatCompletionRequestFunctionMessage {
568 pub content: Option<String>,
570 pub name: String,
572}
573
574#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, PartialEq)]
575#[serde(tag = "role")]
576#[serde(rename_all = "lowercase")]
577pub enum ChatCompletionRequestMessage {
578 Developer(ChatCompletionRequestDeveloperMessage),
579 System(ChatCompletionRequestSystemMessage),
580 User(ChatCompletionRequestUserMessage),
581 Assistant(ChatCompletionRequestAssistantMessage),
582 Tool(ChatCompletionRequestToolMessage),
583 Function(ChatCompletionRequestFunctionMessage),
584}
585
586#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
587pub struct ChatCompletionMessageToolCall {
588 pub id: String,
590 pub r#type: ChatCompletionToolType,
592 pub function: FunctionCall,
594}
595
596#[derive(ToSchema, Debug, Serialize, Deserialize, Default, Clone, PartialEq)]
597pub struct ChatCompletionResponseMessageAudio {
598 pub id: String,
600 pub expires_at: u32,
602 pub data: String,
604 pub transcript: String,
606}
607
608#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
610pub struct ChatCompletionResponseMessage {
611 #[serde(skip_serializing_if = "Option::is_none")]
613 pub content: Option<ChatCompletionMessageContent>,
614 #[serde(skip_serializing_if = "Option::is_none")]
616 pub refusal: Option<String>,
617 #[serde(skip_serializing_if = "Option::is_none")]
619 pub tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
620
621 pub role: Role,
623
624 #[serde(skip_serializing_if = "Option::is_none")]
627 #[deprecated]
628 pub function_call: Option<FunctionCall>,
629
630 #[serde(skip_serializing_if = "Option::is_none")]
632 pub audio: Option<ChatCompletionResponseMessageAudio>,
633
634 pub reasoning_content: Option<String>,
636}
637
638#[derive(ToSchema, Clone, Serialize, Default, Debug, Deserialize, Builder, PartialEq)]
639#[builder(name = "ChatCompletionFunctionsArgs")]
640#[builder(pattern = "mutable")]
641#[builder(setter(into, strip_option), default)]
642#[builder(derive(Debug))]
643#[builder(build_fn(error = "OpenAIError"))]
644#[deprecated]
645pub struct ChatCompletionFunctions {
646 pub name: String,
648 #[serde(skip_serializing_if = "Option::is_none")]
650 pub description: Option<String>,
651 pub parameters: serde_json::Value,
655}
656
657#[derive(ToSchema, Clone, Serialize, Default, Debug, Deserialize, Builder, PartialEq)]
658#[builder(name = "FunctionObjectArgs")]
659#[builder(pattern = "mutable")]
660#[builder(setter(into, strip_option), default)]
661#[builder(derive(Debug))]
662#[builder(build_fn(error = "OpenAIError"))]
663pub struct FunctionObject {
664 pub name: String,
666 #[serde(skip_serializing_if = "Option::is_none")]
668 pub description: Option<String>,
669 #[serde(skip_serializing_if = "Option::is_none")]
673 pub parameters: Option<serde_json::Value>,
674
675 #[serde(skip_serializing_if = "Option::is_none")]
677 pub strict: Option<bool>,
678}
679
680#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
681#[serde(tag = "type", rename_all = "snake_case")]
682pub enum ResponseFormat {
683 Text,
685 JsonObject,
687 JsonSchema {
689 json_schema: ResponseFormatJsonSchema,
690 },
691}
692
693#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
694pub struct ResponseFormatJsonSchema {
695 #[serde(skip_serializing_if = "Option::is_none")]
697 pub description: Option<String>,
698 pub name: String,
700 #[serde(skip_serializing_if = "Option::is_none")]
702 pub schema: Option<serde_json::Value>,
703 #[serde(skip_serializing_if = "Option::is_none")]
705 pub strict: Option<bool>,
706}
707
708#[derive(ToSchema, Clone, Serialize, Default, Debug, Deserialize, PartialEq)]
709#[serde(rename_all = "lowercase")]
710pub enum ChatCompletionToolType {
711 #[default]
712 Function,
713}
714
715#[derive(ToSchema, Clone, Serialize, Default, Debug, Builder, Deserialize, PartialEq)]
716#[builder(name = "ChatCompletionToolArgs")]
717#[builder(pattern = "mutable")]
718#[builder(setter(into, strip_option), default)]
719#[builder(derive(Debug))]
720#[builder(build_fn(error = "OpenAIError"))]
721pub struct ChatCompletionTool {
722 #[builder(default = "ChatCompletionToolType::Function")]
723 pub r#type: ChatCompletionToolType,
724 pub function: FunctionObject,
725}
726
727#[derive(ToSchema, Clone, Serialize, Default, Debug, Deserialize, PartialEq)]
728pub struct FunctionName {
729 pub name: String,
731}
732
733#[derive(ToSchema, Clone, Serialize, Default, Debug, Deserialize, PartialEq)]
735pub struct ChatCompletionNamedToolChoice {
736 pub r#type: ChatCompletionToolType,
738
739 pub function: FunctionName,
740}
741
742#[derive(ToSchema, Clone, Serialize, Default, Debug, Deserialize, PartialEq)]
750#[serde(rename_all = "lowercase")]
751pub enum ChatCompletionToolChoiceOption {
752 #[default]
753 None,
754 Auto,
755 Required,
756 #[serde(untagged)]
757 Named(ChatCompletionNamedToolChoice),
758}
759
760#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq, Default)]
761#[serde(rename_all = "lowercase")]
762pub enum WebSearchContextSize {
764 Low,
765 #[default]
766 Medium,
767 High,
768}
769
770#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
771#[serde(rename_all = "lowercase")]
772pub enum WebSearchUserLocationType {
773 Approximate,
774}
775
776#[derive(ToSchema, Clone, Serialize, Debug, Default, Deserialize, PartialEq)]
778pub struct WebSearchLocation {
779 pub country: Option<String>,
781 pub region: Option<String>,
783 pub city: Option<String>,
785 pub timezone: Option<String>,
787}
788
789#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
790pub struct WebSearchUserLocation {
791 pub r#type: WebSearchUserLocationType,
793
794 pub approximate: WebSearchLocation,
795}
796
797#[derive(ToSchema, Clone, Serialize, Debug, Default, Deserialize, PartialEq)]
799pub struct WebSearchOptions {
800 pub search_context_size: Option<WebSearchContextSize>,
802
803 pub user_location: Option<WebSearchUserLocation>,
805}
806
807#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
808#[serde(rename_all = "lowercase")]
809pub enum ServiceTier {
810 Auto,
811 Default,
812 Flex,
813 Scale,
814 Priority,
815}
816
817#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
818#[serde(rename_all = "lowercase")]
819pub enum ServiceTierResponse {
820 Scale,
821 Default,
822 Flex,
823 Priority,
824}
825
826#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
827#[serde(rename_all = "lowercase")]
828pub enum ReasoningEffort {
829 Minimal,
830 Low,
831 Medium,
832 High,
833}
834
835#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
842#[serde(rename_all = "lowercase")]
843pub enum ChatCompletionModalities {
844 Text,
845 Audio,
846}
847
848#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
850#[serde(untagged)]
851pub enum PredictionContentContent {
852 Text(String),
854 Array(Vec<ChatCompletionRequestMessageContentPartText>),
856}
857
858#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
860#[serde(tag = "type", rename_all = "lowercase", content = "content")]
861pub enum PredictionContent {
862 Content(PredictionContentContent),
865}
866
867#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
868#[serde(rename_all = "lowercase")]
869pub enum ChatCompletionAudioVoice {
870 Alloy,
871 Ash,
872 Ballad,
873 Coral,
874 Echo,
875 Sage,
876 Shimmer,
877 Verse,
878}
879
880#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
881#[serde(rename_all = "lowercase")]
882pub enum ChatCompletionAudioFormat {
883 Wav,
884 Mp3,
885 Flac,
886 Opus,
887 Pcm16,
888}
889
890#[derive(ToSchema, Clone, Serialize, Debug, Deserialize, PartialEq)]
891pub struct ChatCompletionAudio {
892 pub voice: ChatCompletionAudioVoice,
894 pub format: ChatCompletionAudioFormat,
896}
897
898#[derive(ToSchema, Clone, Serialize, Default, Debug, Builder, Deserialize, PartialEq)]
899#[builder(name = "CreateChatCompletionRequestArgs")]
900#[builder(pattern = "mutable")]
901#[builder(setter(into, strip_option), default)]
902#[builder(derive(Debug))]
903#[builder(build_fn(error = "OpenAIError"))]
904pub struct CreateChatCompletionRequest {
905 pub messages: Vec<ChatCompletionRequestMessage>, pub model: String,
911
912 #[serde(skip_serializing_if = "Option::is_none")]
914 pub mm_processor_kwargs: Option<serde_json::Value>,
915
916 #[serde(skip_serializing_if = "Option::is_none")]
920 pub store: Option<bool>, #[serde(skip_serializing_if = "Option::is_none")]
932 pub reasoning_effort: Option<ReasoningEffort>,
933
934 #[serde(skip_serializing_if = "Option::is_none")]
936 pub metadata: Option<serde_json::Value>, #[serde(skip_serializing_if = "Option::is_none")]
940 pub frequency_penalty: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
949 pub logit_bias: Option<HashMap<String, serde_json::Value>>, #[serde(skip_serializing_if = "Option::is_none")]
953 pub logprobs: Option<bool>,
954
955 #[serde(skip_serializing_if = "Option::is_none")]
957 pub top_logprobs: Option<u8>,
958
959 #[deprecated]
965 #[serde(skip_serializing_if = "Option::is_none")]
966 pub max_tokens: Option<u32>,
967
968 #[serde(skip_serializing_if = "Option::is_none")]
970 pub max_completion_tokens: Option<u32>,
971
972 #[serde(skip_serializing_if = "Option::is_none")]
974 pub n: Option<u8>, #[serde(skip_serializing_if = "Option::is_none")]
977 pub modalities: Option<Vec<ChatCompletionModalities>>,
978
979 #[serde(skip_serializing_if = "Option::is_none")]
981 pub prediction: Option<PredictionContent>,
982
983 #[serde(skip_serializing_if = "Option::is_none")]
985 pub audio: Option<ChatCompletionAudio>,
986
987 #[serde(skip_serializing_if = "Option::is_none")]
989 pub presence_penalty: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
999 pub response_format: Option<ResponseFormat>,
1000
1001 #[serde(skip_serializing_if = "Option::is_none")]
1006 pub seed: Option<i64>,
1007
1008 #[serde(skip_serializing_if = "Option::is_none")]
1015 pub service_tier: Option<ServiceTier>,
1016
1017 #[serde(skip_serializing_if = "Option::is_none")]
1019 pub stop: Option<Stop>,
1020
1021 #[serde(skip_serializing_if = "Option::is_none")]
1025 pub stream: Option<bool>,
1026
1027 #[serde(skip_serializing_if = "Option::is_none")]
1028 pub stream_options: Option<ChatCompletionStreamOptions>,
1029
1030 #[serde(skip_serializing_if = "Option::is_none")]
1035 pub temperature: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
1043 pub top_p: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
1048 pub tools: Option<Vec<ChatCompletionTool>>,
1049
1050 #[serde(skip_serializing_if = "Option::is_none")]
1051 pub tool_choice: Option<ChatCompletionToolChoiceOption>,
1052
1053 #[serde(skip_serializing_if = "Option::is_none")]
1055 pub parallel_tool_calls: Option<bool>,
1056
1057 #[serde(skip_serializing_if = "Option::is_none")]
1059 pub user: Option<String>,
1060
1061 #[serde(skip_serializing_if = "Option::is_none")]
1064 pub web_search_options: Option<WebSearchOptions>,
1065
1066 #[deprecated]
1075 #[serde(skip_serializing_if = "Option::is_none")]
1076 pub function_call: Option<ChatCompletionFunctionCall>,
1077
1078 #[deprecated]
1082 #[serde(skip_serializing_if = "Option::is_none")]
1083 pub functions: Option<Vec<ChatCompletionFunctions>>,
1084}
1085
1086#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
1088pub struct ChatCompletionStreamOptions {
1089 pub include_usage: bool,
1091 #[serde(default)]
1093 pub continuous_usage_stats: bool,
1094}
1095
1096#[derive(ToSchema, Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
1097#[serde(rename_all = "snake_case")]
1098pub enum FinishReason {
1099 Stop,
1100 Length,
1101 ToolCalls,
1102 ContentFilter,
1103 FunctionCall,
1104}
1105
1106#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1107pub struct TopLogprobs {
1108 pub token: String,
1110 pub logprob: f32,
1112 pub bytes: Option<Vec<u8>>,
1114}
1115
1116#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1117pub struct ChatCompletionTokenLogprob {
1118 pub token: String,
1120 pub logprob: f32,
1122 pub bytes: Option<Vec<u8>>,
1124 pub top_logprobs: Vec<TopLogprobs>,
1126}
1127
1128#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1129pub struct ChatChoiceLogprobs {
1130 pub content: Option<Vec<ChatCompletionTokenLogprob>>,
1132 pub refusal: Option<Vec<ChatCompletionTokenLogprob>>,
1133}
1134
1135#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1136#[serde(untagged)]
1137pub enum StopReason {
1138 String(String), Int(i64), }
1141
1142#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1143pub struct ChatChoice {
1144 pub index: u32,
1146 pub message: ChatCompletionResponseMessage,
1147 #[serde(skip_serializing_if = "Option::is_none")]
1152 pub finish_reason: Option<FinishReason>,
1153 #[serde(skip_serializing_if = "Option::is_none")]
1156 pub stop_reason: Option<StopReason>,
1157 #[serde(skip_serializing_if = "Option::is_none")]
1159 pub logprobs: Option<ChatChoiceLogprobs>,
1160}
1161
1162#[derive(ToSchema, Debug, Deserialize, Clone, PartialEq, Serialize)]
1164pub struct CreateChatCompletionResponse {
1165 pub id: String,
1167 pub choices: Vec<ChatChoice>,
1169 pub created: u32,
1171 pub model: String,
1173 #[serde(skip_serializing_if = "Option::is_none")]
1175 pub service_tier: Option<ServiceTierResponse>,
1176 #[serde(skip_serializing_if = "Option::is_none")]
1180 pub system_fingerprint: Option<String>,
1181
1182 pub object: String,
1184 pub usage: Option<CompletionUsage>,
1185
1186 #[serde(skip_serializing_if = "Option::is_none")]
1188 pub nvext: Option<serde_json::Value>,
1189}
1190
1191pub type ChatCompletionResponseStream =
1193 Pin<Box<dyn Stream<Item = Result<CreateChatCompletionStreamResponse, OpenAIError>> + Send>>;
1194
1195#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1196pub struct FunctionCallStream {
1197 pub name: Option<String>,
1199 pub arguments: Option<String>,
1204}
1205
1206#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1207pub struct ChatCompletionMessageToolCallChunk {
1208 pub index: u32,
1209 pub id: Option<String>,
1211 pub r#type: Option<ChatCompletionToolType>,
1213 pub function: Option<FunctionCallStream>,
1214}
1215
1216#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1218pub struct ChatCompletionStreamResponseDelta {
1219 pub content: Option<ChatCompletionMessageContent>,
1221 #[deprecated]
1223 pub function_call: Option<FunctionCallStream>,
1224
1225 pub tool_calls: Option<Vec<ChatCompletionMessageToolCallChunk>>,
1226 pub role: Option<Role>,
1228 pub refusal: Option<String>,
1230
1231 pub reasoning_content: Option<String>,
1233}
1234
1235#[derive(ToSchema, Debug, Deserialize, Serialize, Clone, PartialEq)]
1236pub struct ChatChoiceStream {
1237 pub index: u32,
1239 pub delta: ChatCompletionStreamResponseDelta,
1240 #[serde(skip_serializing_if = "Option::is_none")]
1251 pub finish_reason: Option<FinishReason>,
1252 #[serde(skip_serializing_if = "Option::is_none")]
1255 pub stop_reason: Option<StopReason>,
1256 #[serde(skip_serializing_if = "Option::is_none")]
1258 pub logprobs: Option<ChatChoiceLogprobs>,
1259}
1260
1261#[derive(ToSchema, Debug, Deserialize, Clone, PartialEq, Serialize)]
1262pub struct CreateChatCompletionStreamResponse {
1264 pub id: String,
1266 pub choices: Vec<ChatChoiceStream>,
1268
1269 pub created: u32,
1271 pub model: String,
1273 pub service_tier: Option<ServiceTierResponse>,
1275 pub system_fingerprint: Option<String>,
1278 pub object: String,
1280
1281 pub usage: Option<CompletionUsage>,
1284
1285 #[serde(skip_serializing_if = "Option::is_none")]
1287 pub nvext: Option<serde_json::Value>,
1288}
1289
1290#[cfg(test)]
1291mod tests {
1292 use super::*;
1293 use serde_json;
1294
1295 #[test]
1296 fn test_audio_url_content_part_json() {
1297 let json = r#"{"type": "audio_url", "audio_url": {"url": "https://example.com/audio.mp3", "uuid": "67e55044-10b1-426f-9247-bb680e5fe0c8"}}"#;
1298 let content_part: ChatCompletionRequestUserMessageContentPart =
1299 serde_json::from_str(json).unwrap();
1300
1301 match content_part {
1302 ChatCompletionRequestUserMessageContentPart::AudioUrl(part) => {
1303 assert_eq!(
1304 part.audio_url.url,
1305 "https://example.com/audio.mp3".parse().unwrap()
1306 );
1307 assert_eq!(
1308 part.audio_url.uuid,
1309 Some(uuid!("67e55044-10b1-426f-9247-bb680e5fe0c8"))
1310 );
1311 }
1312 _ => panic!("Expected AudioUrl variant"),
1313 }
1314 }
1315
1316 #[test]
1317 fn test_mm_processor_kwargs() {
1318 let request = CreateChatCompletionRequest {
1319 messages: vec![],
1320 model: "test-model".to_string(),
1321 mm_processor_kwargs: Some(serde_json::json!({"max_pixels": 768})),
1322 ..Default::default()
1323 };
1324
1325 let json = serde_json::to_string(&request).unwrap();
1326 assert!(json.contains("mm_processor_kwargs"));
1327 }
1328
1329 #[test]
1330 fn test_assistant_request_reasoning_content_text_roundtrip() {
1331 let json = r#"{
1332 "model": "deepseek-v3.2",
1333 "messages": [
1334 {"role": "user", "content": "test"},
1335 {
1336 "role": "assistant",
1337 "reasoning_content": "thinking...",
1338 "tool_calls": [{
1339 "id": "call_1",
1340 "type": "function",
1341 "function": {
1342 "name": "f",
1343 "arguments": "{}"
1344 }
1345 }]
1346 }
1347 ]
1348 }"#;
1349
1350 let request: CreateChatCompletionRequest = serde_json::from_str(json).unwrap();
1351 let assistant = match &request.messages[1] {
1352 ChatCompletionRequestMessage::Assistant(msg) => msg,
1353 _ => panic!("expected assistant message"),
1354 };
1355
1356 assert_eq!(
1357 assistant.reasoning_content,
1358 Some(ReasoningContent::Text("thinking...".into()))
1359 );
1360 assert_eq!(
1361 assistant
1362 .reasoning_content
1363 .as_ref()
1364 .unwrap()
1365 .to_flat_string(),
1366 "thinking..."
1367 );
1368 assert!(
1369 assistant
1370 .reasoning_content
1371 .as_ref()
1372 .unwrap()
1373 .segments()
1374 .is_none()
1375 );
1376
1377 let serialized = serde_json::to_value(&request).unwrap();
1378 assert_eq!(
1379 serialized["messages"][1]["reasoning_content"],
1380 serde_json::Value::String("thinking...".to_string())
1381 );
1382 }
1383
1384 #[test]
1385 fn test_assistant_request_reasoning_content_segments_roundtrip() {
1386 let json = r#"{
1387 "model": "deepseek-v3.2",
1388 "messages": [
1389 {"role": "user", "content": "test"},
1390 {
1391 "role": "assistant",
1392 "reasoning_content": ["seg1", "seg2", ""],
1393 "tool_calls": [{
1394 "id": "call_1",
1395 "type": "function",
1396 "function": {"name": "f1", "arguments": "{}"}
1397 }, {
1398 "id": "call_2",
1399 "type": "function",
1400 "function": {"name": "f2", "arguments": "{}"}
1401 }]
1402 }
1403 ]
1404 }"#;
1405
1406 let request: CreateChatCompletionRequest = serde_json::from_str(json).unwrap();
1407 let assistant = match &request.messages[1] {
1408 ChatCompletionRequestMessage::Assistant(msg) => msg,
1409 _ => panic!("expected assistant message"),
1410 };
1411
1412 assert_eq!(
1413 assistant.reasoning_content,
1414 Some(ReasoningContent::Segments(vec![
1415 "seg1".into(),
1416 "seg2".into(),
1417 "".into()
1418 ]))
1419 );
1420 assert_eq!(
1421 assistant
1422 .reasoning_content
1423 .as_ref()
1424 .unwrap()
1425 .to_flat_string(),
1426 "seg1\nseg2"
1427 );
1428 let segs = assistant
1429 .reasoning_content
1430 .as_ref()
1431 .unwrap()
1432 .segments()
1433 .expect("should be Segments");
1434 assert_eq!(segs.len(), 3);
1435
1436 let serialized = serde_json::to_value(&request).unwrap();
1437 assert_eq!(
1438 serialized["messages"][1]["reasoning_content"],
1439 serde_json::json!(["seg1", "seg2", ""])
1440 );
1441 }
1442}