portkey_sdk/model/
chat.rs

1//! Chat completion models for the Portkey API.
2//!
3//! This module contains all data models for chat completions, including
4//! request and response types following the OpenAI-compatible format.
5
6use serde::{Deserialize, Serialize};
7#[cfg(feature = "strum")]
8use strum::{Display, EnumString};
9
10/// A chat completion message in a conversation.
11#[derive(Debug, Clone, Serialize, Deserialize)]
12#[serde(tag = "role", rename_all = "lowercase")]
13pub enum ChatCompletionRequestMessage {
14    /// System message that sets the behavior of the assistant
15    System {
16        /// The contents of the system message
17        content: String,
18        /// Optional name for the participant
19        #[serde(skip_serializing_if = "Option::is_none")]
20        name: Option<String>,
21    },
22    /// Developer message (new role by OpenAI for select models)
23    Developer {
24        /// The contents of the developer message
25        content: String,
26        /// Optional name for the participant
27        #[serde(skip_serializing_if = "Option::is_none")]
28        name: Option<String>,
29    },
30    /// User message in the conversation
31    User {
32        /// The contents of the user message (text or array of content parts)
33        content: ChatCompletionUserMessageContent,
34        /// Optional name for the participant
35        #[serde(skip_serializing_if = "Option::is_none")]
36        name: Option<String>,
37    },
38    /// Assistant message in the conversation
39    Assistant {
40        /// The contents of the assistant message
41        #[serde(skip_serializing_if = "Option::is_none")]
42        content: Option<String>,
43        /// Optional name for the participant
44        #[serde(skip_serializing_if = "Option::is_none")]
45        name: Option<String>,
46        /// Tool calls generated by the model
47        #[serde(skip_serializing_if = "Option::is_none")]
48        tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
49        /// Deprecated: Function call (use tool_calls instead)
50        #[serde(skip_serializing_if = "Option::is_none")]
51        function_call: Option<FunctionCall>,
52    },
53    /// Tool response message
54    Tool {
55        /// The contents of the tool message
56        content: String,
57        /// Tool call ID that this message is responding to
58        tool_call_id: String,
59    },
60    /// Function response message (deprecated)
61    Function {
62        /// The contents of the function message
63        content: String,
64        /// The name of the function
65        name: String,
66    },
67}
68
69impl ChatCompletionRequestMessage {
70    /// Creates a system message.
71    ///
72    /// # Example
73    ///
74    /// ```
75    /// use portkey_sdk::model::ChatCompletionRequestMessage;
76    ///
77    /// let msg = ChatCompletionRequestMessage::system("You are a helpful assistant.");
78    /// ```
79    pub fn system(content: impl Into<String>) -> Self {
80        Self::System {
81            content: content.into(),
82            name: None,
83        }
84    }
85
86    /// Creates a user message with text content.
87    ///
88    /// # Example
89    ///
90    /// ```
91    /// use portkey_sdk::model::ChatCompletionRequestMessage;
92    ///
93    /// let msg = ChatCompletionRequestMessage::user("Hello!");
94    /// ```
95    pub fn user(content: impl Into<String>) -> Self {
96        Self::User {
97            content: ChatCompletionUserMessageContent::Text(content.into()),
98            name: None,
99        }
100    }
101
102    /// Creates an assistant message.
103    ///
104    /// # Example
105    ///
106    /// ```
107    /// use portkey_sdk::model::ChatCompletionRequestMessage;
108    ///
109    /// let msg = ChatCompletionRequestMessage::assistant("Hello! How can I help you?");
110    /// ```
111    pub fn assistant(content: impl Into<String>) -> Self {
112        Self::Assistant {
113            content: Some(content.into()),
114            name: None,
115            tool_calls: None,
116            function_call: None,
117        }
118    }
119}
120
121/// Content of a user message (can be text or multimodal)
122#[derive(Debug, Clone, Serialize, Deserialize)]
123#[serde(untagged)]
124pub enum ChatCompletionUserMessageContent {
125    /// Plain text content
126    Text(String),
127    /// Array of content parts (text and/or images)
128    Parts(Vec<ChatCompletionContentPart>),
129}
130
131/// A content part in a multimodal message
132#[derive(Debug, Clone, Serialize, Deserialize)]
133#[serde(tag = "type", rename_all = "snake_case")]
134pub enum ChatCompletionContentPart {
135    /// Text content part
136    Text {
137        /// The text content
138        text: String,
139    },
140    /// Image URL content part
141    ImageUrl {
142        /// Image URL or base64 encoded image data
143        image_url: ImageUrl,
144    },
145}
146
147/// Image URL configuration
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct ImageUrl {
150    /// URL of the image or base64 encoded image data
151    pub url: String,
152    /// Detail level of the image (auto, low, or high)
153    #[serde(skip_serializing_if = "Option::is_none")]
154    pub detail: Option<ImageDetail>,
155}
156
157/// Image detail level for vision models
158#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
159#[cfg_attr(feature = "strum", derive(Display, EnumString))]
160#[serde(rename_all = "lowercase")]
161#[cfg_attr(feature = "strum", strum(serialize_all = "lowercase"))]
162pub enum ImageDetail {
163    /// Auto detail level (default)
164    #[default]
165    Auto,
166    /// Low detail level
167    Low,
168    /// High detail level
169    High,
170}
171
172/// Function call information
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct FunctionCall {
175    /// The name of the function to call
176    pub name: String,
177    /// The arguments to call the function with (JSON format)
178    pub arguments: String,
179}
180
181/// Tool call made by the model
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct ChatCompletionMessageToolCall {
184    /// The ID of the tool call
185    pub id: String,
186    /// The type of tool (currently only "function")
187    #[serde(rename = "type")]
188    pub tool_type: String,
189    /// The function to call
190    pub function: FunctionCall,
191}
192
193/// Response format configuration
194#[derive(Debug, Clone, Serialize, Deserialize)]
195#[serde(tag = "type", rename_all = "snake_case")]
196pub enum ResponseFormat {
197    /// Text response format (default)
198    Text,
199    /// JSON object response format
200    JsonObject,
201    /// JSON schema response format (Structured Outputs)
202    JsonSchema {
203        /// JSON schema configuration
204        json_schema: JsonSchema,
205    },
206}
207
208impl ResponseFormat {
209    /// Creates a JSON schema from a type implementing `schemars::JsonSchema`.
210    ///
211    /// Returns a `JsonSchema` that can be used directly or customized with builder methods.
212    /// To use with `ResponseFormat`, wrap in `ResponseFormat::JsonSchema { json_schema: ... }`.
213    ///
214    /// # Example
215    ///
216    /// ```ignore
217    /// use schemars::JsonSchema;
218    /// use serde::{Deserialize, Serialize};
219    /// use portkey_sdk::model::ResponseFormat;
220    ///
221    /// #[derive(Serialize, Deserialize, JsonSchema)]
222    /// struct MyResponse {
223    ///     message: String,
224    ///     count: i32,
225    /// }
226    ///
227    /// let response_format = ResponseFormat::JsonSchema {
228    ///     json_schema: ResponseFormat::json_schema::<MyResponse>()
229    ///         .with_description("A custom response structure")
230    ///         .with_strict(true),
231    /// };
232    /// ```
233    #[cfg(feature = "schema")]
234    #[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
235    pub fn json_schema<T>() -> JsonSchema
236    where
237        T: schemars::JsonSchema,
238    {
239        JsonSchema::from_type::<T>()
240    }
241}
242
243/// JSON Schema configuration for Structured Outputs
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct JsonSchema {
246    /// A description of what the response format is for
247    #[serde(skip_serializing_if = "Option::is_none")]
248    pub description: Option<String>,
249    /// The name of the response format
250    pub name: String,
251    /// The JSON Schema object
252    pub schema: serde_json::Value,
253    /// Whether to enable strict schema adherence
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub strict: Option<bool>,
256}
257
258impl JsonSchema {
259    /// Creates a new JSON schema configuration from a type implementing `schemars::JsonSchema`.
260    ///
261    /// Uses the type's fully qualified name as the default name. Use builder methods
262    /// `with_name()`, `with_description()`, and `with_strict()` to customize.
263    ///
264    /// # Example
265    ///
266    /// ```ignore
267    /// use schemars::JsonSchema;
268    /// use serde::{Deserialize, Serialize};
269    /// use portkey_sdk::model::JsonSchema as PortkeyJsonSchema;
270    ///
271    /// #[derive(Serialize, Deserialize, JsonSchema)]
272    /// struct MyResponse {
273    ///     message: String,
274    ///     count: i32,
275    /// }
276    ///
277    /// let schema = PortkeyJsonSchema::from_type::<MyResponse>()
278    ///     .with_name("MyResponse")
279    ///     .with_description("A custom response structure")
280    ///     .with_strict(true);
281    /// ```
282    #[cfg(feature = "schema")]
283    #[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
284    pub fn from_type<T>() -> Self
285    where
286        T: schemars::JsonSchema,
287    {
288        let schema_obj = schemars::schema_for!(T);
289        let type_name = std::any::type_name::<T>();
290        let name = type_name.split("::").last().unwrap_or(type_name);
291
292        Self {
293            description: None,
294            name: name.to_string(),
295            schema: serde_json::to_value(schema_obj).expect("Failed to serialize schema"),
296            strict: None,
297        }
298    }
299
300    /// Sets the name for this JSON schema.
301    #[cfg(feature = "schema")]
302    #[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
303    pub fn with_name(mut self, name: impl Into<String>) -> Self {
304        self.name = name.into();
305        self
306    }
307
308    /// Sets the description for this JSON schema.
309    #[cfg(feature = "schema")]
310    #[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
311    pub fn with_description(mut self, description: impl Into<String>) -> Self {
312        self.description = Some(description.into());
313        self
314    }
315
316    /// Sets whether to enable strict schema adherence.
317    #[cfg(feature = "schema")]
318    #[cfg_attr(docsrs, doc(cfg(feature = "schema")))]
319    pub fn with_strict(mut self, strict: bool) -> Self {
320        self.strict = Some(strict);
321        self
322    }
323}
324
325/// Stream options for streaming responses
326#[derive(Debug, Clone, Serialize, Deserialize)]
327pub struct StreamOptions {
328    /// If set, include usage information in the final chunk
329    #[serde(skip_serializing_if = "Option::is_none")]
330    pub include_usage: Option<bool>,
331}
332
333/// Thinking mode configuration for Claude models
334#[derive(Debug, Clone, Serialize, Deserialize)]
335pub struct ThinkingConfig {
336    /// Type of thinking mode (enabled or disabled)
337    #[serde(rename = "type")]
338    pub thinking_type: ThinkingType,
339    /// Maximum number of tokens to allocate for thinking
340    #[serde(skip_serializing_if = "Option::is_none")]
341    pub budget_tokens: Option<i32>,
342}
343
344/// Thinking mode type
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
346#[cfg_attr(feature = "strum", derive(Display, EnumString))]
347#[serde(rename_all = "lowercase")]
348#[cfg_attr(feature = "strum", strum(serialize_all = "lowercase"))]
349pub enum ThinkingType {
350    /// Thinking mode enabled
351    Enabled,
352    /// Thinking mode disabled
353    Disabled,
354}
355
356/// Function definition for function calling
357#[derive(Debug, Clone, Serialize, Deserialize)]
358pub struct FunctionObject {
359    /// The name of the function
360    pub name: String,
361    /// A description of what the function does
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub description: Option<String>,
364    /// The parameters the function accepts (JSON Schema)
365    #[serde(skip_serializing_if = "Option::is_none")]
366    pub parameters: Option<serde_json::Value>,
367    /// Whether to enable strict schema adherence
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub strict: Option<bool>,
370}
371
372/// Tool definition
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct Tool {
375    /// The type of tool (currently only "function")
376    #[serde(rename = "type")]
377    pub tool_type: String,
378    /// The function definition
379    pub function: FunctionObject,
380}
381
382/// Tool choice option
383#[derive(Debug, Clone, Serialize, Deserialize)]
384#[serde(untagged)]
385pub enum ToolChoice {
386    /// Simple string choice (none, auto, required)
387    Simple(ToolChoiceSimple),
388    /// Named tool choice
389    Named(NamedToolChoice),
390}
391
392/// Simple tool choice options
393#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
394#[cfg_attr(feature = "strum", derive(Display, EnumString))]
395#[serde(rename_all = "lowercase")]
396#[cfg_attr(feature = "strum", strum(serialize_all = "lowercase"))]
397pub enum ToolChoiceSimple {
398    /// No tool will be called
399    None,
400    /// Model can choose to call a tool or not
401    Auto,
402    /// Model must call one or more tools
403    Required,
404}
405
406/// Named tool choice to force a specific tool
407#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct NamedToolChoice {
409    /// The type of tool
410    #[serde(rename = "type")]
411    pub tool_type: String,
412    /// The function to call
413    pub function: NamedFunction,
414}
415
416/// Named function for tool choice
417#[derive(Debug, Clone, Serialize, Deserialize)]
418pub struct NamedFunction {
419    /// The name of the function to call
420    pub name: String,
421}
422
423/// Chat completion request
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct ChatCompletionRequest {
426    /// ID of the model to use
427    pub model: String,
428    /// A list of messages comprising the conversation
429    pub messages: Vec<ChatCompletionRequestMessage>,
430    /// Number between -2.0 and 2.0 for frequency penalty
431    #[serde(skip_serializing_if = "Option::is_none")]
432    pub frequency_penalty: Option<f32>,
433    /// Modify the likelihood of specified tokens
434    #[serde(skip_serializing_if = "Option::is_none")]
435    pub logit_bias: Option<serde_json::Map<String, serde_json::Value>>,
436    /// Whether to return log probabilities
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub logprobs: Option<bool>,
439    /// Number of most likely tokens to return at each position
440    #[serde(skip_serializing_if = "Option::is_none")]
441    pub top_logprobs: Option<i32>,
442    /// Maximum number of tokens to generate
443    #[serde(skip_serializing_if = "Option::is_none")]
444    pub max_tokens: Option<i32>,
445    /// How many chat completion choices to generate
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub n: Option<i32>,
448    /// Number between -2.0 and 2.0 for presence penalty
449    #[serde(skip_serializing_if = "Option::is_none")]
450    pub presence_penalty: Option<f32>,
451    /// Response format configuration
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub response_format: Option<ResponseFormat>,
454    /// Seed for deterministic sampling
455    #[serde(skip_serializing_if = "Option::is_none")]
456    pub seed: Option<i64>,
457    /// Stop sequences
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub stop: Option<Stop>,
460    /// Whether to stream the response
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub stream: Option<bool>,
463    /// Stream options
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub stream_options: Option<StreamOptions>,
466    /// Thinking mode configuration (Claude models)
467    #[serde(skip_serializing_if = "Option::is_none")]
468    pub thinking: Option<ThinkingConfig>,
469    /// Sampling temperature (0-2)
470    #[serde(skip_serializing_if = "Option::is_none")]
471    pub temperature: Option<f32>,
472    /// Nucleus sampling parameter (0-1)
473    #[serde(skip_serializing_if = "Option::is_none")]
474    pub top_p: Option<f32>,
475    /// List of tools the model may call
476    #[serde(skip_serializing_if = "Option::is_none")]
477    pub tools: Option<Vec<Tool>>,
478    /// Controls which tool is called
479    #[serde(skip_serializing_if = "Option::is_none")]
480    pub tool_choice: Option<ToolChoice>,
481    /// Whether to enable parallel tool calls
482    #[serde(skip_serializing_if = "Option::is_none")]
483    pub parallel_tool_calls: Option<bool>,
484    /// A unique identifier for the end-user
485    #[serde(skip_serializing_if = "Option::is_none")]
486    pub user: Option<String>,
487}
488
489impl ChatCompletionRequest {
490    /// Creates a new chat completion request with the minimum required fields.
491    ///
492    /// # Arguments
493    ///
494    /// * `model` - The model ID to use (e.g., "gpt-4o", "claude-3-5-sonnet-20241022")
495    /// * `messages` - The conversation messages
496    ///
497    /// # Example
498    ///
499    /// ```
500    /// use portkey_sdk::model::{ChatCompletionRequest, ChatCompletionRequestMessage};
501    ///
502    /// let request = ChatCompletionRequest::new(
503    ///     "gpt-4o",
504    ///     vec![
505    ///         ChatCompletionRequestMessage::user("Hello!"),
506    ///     ],
507    /// );
508    /// ```
509    pub fn new(model: impl Into<String>, messages: Vec<ChatCompletionRequestMessage>) -> Self {
510        Self {
511            model: model.into(),
512            messages,
513            frequency_penalty: None,
514            logit_bias: None,
515            logprobs: None,
516            top_logprobs: None,
517            max_tokens: None,
518            n: None,
519            presence_penalty: None,
520            response_format: None,
521            seed: None,
522            stop: None,
523            stream: None,
524            stream_options: None,
525            thinking: None,
526            temperature: None,
527            top_p: None,
528            tools: None,
529            tool_choice: None,
530            parallel_tool_calls: None,
531            user: None,
532        }
533    }
534}
535
536/// Stop sequences (can be a string or array of strings)
537#[derive(Debug, Clone, Serialize, Deserialize)]
538#[serde(untagged)]
539pub enum Stop {
540    /// Single stop sequence
541    Single(String),
542    /// Multiple stop sequences
543    Multiple(Vec<String>),
544}
545
546/// Chat completion response message
547#[derive(Debug, Clone, Serialize, Deserialize)]
548pub struct ChatCompletionResponseMessage {
549    /// The role of the message author
550    pub role: String,
551    /// The contents of the message
552    pub content: Option<String>,
553    /// Tool calls made by the model
554    #[serde(skip_serializing_if = "Option::is_none")]
555    pub tool_calls: Option<Vec<ChatCompletionMessageToolCall>>,
556    /// Deprecated: Function call (use tool_calls instead)
557    #[serde(skip_serializing_if = "Option::is_none")]
558    pub function_call: Option<FunctionCall>,
559    /// Content blocks (for providers with strict_openai_compliance=false)
560    #[serde(skip_serializing_if = "Option::is_none")]
561    pub content_blocks: Option<Vec<ContentBlock>>,
562}
563
564impl ChatCompletionResponseMessage {
565    /// Deserializes the JSON content of the message into a custom type.
566    ///
567    /// This is useful when using structured outputs with JSON schema,
568    /// allowing you to deserialize the response into your custom type.
569    ///
570    /// Returns `Ok(None)` if the message has no content, `Ok(Some(T))` if deserialization succeeds,
571    /// or `Err` if the content is not valid JSON for the target type.
572    ///
573    /// # Example
574    ///
575    /// ```ignore
576    /// use serde::{Deserialize, Serialize};
577    ///
578    /// #[derive(Serialize, Deserialize)]
579    /// struct MyResponse {
580    ///     message: String,
581    ///     count: i32,
582    /// }
583    ///
584    /// let response_message = /* ... get from API response ... */;
585    /// if let Some(parsed) = response_message.deserialize_content::<MyResponse>()? {
586    ///     println!("Message: {}, Count: {}", parsed.message, parsed.count);
587    /// }
588    /// ```
589    pub fn deserialize_content<T>(&self) -> crate::Result<Option<T>>
590    where
591        T: serde::de::DeserializeOwned,
592    {
593        match &self.content {
594            Some(content) => Ok(Some(serde_json::from_str(content)?)),
595            None => Ok(None),
596        }
597    }
598
599    /// Deserializes JSON from markdown code blocks within the message content.
600    ///
601    /// This method searches for markdown code blocks (` ```json` or ` ``` `) within the content
602    /// and extracts the JSON from within them. It can handle content that has text before or
603    /// after the code block. If no code block is found, it attempts to parse the entire content.
604    ///
605    /// Returns `Ok(None)` if the message has no content, `Ok(Some(T))` if deserialization succeeds,
606    /// or `Err` if the content is not valid JSON for the target type.
607    ///
608    /// # Example
609    ///
610    /// ```ignore
611    /// use serde::{Deserialize, Serialize};
612    ///
613    /// #[derive(Serialize, Deserialize)]
614    /// struct MyResponse {
615    ///     message: String,
616    ///     count: i32,
617    /// }
618    ///
619    /// // Content might be: "Here's your data:\n```json\n{\"message\": \"Hello\", \"count\": 42}\n```\nEnjoy!"
620    /// let response_message = /* ... get from API response ... */;
621    /// if let Some(parsed) = response_message.deserialize_markdown::<MyResponse>()? {
622    ///     println!("Message: {}, Count: {}", parsed.message, parsed.count);
623    /// }
624    /// ```
625    pub fn deserialize_markdown<T>(&self) -> crate::Result<Option<T>>
626    where
627        T: serde::de::DeserializeOwned,
628    {
629        match &self.content {
630            Some(content) => {
631                // Try to find a markdown code block with ```json or ```
632                let json_content = if let Some(start_json) = content.find("```json") {
633                    // Found ```json, extract content between ```json and closing ```
634                    let after_start = &content[start_json + 7..]; // Skip "```json"
635                    if let Some(end) = after_start.find("```") {
636                        after_start[..end].trim()
637                    } else {
638                        // No closing ```, use everything after ```json
639                        after_start.trim()
640                    }
641                } else if let Some(start) = content.find("```") {
642                    // Found plain ```, extract content between ``` and closing ```
643                    let after_start = &content[start + 3..]; // Skip "```"
644                    if let Some(end) = after_start.find("```") {
645                        after_start[..end].trim()
646                    } else {
647                        // No closing ```, use everything after ```
648                        after_start.trim()
649                    }
650                } else {
651                    // No markdown code block found, try to parse the entire content
652                    content.trim()
653                };
654
655                Ok(Some(serde_json::from_str(json_content)?))
656            }
657            None => Ok(None),
658        }
659    }
660}
661
662/// Content block in a response message
663#[derive(Debug, Clone, Serialize, Deserialize)]
664#[serde(tag = "type", rename_all = "snake_case")]
665pub enum ContentBlock {
666    /// Text content block
667    Text {
668        /// The text content
669        text: String,
670    },
671    /// Thinking content block
672    Thinking {
673        /// The thinking content
674        thinking: String,
675    },
676    /// Redacted thinking content block
677    RedactedThinking {
678        /// The redacted thinking data
679        data: String,
680    },
681}
682
683/// Token log probability information
684#[derive(Debug, Clone, Serialize, Deserialize)]
685pub struct TokenLogprob {
686    /// The token
687    pub token: String,
688    /// Log probability of the token
689    pub logprob: f64,
690    /// UTF-8 bytes representation of the token
691    pub bytes: Option<Vec<i32>>,
692    /// Top log probabilities at this position
693    pub top_logprobs: Vec<TopLogprob>,
694}
695
696/// Top token log probability
697#[derive(Debug, Clone, Serialize, Deserialize)]
698pub struct TopLogprob {
699    /// The token
700    pub token: String,
701    /// Log probability of the token
702    pub logprob: f64,
703    /// UTF-8 bytes representation of the token
704    pub bytes: Option<Vec<i32>>,
705}
706
707/// Log probability information for a choice
708#[derive(Debug, Clone, Serialize, Deserialize)]
709pub struct Logprobs {
710    /// List of message content tokens with log probability information
711    pub content: Option<Vec<TokenLogprob>>,
712}
713
714/// A chat completion choice
715#[derive(Debug, Clone, Serialize, Deserialize)]
716pub struct ChatCompletionChoice {
717    /// The reason the model stopped generating tokens
718    pub finish_reason: String,
719    /// The index of this choice
720    pub index: i32,
721    /// The completion message
722    pub message: ChatCompletionResponseMessage,
723    /// Log probability information
724    pub logprobs: Option<Logprobs>,
725}
726
727/// Token usage statistics
728#[derive(Debug, Clone, Serialize, Deserialize)]
729pub struct Usage {
730    /// Number of tokens in the prompt
731    pub prompt_tokens: i32,
732    /// Number of tokens in the completion
733    pub completion_tokens: i32,
734    /// Total number of tokens used
735    pub total_tokens: i32,
736}
737
738/// Chat completion response
739#[derive(Debug, Clone, Serialize, Deserialize)]
740pub struct ChatCompletionResponse {
741    /// A unique identifier for the chat completion
742    pub id: String,
743    /// The object type (always "chat.completion")
744    pub object: String,
745    /// The Unix timestamp when the completion was created
746    pub created: i64,
747    /// The model used for the chat completion
748    pub model: String,
749    /// A list of chat completion choices
750    pub choices: Vec<ChatCompletionChoice>,
751    /// Usage statistics
752    #[serde(skip_serializing_if = "Option::is_none")]
753    pub usage: Option<Usage>,
754    /// System fingerprint
755    #[serde(skip_serializing_if = "Option::is_none")]
756    pub system_fingerprint: Option<String>,
757}