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}