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