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