sure_client_rs/client/
chats.rs

1use crate::ApiError;
2use crate::error::ApiResult;
3use crate::models::PaginatedResponse;
4use crate::models::chat::{
5    ChatCollection, ChatDetail, CreateChatRequest, CreateMessageRequest, MessageResponse,
6    RetryResponse, UpdateChatRequest,
7};
8use bon::bon;
9use reqwest::Method;
10use std::collections::HashMap;
11use uuid::Uuid;
12
13use super::SureClient;
14
15const MAX_PER_PAGE: u32 = 100;
16
17#[bon]
18impl SureClient {
19    /// List chats
20    ///
21    /// Retrieves a paginated list of chats.
22    ///
23    /// # Arguments
24    /// * `page` - Page number (default: 1)
25    /// * `per_page` - Items per page (default: 25, max: 100)
26    ///
27    /// # Returns
28    /// A paginated response containing chats and pagination metadata.
29    ///
30    /// # Errors
31    /// Returns `ApiError::Forbidden` if AI features are disabled.
32    /// Returns `ApiError::Unauthorized` if the API key is invalid.
33    /// Returns `ApiError::Network` if the request fails due to network issues.
34    ///
35    /// # Example
36    /// ```no_run
37    /// use sure_client_rs::{SureClient, BearerToken};
38    ///
39    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
40    /// // Use defaults (page 1, per_page 25)
41    /// let response = client.get_chats().call().await?;
42    ///
43    /// for chat in response.items.chats {
44    ///     println!("Chat: {} ({} messages)", chat.title, chat.message_count);
45    /// }
46    ///
47    /// // Or customize parameters using the builder
48    /// let response = client.get_chats().page(2).per_page(50).call().await?;
49    /// # Ok(())
50    /// # }
51    /// ```
52    #[builder]
53    pub async fn get_chats(
54        &self,
55        #[builder(default = 1)] page: u32,
56        #[builder(default = 25)] per_page: u32,
57    ) -> ApiResult<PaginatedResponse<ChatCollection>> {
58        if per_page > MAX_PER_PAGE {
59            return Err(ApiError::InvalidParameter(format!(
60                "per_page cannot exceed {MAX_PER_PAGE}",
61            )));
62        }
63
64        let mut query_params = HashMap::new();
65
66        query_params.insert("page", page.to_string());
67        query_params.insert("per_page", per_page.to_string());
68
69        self.execute_request(Method::GET, "/api/v1/chats", Some(&query_params), None)
70            .await
71    }
72
73    /// Create a new chat
74    ///
75    /// Creates a new chat with an optional initial message.
76    ///
77    /// # Arguments
78    /// * `title` - Chat title (required)
79    /// * `message` - Optional initial message
80    /// * `model` - Optional OpenAI model identifier
81    ///
82    /// # Returns
83    /// Detailed information about the created chat.
84    ///
85    /// # Errors
86    /// Returns `ApiError::ValidationError` if validation fails.
87    /// Returns `ApiError::Unauthorized` if the API key is invalid.
88    /// Returns `ApiError::Network` if the request fails due to network issues.
89    ///
90    /// # Example
91    /// ```no_run
92    /// use sure_client_rs::{SureClient, BearerToken};
93    ///
94    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
95    /// let chat = client.create_chat()
96    ///     .title("Monthly budget review".to_string())
97    ///     .message("Help me analyze my spending".to_string())
98    ///     .call()
99    ///     .await?;
100    ///
101    /// println!("Created chat: {}", chat.title);
102    /// # Ok(())
103    /// # }
104    /// ```
105    ///
106    #[builder]
107    pub async fn create_chat(
108        &self,
109        title: String,
110        message: Option<String>,
111        model: Option<String>,
112    ) -> ApiResult<ChatDetail> {
113        let request = CreateChatRequest {
114            title,
115            message,
116            model,
117        };
118
119        self.execute_request(
120            Method::POST,
121            "/api/v1/chats",
122            None,
123            Some(serde_json::to_string(&request)?),
124        )
125        .await
126    }
127
128    /// Get a specific chat
129    ///
130    /// Retrieves detailed information about a chat including its messages.
131    ///
132    /// # Arguments
133    /// * `id` - The chat ID to retrieve
134    ///
135    /// # Returns
136    /// Detailed chat information including messages.
137    ///
138    /// # Errors
139    /// Returns `ApiError::NotFound` if the chat doesn't exist.
140    /// Returns `ApiError::Unauthorized` if the API key is invalid.
141    /// Returns `ApiError::Network` if the request fails due to network issues.
142    ///
143    /// # Example
144    /// ```no_run
145    /// use sure_client_rs::{SureClient, BearerToken};
146    /// use uuid::Uuid;
147    ///
148    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
149    /// let chat_id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
150    /// let chat = client.get_chat(&chat_id).await?;
151    ///
152    /// println!("Chat: {} with {} messages", chat.title, chat.messages.len());
153    /// # Ok(())
154    /// # }
155    /// ```
156    ///
157    pub async fn get_chat(&self, id: &Uuid) -> ApiResult<ChatDetail> {
158        self.execute_request(Method::GET, &format!("/api/v1/chats/{}", id), None, None)
159            .await
160    }
161
162    /// Update a chat
163    ///
164    /// Updates the title of an existing chat.
165    ///
166    /// # Arguments
167    /// * `id` - The chat ID to update
168    /// * `title` - Updated chat title
169    ///
170    /// # Returns
171    /// Updated chat information.
172    ///
173    /// # Errors
174    /// Returns `ApiError::NotFound` if the chat doesn't exist.
175    /// Returns `ApiError::ValidationError` if validation fails.
176    /// Returns `ApiError::Unauthorized` if the API key is invalid.
177    /// Returns `ApiError::Network` if the request fails due to network issues.
178    ///
179    /// # Example
180    /// ```no_run
181    /// use sure_client_rs::{SureClient, BearerToken};
182    /// use uuid::Uuid;
183    ///
184    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
185    /// let chat_id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
186    ///
187    /// let chat = client.update_chat()
188    ///     .id(&chat_id)
189    ///     .title("Updated chat title".to_string())
190    ///     .call()
191    ///     .await?;
192    ///
193    /// println!("Updated chat: {}", chat.title);
194    /// # Ok(())
195    /// # }
196    /// ```
197    ///
198    #[builder]
199    pub async fn update_chat(&self, id: &Uuid, title: String) -> ApiResult<ChatDetail> {
200        let request = UpdateChatRequest { title };
201
202        self.execute_request(
203            Method::PATCH,
204            &format!("/api/v1/chats/{}", id),
205            None,
206            Some(serde_json::to_string(&request)?),
207        )
208        .await
209    }
210
211    /// Delete a chat
212    ///
213    /// Permanently deletes a chat and all its messages.
214    ///
215    /// # Arguments
216    /// * `id` - The chat ID to delete
217    ///
218    /// # Returns
219    /// Unit type on successful deletion.
220    ///
221    /// # Errors
222    /// Returns `ApiError::NotFound` if the chat doesn't exist.
223    /// Returns `ApiError::Unauthorized` if the API key is invalid.
224    /// Returns `ApiError::Network` if the request fails due to network issues.
225    ///
226    /// # Example
227    /// ```no_run
228    /// use sure_client_rs::{SureClient, BearerToken};
229    /// use uuid::Uuid;
230    ///
231    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
232    /// let chat_id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
233    /// client.delete_chat(&chat_id).await?;
234    /// println!("Chat deleted");
235    /// # Ok(())
236    /// # }
237    /// ```
238    ///
239    pub async fn delete_chat(&self, id: &Uuid) -> ApiResult<()> {
240        self.execute_request::<serde_json::Value>(
241            Method::DELETE,
242            &format!("/api/v1/chats/{}", id),
243            None,
244            None,
245        )
246        .await?;
247        Ok(())
248    }
249
250    /// Create a message in a chat
251    ///
252    /// Sends a new message to a chat and triggers an AI response.
253    ///
254    /// # Arguments
255    /// * `chat_id` - The chat ID to send the message to
256    /// * `content` - Message content
257    /// * `model` - Optional model identifier
258    ///
259    /// # Returns
260    /// The created message with response status.
261    ///
262    /// # Errors
263    /// Returns `ApiError::NotFound` if the chat doesn't exist.
264    /// Returns `ApiError::ValidationError` if validation fails.
265    /// Returns `ApiError::Unauthorized` if the API key is invalid.
266    /// Returns `ApiError::Network` if the request fails due to network issues.
267    ///
268    /// # Example
269    /// ```no_run
270    /// use sure_client_rs::{SureClient, BearerToken};
271    /// use uuid::Uuid;
272    ///
273    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
274    /// let chat_id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
275    ///
276    /// let message = client.create_message()
277    ///     .chat_id(&chat_id)
278    ///     .content("What were my expenses last month?".to_string())
279    ///     .call()
280    ///     .await?;
281    ///
282    /// println!("Message sent: {}", message.content);
283    /// # Ok(())
284    /// # }
285    /// ```
286    ///
287    #[builder]
288    pub async fn create_message(
289        &self,
290        chat_id: &Uuid,
291        content: String,
292        model: Option<String>,
293    ) -> ApiResult<MessageResponse> {
294        let request = CreateMessageRequest { content, model };
295
296        self.execute_request(
297            Method::POST,
298            &format!("/api/v1/chats/{}/messages", chat_id),
299            None,
300            Some(serde_json::to_string(&request)?),
301        )
302        .await
303    }
304
305    /// Retry the last assistant response
306    ///
307    /// Retries generating the last assistant message in a chat.
308    ///
309    /// # Arguments
310    /// * `chat_id` - The chat ID to retry the response for
311    ///
312    /// # Returns
313    /// Retry response with the new message ID.
314    ///
315    /// # Errors
316    /// Returns `ApiError::NotFound` if the chat doesn't exist.
317    /// Returns `ApiError::ValidationError` if no assistant message is available to retry.
318    /// Returns `ApiError::Unauthorized` if the API key is invalid.
319    /// Returns `ApiError::Network` if the request fails due to network issues.
320    ///
321    /// # Example
322    /// ```no_run
323    /// use sure_client_rs::{SureClient, BearerToken};
324    /// use uuid::Uuid;
325    ///
326    /// # async fn example(client: SureClient) -> Result<(), Box<dyn std::error::Error>> {
327    /// let chat_id = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
328    /// let response = client.retry_message(&chat_id).await?;
329    /// println!("Retry started: {}", response.message);
330    /// # Ok(())
331    /// # }
332    /// ```
333    ///
334    pub async fn retry_message(&self, chat_id: &Uuid) -> ApiResult<RetryResponse> {
335        self.execute_request(
336            Method::POST,
337            &format!("/api/v1/chats/{}/messages/retry", chat_id),
338            None,
339            None,
340        )
341        .await
342    }
343}