openai_api_rs/v1/chat_completion/
mod.rs

1use crate::v1::types;
2use serde::de::{self, MapAccess, SeqAccess, Visitor};
3use serde::ser::SerializeMap;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt;
6
7#[allow(clippy::module_inception)]
8pub mod chat_completion;
9pub mod chat_completion_stream;
10
11#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
12pub enum ToolChoiceType {
13    None,
14    Auto,
15    Required,
16    ToolChoice { tool: Tool },
17}
18
19#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
20#[serde(rename_all = "lowercase")]
21pub enum ReasoningEffort {
22    Low,
23    Medium,
24    High,
25}
26
27#[derive(Debug, Serialize, Deserialize, Clone)]
28#[serde(untagged)]
29pub enum ReasoningMode {
30    Effort { effort: ReasoningEffort },
31    MaxTokens { max_tokens: i64 },
32}
33
34#[derive(Debug, Serialize, Deserialize, Clone)]
35pub struct Reasoning {
36    #[serde(flatten)]
37    pub mode: Option<ReasoningMode>,
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub exclude: Option<bool>,
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub enabled: Option<bool>,
42}
43
44#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
45#[allow(non_camel_case_types)]
46pub enum MessageRole {
47    user,
48    system,
49    assistant,
50    function,
51    tool,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub enum Content {
56    Text(String),
57    ImageUrl(Vec<ImageUrl>),
58}
59
60impl serde::Serialize for Content {
61    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
62    where
63        S: serde::Serializer,
64    {
65        match *self {
66            Content::Text(ref text) => {
67                if text.is_empty() {
68                    serializer.serialize_none()
69                } else {
70                    serializer.serialize_str(text)
71                }
72            }
73            Content::ImageUrl(ref image_url) => image_url.serialize(serializer),
74        }
75    }
76}
77
78impl<'de> Deserialize<'de> for Content {
79    fn deserialize<D>(deserializer: D) -> Result<Content, D::Error>
80    where
81        D: Deserializer<'de>,
82    {
83        struct ContentVisitor;
84
85        impl<'de> Visitor<'de> for ContentVisitor {
86            type Value = Content;
87
88            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
89                formatter.write_str("a valid content type")
90            }
91
92            fn visit_str<E>(self, value: &str) -> Result<Content, E>
93            where
94                E: de::Error,
95            {
96                Ok(Content::Text(value.to_string()))
97            }
98
99            fn visit_seq<A>(self, seq: A) -> Result<Content, A::Error>
100            where
101                A: SeqAccess<'de>,
102            {
103                let image_urls: Vec<ImageUrl> =
104                    Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
105                Ok(Content::ImageUrl(image_urls))
106            }
107
108            fn visit_map<M>(self, map: M) -> Result<Content, M::Error>
109            where
110                M: MapAccess<'de>,
111            {
112                let image_urls: Vec<ImageUrl> =
113                    Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
114                Ok(Content::ImageUrl(image_urls))
115            }
116
117            fn visit_none<E>(self) -> Result<Self::Value, E>
118            where
119                E: de::Error,
120            {
121                Ok(Content::Text(String::new()))
122            }
123
124            fn visit_unit<E>(self) -> Result<Self::Value, E>
125            where
126                E: de::Error,
127            {
128                Ok(Content::Text(String::new()))
129            }
130        }
131
132        deserializer.deserialize_any(ContentVisitor)
133    }
134}
135
136#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
137#[allow(non_camel_case_types)]
138pub enum ContentType {
139    text,
140    image_url,
141}
142
143#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
144#[allow(non_camel_case_types)]
145pub struct ImageUrlType {
146    pub url: String,
147}
148
149#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
150#[allow(non_camel_case_types)]
151pub struct ImageUrl {
152    pub r#type: ContentType,
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub text: Option<String>,
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub image_url: Option<ImageUrlType>,
157}
158
159#[derive(Debug, Deserialize, Serialize, Clone)]
160pub struct ChatCompletionMessage {
161    pub role: MessageRole,
162    pub content: Content,
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub name: Option<String>,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub tool_calls: Option<Vec<ToolCall>>,
167    #[serde(skip_serializing_if = "Option::is_none")]
168    pub tool_call_id: Option<String>,
169}
170
171#[derive(Debug, Deserialize, Serialize, Clone)]
172pub struct ChatCompletionMessageForResponse {
173    pub role: MessageRole,
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub content: Option<String>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub reasoning_content: Option<String>,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub name: Option<String>,
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub tool_calls: Option<Vec<ToolCall>>,
182}
183
184#[derive(Debug, Deserialize, Serialize)]
185pub struct ChatCompletionChoice {
186    pub index: i64,
187    pub message: ChatCompletionMessageForResponse,
188    pub finish_reason: Option<FinishReason>,
189    pub finish_details: Option<FinishDetails>,
190}
191
192#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
193#[allow(non_camel_case_types)]
194pub enum FinishReason {
195    stop,
196    length,
197    content_filter,
198    tool_calls,
199    null,
200}
201
202#[derive(Debug, Deserialize, Serialize)]
203#[allow(non_camel_case_types)]
204pub struct FinishDetails {
205    pub r#type: FinishReason,
206    pub stop: String,
207}
208
209#[derive(Debug, Deserialize, Serialize, Clone)]
210pub struct ToolCall {
211    pub id: String,
212    pub r#type: String,
213    pub function: ToolCallFunction,
214}
215
216#[derive(Debug, Deserialize, Serialize, Clone)]
217pub struct ToolCallFunction {
218    #[serde(skip_serializing_if = "Option::is_none")]
219    pub name: Option<String>,
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub arguments: Option<String>,
222}
223
224pub fn serialize_tool_choice<S>(
225    value: &Option<ToolChoiceType>,
226    serializer: S,
227) -> Result<S::Ok, S::Error>
228where
229    S: Serializer,
230{
231    match value {
232        Some(ToolChoiceType::None) => serializer.serialize_str("none"),
233        Some(ToolChoiceType::Auto) => serializer.serialize_str("auto"),
234        Some(ToolChoiceType::Required) => serializer.serialize_str("required"),
235        Some(ToolChoiceType::ToolChoice { tool }) => {
236            let mut map = serializer.serialize_map(Some(2))?;
237            map.serialize_entry("type", &tool.r#type)?;
238            map.serialize_entry("function", &tool.function)?;
239            map.end()
240        }
241        None => serializer.serialize_none(),
242    }
243}
244
245#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
246pub struct Tool {
247    pub r#type: ToolType,
248    pub function: types::Function,
249}
250
251#[derive(Debug, Deserialize, Serialize, Copy, Clone, PartialEq, Eq)]
252#[serde(rename_all = "snake_case")]
253pub enum ToolType {
254    Function,
255}