Skip to main content

redis_vl/vectorizers/
anthropic.rs

1//! Anthropic embedding adapter.
2//!
3//! Enabled by the `anthropic` feature flag. Anthropic does not provide its own
4//! embedding model; instead it recommends [Voyage AI](https://www.voyageai.com/).
5//! This adapter wraps [`VoyageAITextVectorizer`] with Anthropic-oriented
6//! defaults and environment variable conventions.
7//!
8//! The default model is `voyage-3-large`, which Anthropic recommends for
9//! general-purpose and multilingual retrieval.
10
11use async_trait::async_trait;
12
13use super::voyageai::{VoyageAIConfig, VoyageAITextVectorizer};
14use super::{AsyncVectorizer, Vectorizer};
15use crate::error::Result;
16
17/// Default Voyage AI model recommended by Anthropic.
18const DEFAULT_MODEL: &str = "voyage-3-large";
19
20/// Configuration for the Anthropic (Voyage AI) embedding provider.
21///
22/// Since Anthropic does not offer its own embeddings, this delegates to Voyage AI.
23/// The API key can be supplied directly or read from the `VOYAGE_API_KEY`
24/// environment variable.
25#[derive(Debug, Clone)]
26pub struct AnthropicConfig {
27    /// Underlying Voyage AI configuration.
28    pub(crate) inner: VoyageAIConfig,
29}
30
31impl AnthropicConfig {
32    /// Creates a new Anthropic config with the given API key and model.
33    ///
34    /// The `api_key` is the Voyage AI API key (Anthropic recommends Voyage AI
35    /// for embeddings). The `model` selects the Voyage AI embedding model.
36    pub fn new(
37        api_key: impl Into<String>,
38        model: impl Into<String>,
39        input_type: Option<String>,
40    ) -> Self {
41        Self {
42            inner: VoyageAIConfig::new(api_key, model, input_type),
43        }
44    }
45
46    /// Creates an Anthropic config using the Anthropic-recommended default
47    /// model (`voyage-3-large`).
48    pub fn with_defaults(api_key: impl Into<String>, input_type: Option<String>) -> Self {
49        Self::new(api_key, DEFAULT_MODEL, input_type)
50    }
51
52    /// Constructs from the `VOYAGE_API_KEY` environment variable with the
53    /// Anthropic-recommended default model.
54    pub fn from_env(input_type: Option<String>) -> Result<Self> {
55        let inner = VoyageAIConfig::from_env(DEFAULT_MODEL, input_type)?;
56        Ok(Self { inner })
57    }
58
59    /// Constructs from the `VOYAGE_API_KEY` environment variable with a
60    /// specific model.
61    pub fn from_env_with_model(
62        model: impl Into<String>,
63        input_type: Option<String>,
64    ) -> Result<Self> {
65        let inner = VoyageAIConfig::from_env(model, input_type)?;
66        Ok(Self { inner })
67    }
68}
69
70/// Anthropic embedding adapter backed by Voyage AI.
71///
72/// Anthropic [recommends Voyage AI](https://docs.anthropic.com/en/docs/build-with-claude/embeddings)
73/// for embedding tasks. This adapter provides a convenience wrapper around
74/// [`VoyageAITextVectorizer`] with Anthropic-oriented defaults.
75///
76/// # Example
77///
78/// ```no_run
79/// use redis_vl::vectorizers::{AnthropicConfig, AnthropicTextVectorizer, Vectorizer};
80///
81/// let config = AnthropicConfig::with_defaults("my-voyage-key", Some("document".into()));
82/// let vectorizer = AnthropicTextVectorizer::new(config);
83/// let embedding = vectorizer.embed("Hello, world!").unwrap();
84/// ```
85#[derive(Debug, Clone)]
86pub struct AnthropicTextVectorizer {
87    inner: VoyageAITextVectorizer,
88}
89
90impl AnthropicTextVectorizer {
91    /// Creates a new Anthropic embedding adapter.
92    pub fn new(config: AnthropicConfig) -> Self {
93        Self {
94            inner: VoyageAITextVectorizer::new(config.inner),
95        }
96    }
97}
98
99impl Vectorizer for AnthropicTextVectorizer {
100    fn embed(&self, text: &str) -> Result<Vec<f32>> {
101        Vectorizer::embed(&self.inner, text)
102    }
103
104    fn embed_many(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>> {
105        Vectorizer::embed_many(&self.inner, texts)
106    }
107}
108
109#[async_trait]
110impl AsyncVectorizer for AnthropicTextVectorizer {
111    async fn embed(&self, text: &str) -> Result<Vec<f32>> {
112        AsyncVectorizer::embed(&self.inner, text).await
113    }
114
115    async fn embed_many(&self, texts: &[&str]) -> Result<Vec<Vec<f32>>> {
116        AsyncVectorizer::embed_many(&self.inner, texts).await
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn anthropic_config_stores_fields() {
126        let cfg = AnthropicConfig::new("key", "voyage-3-large", Some("document".into()));
127        assert_eq!(cfg.inner.api_key, "key");
128        assert_eq!(cfg.inner.model, "voyage-3-large");
129        assert_eq!(cfg.inner.input_type.as_deref(), Some("document"));
130    }
131
132    #[test]
133    fn anthropic_config_with_defaults_uses_default_model() {
134        let cfg = AnthropicConfig::with_defaults("key", None);
135        assert_eq!(cfg.inner.model, DEFAULT_MODEL);
136        assert!(cfg.inner.input_type.is_none());
137    }
138
139    #[test]
140    fn anthropic_vectorizer_is_send_sync() {
141        fn assert_send_sync<T: Send + Sync>() {}
142        assert_send_sync::<AnthropicTextVectorizer>();
143    }
144}