alith_interface/llms/api/anthropic/
mod.rs1pub mod builder;
2pub mod completion;
3
4use super::{
5 client::ApiClient,
6 config::{ApiConfig, ApiConfigTrait},
7};
8use crate::requests::completion::{
9 error::CompletionError, request::CompletionRequest, response::CompletionResponse,
10};
11use alith_devices::logging::LoggingConfig;
12use alith_models::api_model::ApiLLMModel;
13use completion::AnthropicCompletionRequest;
14use reqwest::header::HeaderMap;
15use secrecy::{ExposeSecret, SecretString};
16
17pub const ANTHROPIC_API_HOST: &str = "api.anthropic.com/v1";
19pub const ANTHROPIC_VERSION_HEADER: &str = "anthropic-version";
21pub const ANTHROPIC_BETA_HEADER: &str = "anthropic-beta";
23
24pub struct AnthropicBackend {
25 pub(crate) client: ApiClient<AnthropicConfig>,
26 pub model: ApiLLMModel,
27}
28
29impl AnthropicBackend {
30 pub fn new(mut config: AnthropicConfig, model: ApiLLMModel) -> crate::Result<Self> {
31 config.logging_config.load_logger()?;
32 config.api_config.api_key = Some(config.api_config.load_api_key()?);
33 Ok(Self {
34 client: ApiClient::new(config),
35 model,
36 })
37 }
38 pub(crate) async fn completion_request(
39 &self,
40 request: &CompletionRequest,
41 ) -> crate::Result<CompletionResponse, CompletionError> {
42 match self
43 .client
44 .post("/messages", AnthropicCompletionRequest::new(request)?)
45 .await
46 {
47 Err(e) => Err(CompletionError::ClientError(e)),
48 Ok(res) => Ok(CompletionResponse::new_from_anthropic(request, res)?),
49 }
50 }
51}
52
53#[derive(Clone, Debug)]
54pub struct AnthropicConfig {
55 pub api_config: ApiConfig,
56 pub logging_config: LoggingConfig,
57 pub anthropic_version: String,
58 pub anthropic_beta: Option<String>,
59}
60
61impl Default for AnthropicConfig {
62 fn default() -> Self {
63 Self {
64 api_config: ApiConfig {
65 host: ANTHROPIC_API_HOST.to_string(),
66 port: None,
67 api_key: None,
68 api_key_env_var: "ANTHROPIC_API_KEY".to_string(),
69 },
70 logging_config: LoggingConfig {
71 logger_name: "anthropic".to_string(),
72 ..Default::default()
73 },
74 anthropic_version: "2023-06-01".to_string(),
75 anthropic_beta: None,
76 }
77 }
78}
79
80impl AnthropicConfig {
81 pub fn new() -> Self {
82 Default::default()
83 }
84
85 pub fn with_anthropic_version<S: Into<String>>(mut self, version: S) -> Self {
86 self.anthropic_version = version.into();
87 self
88 }
89
90 pub fn with_anthropic_beta<S: Into<String>>(mut self, beta: S) -> Self {
91 self.anthropic_beta = Some(beta.into());
92 self
93 }
94}
95
96impl ApiConfigTrait for AnthropicConfig {
97 fn headers(&self) -> HeaderMap {
98 let mut headers = HeaderMap::new();
99 headers.insert(
100 ANTHROPIC_VERSION_HEADER,
101 self.anthropic_version.as_str().parse().unwrap(),
102 );
103
104 if let Some(anthropic_beta) = &self.anthropic_beta {
105 headers.insert(
106 ANTHROPIC_BETA_HEADER,
107 anthropic_beta.as_str().parse().unwrap(),
108 );
109 }
110
111 if let Some(api_key) = self.api_key() {
112 headers.insert(
113 reqwest::header::HeaderName::from_static("x-api-key"),
114 reqwest::header::HeaderValue::from_str(api_key.expose_secret()).unwrap(),
115 );
116 }
117
118 headers
119 }
120
121 fn url(&self, path: &str) -> String {
122 format!("https://{}{}", self.api_config.host, path)
123 }
124
125 fn api_key(&self) -> &Option<SecretString> {
126 &self.api_config.api_key
127 }
128}