| 157 | |
| 158 | #[cfg(not(target_arch = "wasm32"))] |
| 159 | fn fastembed_embedding(text: &str, model: TextEmbeddingModel) -> anyhow::Result<Vec<f32>> { |
| 160 | let lock = FASTEMBED_TEXT_MODEL |
| 161 | .get_or_init(|| Mutex::new(SmallModelCache::new(text_model_cache_limit()))); |
| 162 | let mut guard = lock |
| 163 | .lock() |
| 164 | .map_err(|err| anyhow::anyhow!("text embedding mutex poisoned: {err}"))?; |
| 165 | |
| 166 | let instance = guard.get_or_try_insert_mut(model, || { |
| 167 | // Model initialization is expensive; cache a small LRU set for reuse. |
| 168 | let options = TextInitOptions::new(model.to_fastembed()); |
| 169 | TextEmbedding::try_new(options).map_err(|err| { |
| 170 | anyhow::anyhow!("failed to initialize text embedding model {:?}: {err}", model) |
| 171 | }) |
| 172 | })?; |
| 173 | let mut embeddings = instance |
| 174 | .embed(vec![text], None) |
| 175 | .map_err(|err| anyhow::anyhow!("text embedding failed for model {:?}: {err}", model))?; |
| 176 | embeddings.pop().ok_or_else(|| { |
| 177 | anyhow::anyhow!("text embedding model {:?} returned empty embedding result", model) |
| 178 | }) |
| 179 | } |
| 180 | |
| 181 | #[cfg(not(target_arch = "wasm32"))] |
| 182 | fn text_model_cache_limit() -> usize { |