Skip to main content

solo_core/
embedder.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! `Embedder` trait. See ADR-0002 for design rationale.
4
5use crate::{
6    error::{Error, Result},
7    types::{Embedding, EmbeddingDtype},
8};
9use async_trait::async_trait;
10
11/// Pluggable embedder. Production impls live in `solo-storage` (or a future
12/// `solo-embed` crate); this trait is the contract.
13#[async_trait]
14pub trait Embedder: Send + Sync {
15    /// Embedder identity. The migration tool `solo reembed` keys on
16    /// `(name, version)` to decide whether stored embeddings need to be
17    /// regenerated.
18    fn name(&self) -> &str;
19
20    /// Embedder version. Bump on any change that produces different vectors
21    /// for the same input.
22    fn version(&self) -> &str;
23
24    /// Output dimension. Must be invariant across calls for a given Embedder
25    /// instance.
26    fn dim(&self) -> usize;
27
28    /// Output dtype. Determines how raw bytes in `Embedding::data` are
29    /// interpreted.
30    fn dtype(&self) -> EmbeddingDtype;
31
32    /// Embed a batch of texts. Output is in input order with the same length
33    /// as the input. Implementations should batch internally for throughput;
34    /// callers may pass any number of texts (including 1).
35    async fn embed_batch(&self, texts: &[&str]) -> Result<Vec<Embedding>>;
36
37    /// Convenience: embed a single text. Default impl calls embed_batch.
38    async fn embed(&self, text: &str) -> Result<Embedding> {
39        let mut results = self.embed_batch(&[text]).await?;
40        results.pop().ok_or(Error::EmbedderProtocol(
41            "embed_batch returned empty for non-empty input",
42        ))
43    }
44}