anthropic_async/
client.rs1use serde::{Serialize, de::DeserializeOwned};
2
3use crate::{config::Config, error::AnthropicError, retry};
4
5#[derive(Debug, Clone)]
10pub struct Client<C: Config> {
11 http: reqwest::Client,
12 config: C,
13 backoff: backoff::ExponentialBackoff,
14}
15
16impl Client<crate::config::AnthropicConfig> {
17 #[must_use]
23 pub fn new() -> Self {
24 Self::with_config(crate::config::AnthropicConfig::new())
25 }
26}
27
28impl<C: Config + Default> Default for Client<C> {
29 fn default() -> Self {
30 Self::with_config(C::default())
31 }
32}
33
34impl<C: Config> Client<C> {
35 #[must_use]
41 pub fn with_config(config: C) -> Self {
42 Self {
43 http: reqwest::Client::builder()
44 .connect_timeout(std::time::Duration::from_secs(5))
45 .timeout(std::time::Duration::from_secs(600))
46 .build()
47 .expect("reqwest client"),
48 config,
49 backoff: retry::default_backoff(),
50 }
51 }
52
53 #[must_use]
57 pub fn with_http_client(mut self, http: reqwest::Client) -> Self {
58 self.http = http;
59 self
60 }
61
62 #[must_use]
66 pub const fn with_backoff(mut self, backoff: backoff::ExponentialBackoff) -> Self {
67 self.backoff = backoff;
68 self
69 }
70
71 #[must_use]
73 pub const fn config(&self) -> &C {
74 &self.config
75 }
76
77 pub(crate) async fn get<O: DeserializeOwned>(&self, path: &str) -> Result<O, AnthropicError> {
78 let mk = || async {
79 let headers = self.config.headers()?;
80 Ok(self
81 .http
82 .get(self.config.url(path))
83 .headers(headers)
84 .query(&self.config.query())
85 .build()?)
86 };
87 self.execute(mk).await
88 }
89
90 pub(crate) async fn get_with_query<Q, O>(
91 &self,
92 path: &str,
93 query: &Q,
94 ) -> Result<O, AnthropicError>
95 where
96 Q: Serialize + Sync + ?Sized,
97 O: DeserializeOwned,
98 {
99 let mk = || async {
100 let headers = self.config.headers()?;
101 Ok(self
102 .http
103 .get(self.config.url(path))
104 .headers(headers)
105 .query(&self.config.query())
106 .query(query)
107 .build()?)
108 };
109 self.execute(mk).await
110 }
111
112 pub(crate) async fn post<I, O>(&self, path: &str, body: I) -> Result<O, AnthropicError>
113 where
114 I: Serialize + Send + Sync,
115 O: DeserializeOwned,
116 {
117 let mk = || async {
118 let headers = self.config.headers()?;
119 Ok(self
120 .http
121 .post(self.config.url(path))
122 .headers(headers)
123 .query(&self.config.query())
124 .json(&body)
125 .build()?)
126 };
127 self.execute(mk).await
128 }
129
130 async fn execute<O, M, Fut>(&self, mk: M) -> Result<O, AnthropicError>
131 where
132 O: DeserializeOwned,
133 M: Fn() -> Fut + Send + Sync,
134 Fut: core::future::Future<Output = Result<reqwest::Request, AnthropicError>> + Send,
135 {
136 self.config.validate_auth()?;
138
139 let bytes = self.execute_raw(mk).await?;
140 let resp: O =
141 serde_json::from_slice(&bytes).map_err(|e| crate::error::map_deser(&e, &bytes))?;
142 Ok(resp)
143 }
144
145 async fn execute_raw<M, Fut>(&self, mk: M) -> Result<bytes::Bytes, AnthropicError>
146 where
147 M: Fn() -> Fut + Send + Sync,
148 Fut: core::future::Future<Output = Result<reqwest::Request, AnthropicError>> + Send,
149 {
150 let http_client = self.http.clone();
151
152 backoff::future::retry(self.backoff.clone(), || async {
153 let request = mk().await.map_err(backoff::Error::Permanent)?;
154 let response = http_client
155 .execute(request)
156 .await
157 .map_err(AnthropicError::Reqwest)
158 .map_err(backoff::Error::Permanent)?;
159
160 let status = response.status();
161 let headers = response.headers().clone();
162 let bytes = response
163 .bytes()
164 .await
165 .map_err(AnthropicError::Reqwest)
166 .map_err(backoff::Error::Permanent)?;
167
168 if status.is_success() {
169 return Ok(bytes);
170 }
171
172 if crate::retry::is_retryable_status(status.as_u16()) {
173 let err = crate::error::deserialize_api_error(status, &bytes);
174 if let Some(retry_after) = crate::retry::parse_retry_after(&headers) {
175 return Err(backoff::Error::retry_after(err, retry_after));
176 }
177 return Err(backoff::Error::transient(err));
178 }
179
180 Err(backoff::Error::Permanent(
181 crate::error::deserialize_api_error(status, &bytes),
182 ))
183 })
184 .await
185 }
186}