mod jina_colbert;
mod m3_colbert;
mod stanford_colbert;
use std::path::Path;
use std::path::PathBuf;
use candle_core::DType;
use candle_core::Tensor;
use candle_nn::VarBuilder;
use serde::Deserialize;
use serde::Serialize;
use super::EncoderInput;
use crate::error::CanddingError;
use crate::error::Result;
use crate::loader::COLBERT_LINEAR_FILENAME;
use crate::multi_vector::MultiVectorEmbedding;
pub(super) fn l2_normalize_rows(xs: &Tensor) -> Result<Tensor> {
let last = xs.rank() - 1;
let norms = xs.sqr()?.sum_keepdim(last)?.sqrt()?.maximum(1e-12)?;
Ok(xs.broadcast_div(&norms)?)
}
pub trait MultiVectorModel: Send + Sync {
fn forward(&self, input: &EncoderInput) -> Result<Vec<MultiVectorEmbedding>>;
fn forward_query(&self, input: &EncoderInput) -> Result<Vec<MultiVectorEmbedding>>;
fn dim(&self) -> usize;
fn max_position_embeddings(&self) -> usize;
fn weights_dtype(&self) -> DType;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum MultiVectorHead {
M3Colbert,
StanfordColbert,
JinaColbert,
}
#[derive(Clone, Copy, Debug)]
pub struct MultiVectorConventions {
pub keeps_cls_row: bool,
pub projection_in: usize,
pub projection_out: usize,
pub projection_bias: bool,
pub normalize: bool,
pub attends_to_mask_tokens: bool,
pub position_scheme: Option<super::xlm_roberta_flash::PositionScheme>,
}
pub fn load_multi_vector(
head: MultiVectorHead,
config_json: &str,
vb: VarBuilder,
colbert_linear: Option<&Path>,
tokenizer_path: &Path,
conventions: MultiVectorConventions,
) -> Result<Box<dyn MultiVectorModel>> {
match head {
MultiVectorHead::M3Colbert => {
let colbert_linear = colbert_linear
.ok_or_else(|| CanddingError::MissingFile(PathBuf::from(COLBERT_LINEAR_FILENAME)))?;
Ok(Box::new(m3_colbert::M3Colbert::load(
serde_json::from_str(config_json)?,
vb,
colbert_linear,
conventions,
)?))
}
MultiVectorHead::StanfordColbert => {
let punctuation_ids = crate::tokenizer::ascii_punctuation_ids(tokenizer_path)?;
Ok(Box::new(stanford_colbert::StanfordColbert::load(
serde_json::from_str(config_json)?,
vb,
punctuation_ids,
conventions,
)?))
}
MultiVectorHead::JinaColbert => {
let punctuation_ids = crate::tokenizer::ascii_punctuation_ids(tokenizer_path)?;
Ok(Box::new(jina_colbert::JinaColbert::load(
serde_json::from_str(config_json)?,
vb,
punctuation_ids,
conventions,
)?))
}
}
}
#[cfg(test)]
mod tests;