rig/providers/anthropic/
client.rs

1//! Anthropic client api implementation
2
3use super::completion::{CompletionModel, ANTHROPIC_VERSION_LATEST};
4use crate::client::{impl_conversion_traits, CompletionClient, ProviderClient};
5
6// ================================================================
7// Main Anthropic Client
8// ================================================================
9const ANTHROPIC_API_BASE_URL: &str = "https://api.anthropic.com";
10
11#[derive(Clone, Debug)]
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}
18
19/// Create a new anthropic client using the builder
20///
21/// # Example
22/// ```
23/// use rig::providers::anthropic::{ClientBuilder, self};
24///
25/// // Initialize the Anthropic client
26/// let anthropic_client = ClientBuilder::new("your-claude-api-key")
27///    .anthropic_version(ANTHROPIC_VERSION_LATEST)
28///    .anthropic_beta("prompt-caching-2024-07-31")
29///    .build()
30/// ```
31impl<'a> ClientBuilder<'a> {
32    pub fn new(api_key: &'a str) -> Self {
33        Self {
34            api_key,
35            base_url: ANTHROPIC_API_BASE_URL,
36            anthropic_version: ANTHROPIC_VERSION_LATEST,
37            anthropic_betas: None,
38        }
39    }
40
41    pub fn base_url(mut self, base_url: &'a str) -> Self {
42        self.base_url = base_url;
43        self
44    }
45
46    pub fn anthropic_version(mut self, anthropic_version: &'a str) -> Self {
47        self.anthropic_version = anthropic_version;
48        self
49    }
50
51    pub fn anthropic_beta(mut self, anthropic_beta: &'a str) -> Self {
52        if let Some(mut betas) = self.anthropic_betas {
53            betas.push(anthropic_beta);
54            self.anthropic_betas = Some(betas);
55        } else {
56            self.anthropic_betas = Some(vec![anthropic_beta]);
57        }
58        self
59    }
60
61    pub fn build(self) -> Client {
62        Client::new(
63            self.api_key,
64            self.base_url,
65            self.anthropic_betas,
66            self.anthropic_version,
67        )
68    }
69}
70
71#[derive(Clone, Debug)]
72pub struct Client {
73    base_url: String,
74    http_client: reqwest::Client,
75}
76
77impl Client {
78    /// Create a new Anthropic client with the given API key, base URL, betas, and version.
79    /// Note, you probably want to use the `ClientBuilder` instead.
80    ///
81    /// Panics:
82    /// - If the API key or version cannot be parsed as a Json value from a String.
83    ///   - This should really never happen.
84    /// - If the reqwest client cannot be built (if the TLS backend cannot be initialized).
85    pub fn new(api_key: &str, base_url: &str, betas: Option<Vec<&str>>, version: &str) -> Self {
86        Self {
87            base_url: base_url.to_string(),
88            http_client: reqwest::Client::builder()
89                .default_headers({
90                    let mut headers = reqwest::header::HeaderMap::new();
91                    headers.insert("x-api-key", api_key.parse().expect("API key should parse"));
92                    headers.insert(
93                        "anthropic-version",
94                        version.parse().expect("Anthropic version should parse"),
95                    );
96                    if let Some(betas) = betas {
97                        headers.insert(
98                            "anthropic-beta",
99                            betas
100                                .join(",")
101                                .parse()
102                                .expect("Anthropic betas should parse"),
103                        );
104                    }
105                    headers
106                })
107                .build()
108                .expect("Anthropic reqwest client should build"),
109        }
110    }
111
112    pub fn post(&self, path: &str) -> reqwest::RequestBuilder {
113        let url = format!("{}/{}", self.base_url, path).replace("//", "/");
114        self.http_client.post(url)
115    }
116}
117
118impl ProviderClient for Client {
119    /// Create a new Anthropic client from the `ANTHROPIC_API_KEY` environment variable.
120    /// Panics if the environment variable is not set.
121    fn from_env() -> Self {
122        let api_key = std::env::var("ANTHROPIC_API_KEY").expect("ANTHROPIC_API_KEY not set");
123        ClientBuilder::new(&api_key).build()
124    }
125}
126
127impl CompletionClient for Client {
128    type CompletionModel = CompletionModel;
129    fn completion_model(&self, model: &str) -> CompletionModel {
130        CompletionModel::new(self.clone(), model)
131    }
132}
133
134impl_conversion_traits!(
135    AsTranscription,
136    AsEmbeddings,
137    AsImageGeneration,
138    AsAudioGeneration for Client
139);