Skip to main content

linger_openai_sdk/
threads.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/threads`.
8/// 中文:`POST /v1/threads` 的请求体。
9#[derive(Clone, Debug, Default, Serialize, PartialEq)]
10#[non_exhaustive]
11pub struct CreateThreadRequest {
12    /// EN: Initial messages for the thread.
13    /// 中文:线程的初始消息。
14    #[serde(skip_serializing_if = "Vec::is_empty")]
15    pub messages: Vec<Value>,
16    /// EN: Optional tool resources.
17    /// 中文:可选的工具资源。
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub tool_resources: Option<Value>,
20    /// EN: Optional metadata.
21    /// 中文:可选元数据。
22    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
23    pub metadata: BTreeMap<String, String>,
24}
25
26impl CreateThreadRequest {
27    /// EN: Starts building a thread creation request.
28    /// 中文:开始构建线程创建请求。
29    pub fn builder() -> CreateThreadRequestBuilder {
30        CreateThreadRequestBuilder::default()
31    }
32}
33
34/// EN: Builder for thread creation requests.
35/// 中文:线程创建请求的构建器。
36#[derive(Clone, Debug, Default)]
37#[non_exhaustive]
38pub struct CreateThreadRequestBuilder {
39    messages: Vec<Value>,
40    tool_resources: Option<Value>,
41    metadata: BTreeMap<String, String>,
42}
43
44impl CreateThreadRequestBuilder {
45    /// EN: Adds an initial message descriptor.
46    /// 中文:添加一个初始消息描述。
47    pub fn message(mut self, message: Value) -> Self {
48        self.messages.push(message);
49        self
50    }
51
52    /// EN: Replaces the initial message list.
53    /// 中文:替换初始消息列表。
54    pub fn messages(mut self, messages: impl IntoIterator<Item = Value>) -> Self {
55        self.messages = messages.into_iter().collect();
56        self
57    }
58
59    /// EN: Sets optional tool resources.
60    /// 中文:设置可选的工具资源。
61    pub fn tool_resources(mut self, tool_resources: Value) -> Self {
62        self.tool_resources = Some(tool_resources);
63        self
64    }
65
66    /// EN: Adds a metadata key/value pair.
67    /// 中文:添加一个元数据键值对。
68    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
69        self.metadata.insert(key.into(), value.into());
70        self
71    }
72
73    /// EN: Builds and validates the request.
74    /// 中文:构建并校验请求。
75    pub fn build(self) -> Result<CreateThreadRequest, LingerError> {
76        validate_messages(&self.messages)?;
77        validate_metadata(&self.metadata)?;
78        if self.tool_resources.as_ref().is_some_and(Value::is_null) {
79            return Err(LingerError::invalid_config(
80                "tool_resources must not be null",
81            ));
82        }
83        Ok(CreateThreadRequest {
84            messages: self.messages,
85            tool_resources: self.tool_resources,
86            metadata: self.metadata,
87        })
88    }
89}
90
91/// EN: Request body for `POST /v1/threads/{thread_id}`.
92/// 中文:`POST /v1/threads/{thread_id}` 的请求体。
93#[derive(Clone, Debug, Default, Serialize, PartialEq)]
94#[non_exhaustive]
95pub struct ModifyThreadRequest {
96    /// EN: Optional tool resources.
97    /// 中文:可选的工具资源。
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub tool_resources: Option<Value>,
100    /// EN: Optional metadata.
101    /// 中文:可选元数据。
102    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
103    pub metadata: BTreeMap<String, String>,
104}
105
106impl ModifyThreadRequest {
107    /// EN: Starts building a thread modification request.
108    /// 中文:开始构建线程修改请求。
109    pub fn builder() -> ModifyThreadRequestBuilder {
110        ModifyThreadRequestBuilder::default()
111    }
112}
113
114/// EN: Builder for thread modification requests.
115/// 中文:线程修改请求的构建器。
116#[derive(Clone, Debug, Default)]
117#[non_exhaustive]
118pub struct ModifyThreadRequestBuilder {
119    tool_resources: Option<Value>,
120    metadata: BTreeMap<String, String>,
121}
122
123impl ModifyThreadRequestBuilder {
124    /// EN: Sets optional tool resources.
125    /// 中文:设置可选的工具资源。
126    pub fn tool_resources(mut self, tool_resources: Value) -> Self {
127        self.tool_resources = Some(tool_resources);
128        self
129    }
130
131    /// EN: Adds a metadata key/value pair.
132    /// 中文:添加一个元数据键值对。
133    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
134        self.metadata.insert(key.into(), value.into());
135        self
136    }
137
138    /// EN: Builds and validates the request.
139    /// 中文:构建并校验请求。
140    pub fn build(self) -> Result<ModifyThreadRequest, LingerError> {
141        validate_metadata(&self.metadata)?;
142        if self.tool_resources.as_ref().is_some_and(Value::is_null) {
143            return Err(LingerError::invalid_config(
144                "tool_resources must not be null",
145            ));
146        }
147        Ok(ModifyThreadRequest {
148            tool_resources: self.tool_resources,
149            metadata: self.metadata,
150        })
151    }
152}
153
154/// EN: Request body for `POST /v1/threads/{thread_id}/messages`.
155/// 中文:`POST /v1/threads/{thread_id}/messages` 的请求体。
156#[derive(Clone, Debug, Serialize, PartialEq)]
157#[non_exhaustive]
158pub struct CreateThreadMessageRequest {
159    /// EN: Message role.
160    /// 中文:消息角色。
161    pub role: String,
162    /// EN: Message content.
163    /// 中文:消息内容。
164    pub content: Value,
165    /// EN: Optional file attachments.
166    /// 中文:可选文件附件。
167    #[serde(skip_serializing_if = "Vec::is_empty")]
168    pub attachments: Vec<Value>,
169    /// EN: Optional metadata.
170    /// 中文:可选元数据。
171    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
172    pub metadata: BTreeMap<String, String>,
173}
174
175impl CreateThreadMessageRequest {
176    /// EN: Starts building a thread message creation request.
177    /// 中文:开始构建线程消息创建请求。
178    pub fn builder() -> CreateThreadMessageRequestBuilder {
179        CreateThreadMessageRequestBuilder::default()
180    }
181}
182
183/// EN: Builder for thread message creation requests.
184/// 中文:线程消息创建请求的构建器。
185#[derive(Clone, Debug, Default)]
186#[non_exhaustive]
187pub struct CreateThreadMessageRequestBuilder {
188    role: Option<String>,
189    content: Option<Value>,
190    attachments: Vec<Value>,
191    metadata: BTreeMap<String, String>,
192}
193
194impl CreateThreadMessageRequestBuilder {
195    /// EN: Sets the message role.
196    /// 中文:设置消息角色。
197    pub fn role(mut self, role: impl Into<String>) -> Self {
198        self.role = Some(role.into());
199        self
200    }
201
202    /// EN: Sets text content.
203    /// 中文:设置文本内容。
204    pub fn content(mut self, content: impl Into<String>) -> Self {
205        self.content = Some(Value::String(content.into()));
206        self
207    }
208
209    /// EN: Sets raw JSON content.
210    /// 中文:设置原始 JSON 内容。
211    pub fn content_json(mut self, content: Value) -> Self {
212        self.content = Some(content);
213        self
214    }
215
216    /// EN: Adds an attachment descriptor.
217    /// 中文:添加一个附件描述。
218    pub fn attachment(mut self, attachment: Value) -> Self {
219        self.attachments.push(attachment);
220        self
221    }
222
223    /// EN: Adds a metadata key/value pair.
224    /// 中文:添加一个元数据键值对。
225    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
226        self.metadata.insert(key.into(), value.into());
227        self
228    }
229
230    /// EN: Builds and validates the request.
231    /// 中文:构建并校验请求。
232    pub fn build(self) -> Result<CreateThreadMessageRequest, LingerError> {
233        let role = required_string("role", self.role)?;
234        let content = self
235            .content
236            .filter(|value| !value.is_null())
237            .ok_or_else(|| LingerError::invalid_config("content is required"))?;
238        if self.attachments.iter().any(Value::is_null) {
239            return Err(LingerError::invalid_config(
240                "attachments must not contain null",
241            ));
242        }
243        validate_metadata(&self.metadata)?;
244        Ok(CreateThreadMessageRequest {
245            role,
246            content,
247            attachments: self.attachments,
248            metadata: self.metadata,
249        })
250    }
251}
252
253/// EN: Request body for `POST /v1/threads/{thread_id}/messages/{message_id}`.
254/// 中文:`POST /v1/threads/{thread_id}/messages/{message_id}` 的请求体。
255#[derive(Clone, Debug, Default, Serialize, PartialEq)]
256#[non_exhaustive]
257pub struct ModifyThreadMessageRequest {
258    /// EN: Optional metadata.
259    /// 中文:可选元数据。
260    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
261    pub metadata: BTreeMap<String, String>,
262}
263
264impl ModifyThreadMessageRequest {
265    /// EN: Starts building a thread message modification request.
266    /// 中文:开始构建线程消息修改请求。
267    pub fn builder() -> ModifyThreadMessageRequestBuilder {
268        ModifyThreadMessageRequestBuilder::default()
269    }
270}
271
272/// EN: Builder for thread message modification requests.
273/// 中文:线程消息修改请求的构建器。
274#[derive(Clone, Debug, Default)]
275#[non_exhaustive]
276pub struct ModifyThreadMessageRequestBuilder {
277    metadata: BTreeMap<String, String>,
278}
279
280impl ModifyThreadMessageRequestBuilder {
281    /// EN: Adds a metadata key/value pair.
282    /// 中文:添加一个元数据键值对。
283    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
284        self.metadata.insert(key.into(), value.into());
285        self
286    }
287
288    /// EN: Builds and validates the request.
289    /// 中文:构建并校验请求。
290    pub fn build(self) -> Result<ModifyThreadMessageRequest, LingerError> {
291        validate_metadata(&self.metadata)?;
292        Ok(ModifyThreadMessageRequest {
293            metadata: self.metadata,
294        })
295    }
296}
297
298/// EN: Request body for `POST /v1/threads/runs`.
299/// 中文:`POST /v1/threads/runs` 的请求体。
300#[derive(Clone, Debug, Serialize, PartialEq)]
301#[non_exhaustive]
302pub struct CreateThreadAndRunRequest {
303    /// EN: Assistant id used to execute the run.
304    /// 中文:用于执行 Run 的 Assistant ID。
305    pub assistant_id: String,
306    /// EN: Optional thread descriptor created with the run.
307    /// 中文:随 Run 一起创建的可选线程描述。
308    #[serde(skip_serializing_if = "Option::is_none")]
309    pub thread: Option<Value>,
310    /// EN: Optional model override for this run.
311    /// 中文:此 Run 的可选模型覆盖值。
312    #[serde(skip_serializing_if = "Option::is_none")]
313    pub model: Option<String>,
314    /// EN: Optional instruction override for this run.
315    /// 中文:此 Run 的可选指令覆盖值。
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub instructions: Option<String>,
318    /// EN: Optional tool overrides for this run.
319    /// 中文:此 Run 的可选工具覆盖值。
320    #[serde(skip_serializing_if = "Vec::is_empty")]
321    pub tools: Vec<Value>,
322    /// EN: Optional metadata.
323    /// 中文:可选元数据。
324    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
325    pub metadata: BTreeMap<String, String>,
326    /// EN: Forward-compatible optional fields not yet covered by handwritten types.
327    /// 中文:手写类型尚未覆盖的前向兼容可选字段。
328    #[serde(flatten)]
329    pub extra: BTreeMap<String, Value>,
330}
331
332impl CreateThreadAndRunRequest {
333    /// EN: Starts building a create-thread-and-run request.
334    /// 中文:开始构建创建线程并运行的请求。
335    pub fn builder() -> CreateThreadAndRunRequestBuilder {
336        CreateThreadAndRunRequestBuilder::default()
337    }
338}
339
340/// EN: Builder for create-thread-and-run requests.
341/// 中文:创建线程并运行请求的构建器。
342#[derive(Clone, Debug, Default)]
343#[non_exhaustive]
344pub struct CreateThreadAndRunRequestBuilder {
345    assistant_id: Option<String>,
346    thread: Option<Value>,
347    model: Option<String>,
348    instructions: Option<String>,
349    tools: Vec<Value>,
350    metadata: BTreeMap<String, String>,
351    extra: BTreeMap<String, Value>,
352}
353
354impl CreateThreadAndRunRequestBuilder {
355    /// EN: Sets the Assistant id.
356    /// 中文:设置 Assistant ID。
357    pub fn assistant_id(mut self, assistant_id: impl Into<String>) -> Self {
358        self.assistant_id = Some(assistant_id.into());
359        self
360    }
361
362    /// EN: Sets the thread descriptor to create.
363    /// 中文:设置要创建的线程描述。
364    pub fn thread(mut self, thread: Value) -> Self {
365        self.thread = Some(thread);
366        self
367    }
368
369    /// EN: Sets an optional model override.
370    /// 中文:设置可选模型覆盖值。
371    pub fn model(mut self, model: impl Into<String>) -> Self {
372        self.model = Some(model.into());
373        self
374    }
375
376    /// EN: Sets optional run instructions.
377    /// 中文:设置可选 Run 指令。
378    pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
379        self.instructions = Some(instructions.into());
380        self
381    }
382
383    /// EN: Adds a tool descriptor.
384    /// 中文:添加一个工具描述。
385    pub fn tool(mut self, tool: Value) -> Self {
386        self.tools.push(tool);
387        self
388    }
389
390    /// EN: Adds a metadata key/value pair.
391    /// 中文:添加一个元数据键值对。
392    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
393        self.metadata.insert(key.into(), value.into());
394        self
395    }
396
397    /// EN: Adds a forward-compatible JSON field.
398    /// 中文:添加前向兼容的 JSON 字段。
399    pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
400        self.extra.insert(name.into(), value);
401        self
402    }
403
404    /// EN: Builds and validates the request.
405    /// 中文:构建并校验请求。
406    pub fn build(self) -> Result<CreateThreadAndRunRequest, LingerError> {
407        let assistant_id = required_string("assistant_id", self.assistant_id)?;
408        if self.thread.as_ref().is_some_and(Value::is_null) {
409            return Err(LingerError::invalid_config("thread must not be null"));
410        }
411        validate_optional_string("model", &self.model)?;
412        validate_optional_string("instructions", &self.instructions)?;
413        validate_json_items("tools", &self.tools)?;
414        validate_metadata(&self.metadata)?;
415        validate_extra_fields(&self.extra)?;
416        Ok(CreateThreadAndRunRequest {
417            assistant_id,
418            thread: self.thread,
419            model: self.model,
420            instructions: self.instructions,
421            tools: self.tools,
422            metadata: self.metadata,
423            extra: self.extra,
424        })
425    }
426}
427
428/// EN: Request body for `POST /v1/threads/{thread_id}/runs`.
429/// 中文:`POST /v1/threads/{thread_id}/runs` 的请求体。
430#[derive(Clone, Debug, Serialize, PartialEq)]
431#[non_exhaustive]
432pub struct CreateThreadRunRequest {
433    /// EN: Assistant id used to execute the run.
434    /// 中文:用于执行 Run 的 Assistant ID。
435    pub assistant_id: String,
436    /// EN: Optional model override for this run.
437    /// 中文:此 Run 的可选模型覆盖值。
438    #[serde(skip_serializing_if = "Option::is_none")]
439    pub model: Option<String>,
440    /// EN: Optional instruction override for this run.
441    /// 中文:此 Run 的可选指令覆盖值。
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub instructions: Option<String>,
444    /// EN: Optional additional instructions appended for this run.
445    /// 中文:此 Run 追加的可选补充指令。
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub additional_instructions: Option<String>,
448    /// EN: Optional additional messages inserted before creating the run.
449    /// 中文:创建 Run 前插入的可选补充消息。
450    #[serde(skip_serializing_if = "Vec::is_empty")]
451    pub additional_messages: Vec<Value>,
452    /// EN: Optional tool overrides for this run.
453    /// 中文:此 Run 的可选工具覆盖值。
454    #[serde(skip_serializing_if = "Vec::is_empty")]
455    pub tools: Vec<Value>,
456    /// EN: Optional metadata.
457    /// 中文:可选元数据。
458    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
459    pub metadata: BTreeMap<String, String>,
460    /// EN: Forward-compatible optional fields not yet covered by handwritten types.
461    /// 中文:手写类型尚未覆盖的前向兼容可选字段。
462    #[serde(flatten)]
463    pub extra: BTreeMap<String, Value>,
464    #[serde(skip)]
465    include_file_search_result_content: bool,
466}
467
468impl CreateThreadRunRequest {
469    /// EN: Starts building a thread-run creation request.
470    /// 中文:开始构建线程 Run 创建请求。
471    pub fn builder() -> CreateThreadRunRequestBuilder {
472        CreateThreadRunRequestBuilder::default()
473    }
474
475    pub(crate) fn path(&self, thread_id: &str) -> String {
476        path_with_query(
477            &format!("/v1/threads/{thread_id}/runs"),
478            ThreadListQuery {
479                limit: None,
480                order: None,
481                after: None,
482                before: None,
483                run_id: None,
484                include_file_search_result_content: self.include_file_search_result_content,
485            },
486        )
487    }
488}
489
490/// EN: Builder for thread-run creation requests.
491/// 中文:线程 Run 创建请求的构建器。
492#[derive(Clone, Debug, Default)]
493#[non_exhaustive]
494pub struct CreateThreadRunRequestBuilder {
495    assistant_id: Option<String>,
496    model: Option<String>,
497    instructions: Option<String>,
498    additional_instructions: Option<String>,
499    additional_messages: Vec<Value>,
500    tools: Vec<Value>,
501    metadata: BTreeMap<String, String>,
502    extra: BTreeMap<String, Value>,
503    include_file_search_result_content: bool,
504}
505
506impl CreateThreadRunRequestBuilder {
507    /// EN: Sets the Assistant id.
508    /// 中文:设置 Assistant ID。
509    pub fn assistant_id(mut self, assistant_id: impl Into<String>) -> Self {
510        self.assistant_id = Some(assistant_id.into());
511        self
512    }
513
514    /// EN: Sets an optional model override.
515    /// 中文:设置可选模型覆盖值。
516    pub fn model(mut self, model: impl Into<String>) -> Self {
517        self.model = Some(model.into());
518        self
519    }
520
521    /// EN: Sets optional run instructions.
522    /// 中文:设置可选 Run 指令。
523    pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
524        self.instructions = Some(instructions.into());
525        self
526    }
527
528    /// EN: Sets optional additional run instructions.
529    /// 中文:设置可选的补充 Run 指令。
530    pub fn additional_instructions(mut self, additional_instructions: impl Into<String>) -> Self {
531        self.additional_instructions = Some(additional_instructions.into());
532        self
533    }
534
535    /// EN: Adds an additional message descriptor.
536    /// 中文:添加一个补充消息描述。
537    pub fn additional_message(mut self, message: Value) -> Self {
538        self.additional_messages.push(message);
539        self
540    }
541
542    /// EN: Replaces the additional message list.
543    /// 中文:替换补充消息列表。
544    pub fn additional_messages(mut self, messages: impl IntoIterator<Item = Value>) -> Self {
545        self.additional_messages = messages.into_iter().collect();
546        self
547    }
548
549    /// EN: Adds a tool descriptor.
550    /// 中文:添加一个工具描述。
551    pub fn tool(mut self, tool: Value) -> Self {
552        self.tools.push(tool);
553        self
554    }
555
556    /// EN: Replaces the tool descriptor list.
557    /// 中文:替换工具描述列表。
558    pub fn tools(mut self, tools: impl IntoIterator<Item = Value>) -> Self {
559        self.tools = tools.into_iter().collect();
560        self
561    }
562
563    /// EN: Adds a metadata key/value pair.
564    /// 中文:添加一个元数据键值对。
565    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
566        self.metadata.insert(key.into(), value.into());
567        self
568    }
569
570    /// EN: Adds a forward-compatible JSON field.
571    /// 中文:添加前向兼容的 JSON 字段。
572    pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
573        self.extra.insert(name.into(), value);
574        self
575    }
576
577    /// EN: Includes file search result content in step details for the created run.
578    /// 中文:在创建的 Run 的 step details 中包含 file search 结果内容。
579    pub fn include_file_search_result_content(mut self) -> Self {
580        self.include_file_search_result_content = true;
581        self
582    }
583
584    /// EN: Builds and validates the request.
585    /// 中文:构建并校验请求。
586    pub fn build(self) -> Result<CreateThreadRunRequest, LingerError> {
587        let assistant_id = required_string("assistant_id", self.assistant_id)?;
588        validate_optional_string("model", &self.model)?;
589        validate_optional_string("instructions", &self.instructions)?;
590        validate_optional_string("additional_instructions", &self.additional_instructions)?;
591        validate_json_items("additional_messages", &self.additional_messages)?;
592        validate_json_items("tools", &self.tools)?;
593        validate_metadata(&self.metadata)?;
594        validate_extra_fields(&self.extra)?;
595        Ok(CreateThreadRunRequest {
596            assistant_id,
597            model: self.model,
598            instructions: self.instructions,
599            additional_instructions: self.additional_instructions,
600            additional_messages: self.additional_messages,
601            tools: self.tools,
602            metadata: self.metadata,
603            extra: self.extra,
604            include_file_search_result_content: self.include_file_search_result_content,
605        })
606    }
607}
608
609/// EN: Request body for `POST /v1/threads/{thread_id}/runs/{run_id}`.
610/// 中文:`POST /v1/threads/{thread_id}/runs/{run_id}` 的请求体。
611#[derive(Clone, Debug, Default, Serialize, PartialEq)]
612#[non_exhaustive]
613pub struct ModifyThreadRunRequest {
614    /// EN: Optional metadata.
615    /// 中文:可选元数据。
616    #[serde(skip_serializing_if = "BTreeMap::is_empty")]
617    pub metadata: BTreeMap<String, String>,
618}
619
620impl ModifyThreadRunRequest {
621    /// EN: Starts building a thread-run modification request.
622    /// 中文:开始构建线程 Run 修改请求。
623    pub fn builder() -> ModifyThreadRunRequestBuilder {
624        ModifyThreadRunRequestBuilder::default()
625    }
626}
627
628/// EN: Builder for thread-run modification requests.
629/// 中文:线程 Run 修改请求的构建器。
630#[derive(Clone, Debug, Default)]
631#[non_exhaustive]
632pub struct ModifyThreadRunRequestBuilder {
633    metadata: BTreeMap<String, String>,
634}
635
636impl ModifyThreadRunRequestBuilder {
637    /// EN: Adds a metadata key/value pair.
638    /// 中文:添加一个元数据键值对。
639    pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
640        self.metadata.insert(key.into(), value.into());
641        self
642    }
643
644    /// EN: Builds and validates the request.
645    /// 中文:构建并校验请求。
646    pub fn build(self) -> Result<ModifyThreadRunRequest, LingerError> {
647        validate_metadata(&self.metadata)?;
648        Ok(ModifyThreadRunRequest {
649            metadata: self.metadata,
650        })
651    }
652}
653
654/// EN: Tool output item submitted to a waiting run.
655/// 中文:提交给等待中 Run 的工具输出项。
656#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
657#[non_exhaustive]
658pub struct SubmitToolOutput {
659    /// EN: Tool call id returned by the run.
660    /// 中文:Run 返回的工具调用 ID。
661    pub tool_call_id: String,
662    /// EN: Tool output string.
663    /// 中文:工具输出字符串。
664    pub output: String,
665}
666
667/// EN: Request body for `POST /v1/threads/{thread_id}/runs/{run_id}/submit_tool_outputs`.
668/// 中文:`POST /v1/threads/{thread_id}/runs/{run_id}/submit_tool_outputs` 的请求体。
669#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
670#[non_exhaustive]
671pub struct SubmitToolOutputsRequest {
672    /// EN: Tool outputs required by the run.
673    /// 中文:Run 所需的工具输出。
674    pub tool_outputs: Vec<SubmitToolOutput>,
675}
676
677impl SubmitToolOutputsRequest {
678    /// EN: Starts building a submit-tool-outputs request.
679    /// 中文:开始构建提交工具输出请求。
680    pub fn builder() -> SubmitToolOutputsRequestBuilder {
681        SubmitToolOutputsRequestBuilder::default()
682    }
683}
684
685/// EN: Builder for submit-tool-outputs requests.
686/// 中文:提交工具输出请求的构建器。
687#[derive(Clone, Debug, Default)]
688#[non_exhaustive]
689pub struct SubmitToolOutputsRequestBuilder {
690    tool_outputs: Vec<SubmitToolOutput>,
691}
692
693impl SubmitToolOutputsRequestBuilder {
694    /// EN: Adds a tool output.
695    /// 中文:添加一个工具输出。
696    pub fn tool_output(
697        mut self,
698        tool_call_id: impl Into<String>,
699        output: impl Into<String>,
700    ) -> Self {
701        self.tool_outputs.push(SubmitToolOutput {
702            tool_call_id: tool_call_id.into(),
703            output: output.into(),
704        });
705        self
706    }
707
708    /// EN: Builds and validates the request.
709    /// 中文:构建并校验请求。
710    pub fn build(self) -> Result<SubmitToolOutputsRequest, LingerError> {
711        if self.tool_outputs.is_empty() {
712            return Err(LingerError::invalid_config(
713                "tool_outputs must not be empty",
714            ));
715        }
716        for output in &self.tool_outputs {
717            if output.tool_call_id.trim().is_empty() {
718                return Err(LingerError::invalid_config("tool_call_id is required"));
719            }
720        }
721        Ok(SubmitToolOutputsRequest {
722            tool_outputs: self.tool_outputs,
723        })
724    }
725}
726
727/// EN: Thread object returned by the Threads API.
728/// 中文:Threads API 返回的 Thread 对象。
729#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
730#[non_exhaustive]
731pub struct Thread {
732    /// EN: Thread id.
733    /// 中文:Thread ID。
734    pub id: String,
735    /// EN: API object type.
736    /// 中文:API 对象类型。
737    pub object: String,
738    /// EN: Unix timestamp for creation.
739    /// 中文:创建时间的 Unix 时间戳。
740    pub created_at: u64,
741    /// EN: Tool resources returned by the API, when present.
742    /// 中文:API 返回的工具资源,如存在。
743    #[serde(default)]
744    pub tool_resources: Option<Value>,
745    /// EN: Metadata returned by the API.
746    /// 中文:API 返回的元数据。
747    #[serde(default)]
748    pub metadata: BTreeMap<String, String>,
749    /// EN: Additional fields preserved for forward compatibility.
750    /// 中文:为前向兼容保留的额外字段。
751    #[serde(flatten)]
752    pub extra: BTreeMap<String, Value>,
753    /// EN: OpenAI request id from response headers.
754    /// 中文:响应头中的 OpenAI 请求 ID。
755    #[serde(skip)]
756    request_id: Option<RequestId>,
757}
758
759/// EN: Thread message object returned by the Thread Messages API.
760/// 中文:Thread Messages API 返回的线程消息对象。
761#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
762#[non_exhaustive]
763pub struct ThreadMessage {
764    /// EN: Message id.
765    /// 中文:消息 ID。
766    pub id: String,
767    /// EN: API object type.
768    /// 中文:API 对象类型。
769    pub object: String,
770    /// EN: Unix timestamp for creation.
771    /// 中文:创建时间的 Unix 时间戳。
772    pub created_at: u64,
773    /// EN: Parent thread id.
774    /// 中文:父线程 ID。
775    pub thread_id: String,
776    /// EN: Message role.
777    /// 中文:消息角色。
778    pub role: String,
779    /// EN: Message content items.
780    /// 中文:消息内容项。
781    #[serde(default)]
782    pub content: Vec<Value>,
783    /// EN: Message status, when returned.
784    /// 中文:消息状态,如响应中存在。
785    #[serde(default)]
786    pub status: Option<String>,
787    /// EN: Incomplete details, when returned.
788    /// 中文:未完成详情,如响应中存在。
789    #[serde(default)]
790    pub incomplete_details: Option<Value>,
791    /// EN: Completed timestamp, when returned.
792    /// 中文:完成时间戳,如响应中存在。
793    #[serde(default)]
794    pub completed_at: Option<u64>,
795    /// EN: Incomplete timestamp, when returned.
796    /// 中文:未完成时间戳,如响应中存在。
797    #[serde(default)]
798    pub incomplete_at: Option<u64>,
799    /// EN: Assistant id, when returned.
800    /// 中文:Assistant ID,如响应中存在。
801    #[serde(default)]
802    pub assistant_id: Option<String>,
803    /// EN: Run id, when returned.
804    /// 中文:Run ID,如响应中存在。
805    #[serde(default)]
806    pub run_id: Option<String>,
807    /// EN: Attachment descriptors returned by the API.
808    /// 中文:API 返回的附件描述。
809    #[serde(default)]
810    pub attachments: Vec<Value>,
811    /// EN: Metadata returned by the API.
812    /// 中文:API 返回的元数据。
813    #[serde(default)]
814    pub metadata: BTreeMap<String, String>,
815    /// EN: Additional fields preserved for forward compatibility.
816    /// 中文:为前向兼容保留的额外字段。
817    #[serde(flatten)]
818    pub extra: BTreeMap<String, Value>,
819    /// EN: OpenAI request id from response headers.
820    /// 中文:响应头中的 OpenAI 请求 ID。
821    #[serde(skip)]
822    request_id: Option<RequestId>,
823}
824
825/// EN: Run object returned by the Thread Runs API.
826/// 中文:Thread Runs API 返回的 Run 对象。
827#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
828#[non_exhaustive]
829pub struct ThreadRun {
830    /// EN: Run id.
831    /// 中文:Run ID。
832    pub id: String,
833    /// EN: API object type.
834    /// 中文:API 对象类型。
835    pub object: String,
836    /// EN: Unix timestamp for creation.
837    /// 中文:创建时间的 Unix 时间戳。
838    pub created_at: u64,
839    /// EN: Parent thread id.
840    /// 中文:父线程 ID。
841    pub thread_id: String,
842    /// EN: Assistant id used by this run.
843    /// 中文:此 Run 使用的 Assistant ID。
844    pub assistant_id: String,
845    /// EN: Run status.
846    /// 中文:Run 状态。
847    pub status: String,
848    /// EN: Required action details, when the run is waiting for action.
849    /// 中文:Run 等待操作时的必需操作详情。
850    #[serde(default)]
851    pub required_action: Option<Value>,
852    /// EN: Last run error, when returned.
853    /// 中文:API 返回的最后一个 Run 错误。
854    #[serde(default)]
855    pub last_error: Option<Value>,
856    /// EN: Expiration timestamp, when returned.
857    /// 中文:过期时间戳,如响应中存在。
858    #[serde(default)]
859    pub expires_at: Option<u64>,
860    /// EN: Start timestamp, when returned.
861    /// 中文:开始时间戳,如响应中存在。
862    #[serde(default)]
863    pub started_at: Option<u64>,
864    /// EN: Cancellation timestamp, when returned.
865    /// 中文:取消时间戳,如响应中存在。
866    #[serde(default)]
867    pub cancelled_at: Option<u64>,
868    /// EN: Failure timestamp, when returned.
869    /// 中文:失败时间戳,如响应中存在。
870    #[serde(default)]
871    pub failed_at: Option<u64>,
872    /// EN: Completion timestamp, when returned.
873    /// 中文:完成时间戳,如响应中存在。
874    #[serde(default)]
875    pub completed_at: Option<u64>,
876    /// EN: Incomplete details, when returned.
877    /// 中文:未完成详情,如响应中存在。
878    #[serde(default)]
879    pub incomplete_details: Option<Value>,
880    /// EN: Model used by this run.
881    /// 中文:此 Run 使用的模型。
882    pub model: String,
883    /// EN: Run instructions, when returned.
884    /// 中文:Run 指令,如响应中存在。
885    #[serde(default)]
886    pub instructions: Option<String>,
887    /// EN: Tool descriptors returned by the API.
888    /// 中文:API 返回的工具描述。
889    #[serde(default)]
890    pub tools: Vec<Value>,
891    /// EN: Metadata returned by the API.
892    /// 中文:API 返回的元数据。
893    #[serde(default)]
894    pub metadata: BTreeMap<String, String>,
895    /// EN: Usage details, when returned.
896    /// 中文:用量详情,如响应中存在。
897    #[serde(default)]
898    pub usage: Option<Value>,
899    /// EN: Additional fields preserved for forward compatibility.
900    /// 中文:为前向兼容保留的额外字段。
901    #[serde(flatten)]
902    pub extra: BTreeMap<String, Value>,
903    /// EN: OpenAI request id from response headers.
904    /// 中文:响应头中的 OpenAI 请求 ID。
905    #[serde(skip)]
906    request_id: Option<RequestId>,
907}
908
909/// EN: Paginated thread-run list.
910/// 中文:分页线程 Run 列表。
911#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
912#[non_exhaustive]
913pub struct ThreadRunPage {
914    /// EN: API list object type.
915    /// 中文:API 列表对象类型。
916    pub object: String,
917    /// EN: Runs on this page.
918    /// 中文:本页的 Run。
919    #[serde(default)]
920    pub data: Vec<ThreadRun>,
921    /// EN: First run id on this page.
922    /// 中文:本页第一个 Run ID。
923    #[serde(default)]
924    pub first_id: Option<String>,
925    /// EN: Last run id on this page.
926    /// 中文:本页最后一个 Run ID。
927    #[serde(default)]
928    pub last_id: Option<String>,
929    /// EN: Whether more runs are available.
930    /// 中文:是否还有更多 Run。
931    pub has_more: bool,
932    /// EN: OpenAI request id from response headers.
933    /// 中文:响应头中的 OpenAI 请求 ID。
934    #[serde(skip)]
935    request_id: Option<RequestId>,
936}
937
938/// EN: Run step object returned by the Run Steps API.
939/// 中文:Run Steps API 返回的 Run Step 对象。
940#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
941#[non_exhaustive]
942pub struct RunStep {
943    /// EN: Run step id.
944    /// 中文:Run Step ID。
945    pub id: String,
946    /// EN: API object type.
947    /// 中文:API 对象类型。
948    pub object: String,
949    /// EN: Unix timestamp for creation.
950    /// 中文:创建时间的 Unix 时间戳。
951    pub created_at: u64,
952    /// EN: Assistant id associated with this step.
953    /// 中文:此 Step 关联的 Assistant ID。
954    pub assistant_id: String,
955    /// EN: Parent thread id.
956    /// 中文:父线程 ID。
957    pub thread_id: String,
958    /// EN: Parent run id.
959    /// 中文:父 Run ID。
960    pub run_id: String,
961    /// EN: Run step type.
962    /// 中文:Run Step 类型。
963    #[serde(rename = "type")]
964    pub kind: String,
965    /// EN: Run step status.
966    /// 中文:Run Step 状态。
967    pub status: String,
968    /// EN: Run step details.
969    /// 中文:Run Step 详情。
970    pub step_details: Value,
971    /// EN: Last step error, when returned.
972    /// 中文:API 返回的最后一个 Step 错误。
973    #[serde(default)]
974    pub last_error: Option<Value>,
975    /// EN: Expiration timestamp, when returned.
976    /// 中文:过期时间戳,如响应中存在。
977    #[serde(default)]
978    pub expired_at: Option<u64>,
979    /// EN: Cancellation timestamp, when returned.
980    /// 中文:取消时间戳,如响应中存在。
981    #[serde(default)]
982    pub cancelled_at: Option<u64>,
983    /// EN: Failure timestamp, when returned.
984    /// 中文:失败时间戳,如响应中存在。
985    #[serde(default)]
986    pub failed_at: Option<u64>,
987    /// EN: Completion timestamp, when returned.
988    /// 中文:完成时间戳,如响应中存在。
989    #[serde(default)]
990    pub completed_at: Option<u64>,
991    /// EN: Metadata returned by the API.
992    /// 中文:API 返回的元数据。
993    #[serde(default)]
994    pub metadata: BTreeMap<String, String>,
995    /// EN: Usage details, when returned.
996    /// 中文:用量详情,如响应中存在。
997    #[serde(default)]
998    pub usage: Option<Value>,
999    /// EN: Additional fields preserved for forward compatibility.
1000    /// 中文:为前向兼容保留的额外字段。
1001    #[serde(flatten)]
1002    pub extra: BTreeMap<String, Value>,
1003    /// EN: OpenAI request id from response headers.
1004    /// 中文:响应头中的 OpenAI 请求 ID。
1005    #[serde(skip)]
1006    request_id: Option<RequestId>,
1007}
1008
1009/// EN: Paginated run-step list.
1010/// 中文:分页 Run Step 列表。
1011#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
1012#[non_exhaustive]
1013pub struct RunStepPage {
1014    /// EN: API list object type.
1015    /// 中文:API 列表对象类型。
1016    pub object: String,
1017    /// EN: Run steps on this page.
1018    /// 中文:本页的 Run Step。
1019    #[serde(default)]
1020    pub data: Vec<RunStep>,
1021    /// EN: First run-step id on this page.
1022    /// 中文:本页第一个 Run Step ID。
1023    #[serde(default)]
1024    pub first_id: Option<String>,
1025    /// EN: Last run-step id on this page.
1026    /// 中文:本页最后一个 Run Step ID。
1027    #[serde(default)]
1028    pub last_id: Option<String>,
1029    /// EN: Whether more run steps are available.
1030    /// 中文:是否还有更多 Run Step。
1031    pub has_more: bool,
1032    /// EN: OpenAI request id from response headers.
1033    /// 中文:响应头中的 OpenAI 请求 ID。
1034    #[serde(skip)]
1035    request_id: Option<RequestId>,
1036}
1037
1038impl ThreadMessage {
1039    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1040        self.request_id = request_id;
1041        self
1042    }
1043
1044    /// EN: Returns the OpenAI request id, when present.
1045    /// 中文:返回 OpenAI 请求 ID,如存在。
1046    pub fn request_id(&self) -> Option<&RequestId> {
1047        self.request_id.as_ref()
1048    }
1049}
1050
1051impl ThreadRun {
1052    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1053        self.request_id = request_id;
1054        self
1055    }
1056
1057    /// EN: Returns the OpenAI request id, when present.
1058    /// 中文:返回 OpenAI 请求 ID,如存在。
1059    pub fn request_id(&self) -> Option<&RequestId> {
1060        self.request_id.as_ref()
1061    }
1062}
1063
1064impl ThreadRunPage {
1065    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1066        self.request_id = request_id;
1067        self
1068    }
1069
1070    /// EN: Returns the OpenAI request id, when present.
1071    /// 中文:返回 OpenAI 请求 ID,如存在。
1072    pub fn request_id(&self) -> Option<&RequestId> {
1073        self.request_id.as_ref()
1074    }
1075}
1076
1077/// EN: Sort order for thread run list pagination.
1078/// 中文:thread run 列表分页的排序方向。
1079#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1080#[non_exhaustive]
1081pub enum ThreadRunListOrder {
1082    /// EN: Ascending order.
1083    /// 中文:升序。
1084    Asc,
1085    /// EN: Descending order.
1086    /// 中文:降序。
1087    Desc,
1088}
1089
1090impl ThreadRunListOrder {
1091    pub(crate) fn as_query_value(self) -> &'static str {
1092        match self {
1093            Self::Asc => "asc",
1094            Self::Desc => "desc",
1095        }
1096    }
1097}
1098
1099/// EN: Query parameters for `GET /v1/threads/{thread_id}/runs`.
1100/// 中文:`GET /v1/threads/{thread_id}/runs` 的查询参数。
1101#[derive(Clone, Debug, Default, PartialEq, Eq)]
1102#[non_exhaustive]
1103pub struct ThreadRunListRequest {
1104    /// EN: Maximum number of runs to retrieve.
1105    /// 中文:要获取的最大 run 数量。
1106    pub limit: Option<u8>,
1107    /// EN: Sort order by creation timestamp.
1108    /// 中文:按创建时间戳排序的方向。
1109    pub order: Option<ThreadRunListOrder>,
1110    /// EN: Cursor after which the next page starts.
1111    /// 中文:下一页开始位置之前的游标。
1112    pub after: Option<String>,
1113    /// EN: Cursor before which the previous page starts.
1114    /// 中文:上一页开始位置之后的游标。
1115    pub before: Option<String>,
1116}
1117
1118impl ThreadRunListRequest {
1119    /// EN: Starts building thread run list query parameters.
1120    /// 中文:开始构建 thread run 列表查询参数。
1121    pub fn builder() -> ThreadRunListRequestBuilder {
1122        ThreadRunListRequestBuilder::default()
1123    }
1124
1125    pub(crate) fn path(&self, thread_id: &str) -> String {
1126        path_with_query(
1127            &format!("/v1/threads/{thread_id}/runs"),
1128            ThreadListQuery {
1129                limit: self.limit,
1130                order: self.order.map(ThreadRunListOrder::as_query_value),
1131                after: self.after.as_deref(),
1132                before: self.before.as_deref(),
1133                run_id: None,
1134                include_file_search_result_content: false,
1135            },
1136        )
1137    }
1138}
1139
1140/// EN: Builder for thread run list query parameters.
1141/// 中文:thread run 列表查询参数的构建器。
1142#[derive(Clone, Debug, Default)]
1143#[non_exhaustive]
1144pub struct ThreadRunListRequestBuilder {
1145    limit: Option<u8>,
1146    order: Option<ThreadRunListOrder>,
1147    after: Option<String>,
1148    before: Option<String>,
1149}
1150
1151impl ThreadRunListRequestBuilder {
1152    /// EN: Sets the maximum number of runs to retrieve.
1153    /// 中文:设置要获取的最大 run 数量。
1154    pub fn limit(mut self, limit: u8) -> Self {
1155        self.limit = Some(limit);
1156        self
1157    }
1158
1159    /// EN: Sets the sort order by creation timestamp.
1160    /// 中文:设置按创建时间戳排序的方向。
1161    pub fn order(mut self, order: ThreadRunListOrder) -> Self {
1162        self.order = Some(order);
1163        self
1164    }
1165
1166    /// EN: Sets the cursor after which the next page starts.
1167    /// 中文:设置下一页开始位置之前的游标。
1168    pub fn after(mut self, after: impl Into<String>) -> Self {
1169        self.after = Some(after.into());
1170        self
1171    }
1172
1173    /// EN: Sets the cursor before which the previous page starts.
1174    /// 中文:设置上一页开始位置之后的游标。
1175    pub fn before(mut self, before: impl Into<String>) -> Self {
1176        self.before = Some(before.into());
1177        self
1178    }
1179
1180    /// EN: Builds and validates the query parameters.
1181    /// 中文:构建并校验查询参数。
1182    pub fn build(self) -> Result<ThreadRunListRequest, LingerError> {
1183        if let Some(limit) = self.limit {
1184            if limit == 0 || limit > 100 {
1185                return Err(LingerError::invalid_config(
1186                    "limit must be between 1 and 100",
1187                ));
1188            }
1189        }
1190        validate_optional_cursor("after", self.after.as_deref())?;
1191        validate_optional_cursor("before", self.before.as_deref())?;
1192        Ok(ThreadRunListRequest {
1193            limit: self.limit,
1194            order: self.order,
1195            after: self.after,
1196            before: self.before,
1197        })
1198    }
1199}
1200
1201impl RunStep {
1202    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1203        self.request_id = request_id;
1204        self
1205    }
1206
1207    /// EN: Returns the OpenAI request id, when present.
1208    /// 中文:返回 OpenAI 请求 ID,如存在。
1209    pub fn request_id(&self) -> Option<&RequestId> {
1210        self.request_id.as_ref()
1211    }
1212}
1213
1214impl RunStepPage {
1215    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1216        self.request_id = request_id;
1217        self
1218    }
1219
1220    /// EN: Returns the OpenAI request id, when present.
1221    /// 中文:返回 OpenAI 请求 ID,如存在。
1222    pub fn request_id(&self) -> Option<&RequestId> {
1223        self.request_id.as_ref()
1224    }
1225}
1226
1227/// EN: Sort order for run step list pagination.
1228/// 中文:run step 列表分页的排序方向。
1229#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1230#[non_exhaustive]
1231pub enum RunStepListOrder {
1232    /// EN: Ascending order.
1233    /// 中文:升序。
1234    Asc,
1235    /// EN: Descending order.
1236    /// 中文:降序。
1237    Desc,
1238}
1239
1240impl RunStepListOrder {
1241    pub(crate) fn as_query_value(self) -> &'static str {
1242        match self {
1243            Self::Asc => "asc",
1244            Self::Desc => "desc",
1245        }
1246    }
1247}
1248
1249/// EN: Query parameters for `GET /v1/threads/{thread_id}/runs/{run_id}/steps`.
1250/// 中文:`GET /v1/threads/{thread_id}/runs/{run_id}/steps` 的查询参数。
1251#[derive(Clone, Debug, Default, PartialEq, Eq)]
1252#[non_exhaustive]
1253pub struct RunStepListRequest {
1254    /// EN: Maximum number of run steps to retrieve.
1255    /// 中文:要获取的最大 run step 数量。
1256    pub limit: Option<u8>,
1257    /// EN: Sort order by creation timestamp.
1258    /// 中文:按创建时间戳排序的方向。
1259    pub order: Option<RunStepListOrder>,
1260    /// EN: Cursor after which the next page starts.
1261    /// 中文:下一页开始位置之前的游标。
1262    pub after: Option<String>,
1263    /// EN: Cursor before which the previous page starts.
1264    /// 中文:上一页开始位置之后的游标。
1265    pub before: Option<String>,
1266    include_file_search_result_content: bool,
1267}
1268
1269impl RunStepListRequest {
1270    /// EN: Starts building run step list query parameters.
1271    /// 中文:开始构建 run step 列表查询参数。
1272    pub fn builder() -> RunStepListRequestBuilder {
1273        RunStepListRequestBuilder::default()
1274    }
1275
1276    pub(crate) fn path(&self, thread_id: &str, run_id: &str) -> String {
1277        path_with_query(
1278            &format!("/v1/threads/{thread_id}/runs/{run_id}/steps"),
1279            ThreadListQuery {
1280                limit: self.limit,
1281                order: self.order.map(RunStepListOrder::as_query_value),
1282                after: self.after.as_deref(),
1283                before: self.before.as_deref(),
1284                run_id: None,
1285                include_file_search_result_content: self.include_file_search_result_content,
1286            },
1287        )
1288    }
1289}
1290
1291/// EN: Builder for run step list query parameters.
1292/// 中文:run step 列表查询参数的构建器。
1293#[derive(Clone, Debug, Default)]
1294#[non_exhaustive]
1295pub struct RunStepListRequestBuilder {
1296    limit: Option<u8>,
1297    order: Option<RunStepListOrder>,
1298    after: Option<String>,
1299    before: Option<String>,
1300    include_file_search_result_content: bool,
1301}
1302
1303impl RunStepListRequestBuilder {
1304    /// EN: Sets the maximum number of run steps to retrieve.
1305    /// 中文:设置要获取的最大 run step 数量。
1306    pub fn limit(mut self, limit: u8) -> Self {
1307        self.limit = Some(limit);
1308        self
1309    }
1310
1311    /// EN: Sets the sort order by creation timestamp.
1312    /// 中文:设置按创建时间戳排序的方向。
1313    pub fn order(mut self, order: RunStepListOrder) -> Self {
1314        self.order = Some(order);
1315        self
1316    }
1317
1318    /// EN: Sets the cursor after which the next page starts.
1319    /// 中文:设置下一页开始位置之前的游标。
1320    pub fn after(mut self, after: impl Into<String>) -> Self {
1321        self.after = Some(after.into());
1322        self
1323    }
1324
1325    /// EN: Sets the cursor before which the previous page starts.
1326    /// 中文:设置上一页开始位置之后的游标。
1327    pub fn before(mut self, before: impl Into<String>) -> Self {
1328        self.before = Some(before.into());
1329        self
1330    }
1331
1332    /// EN: Includes file search result content in step details.
1333    /// 中文:在 step details 中包含 file search 结果内容。
1334    pub fn include_file_search_result_content(mut self) -> Self {
1335        self.include_file_search_result_content = true;
1336        self
1337    }
1338
1339    /// EN: Builds and validates the query parameters.
1340    /// 中文:构建并校验查询参数。
1341    pub fn build(self) -> Result<RunStepListRequest, LingerError> {
1342        if let Some(limit) = self.limit {
1343            if limit == 0 || limit > 100 {
1344                return Err(LingerError::invalid_config(
1345                    "limit must be between 1 and 100",
1346                ));
1347            }
1348        }
1349        validate_optional_cursor("after", self.after.as_deref())?;
1350        validate_optional_cursor("before", self.before.as_deref())?;
1351        Ok(RunStepListRequest {
1352            limit: self.limit,
1353            order: self.order,
1354            after: self.after,
1355            before: self.before,
1356            include_file_search_result_content: self.include_file_search_result_content,
1357        })
1358    }
1359}
1360
1361/// EN: Query parameters for `GET /v1/threads/{thread_id}/runs/{run_id}/steps/{step_id}`.
1362/// 中文:`GET /v1/threads/{thread_id}/runs/{run_id}/steps/{step_id}` 的查询参数。
1363#[derive(Clone, Debug, Default, PartialEq, Eq)]
1364#[non_exhaustive]
1365pub struct RunStepRetrieveRequest {
1366    include_file_search_result_content: bool,
1367}
1368
1369impl RunStepRetrieveRequest {
1370    /// EN: Starts building run step retrieve query parameters.
1371    /// 中文:开始构建 run step 获取查询参数。
1372    pub fn builder() -> RunStepRetrieveRequestBuilder {
1373        RunStepRetrieveRequestBuilder::default()
1374    }
1375
1376    pub(crate) fn path(&self, thread_id: &str, run_id: &str, step_id: &str) -> String {
1377        path_with_query(
1378            &format!("/v1/threads/{thread_id}/runs/{run_id}/steps/{step_id}"),
1379            ThreadListQuery {
1380                limit: None,
1381                order: None,
1382                after: None,
1383                before: None,
1384                run_id: None,
1385                include_file_search_result_content: self.include_file_search_result_content,
1386            },
1387        )
1388    }
1389}
1390
1391/// EN: Builder for run step retrieve query parameters.
1392/// 中文:run step 获取查询参数的构建器。
1393#[derive(Clone, Debug, Default)]
1394#[non_exhaustive]
1395pub struct RunStepRetrieveRequestBuilder {
1396    include_file_search_result_content: bool,
1397}
1398
1399impl RunStepRetrieveRequestBuilder {
1400    /// EN: Includes file search result content in step details.
1401    /// 中文:在 step details 中包含 file search 结果内容。
1402    pub fn include_file_search_result_content(mut self) -> Self {
1403        self.include_file_search_result_content = true;
1404        self
1405    }
1406
1407    /// EN: Builds the query parameters.
1408    /// 中文:构建查询参数。
1409    pub fn build(self) -> RunStepRetrieveRequest {
1410        RunStepRetrieveRequest {
1411            include_file_search_result_content: self.include_file_search_result_content,
1412        }
1413    }
1414}
1415
1416/// EN: Paginated thread message list.
1417/// 中文:分页线程消息列表。
1418#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
1419#[non_exhaustive]
1420pub struct ThreadMessagePage {
1421    /// EN: API list object type.
1422    /// 中文:API 列表对象类型。
1423    pub object: String,
1424    /// EN: Messages on this page.
1425    /// 中文:本页消息。
1426    #[serde(default)]
1427    pub data: Vec<ThreadMessage>,
1428    /// EN: First message id on this page.
1429    /// 中文:本页第一个消息 ID。
1430    #[serde(default)]
1431    pub first_id: Option<String>,
1432    /// EN: Last message id on this page.
1433    /// 中文:本页最后一个消息 ID。
1434    #[serde(default)]
1435    pub last_id: Option<String>,
1436    /// EN: Whether more messages are available.
1437    /// 中文:是否还有更多消息。
1438    pub has_more: bool,
1439    /// EN: OpenAI request id from response headers.
1440    /// 中文:响应头中的 OpenAI 请求 ID。
1441    #[serde(skip)]
1442    request_id: Option<RequestId>,
1443}
1444
1445impl ThreadMessagePage {
1446    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1447        self.request_id = request_id;
1448        self
1449    }
1450
1451    /// EN: Returns the OpenAI request id, when present.
1452    /// 中文:返回 OpenAI 请求 ID,如存在。
1453    pub fn request_id(&self) -> Option<&RequestId> {
1454        self.request_id.as_ref()
1455    }
1456}
1457
1458/// EN: Sort order for thread message list pagination.
1459/// 中文:thread message 列表分页的排序方向。
1460#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1461#[non_exhaustive]
1462pub enum ThreadMessageListOrder {
1463    /// EN: Ascending order.
1464    /// 中文:升序。
1465    Asc,
1466    /// EN: Descending order.
1467    /// 中文:降序。
1468    Desc,
1469}
1470
1471impl ThreadMessageListOrder {
1472    pub(crate) fn as_query_value(self) -> &'static str {
1473        match self {
1474            Self::Asc => "asc",
1475            Self::Desc => "desc",
1476        }
1477    }
1478}
1479
1480/// EN: Query parameters for `GET /v1/threads/{thread_id}/messages`.
1481/// 中文:`GET /v1/threads/{thread_id}/messages` 的查询参数。
1482#[derive(Clone, Debug, Default, PartialEq, Eq)]
1483#[non_exhaustive]
1484pub struct ThreadMessageListRequest {
1485    /// EN: Maximum number of messages to retrieve.
1486    /// 中文:要获取的最大 message 数量。
1487    pub limit: Option<u8>,
1488    /// EN: Sort order by creation timestamp.
1489    /// 中文:按创建时间戳排序的方向。
1490    pub order: Option<ThreadMessageListOrder>,
1491    /// EN: Cursor after which the next page starts.
1492    /// 中文:下一页开始位置之前的游标。
1493    pub after: Option<String>,
1494    /// EN: Cursor before which the previous page starts.
1495    /// 中文:上一页开始位置之后的游标。
1496    pub before: Option<String>,
1497    /// EN: Optional run id used to filter generated messages.
1498    /// 中文:用于过滤生成消息的可选 run ID。
1499    pub run_id: Option<String>,
1500}
1501
1502impl ThreadMessageListRequest {
1503    /// EN: Starts building thread message list query parameters.
1504    /// 中文:开始构建 thread message 列表查询参数。
1505    pub fn builder() -> ThreadMessageListRequestBuilder {
1506        ThreadMessageListRequestBuilder::default()
1507    }
1508
1509    pub(crate) fn path(&self, thread_id: &str) -> String {
1510        path_with_query(
1511            &format!("/v1/threads/{thread_id}/messages"),
1512            ThreadListQuery {
1513                limit: self.limit,
1514                order: self.order.map(ThreadMessageListOrder::as_query_value),
1515                after: self.after.as_deref(),
1516                before: self.before.as_deref(),
1517                run_id: self.run_id.as_deref(),
1518                include_file_search_result_content: false,
1519            },
1520        )
1521    }
1522}
1523
1524/// EN: Builder for thread message list query parameters.
1525/// 中文:thread message 列表查询参数的构建器。
1526#[derive(Clone, Debug, Default)]
1527#[non_exhaustive]
1528pub struct ThreadMessageListRequestBuilder {
1529    limit: Option<u8>,
1530    order: Option<ThreadMessageListOrder>,
1531    after: Option<String>,
1532    before: Option<String>,
1533    run_id: Option<String>,
1534}
1535
1536impl ThreadMessageListRequestBuilder {
1537    /// EN: Sets the maximum number of messages to retrieve.
1538    /// 中文:设置要获取的最大 message 数量。
1539    pub fn limit(mut self, limit: u8) -> Self {
1540        self.limit = Some(limit);
1541        self
1542    }
1543
1544    /// EN: Sets the sort order by creation timestamp.
1545    /// 中文:设置按创建时间戳排序的方向。
1546    pub fn order(mut self, order: ThreadMessageListOrder) -> Self {
1547        self.order = Some(order);
1548        self
1549    }
1550
1551    /// EN: Sets the cursor after which the next page starts.
1552    /// 中文:设置下一页开始位置之前的游标。
1553    pub fn after(mut self, after: impl Into<String>) -> Self {
1554        self.after = Some(after.into());
1555        self
1556    }
1557
1558    /// EN: Sets the cursor before which the previous page starts.
1559    /// 中文:设置上一页开始位置之后的游标。
1560    pub fn before(mut self, before: impl Into<String>) -> Self {
1561        self.before = Some(before.into());
1562        self
1563    }
1564
1565    /// EN: Filters messages by the run id that generated them.
1566    /// 中文:按生成这些 message 的 run ID 过滤。
1567    pub fn run_id(mut self, run_id: impl Into<String>) -> Self {
1568        self.run_id = Some(run_id.into());
1569        self
1570    }
1571
1572    /// EN: Builds and validates the query parameters.
1573    /// 中文:构建并校验查询参数。
1574    pub fn build(self) -> Result<ThreadMessageListRequest, LingerError> {
1575        if let Some(limit) = self.limit {
1576            if limit == 0 || limit > 100 {
1577                return Err(LingerError::invalid_config(
1578                    "limit must be between 1 and 100",
1579                ));
1580            }
1581        }
1582        validate_optional_cursor("after", self.after.as_deref())?;
1583        validate_optional_cursor("before", self.before.as_deref())?;
1584        validate_optional_cursor("run_id", self.run_id.as_deref())?;
1585        Ok(ThreadMessageListRequest {
1586            limit: self.limit,
1587            order: self.order,
1588            after: self.after,
1589            before: self.before,
1590            run_id: self.run_id,
1591        })
1592    }
1593}
1594
1595/// EN: Deletion result returned by the Thread Messages API.
1596/// 中文:Thread Messages API 返回的删除结果。
1597#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1598#[non_exhaustive]
1599pub struct ThreadMessageDeletion {
1600    /// EN: Deleted message id.
1601    /// 中文:已删除的消息 ID。
1602    pub id: String,
1603    /// EN: API object type.
1604    /// 中文:API 对象类型。
1605    pub object: String,
1606    /// EN: Whether the message was deleted.
1607    /// 中文:消息是否已删除。
1608    pub deleted: bool,
1609    /// EN: OpenAI request id from response headers.
1610    /// 中文:响应头中的 OpenAI 请求 ID。
1611    #[serde(skip)]
1612    request_id: Option<RequestId>,
1613}
1614
1615impl ThreadMessageDeletion {
1616    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1617        self.request_id = request_id;
1618        self
1619    }
1620
1621    /// EN: Returns the OpenAI request id, when present.
1622    /// 中文:返回 OpenAI 请求 ID,如存在。
1623    pub fn request_id(&self) -> Option<&RequestId> {
1624        self.request_id.as_ref()
1625    }
1626}
1627
1628impl Thread {
1629    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1630        self.request_id = request_id;
1631        self
1632    }
1633
1634    /// EN: Returns the OpenAI request id, when present.
1635    /// 中文:返回 OpenAI 请求 ID,如存在。
1636    pub fn request_id(&self) -> Option<&RequestId> {
1637        self.request_id.as_ref()
1638    }
1639}
1640
1641/// EN: Deletion result returned by the Threads API.
1642/// 中文:Threads API 返回的删除结果。
1643#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
1644#[non_exhaustive]
1645pub struct ThreadDeletion {
1646    /// EN: Deleted Thread id.
1647    /// 中文:已删除的 Thread ID。
1648    pub id: String,
1649    /// EN: API object type.
1650    /// 中文:API 对象类型。
1651    pub object: String,
1652    /// EN: Whether the Thread was deleted.
1653    /// 中文:Thread 是否已删除。
1654    pub deleted: bool,
1655    /// EN: OpenAI request id from response headers.
1656    /// 中文:响应头中的 OpenAI 请求 ID。
1657    #[serde(skip)]
1658    request_id: Option<RequestId>,
1659}
1660
1661impl ThreadDeletion {
1662    pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
1663        self.request_id = request_id;
1664        self
1665    }
1666
1667    /// EN: Returns the OpenAI request id, when present.
1668    /// 中文:返回 OpenAI 请求 ID,如存在。
1669    pub fn request_id(&self) -> Option<&RequestId> {
1670        self.request_id.as_ref()
1671    }
1672}
1673
1674fn validate_messages(messages: &[Value]) -> Result<(), LingerError> {
1675    if messages.iter().any(Value::is_null) {
1676        return Err(LingerError::invalid_config(
1677            "messages must not contain null",
1678        ));
1679    }
1680    Ok(())
1681}
1682
1683fn validate_json_items(name: &str, values: &[Value]) -> Result<(), LingerError> {
1684    if values.iter().any(Value::is_null) {
1685        return Err(LingerError::invalid_config(format!(
1686            "{name} must not contain null"
1687        )));
1688    }
1689    Ok(())
1690}
1691
1692fn validate_metadata(metadata: &BTreeMap<String, String>) -> Result<(), LingerError> {
1693    for key in metadata.keys() {
1694        if key.trim().is_empty() {
1695            return Err(LingerError::invalid_config(
1696                "metadata keys must not be empty",
1697            ));
1698        }
1699    }
1700    Ok(())
1701}
1702
1703fn validate_extra_fields(extra: &BTreeMap<String, Value>) -> Result<(), LingerError> {
1704    for (key, value) in extra {
1705        if key.trim().is_empty() {
1706            return Err(LingerError::invalid_config(
1707                "extra field names must not be empty",
1708            ));
1709        }
1710        if value.is_null() {
1711            return Err(LingerError::invalid_config(format!(
1712                "extra field {key} must not be null"
1713            )));
1714        }
1715    }
1716    Ok(())
1717}
1718
1719fn validate_optional_string(name: &str, value: &Option<String>) -> Result<(), LingerError> {
1720    if value.as_ref().is_some_and(|value| value.trim().is_empty()) {
1721        return Err(LingerError::invalid_config(format!(
1722            "{name} must not be empty"
1723        )));
1724    }
1725    Ok(())
1726}
1727
1728fn required_string(name: &str, value: Option<String>) -> Result<String, LingerError> {
1729    value
1730        .filter(|value| !value.trim().is_empty())
1731        .ok_or_else(|| LingerError::invalid_config(format!("{name} is required")))
1732}
1733
1734fn validate_optional_cursor(name: &str, value: Option<&str>) -> Result<(), LingerError> {
1735    if value.is_some_and(|value| value.trim().is_empty()) {
1736        return Err(LingerError::invalid_config(format!(
1737            "{name} must not be empty"
1738        )));
1739    }
1740    Ok(())
1741}
1742
1743struct ThreadListQuery<'a> {
1744    limit: Option<u8>,
1745    order: Option<&'static str>,
1746    after: Option<&'a str>,
1747    before: Option<&'a str>,
1748    run_id: Option<&'a str>,
1749    include_file_search_result_content: bool,
1750}
1751
1752fn path_with_query(base: &str, params: ThreadListQuery<'_>) -> String {
1753    let mut query = Vec::new();
1754    if let Some(limit) = params.limit {
1755        query.push(format!("limit={limit}"));
1756    }
1757    if let Some(order) = params.order {
1758        query.push(format!("order={order}"));
1759    }
1760    if let Some(after) = params.after {
1761        query.push(format!("after={}", encode_query_value(after)));
1762    }
1763    if let Some(before) = params.before {
1764        query.push(format!("before={}", encode_query_value(before)));
1765    }
1766    if let Some(run_id) = params.run_id {
1767        query.push(format!("run_id={}", encode_query_value(run_id)));
1768    }
1769    if params.include_file_search_result_content {
1770        query.push(format!(
1771            "include[]={}",
1772            encode_include_query_value("step_details.tool_calls[*].file_search.results[*].content")
1773        ));
1774    }
1775    if query.is_empty() {
1776        base.to_string()
1777    } else {
1778        format!("{base}?{}", query.join("&"))
1779    }
1780}
1781
1782fn encode_query_value(value: &str) -> String {
1783    encode_query_value_inner(value, false)
1784}
1785
1786fn encode_include_query_value(value: &str) -> String {
1787    encode_query_value_inner(value, true)
1788}
1789
1790fn encode_query_value_inner(value: &str, preserve_wildcards: bool) -> String {
1791    let mut encoded = String::new();
1792    for byte in value.bytes() {
1793        match byte {
1794            b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
1795                encoded.push(byte as char);
1796            }
1797            b'*' if preserve_wildcards => encoded.push('*'),
1798            _ => {
1799                const HEX: &[u8; 16] = b"0123456789ABCDEF";
1800                encoded.push('%');
1801                encoded.push(HEX[(byte >> 4) as usize] as char);
1802                encoded.push(HEX[(byte & 0x0F) as usize] as char);
1803            }
1804        }
1805    }
1806    encoded
1807}