agentik_sdk/resources/
messages.rs1use crate::client::Anthropic;
2use crate::http::streaming::{StreamConfig, StreamRequestBuilder};
3use crate::streaming::MessageStream;
4use crate::types::errors::{AnthropicError, Result};
5use crate::types::messages::*;
6
7pub struct MessagesResource<'a> {
9 client: &'a Anthropic,
10}
11
12impl<'a> MessagesResource<'a> {
13 pub fn new(client: &'a Anthropic) -> Self {
15 Self { client }
16 }
17
18 pub async fn create(&self, params: MessageCreateParams) -> Result<Message> {
42 let url = self.client.http_client().build_url("/v1/messages");
43
44 let request = self
45 .client
46 .http_client()
47 .post(&url)
48 .json(¶ms)
49 .build()
50 .map_err(|e| AnthropicError::Connection {
51 message: e.to_string(),
52 })?;
53
54 let response = self.client.http_client().send(request).await?;
55
56 let request_id = self.client.http_client().extract_request_id(&response);
58
59 let status = response.status().as_u16();
60 let body = response.text().await.map_err(|e| AnthropicError::from_status(status, format!(
61 "failed to read response body: {e}"
62 )))?;
63
64 let mut message: Message = serde_json::from_str(&body)
65 .map_err(|e| AnthropicError::from_status(status, format!(
66 "failed to parse response as JSON: {e}, body: {}",
67 body.chars().take(500).collect::<String>()
68 )))?;
69
70 message.request_id = request_id;
71
72 Ok(message)
73 }
74
75 pub async fn create_stream(&self, mut params: MessageCreateParams) -> Result<MessageStream> {
107 params.stream = Some(true);
109
110 let auth_header = format!("Bearer {}", self.client.config().api_key);
112
113 let stream_builder = StreamRequestBuilder::new(
115 self.client.http_client().client().clone(),
116 self.client.config().base_url.clone(),
117 )
118 .header("Authorization", &auth_header)
119 .header("Content-Type", "application/json")
120 .header("anthropic-version", "2023-06-01")
121 .config(StreamConfig::default());
122
123 let http_stream = stream_builder.post_stream("v1/messages", ¶ms).await?;
125
126 let message_stream = MessageStream::from_http_stream(http_stream)?;
128
129 Ok(message_stream)
130 }
131
132 pub async fn stream(&self, params: MessageCreateParams) -> Result<MessageStream> {
155 self.create_stream(params).await
156 }
157
158 pub fn create_with_builder(
183 &'a self,
184 model: impl Into<String>,
185 max_tokens: u32,
186 ) -> MessageCreateBuilderWithClient<'a> {
187 MessageCreateBuilderWithClient {
188 resource: self,
189 builder: MessageCreateBuilder::new(model, max_tokens),
190 }
191 }
192}
193
194pub struct MessageCreateBuilderWithClient<'a> {
196 resource: &'a MessagesResource<'a>,
197 builder: MessageCreateBuilder,
198}
199
200impl<'a> MessageCreateBuilderWithClient<'a> {
201 pub fn message(mut self, role: Role, content: impl Into<MessageContent>) -> Self {
203 self.builder = self.builder.message(role, content);
204 self
205 }
206
207 pub fn user(mut self, content: impl Into<MessageContent>) -> Self {
209 self.builder = self.builder.user(content);
210 self
211 }
212
213 pub fn assistant(mut self, content: impl Into<MessageContent>) -> Self {
215 self.builder = self.builder.assistant(content);
216 self
217 }
218
219 pub fn system(mut self, system: impl Into<String>) -> Self {
221 self.builder = self.builder.system(system);
222 self
223 }
224
225 pub fn temperature(mut self, temperature: f32) -> Self {
227 self.builder = self.builder.temperature(temperature);
228 self
229 }
230
231 pub fn top_p(mut self, top_p: f32) -> Self {
233 self.builder = self.builder.top_p(top_p);
234 self
235 }
236
237 pub fn top_k(mut self, top_k: u32) -> Self {
239 self.builder = self.builder.top_k(top_k);
240 self
241 }
242
243 pub fn stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
245 self.builder = self.builder.stop_sequences(stop_sequences);
246 self
247 }
248
249 pub fn stream(mut self, stream: bool) -> Self {
251 self.builder = self.builder.stream(stream);
252 self
253 }
254
255 pub async fn send(self) -> Result<Message> {
257 self.resource.create(self.builder.build()).await
258 }
259
260 pub async fn stream_send(self) -> Result<MessageStream> {
279 let params = self.builder.stream(true).build();
280 self.resource.create_stream(params).await
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287 use crate::types::messages::{ContentBlockParam, MessageContent};
288
289 #[test]
290 fn test_message_create_params_serialization() {
291 let params = MessageCreateBuilder::new("claude-3-5-sonnet-latest", 1024)
292 .user("Hello, world!")
293 .system("You are helpful")
294 .temperature(0.7)
295 .build();
296
297 let json = serde_json::to_value(¶ms).unwrap();
298
299 assert_eq!(json["model"], "claude-3-5-sonnet-latest");
300 assert_eq!(json["max_tokens"], 1024);
301 assert_eq!(json["messages"].as_array().unwrap().len(), 1);
302 assert_eq!(json["system"], "You are helpful");
303
304 let temperature = json["temperature"].as_f64().unwrap();
306 assert!(
307 (temperature - 0.7).abs() < 0.001,
308 "Temperature should be close to 0.7, got {}",
309 temperature
310 );
311 }
312
313 #[test]
314 fn test_complex_message_content() {
315 let content = MessageContent::Blocks(vec![
316 ContentBlockParam::text("Here's an image:"),
317 ContentBlockParam::image_base64("image/jpeg", "base64data"),
318 ]);
319
320 let params = MessageCreateBuilder::new("claude-3-5-sonnet-latest", 1024)
321 .user(content)
322 .build();
323
324 let json = serde_json::to_value(¶ms).unwrap();
325 let message_content = &json["messages"][0]["content"];
326
327 assert!(message_content.is_array());
328 assert_eq!(message_content.as_array().unwrap().len(), 2);
329 assert_eq!(message_content[0]["type"], "text");
330 assert_eq!(message_content[1]["type"], "image");
331 }
332
333 #[test]
334 fn test_multi_message_conversation() {
335 let params = MessageCreateBuilder::new("claude-3-5-sonnet-latest", 1024)
336 .user("Hello!")
337 .assistant("Hi there! How can I help you?")
338 .user("What's the weather like?")
339 .build();
340
341 assert_eq!(params.messages.len(), 3);
342 assert_eq!(params.messages[0].role, Role::User);
343 assert_eq!(params.messages[1].role, Role::Assistant);
344 assert_eq!(params.messages[2].role, Role::User);
345 }
346}
347