1use std::{
2 ops::{Deref, DerefMut},
3 pin::Pin,
4};
5
6use derive_builder::Builder;
7use serde::{Deserialize, Serialize, Serializer};
8use serde_json::Value;
9use tokio_stream::Stream;
10
11use super::{errors::AnthropicError, messages};
12
13#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Default)]
14pub struct Usage {
15 pub input_tokens: Option<u32>,
16 pub output_tokens: Option<u32>,
17 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub cache_creation_input_tokens: Option<u32>,
21 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub cache_read_input_tokens: Option<u32>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
31#[serde(tag = "type", rename_all = "snake_case")]
32pub enum CacheControl {
33 Ephemeral {
34 #[serde(default, skip_serializing_if = "Option::is_none")]
36 ttl: Option<String>,
37 },
38}
39
40impl CacheControl {
41 #[must_use]
43 pub fn ephemeral() -> Self {
44 CacheControl::Ephemeral { ttl: None }
45 }
46
47 #[must_use]
49 pub fn ephemeral_with_ttl(ttl: impl Into<String>) -> Self {
50 CacheControl::Ephemeral {
51 ttl: Some(ttl.into()),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
58pub struct Thinking {
59 pub thinking: String,
60 #[serde(default, skip_serializing_if = "Option::is_none")]
66 pub signature: Option<String>,
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub cache_control: Option<CacheControl>,
69}
70
71impl From<Thinking> for MessageContent {
72 fn from(thinking: Thinking) -> Self {
73 MessageContent::Thinking(thinking)
74 }
75}
76
77impl From<Thinking> for MessageContentList {
78 fn from(thinking: Thinking) -> Self {
79 MessageContentList(vec![thinking.into()])
80 }
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
85#[serde(tag = "type", rename_all = "snake_case")]
86pub enum ThinkingConfig {
87 Enabled { budget_tokens: u32 },
88 Disabled,
89}
90
91#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
96pub struct OutputConfig {
97 #[serde(default, skip_serializing_if = "Option::is_none")]
98 pub effort: Option<String>,
99}
100
101#[derive(Clone, Debug, Deserialize)]
102pub enum ToolChoice {
103 Auto,
104 Any,
105 Tool(String),
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize, Builder, PartialEq, Default)]
109#[builder(setter(into, strip_option), default)]
110pub struct Message {
111 pub role: MessageRole,
112 pub content: MessageContentList,
113}
114
115impl Message {
116 pub fn tool_uses(&self) -> Vec<ToolUse> {
118 self.content
119 .0
120 .iter()
121 .filter(|c| matches!(c, MessageContent::ToolUse(_)))
122 .map(|c| match c {
123 MessageContent::ToolUse(tool_use) => tool_use.clone(),
124 _ => unreachable!(),
125 })
126 .collect()
127 }
128
129 pub fn text(&self) -> Option<String> {
131 self.content
132 .0
133 .iter()
134 .filter_map(|c| match c {
135 MessageContent::Text(text) => Some(text.text.clone()),
136 _ => None,
137 })
138 .next()
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
143pub struct MessageContentList(pub Vec<MessageContent>);
144
145impl Deref for MessageContentList {
146 type Target = Vec<MessageContent>;
147
148 fn deref(&self) -> &Self::Target {
149 &self.0
150 }
151}
152
153impl DerefMut for MessageContentList {
154 fn deref_mut(&mut self) -> &mut Self::Target {
155 &mut self.0
156 }
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
160#[serde(rename_all = "snake_case")]
161pub enum MessageRole {
162 #[default]
163 User,
164 Assistant,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
168#[builder(setter(into, strip_option))]
169pub struct CreateMessagesRequest {
170 pub messages: Vec<Message>,
171 pub model: String,
172 #[builder(default = messages::DEFAULT_MAX_TOKENS)]
173 pub max_tokens: i32,
174 #[builder(default)]
175 #[serde(skip_serializing_if = "Option::is_none")]
176 pub metadata: Option<serde_json::Map<String, Value>>,
177 #[serde(skip_serializing_if = "Option::is_none")]
178 #[builder(default)]
179 pub stop_sequences: Option<Vec<String>>,
180 #[builder(default = "false")]
181 pub stream: bool, #[serde(skip_serializing_if = "Option::is_none")]
183 #[builder(default)]
184 pub temperature: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
186 #[builder(default)]
187 pub tool_choice: Option<ToolChoice>,
188 #[serde(skip_serializing_if = "Option::is_none")]
190 #[builder(default)]
191 pub tools: Option<Vec<serde_json::Map<String, Value>>>,
192 #[serde(skip_serializing_if = "Option::is_none")]
193 #[builder(default)]
194 pub top_k: Option<u32>, #[serde(skip_serializing_if = "Option::is_none")]
196 #[builder(default)]
197 pub top_p: Option<f32>, #[serde(skip_serializing_if = "Option::is_none")]
199 #[builder(default)]
200 pub system: Option<String>,
201 #[serde(skip_serializing_if = "Option::is_none")]
202 #[builder(default)]
203 pub thinking: Option<ThinkingConfig>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 #[builder(default)]
206 pub output_config: Option<OutputConfig>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize, Builder)]
210#[builder(setter(into, strip_option))]
211pub struct CreateMessagesResponse {
212 #[serde(default)]
213 pub id: Option<String>,
214 #[serde(default)]
215 pub content: Option<Vec<MessageContent>>,
216 #[serde(default)]
217 pub model: Option<String>,
218 #[serde(default)]
219 pub stop_reason: Option<String>,
220 #[serde(default)]
221 pub stop_sequence: Option<String>,
222 #[serde(default)]
223 pub usage: Option<Usage>,
224}
225
226impl CreateMessagesResponse {
227 pub fn messages(&self) -> Vec<Message> {
229 let Some(content) = &self.content else {
230 return vec![];
231 };
232 content
233 .iter()
234 .map(|c| Message {
235 role: MessageRole::Assistant,
236 content: c.clone().into(),
237 })
238 .collect()
239 }
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
243#[serde(tag = "type", rename_all = "snake_case")]
244pub enum MessageContent {
245 ToolUse(ToolUse),
246 ToolResult(ToolResult),
247 Text(Text),
248 Thinking(Thinking),
249 Image(Image),
250 Document(Document),
251}
252
253impl MessageContent {
254 pub fn as_tool_use(&self) -> Option<&ToolUse> {
255 if let MessageContent::ToolUse(tool_use) = self {
256 Some(tool_use)
257 } else {
258 None
259 }
260 }
261
262 pub fn as_tool_result(&self) -> Option<&ToolResult> {
263 if let MessageContent::ToolResult(tool_result) = self {
264 Some(tool_result)
265 } else {
266 None
267 }
268 }
269
270 pub fn as_text(&self) -> Option<&Text> {
271 if let MessageContent::Text(text) = self {
272 Some(text)
273 } else {
274 None
275 }
276 }
277
278 pub fn as_thinking(&self) -> Option<&Thinking> {
279 if let MessageContent::Thinking(thinking) = self {
280 Some(thinking)
281 } else {
282 None
283 }
284 }
285
286 pub fn as_image(&self) -> Option<&Image> {
287 if let MessageContent::Image(image) = self {
288 Some(image)
289 } else {
290 None
291 }
292 }
293
294 pub fn as_document(&self) -> Option<&Document> {
295 if let MessageContent::Document(document) = self {
296 Some(document)
297 } else {
298 None
299 }
300 }
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
309#[serde(tag = "type", rename_all = "snake_case")]
310pub enum MediaSource {
311 Base64(Base64Source),
312 Url(UrlSource),
313}
314
315impl Default for MediaSource {
316 fn default() -> Self {
317 MediaSource::Base64(Base64Source::default())
318 }
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
324#[builder(setter(into, strip_option), default)]
325pub struct Base64Source {
326 pub media_type: String,
327 pub data: String,
329}
330
331#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
332#[builder(setter(into, strip_option), default)]
333pub struct UrlSource {
334 pub url: String,
335}
336
337#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
339#[builder(setter(into, strip_option), default)]
340pub struct Image {
341 pub source: MediaSource,
342 #[serde(default, skip_serializing_if = "Option::is_none")]
343 pub cache_control: Option<CacheControl>,
344}
345
346impl Image {
347 pub fn base64(media_type: impl Into<String>, data: impl Into<String>) -> Self {
349 Image {
350 source: MediaSource::Base64(Base64Source {
351 media_type: media_type.into(),
352 data: data.into(),
353 }),
354 cache_control: None,
355 }
356 }
357}
358
359impl From<Image> for MessageContent {
360 fn from(image: Image) -> Self {
361 MessageContent::Image(image)
362 }
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
367#[builder(setter(into, strip_option), default)]
368pub struct Document {
369 pub source: MediaSource,
370 #[serde(default, skip_serializing_if = "Option::is_none")]
372 pub title: Option<String>,
373 #[serde(default, skip_serializing_if = "Option::is_none")]
374 pub cache_control: Option<CacheControl>,
375}
376
377impl Document {
378 pub fn base64(media_type: impl Into<String>, data: impl Into<String>) -> Self {
380 Document {
381 source: MediaSource::Base64(Base64Source {
382 media_type: media_type.into(),
383 data: data.into(),
384 }),
385 title: None,
386 cache_control: None,
387 }
388 }
389}
390
391impl From<Document> for MessageContent {
392 fn from(document: Document) -> Self {
393 MessageContent::Document(document)
394 }
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
398#[builder(setter(into, strip_option), default)]
399pub struct ToolUse {
400 pub id: String,
401 pub input: Value,
402 pub name: String,
403 #[serde(default, skip_serializing_if = "Option::is_none")]
404 pub cache_control: Option<CacheControl>,
405}
406
407impl From<ToolUse> for MessageContent {
408 fn from(tool_use: ToolUse) -> Self {
409 MessageContent::ToolUse(tool_use)
410 }
411}
412
413impl From<ToolUse> for MessageContentList {
414 fn from(tool_use: ToolUse) -> Self {
415 MessageContentList(vec![tool_use.into()])
416 }
417}
418
419#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
420#[builder(setter(into, strip_option), default)]
421pub struct ToolResult {
422 pub tool_use_id: String,
423 pub content: Option<ToolResultContent>,
424 pub is_error: bool,
425 #[serde(default, skip_serializing_if = "Option::is_none")]
426 pub cache_control: Option<CacheControl>,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
436#[serde(untagged)]
437pub enum ToolResultContent {
438 Text(String),
439 Blocks(Vec<ToolResultBlock>),
440}
441
442impl Default for ToolResultContent {
443 fn default() -> Self {
444 ToolResultContent::Text(String::new())
445 }
446}
447
448impl From<String> for ToolResultContent {
452 fn from(s: String) -> Self {
453 ToolResultContent::Text(s)
454 }
455}
456
457impl From<&str> for ToolResultContent {
458 fn from(s: &str) -> Self {
459 ToolResultContent::Text(s.to_string())
460 }
461}
462
463impl From<Vec<ToolResultBlock>> for ToolResultContent {
464 fn from(blocks: Vec<ToolResultBlock>) -> Self {
465 ToolResultContent::Blocks(blocks)
466 }
467}
468
469#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
475#[serde(tag = "type", rename_all = "snake_case")]
476pub enum ToolResultBlock {
477 Text(Text),
478 Image(Image),
479}
480
481impl From<Text> for ToolResultBlock {
482 fn from(text: Text) -> Self {
483 ToolResultBlock::Text(text)
484 }
485}
486
487impl From<Image> for ToolResultBlock {
488 fn from(image: Image) -> Self {
489 ToolResultBlock::Image(image)
490 }
491}
492
493impl From<ToolResult> for MessageContent {
494 fn from(tool_result: ToolResult) -> Self {
495 MessageContent::ToolResult(tool_result)
496 }
497}
498
499impl From<ToolResult> for MessageContentList {
500 fn from(tool_result: ToolResult) -> Self {
501 MessageContentList(vec![tool_result.into()])
502 }
503}
504
505#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default, Builder)]
506#[builder(setter(into, strip_option), default)]
507pub struct Text {
508 pub text: String,
509 #[serde(default, skip_serializing_if = "Option::is_none")]
510 pub cache_control: Option<CacheControl>,
511}
512
513impl<S: AsRef<str>> From<S> for Text {
514 fn from(s: S) -> Self {
515 Text {
516 text: s.as_ref().to_string(),
517 ..Default::default()
518 }
519 }
520}
521
522impl From<Text> for MessageContent {
523 fn from(text: Text) -> Self {
524 MessageContent::Text(text)
525 }
526}
527
528impl From<Text> for MessageContentList {
529 fn from(text: Text) -> Self {
530 MessageContentList(vec![text.into()])
531 }
532}
533
534impl<S: AsRef<str>> From<S> for MessageContent {
535 fn from(s: S) -> Self {
536 MessageContent::Text(Text {
537 text: s.as_ref().to_string(),
538 ..Default::default()
539 })
540 }
541}
542
543impl<S: AsRef<str>> From<S> for Message {
544 fn from(s: S) -> Self {
545 MessageBuilder::default()
546 .role(MessageRole::User)
547 .content(s.as_ref().to_string())
548 .build()
549 .expect("infallible")
550 }
551}
552
553impl<S: AsRef<str>> From<S> for MessageContentList {
555 fn from(s: S) -> Self {
556 MessageContentList(vec![s.as_ref().into()])
557 }
558}
559
560impl From<MessageContent> for MessageContentList {
561 fn from(content: MessageContent) -> Self {
562 MessageContentList(vec![content])
563 }
564}
565
566impl Serialize for ToolChoice {
567 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
568 where
569 S: Serializer,
570 {
571 match self {
572 ToolChoice::Auto => {
573 serde::Serialize::serialize(&serde_json::json!({"type": "auto"}), serializer)
574 }
575 ToolChoice::Any => {
576 serde::Serialize::serialize(&serde_json::json!({"type": "any"}), serializer)
577 }
578 ToolChoice::Tool(name) => serde::Serialize::serialize(
579 &serde_json::json!({"type": "tool", "name": name}),
580 serializer,
581 ),
582 }
583 }
584}
585#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
586#[serde(rename_all = "snake_case", tag = "type")]
587pub enum ContentBlockDelta {
588 TextDelta { text: String },
589 InputJsonDelta { partial_json: String },
590 ThinkingDelta { thinking: String },
591 SignatureDelta { signature: String },
592}
593
594#[derive(Clone, Serialize, Deserialize, Debug, Eq, PartialEq)]
595pub struct MessageDelta {
596 pub stop_reason: Option<String>,
597 pub stop_sequence: Option<String>,
598}
599
600#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
601#[serde(rename_all = "snake_case", tag = "type")]
602pub enum MessagesStreamEvent {
603 MessageStart {
604 message: MessageStart,
605 usage: Option<Usage>,
606 },
607 ContentBlockStart {
608 index: usize,
609 content_block: MessageContent,
610 },
611 ContentBlockDelta {
612 index: usize,
613 delta: ContentBlockDelta,
614 },
615 ContentBlockStop {
616 index: usize,
617 },
618 MessageDelta {
619 delta: MessageDelta,
620 #[serde(default)]
621 usage: Option<Usage>,
622 },
623 MessageStop,
624}
625#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
626pub struct MessageStart {
627 pub id: String,
628 pub model: String,
629 pub role: String,
630 pub content: Vec<MessageContent>,
631 #[serde(default)]
632 pub stop_reason: Option<String>,
633 #[serde(default)]
634 pub stop_sequence: Option<String>,
635 #[serde(default)]
636 pub usage: Option<Usage>,
637}
638
639pub type CreateMessagesResponseStream =
640 Pin<Box<dyn Stream<Item = Result<MessagesStreamEvent, AnthropicError>> + Send>>;
641
642#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
643pub struct ListModelsResponse {
644 #[serde(default)]
645 pub data: Vec<Model>,
646
647 #[serde(default)]
648 pub first_id: Option<String>,
649 pub has_more: bool,
650 #[serde(default)]
651 pub last_id: Option<String>,
652}
653
654#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
655pub struct Model {
656 pub created_at: String,
657 pub display_name: String,
658 pub id: String,
659 #[serde(rename = "type")]
660 pub model_type: String,
661}
662
663pub type GetModelResponse = Model;
664
665#[cfg(test)]
666mod tests {
667 use serde_json::json;
668
669 use super::*;
670
671 #[test_log::test(tokio::test)]
672 async fn test_deserialize_response() {
673 let response = json!({
674 "id":"msg_01KkaCASJuaAgTWD2wqdbwC8",
675 "type":"message",
676 "role":"assistant",
677 "model":"claude-3-5-sonnet-20241022",
678 "content":[
679 {"type":"text",
680 "text":"Hi! How can I help you today?"}],
681 "stop_reason":"end_turn",
682 "stop_sequence":null,
683 "usage":{"input_tokens":10,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":12}}).to_string();
684
685 let response = serde_json::from_str::<CreateMessagesResponse>(&response).unwrap();
686
687 let usage = response.usage.as_ref().unwrap();
688
689 assert_eq!(usage.input_tokens, Some(10));
690 assert_eq!(usage.output_tokens, Some(12));
691 assert_eq!(usage.cache_creation_input_tokens, Some(0));
692 assert_eq!(usage.cache_read_input_tokens, Some(0));
693 assert_eq!(
694 response.id,
695 Some("msg_01KkaCASJuaAgTWD2wqdbwC8".to_string())
696 );
697 assert_eq!(
698 response.model,
699 Some("claude-3-5-sonnet-20241022".to_string())
700 );
701 assert_eq!(response.stop_reason, Some("end_turn".to_string()));
702 assert_eq!(response.stop_sequence, None);
703 assert_eq!(
704 response
705 .messages()
706 .first()
707 .unwrap()
708 .content
709 .first()
710 .unwrap()
711 .as_text(),
712 Some(&Text {
713 text: "Hi! How can I help you today?".to_string(),
714 cache_control: None,
715 })
716 );
717 }
718
719 #[test_log::test(tokio::test)]
720 async fn test_from_str() {
721 let message: Message = "Hello world!".into();
722
723 assert_eq!(
724 message,
725 Message {
726 role: MessageRole::User,
727 content: MessageContentList(vec![MessageContent::Text(Text {
728 text: "Hello world!".to_string(),
729 cache_control: None,
730 })]),
731 }
732 );
733
734 assert_eq!(message.text(), Some("Hello world!".to_string()));
735 }
736}