Skip to main content

openai_oxide/resources/beta/
threads.rs

1// Threads resource — client.beta().threads()
2
3use super::BETA_HEADER;
4use crate::client::OpenAI;
5use crate::error::OpenAIError;
6use crate::types::beta::{
7    Message, MessageCreateRequest, MessageList, Thread, ThreadCreateRequest, ThreadDeleted,
8};
9
10/// Access thread endpoints (beta).
11pub struct Threads<'a> {
12    client: &'a OpenAI,
13}
14
15impl<'a> Threads<'a> {
16    pub(crate) fn new(client: &'a OpenAI) -> Self {
17        Self { client }
18    }
19
20    /// Create a thread.
21    ///
22    /// `POST /threads`
23    pub async fn create(&self, request: ThreadCreateRequest) -> Result<Thread, OpenAIError> {
24        let response = self
25            .client
26            .request(reqwest::Method::POST, "/threads")
27            .header(BETA_HEADER.0, BETA_HEADER.1)
28            .json(&request)
29            .send()
30            .await?;
31        OpenAI::handle_response(response).await
32    }
33
34    /// Retrieve a thread.
35    ///
36    /// `GET /threads/{thread_id}`
37    pub async fn retrieve(&self, thread_id: &str) -> Result<Thread, OpenAIError> {
38        let response = self
39            .client
40            .request(reqwest::Method::GET, &format!("/threads/{thread_id}"))
41            .header(BETA_HEADER.0, BETA_HEADER.1)
42            .send()
43            .await?;
44        OpenAI::handle_response(response).await
45    }
46
47    /// Delete a thread.
48    ///
49    /// `DELETE /threads/{thread_id}`
50    pub async fn delete(&self, thread_id: &str) -> Result<ThreadDeleted, OpenAIError> {
51        let response = self
52            .client
53            .request(reqwest::Method::DELETE, &format!("/threads/{thread_id}"))
54            .header(BETA_HEADER.0, BETA_HEADER.1)
55            .send()
56            .await?;
57        OpenAI::handle_response(response).await
58    }
59
60    /// Access messages sub-resource.
61    pub fn messages(&self, thread_id: &str) -> Messages<'_> {
62        Messages {
63            client: self.client,
64            thread_id: thread_id.to_string(),
65        }
66    }
67}
68
69/// Thread messages sub-resource.
70pub struct Messages<'a> {
71    client: &'a OpenAI,
72    thread_id: String,
73}
74
75impl<'a> Messages<'a> {
76    /// Create a message in a thread.
77    ///
78    /// `POST /threads/{thread_id}/messages`
79    pub async fn create(&self, request: MessageCreateRequest) -> Result<Message, OpenAIError> {
80        let response = self
81            .client
82            .request(
83                reqwest::Method::POST,
84                &format!("/threads/{}/messages", self.thread_id),
85            )
86            .header(BETA_HEADER.0, BETA_HEADER.1)
87            .json(&request)
88            .send()
89            .await?;
90        OpenAI::handle_response(response).await
91    }
92
93    /// List messages in a thread.
94    ///
95    /// `GET /threads/{thread_id}/messages`
96    pub async fn list(&self) -> Result<MessageList, OpenAIError> {
97        let response = self
98            .client
99            .request(
100                reqwest::Method::GET,
101                &format!("/threads/{}/messages", self.thread_id),
102            )
103            .header(BETA_HEADER.0, BETA_HEADER.1)
104            .send()
105            .await?;
106        OpenAI::handle_response(response).await
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use crate::OpenAI;
113    use crate::config::ClientConfig;
114    use crate::types::beta::ThreadCreateRequest;
115
116    #[tokio::test]
117    async fn test_threads_create() {
118        let mut server = mockito::Server::new_async().await;
119        let mock = server
120            .mock("POST", "/threads")
121            .match_header("OpenAI-Beta", "assistants=v2")
122            .with_status(200)
123            .with_header("content-type", "application/json")
124            .with_body(
125                r#"{
126                    "id": "thread_abc123",
127                    "object": "thread",
128                    "created_at": 1699012949
129                }"#,
130            )
131            .create_async()
132            .await;
133
134        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
135        let thread = client
136            .beta()
137            .threads()
138            .create(ThreadCreateRequest::default())
139            .await
140            .unwrap();
141        assert_eq!(thread.id, "thread_abc123");
142        mock.assert_async().await;
143    }
144
145    #[tokio::test]
146    async fn test_threads_delete() {
147        let mut server = mockito::Server::new_async().await;
148        let mock = server
149            .mock("DELETE", "/threads/thread_abc123")
150            .match_header("OpenAI-Beta", "assistants=v2")
151            .with_status(200)
152            .with_header("content-type", "application/json")
153            .with_body(r#"{"id": "thread_abc123", "object": "thread.deleted", "deleted": true}"#)
154            .create_async()
155            .await;
156
157        let client = OpenAI::with_config(ClientConfig::new("sk-test").base_url(server.url()));
158        let resp = client
159            .beta()
160            .threads()
161            .delete("thread_abc123")
162            .await
163            .unwrap();
164        assert!(resp.deleted);
165        mock.assert_async().await;
166    }
167}