candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use candle_core::DType;

use super::DtypeSupport;
use super::Family;
use super::Support;
use super::WeightsFormat;
use super::entry::Entry;
use super::t4::T4_BF16_UNTESTED;
use crate::pooling::Pooling;

const NOMIC_QUERY: &str = "search_query: {text}";
const NOMIC_PASSAGE: &str = "search_document: {text}";
const SNOWFLAKE_QUERY: &str = "Represent this sentence for searching relevant passages: {text}";

/// The three's shared fields: 768-wide, 12 layers and heads, 8192 default, no Dense, F32, Apache-2.0, ungated.
/// `status` covers `cpu`, `metal` and every dtype at once, since a golden run flips them together.
const fn nomic(
  id: &'static str,
  pooling: Pooling,
  query_template: Option<&'static str>,
  passage_template: Option<&'static str>,
  mrl: bool,
  layer_norm_after_pooling: bool,
  status: Support,
) -> Entry {
  Entry {
    id,
    family: Family::NomicBert,
    dim: 768,
    default_max_length: 8192,
    pooling,
    cpu: status,
    metal: status,
    cuda: Support::Verified,
    query_template,
    passage_template,
    default_instruction: None,
    dense_dirs: &[],
    mrl,
    layer_norm_after_pooling,
    gated: false,
    default_dtype: DType::F32,
    dtypes: match status {
      Support::Verified => DtypeSupport::verified(),
      _ => DtypeSupport::pending(),
    },
    license: "Apache-2.0",
    size_gb: 0.55,
    phase: 2,
    weights: WeightsFormat::Safetensors,
    accel_only_dtypes: &[],
    cell_exceptions: T4_BF16_UNTESTED,
  }
}

/// `nomic-ai/nomic-embed-text-v1`: mean pooling, the model card's `search_query: ` and `search_document: ` prefixes,
/// and dynamic NTK rescaling past 2048 trained positions, with `rotary_scaling_factor: 2`.
const V1: Entry = nomic(
  "nomic-ai/nomic-embed-text-v1",
  Pooling::Mean,
  Some(NOMIC_QUERY),
  Some(NOMIC_PASSAGE),
  false,
  false,
  Support::Verified,
);

/// `nomic-ai/nomic-embed-text-v1.5`: the one entry with `mrl` and `layer_norm_after_pooling`, from its model card's
/// `encode`; no `Normalize` module, and `rotary_scaling_factor: null`, so its base never rescales.
const V1_5: Entry = nomic(
  "nomic-ai/nomic-embed-text-v1.5",
  Pooling::Mean,
  Some(NOMIC_QUERY),
  Some(NOMIC_PASSAGE),
  true,
  true,
  Support::Verified,
);

/// `Snowflake/snowflake-arctic-embed-m-long`: CLS pooling, a query prompt from `config_sentence_transformers.json`,
/// and `v1`'s dynamic NTK; its modelling code lives in its own repository, so `code_revision` equals `commit`.
const SNOWFLAKE_ARCTIC_EMBED_M_LONG: Entry = nomic(
  "Snowflake/snowflake-arctic-embed-m-long",
  Pooling::Cls,
  Some(SNOWFLAKE_QUERY),
  None,
  false,
  false,
  Support::Verified,
);

pub(super) const ENTRIES: &[Entry] = &[V1, V1_5, SNOWFLAKE_ARCTIC_EMBED_M_LONG];