anthropic_async/resources/
messages.rs1use crate::{
2 client::Client,
3 config::Config,
4 error::AnthropicError,
5 types::common::validate_mixed_ttl_order,
6 types::content::{ContentBlockParam, MessageContentParam, SystemParam},
7 types::messages::{
8 MessageTokensCountRequest, MessageTokensCountResponse, MessagesCreateRequest,
9 MessagesCreateResponse,
10 },
11};
12
13fn validate_messages_create_request(req: &MessagesCreateRequest) -> Result<(), AnthropicError> {
17 let mut ttls = Vec::new();
19
20 if let Some(system) = &req.system
22 && let SystemParam::Blocks(blocks) = system
23 {
24 for tb in blocks {
25 if let Some(cc) = &tb.cache_control
26 && let Some(ttl) = &cc.ttl
27 {
28 ttls.push(ttl.clone());
29 }
30 }
31 }
32
33 for message in &req.messages {
35 if let MessageContentParam::Blocks(blocks) = &message.content {
36 for block in blocks {
37 match block {
38 ContentBlockParam::Text {
39 cache_control: Some(cc),
40 ..
41 }
42 | ContentBlockParam::Image {
43 cache_control: Some(cc),
44 ..
45 }
46 | ContentBlockParam::Document {
47 cache_control: Some(cc),
48 ..
49 }
50 | ContentBlockParam::ToolResult {
51 cache_control: Some(cc),
52 ..
53 } => {
54 if let Some(ttl) = &cc.ttl {
55 ttls.push(ttl.clone());
56 }
57 }
58 _ => {}
59 }
60 }
61 }
62 }
63
64 if !validate_mixed_ttl_order(ttls) {
65 return Err(AnthropicError::Config(
66 "Invalid cache_control TTL ordering: 1h must precede 5m".into(),
67 ));
68 }
69
70 if let Some(t) = req.temperature
72 && !(0.0..=1.0).contains(&t)
73 {
74 return Err(AnthropicError::Config(format!(
75 "Invalid temperature {t}: must be in [0.0, 1.0]"
76 )));
77 }
78
79 if let Some(p) = req.top_p
80 && (!(0.0..=1.0).contains(&p) || p == 0.0)
81 {
82 return Err(AnthropicError::Config(format!(
83 "Invalid top_p {p}: must be in (0.0, 1.0]"
84 )));
85 }
86
87 if let Some(k) = req.top_k
88 && k < 1
89 {
90 return Err(AnthropicError::Config(format!(
91 "Invalid top_k {k}: must be >= 1"
92 )));
93 }
94
95 if req.max_tokens == 0 {
96 return Err(AnthropicError::Config(
97 "max_tokens must be greater than 0".into(),
98 ));
99 }
100
101 Ok(())
102}
103
104pub struct Messages<'c, C: Config> {
108 client: &'c Client<C>,
109}
110
111impl<'c, C: Config> Messages<'c, C> {
112 #[must_use]
114 pub const fn new(client: &'c Client<C>) -> Self {
115 Self { client }
116 }
117
118 pub async fn create(
127 &self,
128 req: MessagesCreateRequest,
129 ) -> Result<MessagesCreateResponse, AnthropicError> {
130 validate_messages_create_request(&req)?;
132
133 self.client.post("/v1/messages", req).await
134 }
135
136 pub async fn count_tokens(
144 &self,
145 req: MessageTokensCountRequest,
146 ) -> Result<MessageTokensCountResponse, AnthropicError> {
147 self.client.post("/v1/messages/count_tokens", req).await
149 }
150
151 #[cfg(feature = "streaming")]
181 pub async fn create_stream(
182 &self,
183 mut req: MessagesCreateRequest,
184 ) -> Result<crate::streaming::EventStream, AnthropicError> {
185 req.stream = Some(true);
187
188 validate_messages_create_request(&req)?;
190
191 let response = self.client.post_stream("/v1/messages", req).await?;
192 Ok(crate::sse::streaming::event_stream_from_response(response))
193 }
194}
195
196impl<C: Config> crate::Client<C> {
198 #[must_use]
200 pub const fn messages(&self) -> Messages<'_, C> {
201 Messages::new(self)
202 }
203}