chatgpt/
types.rs

1#[cfg(feature = "functions")]
2use crate::functions::FunctionCall;
3use serde::{Deserialize, Deserializer, Serialize};
4
5/// A role of a message sender, can be:
6/// - `System`, for starting system message, that sets the tone of model
7/// - `Assistant`, for messages sent by ChatGPT
8/// - `User`, for messages sent by user
9#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize, Eq, Ord)]
10#[serde(rename_all = "lowercase")]
11pub enum Role {
12    /// A system message, automatically sent at the start to set the tone of the model
13    System,
14    /// A message sent by ChatGPT
15    Assistant,
16    /// A message sent by the user
17    User,
18    /// A message related to ChatGPT functions. Does not have much use without the `functions` feature.
19    Function,
20}
21
22/// Container for the sent/received ChatGPT messages
23#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
24pub struct ChatMessage {
25    /// Role of message sender
26    pub role: Role,
27    /// Actual content of the message
28    #[serde(deserialize_with = "deserialize_maybe_null")]
29    pub content: String,
30    /// Function call (if present)
31    #[cfg(feature = "functions")]
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub function_call: Option<FunctionCall>,
34}
35
36fn deserialize_maybe_null<'de, D>(deserializer: D) -> Result<String, D::Error>
37    where D: Deserializer<'de> {
38    let buf = Option::<String>::deserialize(deserializer)?;
39    Ok(buf.unwrap_or(String::new()))
40}
41
42impl ChatMessage {
43    /// Converts multiple response chunks into multiple (or a single) chat messages
44    #[cfg(feature = "streams")]
45    pub fn from_response_chunks(chunks: Vec<ResponseChunk>) -> Vec<Self> {
46        let mut result: Vec<Self> = Vec::new();
47        for chunk in chunks {
48            match chunk {
49                ResponseChunk::Content {
50                    delta,
51                    response_index,
52                } => {
53                    let msg = result
54                        .get_mut(response_index)
55                        .expect("Invalid response chunk sequence!");
56                    msg.content.push_str(&delta);
57                }
58                ResponseChunk::BeginResponse {
59                    role,
60                    response_index: _,
61                } => {
62                    let msg = ChatMessage {
63                        role,
64                        content: String::new(),
65                        #[cfg(feature = "functions")]
66                        function_call: None,
67                    };
68                    result.push(msg);
69                }
70                _ => {}
71            }
72        }
73        result
74    }
75}
76
77/// A request struct sent to the API to request a message completion
78#[derive(Debug, Clone, PartialEq, Serialize)]
79pub struct CompletionRequest<'a> {
80    /// The model to be used, currently `gpt-3.5-turbo`, but may change in future
81    pub model: &'a str,
82    /// The message history, including the message that requires completion, which should be the last one
83    pub messages: &'a Vec<ChatMessage>,
84    /// Whether the message response should be gradually streamed
85    pub stream: bool,
86    /// The extra randomness of response
87    pub temperature: f32,
88    /// Controls diversity via nucleus sampling, not recommended to use with temperature
89    pub top_p: f32,
90    /// Controls the maximum number of tokens to generate in the completion
91    #[serde(skip_serializing_if = "Option::is_none")]
92    pub max_tokens: Option<u32>,
93    /// Determines how much to penalize new tokens based on their existing frequency so far
94    pub frequency_penalty: f32,
95    /// Determines how much to penalize new tokens pased on their existing presence so far
96    pub presence_penalty: f32,
97    /// Determines the amount of output responses
98    #[serde(rename = "n")]
99    pub reply_count: u32,
100    /// All functions that can be called by ChatGPT
101    #[cfg(feature = "functions")]
102    #[serde(skip_serializing_if = "Vec::is_empty")]
103    pub functions: &'a Vec<serde_json::Value>,
104}
105
106/// Represents a response from the API
107#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
108#[serde(untagged)]
109pub enum ServerResponse {
110    /// An error occurred, most likely the model was just overloaded
111    Error {
112        /// The error that happened
113        error: CompletionError,
114    },
115    /// Completion successfuly completed
116    Completion(CompletionResponse),
117}
118
119/// An error happened while requesting completion
120#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
121pub struct CompletionError {
122    /// Message, describing the error
123    pub message: String,
124    /// The type of error. Example: `server_error`
125    #[serde(rename = "type")]
126    pub error_type: String,
127}
128
129/// A response struct received from the API after requesting a message completion
130#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
131pub struct CompletionResponse {
132    /// Unique ID of the message, but not in a UUID format.
133    /// Example: `chatcmpl-6p5FEv1JHictSSnDZsGU4KvbuBsbu`
134    #[serde(rename = "id")]
135    pub message_id: Option<String>,
136    /// Unix seconds timestamp of when the response was created
137    #[serde(rename = "created")]
138    pub created_timestamp: Option<u64>,
139    /// The model that was used for this completion
140    pub model: String,
141    /// Token usage of this completion
142    pub usage: TokenUsage,
143    /// Message choices for this response, guaranteed to contain at least one message response
144    #[serde(rename = "choices")]
145    pub message_choices: Vec<MessageChoice>,
146}
147
148impl CompletionResponse {
149    /// A shortcut to access the message response
150    pub fn message(&self) -> &ChatMessage {
151        // Unwrap is safe here, as we know that at least one message choice is provided
152        &self.message_choices.first().unwrap().message
153    }
154}
155
156/// A message completion choice struct
157#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
158pub struct MessageChoice {
159    /// The actual message
160    pub message: ChatMessage,
161    /// The reason completion was stopped
162    pub finish_reason: String,
163    /// The index of this message in the outer `message_choices` array
164    pub index: u32,
165}
166
167/// The token usage of a specific response
168#[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize)]
169pub struct TokenUsage {
170    /// Tokens spent on the prompt message (including previous messages)
171    pub prompt_tokens: u32,
172    /// Tokens spent on the completion message
173    pub completion_tokens: u32,
174    /// Total amount of tokens used (`prompt_tokens + completion_tokens`)
175    pub total_tokens: u32,
176}
177
178/// A single response chunk, returned from streamed request
179#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
180#[cfg(feature = "streams")]
181pub enum ResponseChunk {
182    /// A chunk of message content
183    Content {
184        /// Piece of message content
185        delta: String,
186        /// Index of the message. Used when `reply_count` is set to more than 1 in API config
187        response_index: usize,
188    },
189    /// Marks beginning of a new message response, with no actual content yet
190    BeginResponse {
191        /// The respondent's role (usually `Assistant`)
192        role: Role,
193        /// Index of the message. Used when `reply_count` is set to more than 1 in API config
194        response_index: usize,
195    },
196    /// Ends a single message response response
197    CloseResponse {
198        /// Index of the message finished. Used when `reply_count` is set to more than 1 in API config
199        response_index: usize,
200    },
201    /// Marks end of stream
202    Done,
203}
204
205/// A part of a chunked inbound response
206#[derive(Debug, Clone, Deserialize)]
207#[cfg(feature = "streams")]
208pub struct InboundResponseChunk {
209    /// All message chunks in this response part (only one usually)
210    pub choices: Vec<InboundChunkChoice>,
211}
212
213/// A single message part of a chunked inbound response
214#[derive(Debug, Clone, Deserialize)]
215#[cfg(feature = "streams")]
216pub struct InboundChunkChoice {
217    /// The part value of the response
218    pub delta: InboundChunkPayload,
219    /// Index of the message this chunk refers to
220    pub index: usize,
221}
222
223/// Contains different chunked inbound response payloads
224#[derive(Debug, Clone, Deserialize)]
225#[serde(untagged)]
226#[cfg(feature = "streams")]
227pub enum InboundChunkPayload {
228    /// Begins a single message by announcing roles (usually `assistant`)
229    AnnounceRoles {
230        /// The announced role
231        role: Role,
232    },
233    /// Streams a part of message content
234    StreamContent {
235        /// The part of content
236        content: String,
237    },
238    /// Closes a single message
239    Close {},
240}