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 candle_core::Device;

use crate::embed::builder::reject_unsupported_dtype;
use crate::error::CanddingError;
use crate::registry::Backend;
use crate::registry::BackendSupport;
use crate::registry::DtypeBackendSupport;
use crate::registry::Support;
use crate::registry::sparse;

const V: Support = Support::Verified;
const U: Support = Support::Unsupported;
const T: Support = Support::Untested;

fn row(cpu: Support, metal: Support, cuda: Support) -> BackendSupport {
  BackendSupport { cpu, metal, cuda }
}

fn axis() -> DtypeBackendSupport {
  DtypeBackendSupport {
    f32: row(V, V, T),
    f16: row(U, V, T),
    bf16: row(U, U, U),
  }
}

/// The checkpoint misses tolerance on the processor rather than returning NaN, hence this wording.
#[test]
fn a_cpu_only_unsupported_cell_is_refused_on_the_cpu_as_accelerator_only() {
  let err = reject_unsupported_dtype("m", DType::F16, axis(), &Device::Cpu).unwrap_err();
  assert!(
    matches!(&err, CanddingError::UnsupportedOnDevice { model, backend: Backend::Cpu, dtype: DType::F16 } if model == "m"),
    "{err}"
  );
  let message = err.to_string();
  assert!(message.contains("f16"), "{message}");
  assert!(message.contains("accelerator-only"), "{message}");
  assert!(!message.contains("NaN"), "{message}");
}

#[test]
fn a_dtype_unsupported_on_every_device_is_refused_as_unsupported() {
  let err = reject_unsupported_dtype("m", DType::BF16, axis(), &Device::Cpu).unwrap_err();
  assert!(
    matches!(&err, CanddingError::UnsupportedDtype { model, dtype: DType::BF16 } if model == "m"),
    "{err}"
  );
}

#[test]
fn a_verified_cell_passes() {
  assert!(reject_unsupported_dtype("m", DType::F32, axis(), &Device::Cpu).is_ok());
}

#[test]
fn a_supplied_row_is_trusted_over_the_registry_entry_the_spec_names() {
  let verified = DtypeBackendSupport {
    f16: row(V, V, T),
    ..axis()
  };
  assert!(reject_unsupported_dtype("BAAI/bge-m3", DType::F16, verified, &Device::Cpu).is_ok());
}

#[test]
fn both_registered_accelerator_only_entries_are_refused_on_the_cpu_and_verified_on_metal() {
  for id in ["BAAI/bge-m3", "jinaai/jina-embeddings-v2-small-en"] {
    let descriptor = sparse::lookup(id).expect("registered");
    assert_eq!(descriptor.dtype_backends.f16.metal, V, "{id}");
    let err = reject_unsupported_dtype(id, DType::F16, descriptor.dtype_backends, &Device::Cpu)
      .unwrap_err();
    assert!(
      matches!(&err, CanddingError::UnsupportedOnDevice { model, backend: Backend::Cpu, dtype: DType::F16 } if model == id),
      "{id}: {err}"
    );
  }
}