rusty_openai/openai_api/
threads.rs

1use crate::{error_handling::OpenAIResult, extend_url_params, openai::OpenAI, setters};
2use serde::Serialize;
3use serde_json::{json, Value};
4
5/// [`ThreadsApi`] struct to interact with thread management endpoints of the API.
6pub struct ThreadsApi<'a>(pub(crate) &'a OpenAI<'a>);
7
8/// Struct representing a request to create a thread.
9#[derive(Default, Serialize)]
10pub struct ThreadCreationRequest {
11    /// Optional list of messages in the thread
12    #[serde(skip_serializing_if = "Option::is_none")]
13    messages: Option<Vec<Value>>,
14
15    /// Optional tool resources related to the thread
16    #[serde(skip_serializing_if = "Option::is_none")]
17    tool_resources: Option<Value>,
18
19    /// Optional metadata associated with the thread
20    #[serde(skip_serializing_if = "Option::is_none")]
21    metadata: Option<Value>,
22}
23
24/// Struct representing a request to modify a thread.
25#[derive(Default, Serialize)]
26pub struct ThreadModificationRequest {
27    /// Optional tool resources related to the thread
28    #[serde(skip_serializing_if = "Option::is_none")]
29    tool_resources: Option<Value>,
30
31    /// Optional metadata associated with the thread
32    #[serde(skip_serializing_if = "Option::is_none")]
33    metadata: Option<Value>,
34}
35
36impl ThreadCreationRequest {
37    setters! {
38        /// Set messages for the thread.
39        messages: Vec<Value>,
40
41        /// Set tool resources for the thread.
42        tool_resources: Value,
43
44        /// Set metadata for the thread.
45        metadata: Value,
46    }
47}
48
49impl ThreadModificationRequest {
50    setters! {
51        /// Set tool resources for the thread.
52        tool_resources: Value,
53
54        /// Set metadata for the thread.
55        metadata: Value,
56    }
57}
58
59#[derive(Serialize)]
60struct CreateMessageRequest<'a> {
61    /// The role of the message sender.
62    role: &'a str,
63
64    /// The content of the message.
65    content: Value,
66
67    /// Optional attachments for the message.
68    #[serde(skip_serializing_if = "Option::is_none")]
69    attachments: Option<Value>,
70
71    /// Optional metadata for the message.
72    #[serde(skip_serializing_if = "Option::is_none")]
73    metadata: Option<Value>,
74}
75
76#[derive(Serialize)]
77struct ModifyMessageRequest {
78    /// The new metadata to apply to the message.
79    metadata: Value,
80}
81
82#[derive(Serialize)]
83struct CreateRunRequest<'a> {
84    assistant_id: &'a str,
85
86    #[serde(skip_serializing_if = "Option::is_none")]
87    model: Option<&'a str>,
88
89    #[serde(skip_serializing_if = "Option::is_none")]
90    instructions: Option<&'a str>,
91
92    #[serde(skip_serializing_if = "Option::is_none")]
93    additional_instructions: Option<&'a str>,
94
95    #[serde(skip_serializing_if = "Option::is_none")]
96    additional_messages: Option<Vec<Value>>,
97
98    #[serde(skip_serializing_if = "Option::is_none")]
99    tools: Option<Vec<Value>>,
100
101    #[serde(skip_serializing_if = "Option::is_none")]
102    metadata: Option<Value>,
103
104    #[serde(skip_serializing_if = "Option::is_none")]
105    temperature: Option<f64>,
106
107    #[serde(skip_serializing_if = "Option::is_none")]
108    top_p: Option<f64>,
109
110    #[serde(skip_serializing_if = "Option::is_none")]
111    stream: Option<bool>,
112
113    #[serde(skip_serializing_if = "Option::is_none")]
114    max_prompt_tokens: Option<u32>,
115
116    #[serde(skip_serializing_if = "Option::is_none")]
117    max_completion_tokens: Option<u32>,
118
119    #[serde(skip_serializing_if = "Option::is_none")]
120    truncation_strategy: Option<Value>,
121
122    #[serde(skip_serializing_if = "Option::is_none")]
123    tool_choice: Option<Value>,
124
125    #[serde(skip_serializing_if = "Option::is_none")]
126    parallel_tool_calls: Option<bool>,
127
128    #[serde(skip_serializing_if = "Option::is_none")]
129    response_format: Option<Value>,
130}
131
132#[derive(Serialize)]
133struct SubmitToolRequest {
134    /// List of tool outputs to submit.
135    tool_outputs: Vec<Value>,
136
137    /// Optional stream parameter for the submission.
138    #[serde(skip_serializing_if = "Option::is_none")]
139    stream: Option<bool>,
140}
141
142impl<'a> ThreadsApi<'a> {
143    /// Create a new thread with the provided request parameters.
144    ///
145    /// # Arguments
146    ///
147    /// * `request` - A [`ThreadCreationRequest`] containing the parameters for the new thread.
148    ///
149    /// # Returns
150    ///
151    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
152    pub async fn create(&self, request: ThreadCreationRequest) -> OpenAIResult<Value> {
153        self.0.post_json("/threads", &request).await
154    }
155
156    /// Retrieve the details of a specific thread by its ID.
157    ///
158    /// # Arguments
159    ///
160    /// * `thread_id` - The ID of the thread to be retrieved.
161    ///
162    /// # Returns
163    ///
164    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
165    pub async fn retrieve(&self, thread_id: &str) -> OpenAIResult<Value> {
166        let url = format!("/threads/{thread_id}");
167
168        self.0.get(&url).await
169    }
170
171    /// Modify an existing thread's details using the provided request parameters.
172    ///
173    /// # Arguments
174    ///
175    /// * `thread_id` - The ID of the thread to be modified.
176    /// * `request` - A [`ThreadModificationRequest`] containing the modification parameters.
177    ///
178    /// # Returns
179    ///
180    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
181    pub async fn modify(
182        &self,
183        thread_id: &str,
184        request: ThreadModificationRequest,
185    ) -> OpenAIResult<Value> {
186        let url = format!("/threads/{thread_id}");
187
188        self.0.post_json(&url, &request).await
189    }
190
191    /// Delete a specific thread by its ID.
192    ///
193    /// # Arguments
194    ///
195    /// * `thread_id` - The ID of the thread to be deleted.
196    ///
197    /// # Returns
198    ///
199    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
200    pub async fn delete(&self, thread_id: &str) -> OpenAIResult<Value> {
201        let url = format!("/threads/{thread_id}");
202
203        self.0.delete(&url).await
204    }
205
206    /// Create a new message in a specific thread.
207    ///
208    /// # Arguments
209    ///
210    /// * `thread_id` - The ID of the thread to add a message to.
211    /// * `role` - The role of the message sender.
212    /// * `content` - The content of the message.
213    /// * `attachments` - Optional attachments for the message.
214    /// * `metadata` - Optional metadata for the message.
215    ///
216    /// # Returns
217    ///
218    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
219    pub async fn create_message(
220        &self,
221        thread_id: &str,
222        role: &str,
223        content: Value,
224        attachments: Option<Value>,
225        metadata: Option<Value>,
226    ) -> OpenAIResult<Value> {
227        let url = format!("/threads/{thread_id}/messages");
228        let body = CreateMessageRequest {
229            role,
230            content,
231            attachments,
232            metadata,
233        };
234
235        self.0.post_json(&url, &body).await
236    }
237
238    /// List messages in a specific thread with optional filters.
239    ///
240    /// # Arguments
241    ///
242    /// * `thread_id` - The ID of the thread to list messages from.
243    /// * `limit` - Optional limit on the number of messages to list.
244    /// * `order` - Optional order parameter for the message listing.
245    /// * `after` - Optional parameter to list messages after a specific time.
246    /// * `before` - Optional parameter to list messages before a specific time.
247    ///
248    /// # Returns
249    ///
250    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
251    pub async fn list_messages(
252        &self,
253        thread_id: &str,
254        limit: Option<u32>,
255        order: Option<&str>,
256        after: Option<&str>,
257        before: Option<&str>,
258    ) -> OpenAIResult<Value> {
259        let mut url = format!("/threads/{thread_id}/messages?");
260
261        extend_url_params!(url, limit, order, after, before);
262        url.pop();
263
264        self.0.get(&url).await
265    }
266
267    /// Retrieve a specific message by its ID from a thread.
268    ///
269    /// # Arguments
270    ///
271    /// * `thread_id` - The ID of the thread to retrieve the message from.
272    /// * `message_id` - The ID of the message to retrieve.
273    ///
274    /// # Returns
275    ///
276    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
277    pub async fn retrieve_message(&self, thread_id: &str, message_id: &str) -> OpenAIResult<Value> {
278        let url = format!("/threads/{thread_id}/messages/{message_id}");
279
280        self.0.get(&url).await
281    }
282
283    /// Modify a specific message's metadata in a thread.
284    ///
285    /// # Arguments
286    ///
287    /// * `thread_id` - The ID of the thread containing the message.
288    /// * `message_id` - The ID of the message to modify.
289    /// * `metadata` - The new metadata to apply to the message.
290    ///
291    /// # Returns
292    ///
293    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
294    pub async fn modify_message(
295        &self,
296        thread_id: &str,
297        message_id: &str,
298        metadata: Value,
299    ) -> OpenAIResult<Value> {
300        let url = format!("/threads/{thread_id}/messages/{message_id}");
301        let body = ModifyMessageRequest { metadata };
302
303        self.0.post_json(&url, &body).await
304    }
305
306    /// Delete a specific message by its ID in a thread.
307    ///
308    /// # Arguments
309    ///
310    /// * `thread_id` - The ID of the thread containing the message.
311    /// * `message_id` - The ID of the message to delete.
312    ///
313    /// # Returns
314    ///
315    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
316    pub async fn delete_message(&self, thread_id: &str, message_id: &str) -> OpenAIResult<Value> {
317        let url = format!("/threads/{thread_id}/messages/{message_id}");
318
319        self.0.delete(&url).await
320    }
321
322    /// Create and initiate a run in a specific thread with specified parameters.
323    ///
324    /// # Arguments
325    ///
326    /// * Various parameters used to customize the creation of the run.
327    ///
328    /// # Returns
329    ///
330    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
331    pub async fn create_run(
332        &self,
333        thread_id: &str,
334        assistant_id: &str,
335        model: Option<&str>,
336        instructions: Option<&str>,
337        additional_instructions: Option<&str>,
338        additional_messages: Option<Vec<Value>>,
339        tools: Option<Vec<Value>>,
340        metadata: Option<Value>,
341        temperature: Option<f64>,
342        top_p: Option<f64>,
343        stream: Option<bool>,
344        max_prompt_tokens: Option<u32>,
345        max_completion_tokens: Option<u32>,
346        truncation_strategy: Option<Value>,
347        tool_choice: Option<Value>,
348        parallel_tool_calls: Option<bool>,
349        response_format: Option<Value>,
350    ) -> OpenAIResult<Value> {
351        let url = format!("/threads/{thread_id}/runs");
352        let body = CreateRunRequest {
353            assistant_id,
354            model,
355            instructions,
356            additional_instructions,
357            additional_messages,
358            tools,
359            metadata,
360            temperature,
361            top_p,
362            stream,
363            max_prompt_tokens,
364            max_completion_tokens,
365            truncation_strategy,
366            tool_choice,
367            parallel_tool_calls,
368            response_format,
369        };
370
371        self.0.post_json(&url, &body).await
372    }
373
374    /// List runs within a specific thread with optional filters.
375    ///
376    /// # Arguments
377    ///
378    /// * `thread_id` - The ID of the thread to list runs from.
379    /// * `limit` - Optional limit on the number of runs to list.
380    /// * `order` - Optional order parameter for the run listing.
381    /// * `after` - Optional parameter to list runs after a specific time.
382    /// * `before` - Optional parameter to list runs before a specific time.
383    ///
384    /// # Returns
385    ///
386    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
387    pub async fn list_runs(
388        &self,
389        thread_id: &str,
390        limit: Option<u32>,
391        order: Option<&str>,
392        after: Option<&str>,
393        before: Option<&str>,
394    ) -> OpenAIResult<Value> {
395        let mut url = format!("/threads/{thread_id}/runs?");
396
397        extend_url_params!(url, limit, order, after, before);
398        url.pop();
399
400        self.0.get(&url).await
401    }
402
403    /// Retrieve details of a specific run by its ID.
404    ///
405    /// # Arguments
406    ///
407    /// * `thread_id` - The ID of the thread containing the run.
408    /// * `run_id` - The ID of the run to retrieve.
409    ///
410    /// # Returns
411    ///
412    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
413    pub async fn retrieve_run(&self, thread_id: &str, run_id: &str) -> OpenAIResult<Value> {
414        let url = format!("/threads/{thread_id}/runs/{run_id}");
415
416        self.0.get(&url).await
417    }
418
419    /// Modify a specific run's metadata in a thread.
420    ///
421    /// # Arguments
422    ///
423    /// * `thread_id` - The ID of the thread containing the run.
424    /// * `run_id` - The ID of the run to modify.
425    /// * `metadata` - The new metadata to apply to the run.
426    ///
427    /// # Returns
428    ///
429    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
430    pub async fn modify_run(
431        &self,
432        thread_id: &str,
433        run_id: &str,
434        metadata: Value,
435    ) -> OpenAIResult<Value> {
436        let url = format!("/threads/{thread_id}/runs/{run_id}");
437        let body = ModifyMessageRequest { metadata };
438
439        self.0.post_json(&url, &body).await
440    }
441
442    /// Delete a specific run by its ID in a thread.
443    ///
444    /// # Arguments
445    ///
446    /// * `thread_id` - The ID of the thread containing the run.
447    /// * `run_id` - The ID of the run to delete.
448    ///
449    /// # Returns
450    ///
451    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
452    pub async fn delete_run(&self, thread_id: &str, run_id: &str) -> OpenAIResult<Value> {
453        let url = format!("/threads/{thread_id}/runs/{run_id}");
454
455        self.0.delete(&url).await
456    }
457
458    /// Submit tool outputs for a specific run.
459    ///
460    /// # Arguments
461    ///
462    /// * `thread_id` - The ID of the thread containing the run.
463    /// * `run_id` - The ID of the run to submit outputs to.
464    /// * `tool_outputs` - List of tool outputs to submit.
465    /// * `stream` - Optional stream parameter for the submission.
466    ///
467    /// # Returns
468    ///
469    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
470    pub async fn submit_tool_outputs(
471        &self,
472        thread_id: &str,
473        run_id: &str,
474        tool_outputs: Vec<Value>,
475        stream: Option<bool>,
476    ) -> OpenAIResult<Value> {
477        let url = format!("/threads/{thread_id}/runs/{run_id}/submit_tool_outputs");
478        let body = SubmitToolRequest {
479            tool_outputs,
480            stream,
481        };
482
483        self.0.post_json(&url, &body).await
484    }
485
486    /// Cancel a specific run by its ID in a thread.
487    ///
488    /// # Arguments
489    ///
490    /// * `thread_id` - The ID of the thread containing the run.
491    /// * `run_id` - The ID of the run to cancel.
492    ///
493    /// # Returns
494    ///
495    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
496    pub async fn cancel_run(&self, thread_id: &str, run_id: &str) -> OpenAIResult<Value> {
497        let url = format!("/threads/{thread_id}/runs/{run_id}/cancel");
498        let body = json!({});
499
500        self.0.post_json(&url, &body).await
501    }
502
503    /// List steps for a specific run within a thread.
504    ///
505    /// # Arguments
506    ///
507    /// * `thread_id` - The ID of the thread containing the run.
508    /// * `run_id` - The ID of the run to list steps from.
509    /// * `limit` - Optional limit on the number of steps to list.
510    /// * `order` - Optional order parameter for the steps listing.
511    /// * `after` - Optional parameter to list steps after a specific time.
512    /// * `before` - Optional parameter to list steps before a specific time.
513    ///
514    /// # Returns
515    ///
516    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
517    pub async fn list_run_steps(
518        &self,
519        thread_id: &str,
520        run_id: &str,
521        limit: Option<u32>,
522        order: Option<&str>,
523        after: Option<&str>,
524        before: Option<&str>,
525    ) -> OpenAIResult<Value> {
526        let mut url = format!("/threads/{thread_id}/runs/{run_id}/steps?");
527
528        extend_url_params!(url, limit, order, after, before);
529        url.pop();
530
531        self.0.get(&url).await
532    }
533
534    /// Retrieve a specific step by its ID from a run within a thread.
535    ///
536    /// # Arguments
537    ///
538    /// * `thread_id` - The ID of the thread containing the run.
539    /// * `run_id` - The ID of the run containing the step.
540    /// * `step_id` - The ID of the step to retrieve.
541    ///
542    /// # Returns
543    ///
544    /// A Result containing the JSON response as [`serde_json::Value`] on success, or an [`OpenAIError`][crate::error_handling::OpenAIError] on failure.
545    pub async fn retrieve_run_step(
546        &self,
547        thread_id: &str,
548        run_id: &str,
549        step_id: &str,
550    ) -> OpenAIResult<Value> {
551        let url = format!("/threads/{thread_id}/runs/{run_id}/steps/{step_id}");
552
553        self.0.get(&url).await
554    }
555}