use serde::{Deserialize, Serialize};
use crate::{Dimension, Embedding, EmbeddingTask, Modality, Result};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum ResourceInput<'a> {
Text {
title: Option<&'a str>,
text: &'a str,
},
Image { bytes: &'a [u8] },
Audio {
samples: &'a [f32],
sample_rate: u32,
},
Video { frames: &'a [VideoFrame<'a>] },
}
impl ResourceInput<'_> {
pub fn modality(&self) -> Modality {
match self {
ResourceInput::Text { .. } => Modality::Text,
ResourceInput::Image { .. } => Modality::Image,
ResourceInput::Audio { .. } => Modality::Audio,
ResourceInput::Video { .. } => Modality::Video,
}
}
}
#[derive(Debug, Clone)]
pub struct VideoFrame<'a> {
pub timestamp_ms: u64,
pub width: u32,
pub height: u32,
pub rgb: &'a [u8],
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ModelIdentity {
pub model_id: String,
pub revision: String,
pub variant: String,
pub dimension: Dimension,
pub normalization: String,
pub query_prompt: String,
pub document_prompt: String,
pub max_tokens: usize,
}
#[derive(Debug, Clone)]
pub struct EmbeddingRequest<'a> {
pub input: ResourceInput<'a>,
pub task: EmbeddingTask,
}
pub trait Embedder: Send + Sync {
fn identity(&self) -> &ModelIdentity;
fn dimension(&self) -> Dimension {
self.identity().dimension
}
fn input_identity(&self, _modality: Modality) -> Result<&str> {
Ok("")
}
fn unavailable_reason(&self, _modality: Modality) -> Option<String> {
None
}
fn supports(&self, modality: Modality) -> bool;
fn embed_batch(&self, requests: &[EmbeddingRequest<'_>]) -> Result<Vec<Embedding>>;
fn embed(&self, input: ResourceInput<'_>, task: EmbeddingTask) -> Result<Embedding> {
let mut out = self.embed_batch(&[EmbeddingRequest { input, task }])?;
if out.len() != 1 {
return Err(crate::Error::Embedding(
"embedder returned the wrong number of vectors".into(),
));
}
Ok(out.remove(0))
}
fn embed_text(&self, text: &str, task: EmbeddingTask) -> Result<Embedding> {
self.embed(ResourceInput::Text { title: None, text }, task)
}
fn embed_query(&self, query: &str) -> Result<Embedding> {
self.embed_text(query, EmbeddingTask::SearchQuery)
}
fn embed_resources(&self, inputs: &[ResourceInput<'_>]) -> Result<Vec<Embedding>> {
let requests: Vec<_> = inputs
.iter()
.cloned()
.map(|input| EmbeddingRequest {
input,
task: EmbeddingTask::SearchDocument,
})
.collect();
self.embed_batch(&requests)
}
fn embed_text_document(&self, title: Option<&str>, text: &str) -> Result<Embedding> {
self.embed(
ResourceInput::Text { title, text },
EmbeddingTask::SearchDocument,
)
}
}
impl<E: Embedder + ?Sized> Embedder for Box<E> {
fn identity(&self) -> &ModelIdentity {
(**self).identity()
}
fn input_identity(&self, modality: Modality) -> Result<&str> {
(**self).input_identity(modality)
}
fn unavailable_reason(&self, modality: Modality) -> Option<String> {
(**self).unavailable_reason(modality)
}
fn supports(&self, modality: Modality) -> bool {
(**self).supports(modality)
}
fn embed_batch(&self, requests: &[EmbeddingRequest<'_>]) -> Result<Vec<Embedding>> {
(**self).embed_batch(requests)
}
fn dimension(&self) -> Dimension {
(**self).dimension()
}
fn embed(&self, input: ResourceInput<'_>, task: EmbeddingTask) -> Result<Embedding> {
(**self).embed(input, task)
}
fn embed_text(&self, text: &str, task: EmbeddingTask) -> Result<Embedding> {
(**self).embed_text(text, task)
}
fn embed_query(&self, query: &str) -> Result<Embedding> {
(**self).embed_query(query)
}
fn embed_resources(&self, inputs: &[ResourceInput<'_>]) -> Result<Vec<Embedding>> {
(**self).embed_resources(inputs)
}
fn embed_text_document(&self, title: Option<&str>, text: &str) -> Result<Embedding> {
(**self).embed_text_document(title, text)
}
}
impl<E: Embedder + ?Sized> Embedder for std::sync::Arc<E> {
fn identity(&self) -> &ModelIdentity {
(**self).identity()
}
fn input_identity(&self, modality: Modality) -> Result<&str> {
(**self).input_identity(modality)
}
fn unavailable_reason(&self, modality: Modality) -> Option<String> {
(**self).unavailable_reason(modality)
}
fn supports(&self, modality: Modality) -> bool {
(**self).supports(modality)
}
fn embed_batch(&self, requests: &[EmbeddingRequest<'_>]) -> Result<Vec<Embedding>> {
(**self).embed_batch(requests)
}
fn dimension(&self) -> Dimension {
(**self).dimension()
}
fn embed(&self, input: ResourceInput<'_>, task: EmbeddingTask) -> Result<Embedding> {
(**self).embed(input, task)
}
fn embed_text(&self, text: &str, task: EmbeddingTask) -> Result<Embedding> {
(**self).embed_text(text, task)
}
fn embed_query(&self, query: &str) -> Result<Embedding> {
(**self).embed_query(query)
}
fn embed_resources(&self, inputs: &[ResourceInput<'_>]) -> Result<Vec<Embedding>> {
(**self).embed_resources(inputs)
}
fn embed_text_document(&self, title: Option<&str>, text: &str) -> Result<Embedding> {
(**self).embed_text_document(title, text)
}
}