Skip to main content

linger_openai_sdk/
conversations.rs

1use crate::error::LingerError;
2use crate::RequestId;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7/// EN: Request body for `POST /v1/conversations`.
8/// 中文:`POST /v1/conversations` 的请求体。
9#[derive(Clone, Debug, Default, Serialize, PartialEq)]
10#[non_exhaustive]
11pub struct CreateConversationRequest {
12    /// EN: Optional metadata.
13    /// 中文:可选元数据。
14    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
15    pub metadata: BTreeMap<String, String>,
16    /// EN: Forward-compatible optional fields not yet covered by handwritten types.
17    /// 中文:手写类型尚未覆盖的前向兼容可选字段。
18    #[serde(flatten)]
19    pub extra: BTreeMap<String, Value>,
20}
21
22impl CreateConversationRequest {
23    /// EN: Starts building a conversation creation request.
24    /// 中文:开始构建会话创建请求。
25    pub fn builder() -> CreateConversationRequestBuilder {
26        CreateConversationRequestBuilder::default()
27    }
28}
29
30/// EN: Builder for conversation creation requests.
31/// 中文:会话创建请求的构建器。
32#[derive(Clone, Debug, Default)]
33#[non_exhaustive]
34pub struct CreateConversationRequestBuilder {
35    metadata: BTreeMap<String, String>,
36    extra: BTreeMap<String, Value>,
37}
38
39impl CreateConversationRequestBuilder {
40    /// EN: Adds a metadata key/value pair.
41    /// 中文:添加一个元数据键值对。
42    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
43        self.metadata.insert(key.into(), value.into());
44        self
45    }
46
47    /// EN: Adds a forward-compatible JSON field.
48    /// 中文:添加前向兼容的 JSON 字段。
49    pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
50        self.extra.insert(name.into(), value);
51        self
52    }
53
54    /// EN: Builds and validates the request.
55    /// 中文:构建并校验请求。
56    pub fn build(self) -> Result<CreateConversationRequest, LingerError> {
57        validate_metadata(&self.metadata)?;
58        validate_extra_fields(&self.extra)?;
59        Ok(CreateConversationRequest {
60            metadata: self.metadata,
61            extra: self.extra,
62        })
63    }
64}
65
66/// EN: Request body for `POST /v1/conversations/{conversation_id}`.
67/// 中文:`POST /v1/conversations/{conversation_id}` 的请求体。
68#[derive(Clone, Debug, Default, Serialize, PartialEq)]
69#[non_exhaustive]
70pub struct ModifyConversationRequest {
71    /// EN: Optional metadata.
72    /// 中文:可选元数据。
73    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
74    pub metadata: BTreeMap<String, String>,
75}
76
77impl ModifyConversationRequest {
78    /// EN: Starts building a conversation modification request.
79    /// 中文:开始构建会话修改请求。
80    pub fn builder() -> ModifyConversationRequestBuilder {
81        ModifyConversationRequestBuilder::default()
82    }
83}
84
85/// EN: Builder for conversation modification requests.
86/// 中文:会话修改请求的构建器。
87#[derive(Clone, Debug, Default)]
88#[non_exhaustive]
89pub struct ModifyConversationRequestBuilder {
90    metadata: BTreeMap<String, String>,
91}
92
93impl ModifyConversationRequestBuilder {
94    /// EN: Adds a metadata key/value pair.
95    /// 中文:添加一个元数据键值对。
96    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
97        self.metadata.insert(key.into(), value.into());
98        self
99    }
100
101    /// EN: Builds and validates the request.
102    /// 中文:构建并校验请求。
103    pub fn build(self) -> Result<ModifyConversationRequest, LingerError> {
104        validate_metadata(&self.metadata)?;
105        Ok(ModifyConversationRequest {
106            metadata: self.metadata,
107        })
108    }
109}
110
111/// EN: Request body for `POST /v1/conversations/{conversation_id}/items`.
112/// 中文:`POST /v1/conversations/{conversation_id}/items` 的请求体。
113#[derive(Clone, Debug, Serialize, PartialEq)]
114#[non_exhaustive]
115pub struct CreateConversationItemRequest {
116    /// EN: Conversation item payload.
117    /// 中文:会话项载荷。
118    pub item: Value,
119}
120
121impl CreateConversationItemRequest {
122    /// EN: Starts building a conversation item creation request.
123    /// 中文:开始构建会话项创建请求。
124    pub fn builder() -> CreateConversationItemRequestBuilder {
125        CreateConversationItemRequestBuilder::default()
126    }
127}
128
129/// EN: Builder for conversation item creation requests.
130/// 中文:会话项创建请求的构建器。
131#[derive(Clone, Debug, Default)]
132#[non_exhaustive]
133pub struct CreateConversationItemRequestBuilder {
134    item: Option<Value>,
135}
136
137impl CreateConversationItemRequestBuilder {
138    /// EN: Sets the conversation item payload.
139    /// 中文:设置会话项载荷。
140    pub fn item(mut self, item: Value) -> Self {
141        self.item = Some(item);
142        self
143    }
144
145    /// EN: Builds and validates the request.
146    /// 中文:构建并校验请求。
147    pub fn build(self) -> Result<CreateConversationItemRequest, LingerError> {
148        let item = self
149            .item
150            .filter(|value| !value.is_null())
151            .ok_or_else(|| LingerError::invalid_config("item is required"))?;
152        Ok(CreateConversationItemRequest { item })
153    }
154}
155
156/// EN: Conversation object returned by the Conversations API.
157/// 中文:Conversations API 返回的 Conversation 对象。
158#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
159#[non_exhaustive]
160pub struct Conversation {
161    /// EN: Conversation id.
162    /// 中文:Conversation ID。
163    pub id: String,
164    /// EN: API object type.
165    /// 中文:API 对象类型。
166    pub object: String,
167    /// EN: Unix timestamp for creation, when returned.
168    /// 中文:创建时间的 Unix 时间戳,如响应中存在。
169    #[serde(default)]
170    pub created_at: Option<u64>,
171    /// EN: Metadata returned by the API.
172    /// 中文:API 返回的元数据。
173    #[serde(default)]
174    pub metadata: BTreeMap<String, String>,
175    /// EN: Additional fields preserved for forward compatibility.
176    /// 中文:为前向兼容保留的额外字段。
177    #[serde(flatten)]
178    pub extra: BTreeMap<String, Value>,
179    /// EN: OpenAI request id from response headers.
180    /// 中文:响应头中的 OpenAI 请求 ID。
181    #[serde(skip)]
182    request_id: Option<RequestId>,
183}
184
185impl Conversation {
186    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
187        self.request_id = request_id;
188        self
189    }
190
191    /// EN: Returns the OpenAI request id, when present.
192    /// 中文:返回 OpenAI 请求 ID,如存在。
193    pub fn request_id(&self) -> Option<&RequestId> {
194        self.request_id.as_ref()
195    }
196}
197
198/// EN: Conversation item object returned by the Conversations API.
199/// 中文:Conversations API 返回的 Conversation Item 对象。
200#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
201#[non_exhaustive]
202pub struct ConversationItem {
203    /// EN: Conversation item id.
204    /// 中文:Conversation Item ID。
205    pub id: String,
206    /// EN: API object type.
207    /// 中文:API 对象类型。
208    pub object: String,
209    /// EN: Unix timestamp for creation, when returned.
210    /// 中文:创建时间的 Unix 时间戳,如响应中存在。
211    #[serde(default)]
212    pub created_at: Option<u64>,
213    /// EN: Additional fields preserved for forward compatibility.
214    /// 中文:为前向兼容保留的额外字段。
215    #[serde(flatten)]
216    pub extra: BTreeMap<String, Value>,
217    /// EN: OpenAI request id from response headers.
218    /// 中文:响应头中的 OpenAI 请求 ID。
219    #[serde(skip)]
220    request_id: Option<RequestId>,
221}
222
223impl ConversationItem {
224    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
225        self.request_id = request_id;
226        self
227    }
228
229    /// EN: Returns the OpenAI request id, when present.
230    /// 中文:返回 OpenAI 请求 ID,如存在。
231    pub fn request_id(&self) -> Option<&RequestId> {
232        self.request_id.as_ref()
233    }
234}
235
236/// EN: Paginated conversation item list.
237/// 中文:分页会话项列表。
238#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
239#[non_exhaustive]
240pub struct ConversationItemPage {
241    /// EN: API list object type.
242    /// 中文:API 列表对象类型。
243    pub object: String,
244    /// EN: Conversation items on this page.
245    /// 中文:本页的会话项。
246    #[serde(default)]
247    pub data: Vec<ConversationItem>,
248    /// EN: First item id on this page.
249    /// 中文:本页第一个会话项 ID。
250    #[serde(default)]
251    pub first_id: Option<String>,
252    /// EN: Last item id on this page.
253    /// 中文:本页最后一个会话项 ID。
254    #[serde(default)]
255    pub last_id: Option<String>,
256    /// EN: Whether more items are available.
257    /// 中文:是否还有更多会话项。
258    pub has_more: bool,
259    /// EN: OpenAI request id from response headers.
260    /// 中文:响应头中的 OpenAI 请求 ID。
261    #[serde(skip)]
262    request_id: Option<RequestId>,
263}
264
265impl ConversationItemPage {
266    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
267        self.request_id = request_id;
268        self
269    }
270
271    /// EN: Returns the OpenAI request id, when present.
272    /// 中文:返回 OpenAI 请求 ID,如存在。
273    pub fn request_id(&self) -> Option<&RequestId> {
274        self.request_id.as_ref()
275    }
276}
277
278/// EN: Deletion result returned by the Conversations API.
279/// 中文:Conversations API 返回的删除结果。
280#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
281#[non_exhaustive]
282pub struct ConversationDeletion {
283    /// EN: Deleted conversation id.
284    /// 中文:已删除的 Conversation ID。
285    pub id: String,
286    /// EN: API object type.
287    /// 中文:API 对象类型。
288    pub object: String,
289    /// EN: Whether the conversation was deleted.
290    /// 中文:Conversation 是否已删除。
291    pub deleted: bool,
292    /// EN: OpenAI request id from response headers.
293    /// 中文:响应头中的 OpenAI 请求 ID。
294    #[serde(skip)]
295    request_id: Option<RequestId>,
296}
297
298impl ConversationDeletion {
299    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
300        self.request_id = request_id;
301        self
302    }
303
304    /// EN: Returns the OpenAI request id, when present.
305    /// 中文:返回 OpenAI 请求 ID,如存在。
306    pub fn request_id(&self) -> Option<&RequestId> {
307        self.request_id.as_ref()
308    }
309}
310
311/// EN: Deletion result returned by the Conversation Items API.
312/// 中文:Conversation Items API 返回的删除结果。
313#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
314#[non_exhaustive]
315pub struct ConversationItemDeletion {
316    /// EN: Deleted item id.
317    /// 中文:已删除的会话项 ID。
318    pub id: String,
319    /// EN: API object type.
320    /// 中文:API 对象类型。
321    pub object: String,
322    /// EN: Whether the item was deleted.
323    /// 中文:会话项是否已删除。
324    pub deleted: bool,
325    /// EN: OpenAI request id from response headers.
326    /// 中文:响应头中的 OpenAI 请求 ID。
327    #[serde(skip)]
328    request_id: Option<RequestId>,
329}
330
331impl ConversationItemDeletion {
332    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
333        self.request_id = request_id;
334        self
335    }
336
337    /// EN: Returns the OpenAI request id, when present.
338    /// 中文:返回 OpenAI 请求 ID,如存在。
339    pub fn request_id(&self) -> Option<&RequestId> {
340        self.request_id.as_ref()
341    }
342}
343
344fn validate_metadata(metadata: &BTreeMap<String, String>) -> Result<(), LingerError> {
345    for key in metadata.keys() {
346        if key.trim().is_empty() {
347            return Err(LingerError::invalid_config(
348                "metadata keys must not be empty",
349            ));
350        }
351    }
352    Ok(())
353}
354
355fn validate_extra_fields(extra: &BTreeMap<String, Value>) -> Result<(), LingerError> {
356    for (key, value) in extra {
357        if key.trim().is_empty() {
358            return Err(LingerError::invalid_config(
359                "extra field names must not be empty",
360            ));
361        }
362        if value.is_null() {
363            return Err(LingerError::invalid_config(format!(
364                "extra field {key} must not be null"
365            )));
366        }
367    }
368    Ok(())
369}