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}