1use crate::error::LingerError;
2use crate::RequestId;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7#[derive(Clone, Debug, Serialize, PartialEq)]
10#[non_exhaustive]
11pub struct CreateChatCompletionRequest {
12 pub model: String,
15 pub messages: Vec<ChatMessage>,
18 #[serde(skip_serializing_if = "Option::is_none")]
21 pub max_completion_tokens: Option<u32>,
22 #[serde(skip_serializing_if = "Option::is_none")]
25 pub temperature: Option<f32>,
26 #[serde(flatten)]
29 pub extra: BTreeMap<String, Value>,
30}
31
32impl CreateChatCompletionRequest {
33 pub fn builder() -> CreateChatCompletionRequestBuilder {
36 CreateChatCompletionRequestBuilder::default()
37 }
38}
39
40#[derive(Clone, Debug, Default)]
43#[non_exhaustive]
44pub struct CreateChatCompletionRequestBuilder {
45 model: Option<String>,
46 messages: Vec<ChatMessage>,
47 max_completion_tokens: Option<u32>,
48 temperature: Option<f32>,
49 extra: BTreeMap<String, Value>,
50}
51
52impl CreateChatCompletionRequestBuilder {
53 pub fn model(mut self, model: impl Into<String>) -> Self {
56 self.model = Some(model.into());
57 self
58 }
59
60 pub fn message(mut self, message: ChatMessage) -> Self {
63 self.messages.push(message);
64 self
65 }
66
67 pub fn max_completion_tokens(mut self, max_completion_tokens: u32) -> Self {
70 self.max_completion_tokens = Some(max_completion_tokens);
71 self
72 }
73
74 pub fn temperature(mut self, temperature: f32) -> Self {
77 self.temperature = Some(temperature);
78 self
79 }
80
81 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
84 self.extra.insert(name.into(), value);
85 self
86 }
87
88 pub fn build(self) -> Result<CreateChatCompletionRequest, LingerError> {
91 let model = self
92 .model
93 .filter(|value| !value.trim().is_empty())
94 .ok_or_else(|| LingerError::invalid_config("model is required"))?;
95 if self.messages.is_empty() {
96 return Err(LingerError::invalid_config("messages is required"));
97 }
98 if self
99 .messages
100 .iter()
101 .any(|message| message.role.trim().is_empty() || message.content.is_empty())
102 {
103 return Err(LingerError::invalid_config(
104 "message role and content must not be empty",
105 ));
106 }
107 Ok(CreateChatCompletionRequest {
108 model,
109 messages: self.messages,
110 max_completion_tokens: self.max_completion_tokens,
111 temperature: self.temperature,
112 extra: self.extra,
113 })
114 }
115}
116
117#[derive(Clone, Debug, Default, Serialize, PartialEq)]
120#[non_exhaustive]
121pub struct ModifyChatCompletionRequest {
122 pub metadata: BTreeMap<String, String>,
125}
126
127impl ModifyChatCompletionRequest {
128 pub fn builder() -> ModifyChatCompletionRequestBuilder {
131 ModifyChatCompletionRequestBuilder::default()
132 }
133}
134
135#[derive(Clone, Debug, Default)]
138#[non_exhaustive]
139pub struct ModifyChatCompletionRequestBuilder {
140 metadata: BTreeMap<String, String>,
141}
142
143impl ModifyChatCompletionRequestBuilder {
144 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
147 self.metadata.insert(key.into(), value.into());
148 self
149 }
150
151 pub fn build(self) -> Result<ModifyChatCompletionRequest, LingerError> {
154 validate_metadata(&self.metadata)?;
155 Ok(ModifyChatCompletionRequest {
156 metadata: self.metadata,
157 })
158 }
159}
160
161#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
164#[non_exhaustive]
165pub struct ChatMessage {
166 pub role: String,
169 pub content: String,
172}
173
174impl ChatMessage {
175 pub fn developer(content: impl Into<String>) -> Self {
178 Self::new("developer", content)
179 }
180
181 pub fn system(content: impl Into<String>) -> Self {
184 Self::new("system", content)
185 }
186
187 pub fn user(content: impl Into<String>) -> Self {
190 Self::new("user", content)
191 }
192
193 pub fn assistant(content: impl Into<String>) -> Self {
196 Self::new("assistant", content)
197 }
198
199 pub fn new(role: impl Into<String>, content: impl Into<String>) -> Self {
202 Self {
203 role: role.into(),
204 content: content.into(),
205 }
206 }
207}
208
209#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
212#[non_exhaustive]
213pub struct ChatCompletion {
214 pub id: String,
217 pub object: String,
220 pub created: u64,
223 pub model: String,
226 #[serde(default)]
229 pub choices: Vec<ChatCompletionChoice>,
230 #[serde(default)]
233 pub usage: Option<ChatCompletionUsage>,
234 #[serde(flatten)]
237 pub extra: BTreeMap<String, Value>,
238 #[serde(skip)]
241 request_id: Option<RequestId>,
242}
243
244impl ChatCompletion {
245 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
246 self.request_id = request_id;
247 self
248 }
249
250 pub fn request_id(&self) -> Option<&RequestId> {
253 self.request_id.as_ref()
254 }
255}
256
257#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
260#[non_exhaustive]
261pub struct ChatCompletionPage {
262 pub object: String,
265 #[serde(default)]
268 pub data: Vec<ChatCompletion>,
269 #[serde(default)]
272 pub first_id: Option<String>,
273 #[serde(default)]
276 pub last_id: Option<String>,
277 pub has_more: bool,
280 #[serde(skip)]
283 request_id: Option<RequestId>,
284}
285
286impl ChatCompletionPage {
287 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
288 self.request_id = request_id;
289 self
290 }
291
292 pub fn request_id(&self) -> Option<&RequestId> {
295 self.request_id.as_ref()
296 }
297}
298
299#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
302#[non_exhaustive]
303pub struct ChatCompletionDeletion {
304 pub id: String,
307 pub object: String,
310 pub deleted: bool,
313 #[serde(skip)]
316 request_id: Option<RequestId>,
317}
318
319impl ChatCompletionDeletion {
320 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
321 self.request_id = request_id;
322 self
323 }
324
325 pub fn request_id(&self) -> Option<&RequestId> {
328 self.request_id.as_ref()
329 }
330}
331
332#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
335#[non_exhaustive]
336pub struct ChatCompletionChoice {
337 pub index: u32,
340 pub message: ChatCompletionMessage,
343 #[serde(default)]
346 pub finish_reason: Option<String>,
347 #[serde(flatten)]
350 pub extra: BTreeMap<String, Value>,
351}
352
353#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
356#[non_exhaustive]
357pub struct ChatCompletionMessage {
358 pub role: String,
361 #[serde(default)]
364 pub content: Option<String>,
365 #[serde(flatten)]
368 pub extra: BTreeMap<String, Value>,
369}
370
371#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
374#[non_exhaustive]
375pub struct ChatCompletionStoredMessage {
376 pub id: String,
379 pub object: String,
382 pub role: String,
385 #[serde(default)]
388 pub content: Option<String>,
389 #[serde(flatten)]
392 pub extra: BTreeMap<String, Value>,
393}
394
395#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
398#[non_exhaustive]
399pub struct ChatCompletionMessagePage {
400 pub object: String,
403 #[serde(default)]
406 pub data: Vec<ChatCompletionStoredMessage>,
407 #[serde(default)]
410 pub first_id: Option<String>,
411 #[serde(default)]
414 pub last_id: Option<String>,
415 pub has_more: bool,
418 #[serde(skip)]
421 request_id: Option<RequestId>,
422}
423
424impl ChatCompletionMessagePage {
425 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
426 self.request_id = request_id;
427 self
428 }
429
430 pub fn request_id(&self) -> Option<&RequestId> {
433 self.request_id.as_ref()
434 }
435}
436
437#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
440#[non_exhaustive]
441pub struct ChatCompletionUsage {
442 pub prompt_tokens: u64,
445 pub completion_tokens: u64,
448 pub total_tokens: u64,
451}
452
453fn validate_metadata(metadata: &BTreeMap<String, String>) -> Result<(), LingerError> {
454 for key in metadata.keys() {
455 if key.trim().is_empty() {
456 return Err(LingerError::invalid_config(
457 "metadata keys must not be empty",
458 ));
459 }
460 }
461 Ok(())
462}