openai_interface/chat/mod.rs
1//! Response to a given `chat` conversation.
2
3use std::str::FromStr;
4
5use serde::{Deserialize, Serialize};
6
7use crate::errors::OapiError;
8
9pub mod create;
10pub mod retrieve;
11pub mod update;
12
13/// The service tier used for processing the request.
14///
15/// This enum represents the different service tiers that can be specified when
16/// making a request to the API. Each tier corresponds to different performance
17/// characteristics and pricing models.
18#[derive(Debug, Serialize, Deserialize, Clone)]
19#[serde(rename_all = "lowercase")]
20pub enum ServiceTier {
21 /// Automatically select the service tier based on project settings.
22 Auto,
23 /// Use the default service tier with standard pricing and performance.
24 Default,
25 /// Use the flex service tier for flexible processing requirements.
26 Flex,
27 /// Use the scale service tier for scalable processing needs.
28 Scale,
29 /// Use the priority service tier for high-priority requests.
30 Priority,
31}
32
33#[derive(Debug, Deserialize)]
34pub struct ChatCompletion {
35 /// A unique identifier for the chat completion.
36 pub id: String,
37 /// A list of chat completion choices. Can be more than one
38 /// if `n` is greater than 1.
39 pub choices: Vec<Choice>,
40 /// The Unix timestamp (in seconds) of when the chat completion was created.
41 pub created: u64,
42 /// The model used for the chat completion.
43 pub model: String,
44 /// Specifies the processing type used for serving the request.
45 ///
46 /// - If set to 'auto', then the request will be processed with the service tier
47 /// configured in the Project settings. Unless otherwise configured, the Project
48 /// will use 'default'.
49 /// - If set to 'default', then the request will be processed with the standard
50 /// pricing and performance for the selected model.
51 /// - If set to '[flex](https://platform.openai.com/docs/guides/flex-processing)' or
52 /// '[priority](https://openai.com/api-priority-processing/)', then the request
53 /// will be processed with the corresponding service tier.
54 /// - When not set, the default behavior is 'auto'.
55 ///
56 /// When the `service_tier` parameter is set, the response body will include the
57 /// `service_tier` value based on the processing mode actually used to serve the
58 /// request. This response value may be different from the value set in the
59 /// parameter.
60 pub service_tier: Option<ServiceTier>,
61 /// The system fingerprint used for the chat completion.
62 /// Can be used in conjunction with the `seed` request parameter to understand when
63 /// backend changes have been made that might impact determinism.
64 pub system_fingerprint: Option<String>,
65 /// The object type, which is always `chat.completion`.
66 pub object: ChatCompletionObject,
67 /// Usage statistics for the completion request.
68 pub usage: Option<CompletionUsage>,
69}
70
71/// The object type, which is always `chat.completion`.
72#[derive(Debug, Deserialize)]
73pub enum ChatCompletionObject {
74 /// The object type is always `chat.completion`.
75 #[serde(rename = "chat.completion")]
76 ChatCompletion,
77}
78
79#[derive(Debug, Deserialize)]
80pub struct Choice {
81 /// The reason the model stopped generating tokens.
82 ///
83 /// This will be `stop` if the model hit a natural stop point or a provided stop
84 /// sequence, `length` if the maximum number of tokens specified in the request was
85 /// reached, `content_filter` if content was omitted due to a flag from our content
86 /// filters, `tool_calls` if the model called a tool, or `function_call`
87 /// (deprecated) if the model called a function.
88 pub finish_reason: FinishReason,
89 /// The index of the choice in the list of choices.
90 pub index: usize,
91 /// Log probability information for the choice.
92 pub logprobs: Option<ChoiceLogprobs>,
93 /// A chat completion message generated by the model.
94 pub message: ChatCompletionMessage,
95}
96
97#[derive(Debug, Deserialize, PartialEq)]
98#[serde(rename_all = "snake_case")]
99pub enum FinishReason {
100 Length,
101 Stop,
102 ToolCalls,
103 FunctionCall,
104 ContentFilter,
105 /// This choice can only be found in the manual of DeepSeek
106 InsufficientSystemResource,
107}
108
109/// Fields that are not supported yet:
110/// - _audio_: If the audio output modality is requested, this object contains
111/// data about the audio response from the model.
112/// [Learn more from OpenAI](https://platform.openai.com/docs/guides/audio).
113#[derive(Debug, Deserialize)]
114pub struct ChatCompletionMessage {
115 /// The role of the author of this message. This shall always
116 /// be ResponseRole::Assistant
117 pub role: ResponseRole,
118 /// The contents of the message.
119 pub content: Option<String>,
120 pub reasoning_content: Option<String>,
121 /// The tool calls generated by the model, such as function calls.
122 /// Tool calls deserialization is not supported yet.
123 pub tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
124}
125
126#[derive(Debug, Deserialize)]
127#[serde(tag = "type", rename_all = "snake_case")]
128pub enum ChatCompletionMessageToolCall {
129 /// The type of the tool. Currently, only `function` is supported.
130 /// The field { type = "function" } is added automatically.
131 Function {
132 /// The ID of the tool call.
133 id: String,
134 /// The function that the model called.
135 function: String, // function type
136 },
137 /// The type of the tool. Always `custom`.
138 /// The field { type = "custom" } is added automatically.
139 Custom {
140 /// The id of the tool call.
141 id: String,
142 /// The custom tool that the model called.
143 custom: MessageToolCallCustom,
144 },
145}
146
147#[derive(Debug, Deserialize)]
148pub struct MessageToolCallCustom {
149 /// The input for the custom tool call generated by the model.
150 pub input: String,
151 /// The name of the custom tool to call.
152 pub name: String,
153}
154
155#[derive(Debug, Deserialize)]
156pub struct MessageToolCallFunction {
157 /// The arguments to call the function with, as generated by the model in JSON
158 /// format. Note that the model does not always generate valid JSON, and may
159 /// hallucinate parameters not defined by your function schema. Validate the
160 /// arguments in your code before calling your function.
161 pub arguments: String,
162 /// The name of the function to call.
163 pub name: String,
164}
165
166#[derive(Debug, Deserialize)]
167#[serde(rename_all = "snake_case")]
168pub enum ResponseRole {
169 /// The role of the response message is always assistant.
170 Assistant,
171}
172
173#[derive(Debug, Deserialize)]
174pub struct ChoiceLogprobs {
175 /// A list of message content tokens with log probability information.
176 pub content: Option<Vec<TokenLogProb>>,
177 /// Only found in DeepSeek's manual.
178 pub reasoning_content: Option<Vec<TokenLogProb>>,
179 /// A list of message refusal tokens with log probability information.
180 pub refusal: Option<Vec<TokenLogProb>>,
181}
182
183#[derive(Debug, Deserialize)]
184pub struct TokenLogProb {
185 /// The token.
186 pub token: String,
187 /// The log probability of this token, if it is within the top 20 most likely
188 /// tokens. Otherwise, the value `-9999.0` is used to signify that the token is very
189 /// unlikely.
190 pub logprob: f32,
191 /// A list of integers representing the UTF-8 bytes representation of the token.
192 ///
193 /// Useful in instances where characters are represented by multiple tokens and
194 /// their byte representations must be combined to generate the correct text
195 /// representation. Can be `null` if there is no bytes representation for the token.
196 pub bytes: Option<Vec<u8>>,
197 /// List of the most likely tokens and their log probability, at this token
198 /// position. In rare cases, there may be fewer than the number of requested
199 /// `top_logprobs` returned.
200 pub top_logprobs: Vec<TopLogprob>,
201}
202
203#[derive(Debug, Deserialize)]
204pub struct TopLogprob {
205 /// The token.
206 pub token: String,
207 /// A list of integers representing the UTF-8 bytes representation of the token.
208 ///
209 /// Useful in instances where characters are represented by multiple tokens and
210 /// their byte representations must be combined to generate the correct text
211 /// representation. Can be `null` if there is no bytes representation for the token.
212 pub logprob: f32,
213 /// List of the most likely tokens and their log probability, at this token
214 /// position. In rare cases, there may be fewer than the number of requested
215 /// `top_logprobs` returned.
216 pub bytes: Option<Vec<u8>>,
217}
218
219#[derive(Debug, Deserialize)]
220pub struct CompletionUsage {
221 /// Number of tokens in the generated completion.
222 pub completion_tokens: usize,
223 /// Number of tokens in the prompt.
224 pub prompt_tokens: usize,
225
226 // These two fields seem to be DeepSeek specific.
227 /// Number of tokens in the prompt that hits the context cache.
228 pub prompt_cache_hit_tokens: Option<usize>,
229 /// Number of tokens in the prompt that misses the context cache.
230 pub prompt_cache_miss_tokens: Option<usize>,
231
232 /// Total number of tokens used in the request (prompt + completion).
233 pub total_tokens: usize,
234 /// Breakdown of tokens used in a completion.
235 pub completion_tokens_details: Option<CompletionTokensDetails>,
236 /// Breakdown of tokens used in the prompt.
237 pub prompt_tokens_details: Option<PromptTokensDetails>,
238}
239
240#[derive(Debug, Deserialize)]
241pub struct CompletionTokensDetails {
242 /// When using Predicted Outputs, the number of tokens in the prediction that
243 /// appeared in the completion.
244 pub accepted_prediction_tokens: Option<usize>,
245 /// Audio input tokens generated by the model.
246 pub audio_tokens: Option<usize>,
247 /// Tokens generated by the model for reasoning.
248 pub reasoning_tokens: Option<usize>,
249 /// When using Predicted Outputs, the number of tokens in the prediction that did
250 /// not appear in the completion. However, like reasoning tokens, these tokens are
251 /// still counted in the total completion tokens for purposes of billing, output,
252 /// and context window limits.
253 pub rejected_prediction_tokens: Option<usize>,
254}
255
256#[derive(Debug, Deserialize)]
257pub struct PromptTokensDetails {
258 /// Audio input tokens present in the prompt.
259 pub audio_tokens: Option<usize>,
260 /// Cached tokens present in the prompt.
261 pub cached_tokens: Option<usize>,
262}
263
264impl FromStr for ChatCompletion {
265 type Err = crate::errors::OapiError;
266
267 fn from_str(content: &str) -> Result<Self, Self::Err> {
268 let parse_result: Result<ChatCompletion, _> = serde_json::from_str(content)
269 .map_err(|e| OapiError::DeserializationError(e.to_string()));
270 parse_result
271 }
272}
273
274#[cfg(test)]
275mod test {
276 use super::*;
277
278 #[test]
279 fn no_streaming_example_deepseek() {
280 let json = r#"{
281 "id": "30f6413a-a827-4cf3-9898-f13a8634b798",
282 "object": "chat.completion",
283 "created": 1757944111,
284 "model": "deepseek-chat",
285 "choices": [
286 {
287 "index": 0,
288 "message": {
289 "role": "assistant",
290 "content": "Hello! How can I help you today? 😊"
291 },
292 "logprobs": null,
293 "finish_reason": "stop"
294 }
295 ],
296 "usage": {
297 "prompt_tokens": 10,
298 "completion_tokens": 11,
299 "total_tokens": 21,
300 "prompt_tokens_details": {
301 "cached_tokens": 0
302 },
303 "prompt_cache_hit_tokens": 0,
304 "prompt_cache_miss_tokens": 10
305 },
306 "system_fingerprint": "fp_08f168e49b_prod0820_fp8_kvcache"
307 }"#;
308
309 let parsed = ChatCompletion::from_str(json);
310 match parsed {
311 Ok(_) => {}
312 Err(e) => {
313 panic!("Failed to deserialize: {}", e);
314 }
315 }
316 }
317
318 #[test]
319 fn no_streaming_example_qwen() {
320 let json = r#"{
321 "choices": [
322 {
323 "message": {
324 "role": "assistant",
325 "content": "我是阿里云开发的一款超大规模语言模型,我叫通义千问。"
326 },
327 "finish_reason": "stop",
328 "index": 0,
329 "logprobs": null
330 }
331 ],
332 "object": "chat.completion",
333 "usage": {
334 "prompt_tokens": 3019,
335 "completion_tokens": 104,
336 "total_tokens": 3123,
337 "prompt_tokens_details": {
338 "cached_tokens": 2048
339 }
340 },
341 "created": 1735120033,
342 "system_fingerprint": null,
343 "model": "qwen-plus",
344 "id": "chatcmpl-6ada9ed2-7f33-9de2-8bb0-78bd4035025a"
345 }"#;
346
347 let parsed = ChatCompletion::from_str(json);
348 match parsed {
349 Ok(_) => {}
350 Err(e) => {
351 panic!("Failed to deserialize: {}", e);
352 }
353 }
354 }
355}