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 std::collections::HashSet;

use super::*;
use crate::registry::dense::all;
use crate::registry::dense::by_phase;
use crate::registry::dense::dtype_needs_accelerator;
use crate::registry::dense::lookup;

#[test]
fn ids_are_unique_and_lowercase_lookup_works() {
  let ids: HashSet<String> = all()
    .into_iter()
    .map(|d| d.id.to_ascii_lowercase())
    .collect();
  assert_eq!(ids.len(), all().len());
  assert_eq!(lookup("baai/bge-small-en-v1.5").unwrap().dim, 384);
  assert!(lookup("nope/nothing").is_none());
}

#[test]
fn phase_zero_has_thirteen_bert_models() {
  let phase0 = by_phase(0);
  assert_eq!(phase0.len(), 13);
  assert!(phase0.iter().all(|d| d.family == Family::Bert));
  assert!(
    phase0
      .iter()
      .all(|d| d.dim > 0 && d.default_max_length <= 512 && d.normalize)
  );
}

#[test]
fn descriptor_round_trips_through_json() {
  let descriptor = lookup("BAAI/bge-small-en-v1.5").unwrap();
  let json = serde_json::to_string(&descriptor).unwrap();
  assert!(json.contains("\"pooling\":\"cls\""));
  assert!(json.contains("\"family\":\"bert\""));
  let back: ModelDescriptor = serde_json::from_str(&json).unwrap();
  assert_eq!(back, descriptor);
}

#[test]
fn family_from_model_type() {
  assert_eq!(Family::from_model_type("bert"), Some(Family::Bert));
  assert_eq!(Family::from_model_type("qwen3"), Some(Family::Qwen3));
  assert_eq!(Family::from_model_type("gemma3_text"), Some(Family::Gemma3));
  assert_eq!(Family::from_model_type("jina_bert"), Some(Family::JinaBert));
  assert_eq!(Family::from_model_type("mpnet"), Some(Family::Mpnet));
  assert_eq!(
    Family::from_model_type("modernbert"),
    Some(Family::ModernBert)
  );
  assert_eq!(
    Family::from_model_type("nomic_bert"),
    Some(Family::NomicBert)
  );
  assert_eq!(
    Family::from_model_type("xlm-roberta"),
    Some(Family::XlmRoberta)
  );
  assert_eq!(Family::from_model_type("new"), Some(Family::GteNew));
}

/// All six jina repositories declare `"model_type": "bert"`.
#[test]
fn the_jina_bert_key_does_not_reach_the_bert_family() {
  assert_ne!(Family::from_model_type("jina_bert"), Some(Family::Bert));
  assert_eq!(Family::from_model_type("bert"), Some(Family::Bert));
}

#[test]
fn the_mpnet_family_serializes_as_its_page_name() {
  assert_eq!(serde_json::to_string(&Family::Mpnet).unwrap(), "\"mpnet\"");
  assert_eq!(
    serde_json::from_str::<Family>("\"mpnet\"").unwrap(),
    Family::Mpnet
  );
}

#[test]
fn the_jina_bert_family_serializes_as_its_page_name() {
  assert_eq!(
    serde_json::to_string(&Family::JinaBert).unwrap(),
    "\"jina_bert\""
  );
  assert_eq!(
    serde_json::from_str::<Family>("\"jina_bert\"").unwrap(),
    Family::JinaBert
  );
}

#[test]
fn the_nomic_bert_key_reaches_its_own_family() {
  assert_eq!(
    Family::from_model_type("nomic_bert"),
    Some(Family::NomicBert)
  );
  assert_ne!(Family::from_model_type("nomic_bert"), Some(Family::Bert));
}

#[test]
fn the_nomic_bert_family_serializes_as_its_page_name() {
  assert_eq!(
    serde_json::to_string(&Family::NomicBert).unwrap(),
    "\"nomic_bert\""
  );
}

#[test]
fn the_modernbert_key_reaches_its_own_family() {
  assert_eq!(
    Family::from_model_type("modernbert"),
    Some(Family::ModernBert)
  );
  assert_ne!(Family::from_model_type("modernbert"), Some(Family::Bert));
}

#[test]
fn the_modernbert_family_serializes_as_its_page_name() {
  assert_eq!(
    serde_json::to_string(&Family::ModernBert).unwrap(),
    "\"modernbert\""
  );
}

#[test]
fn the_gte_new_key_reaches_its_own_family() {
  assert_eq!(Family::from_model_type("new"), Some(Family::GteNew));
  assert_ne!(Family::from_model_type("new"), Some(Family::Bert));
}

#[test]
fn the_gte_new_family_serializes_as_its_page_name() {
  assert_eq!(
    serde_json::to_string(&Family::GteNew).unwrap(),
    "\"gte_new\""
  );
}

#[test]
fn phase_zero_is_verified_on_cpu() {
  assert!(
    by_phase(0)
      .iter()
      .all(|d| d.backends.cpu == Support::Verified)
  );
}

#[test]
fn cpu_status_is_never_unsupported() {
  assert!(all().iter().all(|d| d.backends.cpu != Support::Unsupported));
}

#[test]
fn no_status_is_pending_after_phase_two_landed() {
  for d in all() {
    assert_ne!(d.backends.cpu, Support::Pending, "{}", d.id);
    assert_ne!(d.backends.metal, Support::Pending, "{}", d.id);
    assert_ne!(d.backends.cuda, Support::Pending, "{}", d.id);
    for row in [
      d.dtype_backends.f32,
      d.dtype_backends.f16,
      d.dtype_backends.bf16,
    ] {
      for status in [row.cpu, row.metal, row.cuda] {
        assert_ne!(status, Support::Pending, "{}", d.id);
      }
    }
  }
}

#[test]
fn only_the_pickle_entry_reports_pickle() {
  for d in all() {
    let expected = if d.id == "BAAI/bge-m3" {
      WeightsFormat::Pickle
    } else {
      WeightsFormat::Safetensors
    };
    assert_eq!(weights_format(&d.id), expected, "{}", d.id);
  }
  assert_eq!(weights_format("nope/nothing"), WeightsFormat::Safetensors);
}

mod accelerator;
mod cells;
mod constructors;
mod cuda;
mod dtype_backends;
mod entries;
mod gte_new;
mod jina_bert;
mod lookups;
mod modernbert;
mod nomic_bert;
mod phase_two;
mod sparse;
mod verified;