use std::borrow::Cow;
use super::*;
use crate::error::LengthKind;
use crate::tokenizer::PromptTemplate;
const DEFAULT: &str = "Given a web search query, retrieve relevant passages that answer the query";
const CUSTOM: &str =
"Given a question about geography, judge whether the passage names the correct city";
const QUERY: &str = "What is the capital of China?";
const DOCUMENT: &str = "The capital of China is Beijing.";
fn subset_path() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/tiny_tokenizer_qwen3_reranker.json")
}
fn prompt_descriptor(default_instruction: &'static str) -> RerankDescriptor {
let mut descriptor = stub_descriptor();
descriptor.input = RerankInput::Prompt {
template: PromptTemplate::Qwen3Reranker,
default_instruction: Cow::Borrowed(default_instruction),
};
descriptor
}
fn prompt_model(default_instruction: &'static str, max_length: usize) -> TextCrossEncoder {
cross_encoder(
Box::new(StubRerankModel),
prompt_descriptor(default_instruction),
&subset_path(),
max_length,
8,
)
}
fn scored_lengths(model: &TextCrossEncoder, documents: &[&str]) -> Vec<usize> {
model
.rerank(QUERY, documents, None)
.unwrap()
.iter()
.map(|score| score.value() as usize)
.collect()
}
#[test]
fn a_prompt_entry_hands_the_model_each_prompts_real_length() {
let model = prompt_model(DEFAULT, 8192);
let documents = [DOCUMENT, "", "Beijing."];
let scored = scored_lengths(&model, &documents);
assert_eq!(scored[0], 85);
assert_eq!(model.pair_token_count(QUERY, &documents).unwrap(), scored);
assert!(scored[1] < scored[2] && scored[2] < scored[0], "{scored:?}");
}
#[test]
fn the_descriptors_default_instruction_reaches_the_prompt() {
let default = prompt_model(DEFAULT, 8192);
let custom = prompt_model(CUSTOM, 8192);
assert_eq!(default.pair_token_count(QUERY, &[DOCUMENT]).unwrap(), [85]);
assert_eq!(custom.pair_token_count(QUERY, &[DOCUMENT]).unwrap(), [86]);
assert_eq!(scored_lengths(&custom, &[DOCUMENT]), [86]);
}
#[test]
fn the_descriptors_input_kind_decides_what_the_model_reads() {
let pair = cross_encoder(
Box::new(StubRerankModel),
stub_descriptor(),
&subset_path(),
8192,
8,
);
let prompt = prompt_model(DEFAULT, 8192);
let pair_count = pair.pair_token_count(QUERY, &[DOCUMENT]).unwrap()[0];
assert!(pair_count < 85 - 39 - 9, "{pair_count}");
assert_eq!(scored_lengths(&pair, &[DOCUMENT]), [pair_count]);
assert_eq!(scored_lengths(&prompt, &[DOCUMENT]), [85]);
}
#[test]
fn a_prompt_entry_cuts_to_its_max_length_and_refuses_one_below_the_minimum() {
let model = prompt_model(DEFAULT, 49);
assert_eq!(model.max_length(), 49);
assert_eq!(scored_lengths(&model, &[DOCUMENT]), [49]);
let refused = InputTokenizer::from_file(
&prompt_descriptor(DEFAULT).input,
&subset_path(),
48,
PadToken::Id(0),
);
assert!(matches!(
refused,
Err(CanddingError::LengthTooSmall {
requested: 48,
minimum: 49,
kind: LengthKind::Prompt { .. },
})
));
}
#[test]
fn an_instruction_replaces_the_default_in_the_scored_prompt() {
let model = prompt_model(DEFAULT, 8192);
let with = |instruction: &str| {
let scored: Vec<usize> = model
.rerank_with_instruction(QUERY, &[DOCUMENT], instruction, None)
.unwrap()
.iter()
.map(|score| score.value() as usize)
.collect();
let counted = model
.pair_token_count_with_instruction(QUERY, &[DOCUMENT], instruction)
.unwrap();
assert_eq!(scored, counted, "{instruction:?}");
scored
};
assert_eq!(with(CUSTOM), [86]);
assert_eq!(with(DEFAULT), scored_lengths(&model, &[DOCUMENT]));
}
#[test]
fn an_empty_instruction_is_rendered_empty_rather_than_as_the_default() {
let model = prompt_model(DEFAULT, 8192);
let counted = model
.pair_token_count_with_instruction(QUERY, &[DOCUMENT], "")
.unwrap();
assert_eq!(counted, [85 - 13]);
let scored = model
.rerank_with_instruction(QUERY, &[DOCUMENT], "", None)
.unwrap();
assert_eq!(scored[0].value() as usize, 85 - 13);
}