rig/providers/anthropic/
client.rs1use super::completion::{ANTHROPIC_VERSION_LATEST, CompletionModel};
3use crate::client::{
4 ClientBuilderError, CompletionClient, ProviderClient, ProviderValue, impl_conversion_traits,
5};
6
7const ANTHROPIC_API_BASE_URL: &str = "https://api.anthropic.com";
11
12pub struct ClientBuilder<'a> {
13 api_key: &'a str,
14 base_url: &'a str,
15 anthropic_version: &'a str,
16 anthropic_betas: Option<Vec<&'a str>>,
17 http_client: Option<reqwest::Client>,
18}
19
20impl<'a> ClientBuilder<'a> {
33 pub fn new(api_key: &'a str) -> Self {
34 Self {
35 api_key,
36 base_url: ANTHROPIC_API_BASE_URL,
37 anthropic_version: ANTHROPIC_VERSION_LATEST,
38 anthropic_betas: None,
39 http_client: None,
40 }
41 }
42
43 pub fn base_url(mut self, base_url: &'a str) -> Self {
44 self.base_url = base_url;
45 self
46 }
47
48 pub fn anthropic_version(mut self, anthropic_version: &'a str) -> Self {
49 self.anthropic_version = anthropic_version;
50 self
51 }
52
53 pub fn anthropic_beta(mut self, anthropic_beta: &'a str) -> Self {
54 if let Some(mut betas) = self.anthropic_betas {
55 betas.push(anthropic_beta);
56 self.anthropic_betas = Some(betas);
57 } else {
58 self.anthropic_betas = Some(vec![anthropic_beta]);
59 }
60 self
61 }
62
63 pub fn custom_client(mut self, client: reqwest::Client) -> Self {
64 self.http_client = Some(client);
65 self
66 }
67
68 pub fn build(self) -> Result<Client, ClientBuilderError> {
69 let mut default_headers = reqwest::header::HeaderMap::new();
70 default_headers.insert(
71 "anthropic-version",
72 self.anthropic_version
73 .parse()
74 .map_err(|_| ClientBuilderError::InvalidProperty("anthropic-version"))?,
75 );
76 if let Some(betas) = self.anthropic_betas {
77 default_headers.insert(
78 "anthropic-beta",
79 betas
80 .join(",")
81 .parse()
82 .map_err(|_| ClientBuilderError::InvalidProperty("anthropic-beta"))?,
83 );
84 };
85
86 let http_client = if let Some(http_client) = self.http_client {
87 http_client
88 } else {
89 reqwest::Client::builder().build()?
90 };
91
92 Ok(Client {
93 base_url: self.base_url.to_string(),
94 api_key: self.api_key.to_string(),
95 default_headers,
96 http_client,
97 })
98 }
99}
100
101#[derive(Clone)]
102pub struct Client {
103 base_url: String,
105 api_key: String,
107 http_client: reqwest::Client,
109 default_headers: reqwest::header::HeaderMap,
111}
112
113impl std::fmt::Debug for Client {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.debug_struct("Client")
116 .field("base_url", &self.base_url)
117 .field("http_client", &self.http_client)
118 .field("api_key", &"<REDACTED>")
119 .field("default_headers", &self.default_headers)
120 .finish()
121 }
122}
123
124impl Client {
125 pub fn builder(api_key: &str) -> ClientBuilder<'_> {
138 ClientBuilder::new(api_key)
139 }
140
141 pub fn new(api_key: &str) -> Self {
147 Self::builder(api_key)
148 .build()
149 .expect("Anthropic client should build")
150 }
151
152 pub(crate) fn post(&self, path: &str) -> reqwest::RequestBuilder {
153 let url = format!("{}/{}", self.base_url, path).replace("//", "/");
154 self.http_client
155 .post(url)
156 .header("X-Api-Key", &self.api_key)
157 .headers(self.default_headers.clone())
158 }
159}
160
161impl ProviderClient for Client {
162 fn from_env() -> Self {
165 let api_key = std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY not set");
166 Client::new(&api_key)
167 }
168
169 fn from_val(input: crate::client::ProviderValue) -> Self {
170 let ProviderValue::Simple(api_key) = input else {
171 panic!("Incorrect provider value type")
172 };
173 Client::new(&api_key)
174 }
175}
176
177impl CompletionClient for Client {
178 type CompletionModel = CompletionModel;
179 fn completion_model(&self, model: &str) -> CompletionModel {
180 CompletionModel::new(self.clone(), model)
181 }
182}
183
184impl_conversion_traits!(
185 AsTranscription,
186 AsEmbeddings,
187 AsImageGeneration,
188 AsAudioGeneration for Client
189);