rig/providers/anthropic/
client.rs1use http::{HeaderName, HeaderValue};
3
4use super::completion::{ANTHROPIC_VERSION_LATEST, CompletionModel};
5use crate::{
6 client::{
7 self, ApiKey, Capabilities, Capable, DebugExt, Nothing, Provider, ProviderBuilder,
8 ProviderClient,
9 },
10 http_client,
11};
12
13#[derive(Debug, Default, Clone)]
17pub struct AnthropicExt;
18
19impl Provider for AnthropicExt {
20 type Builder = AnthropicBuilder;
21
22 const VERIFY_PATH: &'static str = "/v1/models";
23
24 fn build<H>(
25 _builder: &client::ClientBuilder<Self::Builder, AnthropicKey, H>,
26 ) -> http_client::Result<Self> {
27 Ok(Self)
28 }
29}
30
31impl<H> Capabilities<H> for AnthropicExt {
32 type Completion = Capable<CompletionModel<H>>;
33
34 type Embeddings = Nothing;
35 type Transcription = Nothing;
36 type ModelListing = Nothing;
37 #[cfg(feature = "image")]
38 type ImageGeneration = Nothing;
39 #[cfg(feature = "audio")]
40 type AudioGeneration = Nothing;
41}
42
43#[derive(Debug, Clone)]
44pub struct AnthropicBuilder {
45 anthropic_version: String,
46 anthropic_betas: Vec<String>,
47}
48
49#[derive(Debug, Clone)]
50pub struct AnthropicKey(String);
51
52impl<S> From<S> for AnthropicKey
53where
54 S: Into<String>,
55{
56 fn from(value: S) -> Self {
57 Self(value.into())
58 }
59}
60
61impl ApiKey for AnthropicKey {
62 fn into_header(self) -> Option<http_client::Result<(http::HeaderName, HeaderValue)>> {
63 Some(
64 HeaderValue::from_str(&self.0)
65 .map(|val| (HeaderName::from_static("x-api-key"), val))
66 .map_err(Into::into),
67 )
68 }
69}
70
71pub type Client<H = reqwest::Client> = client::Client<AnthropicExt, H>;
72pub type ClientBuilder<H = reqwest::Client> =
73 client::ClientBuilder<AnthropicBuilder, AnthropicKey, H>;
74
75impl Default for AnthropicBuilder {
76 fn default() -> Self {
77 Self {
78 anthropic_version: ANTHROPIC_VERSION_LATEST.into(),
79 anthropic_betas: Vec::new(),
80 }
81 }
82}
83
84impl ProviderBuilder for AnthropicBuilder {
85 type Output = AnthropicExt;
86 type ApiKey = AnthropicKey;
87
88 const BASE_URL: &'static str = "https://api.anthropic.com";
89
90 fn finish<H>(
91 &self,
92 mut builder: client::ClientBuilder<Self, AnthropicKey, H>,
93 ) -> http_client::Result<client::ClientBuilder<Self, AnthropicKey, H>> {
94 builder.headers_mut().insert(
95 "anthropic-version",
96 HeaderValue::from_str(&self.anthropic_version)?,
97 );
98
99 if !self.anthropic_betas.is_empty() {
100 builder.headers_mut().insert(
101 "anthropic-beta",
102 HeaderValue::from_str(&self.anthropic_betas.join(","))?,
103 );
104 }
105
106 Ok(builder)
107 }
108}
109
110impl DebugExt for AnthropicExt {}
111
112impl ProviderClient for Client {
113 type Input = String;
114
115 fn from_env() -> Self
116 where
117 Self: Sized,
118 {
119 let key = std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY not set");
120
121 Self::builder().api_key(key).build().unwrap()
122 }
123
124 fn from_val(input: Self::Input) -> Self
125 where
126 Self: Sized,
127 {
128 Self::builder().api_key(input).build().unwrap()
129 }
130}
131
132impl<H> ClientBuilder<H> {
145 pub fn anthropic_version(self, anthropic_version: &str) -> Self {
146 self.over_ext(|ext| AnthropicBuilder {
147 anthropic_version: anthropic_version.into(),
148 ..ext
149 })
150 }
151
152 pub fn anthropic_betas(self, anthropic_betas: &[&str]) -> Self {
153 self.over_ext(|mut ext| {
154 ext.anthropic_betas
155 .extend(anthropic_betas.iter().copied().map(String::from));
156
157 ext
158 })
159 }
160
161 pub fn anthropic_beta(self, anthropic_beta: &str) -> Self {
162 self.over_ext(|mut ext| {
163 ext.anthropic_betas.push(anthropic_beta.into());
164
165 ext
166 })
167 }
168}
169#[cfg(test)]
170mod tests {
171 #[test]
172 fn test_client_initialization() {
173 let _client: crate::providers::anthropic::Client =
174 crate::providers::anthropic::Client::new("dummy-key").expect("Client::new() failed");
175 let _client_from_builder: crate::providers::anthropic::Client =
176 crate::providers::anthropic::Client::builder()
177 .api_key("dummy-key")
178 .build()
179 .expect("Client::builder() failed");
180 }
181}