use candle_core::DType;
use candle_core::Device;
use super::*;
use crate::embed::synthetic;
use crate::embed::synthetic::HIDDEN;
use crate::models::SparseHead;
use crate::registry::Backend;
use crate::registry::BackendSupport;
use crate::registry::DtypeBackendSupport;
use crate::registry::Family;
use crate::registry::Support;
use crate::registry::WeightsFormat;
use crate::registry::sparse::SparseComputation;
#[test]
fn default_batch_size_is_eight() {
let builder = SparseTextEmbedding::builder("org/unregistered");
assert_eq!(builder.batch_size, 8);
}
fn splade_descriptor() -> SparseDescriptor {
SparseDescriptor {
id: "synthetic/zero-splade".to_string(),
family: Some(Family::Bert),
head: SparseHead::SpladeMlm,
vocab_size: 16,
default_max_length: 32,
backends: BackendSupport::untested(),
dtype_backends: DtypeBackendSupport::untested(),
license: "unknown".to_string(),
size_gb: 0.0,
phase: u8::MAX,
weights: WeightsFormat::Safetensors,
gated: false,
head_source: None,
head_revision: None,
computation: SparseComputation::Encoded,
}
}
#[test]
fn build_loads_the_weights_in_the_dtype_it_resolves() {
let mut tensors = synthetic::zero_bert("bert.", 16);
for (name, shape) in [
(
"cls.predictions.transform.dense.weight",
vec![HIDDEN, HIDDEN],
),
("cls.predictions.transform.dense.bias", vec![HIDDEN]),
("cls.predictions.transform.LayerNorm.weight", vec![HIDDEN]),
("cls.predictions.transform.LayerNorm.bias", vec![HIDDEN]),
("cls.predictions.bias", vec![16]),
] {
tensors.insert(name.to_string(), synthetic::zeros(&shape));
}
let config = synthetic::bert_config(16);
let dir = synthetic::checkpoint(&config, "tiny_tokenizer.json", &tensors);
for (requested, expected) in [(None, DType::F32), (Some(DType::F16), DType::F16)] {
let mut builder = SparseTextEmbedding::builder(dir.path().to_str().unwrap())
.descriptor(splade_descriptor())
.device(Device::Cpu);
if let Some(dtype) = requested {
builder = builder.dtype(dtype);
}
let model = builder.build().unwrap();
assert_eq!(model.dtype(), expected, "requested {requested:?}");
assert_eq!(model.weights_dtype(), expected, "requested {requested:?}");
}
}
#[test]
fn a_refused_dtype_is_refused_before_anything_is_fetched() {
let dir = tempfile::tempdir().unwrap();
let refused = SparseTextEmbedding::builder(dir.path().to_str().unwrap())
.descriptor(splade_descriptor())
.device(Device::Cpu)
.dtype(DType::BF16)
.build()
.err();
assert!(
matches!(
refused,
Some(CanddingError::UnsupportedOnDevice {
backend: Backend::Cpu,
dtype: DType::BF16,
..
})
),
"{refused:?}"
);
}
#[test]
fn the_dtype_check_reads_the_device_s_own_cell() {
let (v, u) = (Support::Verified, Support::Unsupported);
for (f16, refused) in [
(
BackendSupport {
cpu: u,
metal: v,
cuda: v,
},
true,
),
(
BackendSupport {
cpu: v,
metal: u,
cuda: u,
},
false,
),
] {
let dir = tempfile::tempdir().unwrap();
let mut descriptor = splade_descriptor();
descriptor.dtype_backends.f16 = f16;
let err = SparseTextEmbedding::builder(dir.path().to_str().unwrap())
.descriptor(descriptor)
.device(Device::Cpu)
.dtype(DType::F16)
.build()
.err()
.expect("an empty directory loads nothing");
let on_the_cpu = matches!(
err,
CanddingError::UnsupportedOnDevice {
backend: Backend::Cpu,
dtype: DType::F16,
..
}
);
assert_eq!(on_the_cpu, refused, "{f16:?}: {err}");
assert!(
!matches!(err, CanddingError::UnsupportedDtype { .. }),
"{f16:?}: {err}"
);
}
}