rig/providers/anthropic/
client.rs1use super::completion::{ANTHROPIC_VERSION_LATEST, CompletionModel};
3use crate::client::{CompletionClient, ProviderClient, impl_conversion_traits};
4
5const ANTHROPIC_API_BASE_URL: &str = "https://api.anthropic.com";
9
10#[derive(Clone, Debug)]
11pub struct ClientBuilder<'a> {
12 api_key: &'a str,
13 base_url: &'a str,
14 anthropic_version: &'a str,
15 anthropic_betas: Option<Vec<&'a str>>,
16}
17
18impl<'a> ClientBuilder<'a> {
31 pub fn new(api_key: &'a str) -> Self {
32 Self {
33 api_key,
34 base_url: ANTHROPIC_API_BASE_URL,
35 anthropic_version: ANTHROPIC_VERSION_LATEST,
36 anthropic_betas: None,
37 }
38 }
39
40 pub fn base_url(mut self, base_url: &'a str) -> Self {
41 self.base_url = base_url;
42 self
43 }
44
45 pub fn anthropic_version(mut self, anthropic_version: &'a str) -> Self {
46 self.anthropic_version = anthropic_version;
47 self
48 }
49
50 pub fn anthropic_beta(mut self, anthropic_beta: &'a str) -> Self {
51 if let Some(mut betas) = self.anthropic_betas {
52 betas.push(anthropic_beta);
53 self.anthropic_betas = Some(betas);
54 } else {
55 self.anthropic_betas = Some(vec![anthropic_beta]);
56 }
57 self
58 }
59
60 pub fn build(self) -> Client {
61 Client::new(
62 self.api_key,
63 self.base_url,
64 self.anthropic_betas,
65 self.anthropic_version,
66 )
67 }
68}
69
70#[derive(Clone)]
71pub struct Client {
72 base_url: String,
74 api_key: String,
76 http_client: reqwest::Client,
78 default_headers: reqwest::header::HeaderMap,
80}
81
82impl std::fmt::Debug for Client {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 f.debug_struct("Client")
85 .field("base_url", &self.base_url)
86 .field("http_client", &self.http_client)
87 .field("api_key", &"<REDACTED>")
88 .field("default_headers", &self.default_headers)
89 .finish()
90 }
91}
92
93impl Client {
94 pub fn new(api_key: &str, base_url: &str, betas: Option<Vec<&str>>, version: &str) -> Self {
102 let mut default_headers = reqwest::header::HeaderMap::new();
103 default_headers.insert(
104 "anthropic-version",
105 version.parse().expect("Anthropic version should parse"),
106 );
107 if let Some(betas) = betas {
108 default_headers.insert(
109 "anthropic-beta",
110 betas
111 .join(",")
112 .parse()
113 .expect("Anthropic betas should parse"),
114 );
115 };
116
117 Self {
118 base_url: base_url.to_string(),
119 api_key: api_key.to_string(),
120 default_headers,
121 http_client: reqwest::Client::builder()
122 .build()
123 .expect("Anthropic reqwest client should build"),
124 }
125 }
126
127 pub fn with_custom_client(mut self, client: reqwest::Client) -> Self {
130 self.http_client = client;
131
132 self
133 }
134
135 pub fn post(&self, path: &str) -> reqwest::RequestBuilder {
136 let url = format!("{}/{}", self.base_url, path).replace("//", "/");
137 self.http_client
138 .post(url)
139 .header("X-Api-Key", &self.api_key)
140 .headers(self.default_headers.clone())
141 }
142}
143
144impl ProviderClient for Client {
145 fn from_env() -> Self {
148 let api_key = std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY not set");
149 ClientBuilder::new(&api_key).build()
150 }
151}
152
153impl CompletionClient for Client {
154 type CompletionModel = CompletionModel;
155 fn completion_model(&self, model: &str) -> CompletionModel {
156 CompletionModel::new(self.clone(), model)
157 }
158}
159
160impl_conversion_traits!(
161 AsTranscription,
162 AsEmbeddings,
163 AsImageGeneration,
164 AsAudioGeneration for Client
165);