Skip to main content

rig/providers/anthropic/
client.rs

1//! Anthropic client api implementation
2use 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// ================================================================
14// Main Anthropic Client
15// ================================================================
16#[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
132/// Create a new anthropic client using the builder
133///
134/// # Example
135/// ```
136/// use rig::providers::anthropic::{ClientBuilder, self};
137///
138/// // Initialize the Anthropic client
139/// let anthropic_client = ClientBuilder::new("your-claude-api-key")
140///    .anthropic_version(ANTHROPIC_VERSION_LATEST)
141///    .anthropic_beta("prompt-caching-2024-07-31")
142///    .build()
143/// ```
144impl<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}