anthropic_async/
client.rs1use backon::ExponentialBuilder;
2use backon::Retryable;
3use serde::Serialize;
4use serde::de::DeserializeOwned;
5
6use crate::config::Config;
7use crate::error::AnthropicError;
8use crate::retry;
9
10#[derive(Debug, Clone)]
15pub struct Client<C: Config> {
16 http: reqwest::Client,
17 config: C,
18 backoff: ExponentialBuilder,
19}
20
21impl Client<crate::config::AnthropicConfig> {
22 #[must_use]
28 pub fn new() -> Self {
29 Self::with_config(crate::config::AnthropicConfig::new())
30 }
31}
32
33impl<C: Config + Default> Default for Client<C> {
34 fn default() -> Self {
35 Self::with_config(C::default())
36 }
37}
38
39impl<C: Config> Client<C> {
40 #[must_use]
46 #[expect(
47 clippy::expect_used,
48 reason = "reqwest client build failure is rare (TLS/resolver init) and fatal; matches reqwest::Client::new() pattern"
49 )]
50 pub fn with_config(config: C) -> Self {
51 Self {
52 http: reqwest::Client::builder()
53 .connect_timeout(std::time::Duration::from_secs(5))
54 .timeout(std::time::Duration::from_secs(600))
55 .build()
56 .expect("reqwest client"),
57 config,
58 backoff: retry::default_backoff_builder(),
59 }
60 }
61
62 #[must_use]
66 pub fn with_http_client(mut self, http: reqwest::Client) -> Self {
67 self.http = http;
68 self
69 }
70
71 #[must_use]
75 pub const fn with_backoff(mut self, backoff: ExponentialBuilder) -> Self {
76 self.backoff = backoff;
77 self
78 }
79
80 #[must_use]
82 pub const fn config(&self) -> &C {
83 &self.config
84 }
85
86 pub(crate) async fn get<O: DeserializeOwned>(&self, path: &str) -> Result<O, AnthropicError> {
87 let mk = || async {
88 let headers = self.config.headers()?;
89 Ok(self
90 .http
91 .get(self.config.url(path))
92 .headers(headers)
93 .query(&self.config.query())
94 .build()?)
95 };
96 self.execute(mk).await
97 }
98
99 pub(crate) async fn get_with_query<Q, O>(
100 &self,
101 path: &str,
102 query: &Q,
103 ) -> Result<O, AnthropicError>
104 where
105 Q: Serialize + Sync + ?Sized,
106 O: DeserializeOwned,
107 {
108 let mk = || async {
109 let headers = self.config.headers()?;
110 Ok(self
111 .http
112 .get(self.config.url(path))
113 .headers(headers)
114 .query(&self.config.query())
115 .query(query)
116 .build()?)
117 };
118 self.execute(mk).await
119 }
120
121 pub(crate) async fn post<I, O>(&self, path: &str, body: I) -> Result<O, AnthropicError>
122 where
123 I: Serialize + Send + Sync,
124 O: DeserializeOwned,
125 {
126 let mk = || async {
127 let headers = self.config.headers()?;
128 Ok(self
129 .http
130 .post(self.config.url(path))
131 .headers(headers)
132 .query(&self.config.query())
133 .json(&body)
134 .build()?)
135 };
136 self.execute(mk).await
137 }
138
139 #[cfg(feature = "streaming")]
143 pub(crate) async fn post_stream<I: Serialize + Send + Sync>(
144 &self,
145 path: &str,
146 body: I,
147 ) -> Result<reqwest::Response, AnthropicError> {
148 self.config.validate_auth()?;
150
151 let headers = self.config.headers()?;
152 let request = self
153 .http
154 .post(self.config.url(path))
155 .headers(headers)
156 .query(&self.config.query())
157 .json(&body)
158 .build()?;
159
160 let response = self
161 .http
162 .execute(request)
163 .await
164 .map_err(AnthropicError::Reqwest)?;
165
166 let status = response.status();
167 if status.is_success() {
168 Ok(response)
169 } else {
170 let bytes = response.bytes().await.map_err(AnthropicError::Reqwest)?;
171 Err(crate::error::deserialize_api_error(status, &bytes))
172 }
173 }
174
175 async fn execute<O, M, Fut>(&self, mk: M) -> Result<O, AnthropicError>
176 where
177 O: DeserializeOwned,
178 M: Fn() -> Fut + Send + Sync,
179 Fut: core::future::Future<Output = Result<reqwest::Request, AnthropicError>> + Send,
180 {
181 self.config.validate_auth()?;
183
184 let bytes = self.execute_raw(mk).await?;
185 let resp: O =
186 serde_json::from_slice(&bytes).map_err(|e| crate::error::map_deser(&e, &bytes))?;
187 Ok(resp)
188 }
189
190 async fn execute_raw<M, Fut>(&self, mk: M) -> Result<bytes::Bytes, AnthropicError>
191 where
192 M: Fn() -> Fut + Send + Sync,
193 Fut: core::future::Future<Output = Result<reqwest::Request, AnthropicError>> + Send,
194 {
195 let http_client = self.http.clone();
196
197 (|| async {
198 let request = mk().await?;
199 let response = http_client
200 .execute(request)
201 .await
202 .map_err(AnthropicError::Reqwest)?;
203
204 let status = response.status();
205 let bytes = response.bytes().await.map_err(AnthropicError::Reqwest)?;
206
207 if status.is_success() {
208 return Ok(bytes);
209 }
210
211 Err(crate::error::deserialize_api_error(status, &bytes))
212 })
213 .retry(self.backoff)
214 .when(AnthropicError::is_retryable)
215 .await
216 }
217}