agentik_sdk/http/
client.rs1use crate::config::ClientConfig;
2use crate::http::auth::AuthHandler;
3use crate::types::errors::{AnthropicError, Result};
4use crate::types::shared::RequestId;
5use reqwest::{Client, Request, RequestBuilder, Response};
6use serde_json::Value;
7
8#[derive(Debug, Clone)]
9pub struct HttpClient {
10 client: Client,
11 config: ClientConfig,
12 auth: AuthHandler,
13}
14
15impl HttpClient {
16 pub fn new(config: ClientConfig) -> Result<Self> {
17 config.validate()?;
19
20 let client = Client::builder()
21 .timeout(config.timeout)
22 .build()
23 .map_err(|e| AnthropicError::Connection {
24 message: e.to_string(),
25 })?;
26
27 let auth = AuthHandler::with_method(config.api_key.clone(), config.auth_method.clone());
28
29 Ok(Self {
30 client,
31 config,
32 auth,
33 })
34 }
35
36 pub async fn send(&self, mut request: Request) -> Result<Response> {
38 let headers = request.headers_mut();
40 self.auth.add_auth_headers(headers)?;
41
42 let response =
43 self.client
44 .execute(request)
45 .await
46 .map_err(|e| AnthropicError::Connection {
47 message: e.to_string(),
48 })?;
49
50 self.handle_response_status(response).await
51 }
52
53 pub fn get(&self, url: &str) -> RequestBuilder {
55 self.client.get(url)
56 }
57
58 pub fn post(&self, url: &str) -> RequestBuilder {
60 self.client.post(url)
61 }
62
63 pub fn put(&self, url: &str) -> RequestBuilder {
65 self.client.put(url)
66 }
67
68 pub fn delete(&self, url: &str) -> RequestBuilder {
70 self.client.delete(url)
71 }
72
73 pub fn build_url(&self, path: &str) -> String {
75 format!("{}{}", self.config.base_url.trim_end_matches('/'), path)
76 }
77
78 async fn handle_response_status(&self, response: Response) -> Result<Response> {
80 let status = response.status();
81 if status.is_success() {
82 return Ok(response);
83 }
84
85 let status_code = status.as_u16();
86
87 let error_message = match response.text().await {
89 Ok(body) => {
90 match serde_json::from_str::<Value>(&body) {
92 Ok(json) => json
93 .get("error")
94 .and_then(|e| e.get("message"))
95 .and_then(|m| m.as_str())
96 .unwrap_or(&body)
97 .to_string(),
98 Err(_) => body,
99 }
100 }
101 Err(_) => format!(
102 "HTTP {}: {}",
103 status_code,
104 status.canonical_reason().unwrap_or("Unknown")
105 ),
106 };
107
108 Err(AnthropicError::from_status(status_code, error_message))
109 }
110
111 pub fn extract_request_id(&self, response: &Response) -> Option<RequestId> {
113 response
114 .headers()
115 .get("request-id")
116 .and_then(|value| value.to_str().ok())
117 .map(|id| RequestId::new(id.to_string()))
118 }
119
120 pub fn base_url(&self) -> &str {
122 &self.config.base_url
123 }
124
125 pub fn config(&self) -> &ClientConfig {
127 &self.config
128 }
129
130 pub fn client(&self) -> &Client {
134 &self.client
135 }
136}
137