use super::*;
use crate::error::LengthKind;
use crate::models::flash_fixture;
use crate::models::flash_fixture::Numbering;
use crate::models::xlm_roberta_flash::PositionScheme;
const CLS: u32 = 2;
fn flash_checkpoint() -> TempDir {
let dir = tempdir().unwrap();
fs::write(
dir.path().join("config.json"),
flash_fixture::config_json("absolute"),
)
.unwrap();
fs::copy(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/tiny_tokenizer_double_separator.json"),
dir.path().join("tokenizer.json"),
)
.unwrap();
candle_core::safetensors::save(
&flash_fixture::tensors(),
dir.path().join("model.safetensors"),
)
.unwrap();
dir
}
fn flash_descriptor(position_scheme: Option<PositionScheme>) -> RerankDescriptor {
RerankDescriptor {
id: "synthetic/flash-reranker".to_string(),
family: Family::XlmRobertaFlash,
head: RerankHead::RobertaClassificationHead,
position_scheme,
..descriptor(ScoreActivation::Sigmoid)
}
}
fn build(dir: &TempDir, position_scheme: Option<PositionScheme>) -> Result<TextCrossEncoder> {
TextCrossEncoder::builder(dir.path().to_str().unwrap())
.descriptor(flash_descriptor(position_scheme))
.device(Device::Cpu)
.build()
}
#[test]
fn build_reaches_the_flash_encoder_through_the_entrys_family_and_scheme() {
let dir = flash_checkpoint();
let config = fs::read_to_string(dir.path().join("config.json")).unwrap();
assert!(!config.contains("model_type"), "{config}");
let model = build(&dir, Some(PositionScheme::Absolute)).unwrap();
assert_eq!(
model.max_length(),
flash_fixture::ROWS - flash_fixture::PAD - 1
);
let expected = ScoreActivation::Sigmoid
.apply(flash_fixture::expected_logit(CLS, Numbering::FromPadPlusOne) as f32);
let documents = ["c d", "", "the cats sat on the mat"];
let scores = model.rerank("a b", &documents, None).unwrap();
assert_eq!(scores.len(), documents.len());
for score in scores {
let value = score.value();
assert!(
(value - expected).abs() < 1e-6,
"{value} against {expected}"
);
}
}
#[test]
fn build_refuses_a_flash_entry_whose_scheme_the_config_does_not_name() {
let dir = flash_checkpoint();
for declared in [Some(PositionScheme::Rotary), None] {
match build(&dir, declared) {
Err(CanddingError::PositionSchemeDisagreesWithConfig { config, .. }) => {
assert_eq!(config, "absolute")
}
Ok(_) => panic!("{declared:?} against an absolute config must be refused"),
Err(other) => panic!("{declared:?}: unexpected error: {other}"),
}
}
}
#[test]
fn build_cuts_a_windowed_flash_entrys_documents_into_windows() {
let dir = flash_checkpoint();
let windowed = |query_cap| RerankDescriptor {
input: RerankInput::Windowed {
query_cap,
overlap: 1,
},
..flash_descriptor(Some(PositionScheme::Absolute))
};
let model = TextCrossEncoder::builder(dir.path().to_str().unwrap())
.descriptor(windowed(3))
.device(Device::Cpu)
.build()
.unwrap();
let documents = ["c d", ""];
assert_eq!(model.pair_window_count("a b", &documents).unwrap(), [2, 1]);
assert_eq!(model.pair_token_count("a b", &documents).unwrap(), [12, 5]);
let expected = ScoreActivation::Sigmoid
.apply(flash_fixture::expected_logit(CLS, Numbering::FromPadPlusOne) as f32);
for score in model.rerank("a b", &documents, None).unwrap() {
let value = score.value();
assert!(
(value - expected).abs() < 1e-6,
"{value} against {expected}"
);
}
let refused = TextCrossEncoder::builder(dir.path().to_str().unwrap())
.descriptor(windowed(4))
.device(Device::Cpu)
.build();
assert!(
matches!(
refused,
Err(CanddingError::LengthTooSmall {
requested: 6,
minimum: 8,
kind: LengthKind::Windowed { query_cap: 4 }
})
),
"{:?}",
refused.err()
);
}