1use async_trait::async_trait;
10use ragfs_core::{EmbedError, Embedder, EmbeddingConfig, EmbeddingOutput, Modality};
11
12pub struct NoopEmbedder {
37 dimension: usize,
38 modalities: Vec<Modality>,
39}
40
41impl NoopEmbedder {
42 #[must_use]
44 pub fn new() -> Self {
45 Self {
46 dimension: 384,
47 modalities: vec![Modality::Text],
48 }
49 }
50
51 #[must_use]
53 pub fn with_dimension(dimension: usize) -> Self {
54 Self {
55 dimension,
56 modalities: vec![Modality::Text],
57 }
58 }
59}
60
61impl Default for NoopEmbedder {
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67#[async_trait]
68impl Embedder for NoopEmbedder {
69 fn model_name(&self) -> &str {
70 "noop"
71 }
72
73 fn dimension(&self) -> usize {
74 self.dimension
75 }
76
77 fn max_tokens(&self) -> usize {
78 512
79 }
80
81 fn modalities(&self) -> &[Modality] {
82 &self.modalities
83 }
84
85 async fn embed_text(
86 &self,
87 texts: &[&str],
88 _config: &EmbeddingConfig,
89 ) -> Result<Vec<EmbeddingOutput>, EmbedError> {
90 Ok(texts
91 .iter()
92 .map(|_| EmbeddingOutput {
93 embedding: vec![0.0; self.dimension],
94 token_count: 0,
95 })
96 .collect())
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_noop_new() {
106 let embedder = NoopEmbedder::new();
107 assert_eq!(embedder.dimension(), 384);
108 assert_eq!(embedder.model_name(), "noop");
109 }
110
111 #[test]
112 fn test_noop_default() {
113 let embedder = NoopEmbedder::default();
114 assert_eq!(embedder.dimension(), 384);
115 }
116
117 #[test]
118 fn test_noop_with_dimension() {
119 let embedder = NoopEmbedder::with_dimension(768);
120 assert_eq!(embedder.dimension(), 768);
121 }
122
123 #[test]
124 fn test_noop_modalities() {
125 let embedder = NoopEmbedder::new();
126 let modalities = embedder.modalities();
127 assert_eq!(modalities.len(), 1);
128 assert!(matches!(modalities[0], Modality::Text));
129 }
130
131 #[test]
132 fn test_noop_max_tokens() {
133 let embedder = NoopEmbedder::new();
134 assert_eq!(embedder.max_tokens(), 512);
135 }
136
137 #[tokio::test]
138 async fn test_noop_embed_text() {
139 let embedder = NoopEmbedder::new();
140 let texts: Vec<&str> = vec!["Hello", "World"];
141 let config = EmbeddingConfig::default();
142
143 let outputs = embedder.embed_text(&texts, &config).await.unwrap();
144
145 assert_eq!(outputs.len(), 2);
146 assert_eq!(outputs[0].embedding.len(), 384);
147 assert_eq!(outputs[1].embedding.len(), 384);
148 assert!(outputs[0].embedding.iter().all(|&v| v == 0.0));
149 assert_eq!(outputs[0].token_count, 0);
150 }
151
152 #[tokio::test]
153 async fn test_noop_embed_empty() {
154 let embedder = NoopEmbedder::new();
155 let texts: Vec<&str> = vec![];
156 let config = EmbeddingConfig::default();
157
158 let outputs = embedder.embed_text(&texts, &config).await.unwrap();
159
160 assert!(outputs.is_empty());
161 }
162
163 #[tokio::test]
164 async fn test_noop_embed_custom_dimension() {
165 let embedder = NoopEmbedder::with_dimension(768);
166 let texts: Vec<&str> = vec!["Test"];
167 let config = EmbeddingConfig::default();
168
169 let outputs = embedder.embed_text(&texts, &config).await.unwrap();
170
171 assert_eq!(outputs[0].embedding.len(), 768);
172 }
173}