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}