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}