semtree_embed/embedder.rs
1use async_trait::async_trait;
2
3use crate::{EmbedError, Embedding};
4
5#[async_trait]
6pub trait Embedder: Send + Sync {
7 async fn embed(&self, texts: &[&str]) -> Result<Vec<Embedding>, EmbedError>;
8
9 async fn embed_one(&self, text: &str) -> Result<Embedding, EmbedError> {
10 self.embed(&[text]).await.map(|mut v| v.remove(0))
11 }
12
13 /// Largest number of texts a single [`embed`](Embedder::embed) call should
14 /// receive. Callers indexing many chunks split their work into groups of at
15 /// most this size, bounding peak memory (local models) and request size
16 /// (remote APIs). The default suits the on-device models; raise or lower it
17 /// to match a backend's limits.
18 fn max_batch_size(&self) -> usize {
19 256
20 }
21
22 /// Number of dimensions every vector this embedder produces has.
23 ///
24 /// A store built for one dimension cannot hold vectors of another, so an
25 /// index persists this and refuses to mix them.
26 fn dimension(&self) -> usize;
27
28 /// Stable identifier for the model behind this embedder, e.g.
29 /// `"fastembed:AllMiniLML6V2"` or `"openai:text-embedding-3-small"`.
30 ///
31 /// It must stay the same across runs and versions for the *same* model, and
32 /// differ whenever the produced vectors would be incompatible. It is the
33 /// discriminating half of [`fingerprint`](Embedder::fingerprint).
34 fn model_id(&self) -> &str;
35
36 /// Fingerprint an index stores to detect that it was built with a different
37 /// embedder. Re-indexing is required whenever this changes.
38 fn fingerprint(&self) -> String {
39 format!("{}/{}d", self.model_id(), self.dimension())
40 }
41}