use std::error::Error;
use std::num::NonZeroUsize;
use haem_frames::embedding::Distance;
use super::{Ask, question_for};
use crate::serve::embed::{Answer, Embedded, Longer, ModelId};
use crate::serve::protocol::Metric;
use crate::serve::storage::Failure;
use crate::serve::storage::embedded::{Declaration, ModelName, VectorKind};
type TestResult = Result<(), Box<dyn Error>>;
struct StandIn {
model: &'static str,
answers: Vec<Answer>,
}
impl Ask for StandIn {
fn question(&self, _text: &str) -> Result<Embedded, Failure> {
Ok(Embedded {
model: ModelId {
name: self.model.to_owned(),
revision: "1".to_owned(),
manifest_sha256: "abc".to_owned(),
dimensions: 2,
distance: Distance::Cosine,
normalised: true,
},
answers: self.answers.clone(),
})
}
}
fn declaration() -> Result<Declaration, Box<dyn Error>> {
Ok(Declaration {
kind: VectorKind::Dense,
source: "body".to_owned(),
model: ModelName {
name: "stand-in".to_owned(),
revision: "1".to_owned(),
manifest_sha256: "abc".to_owned(),
},
dimensions: NonZeroUsize::new(2).ok_or("two is not zero")?,
distance: Metric::Cosine,
})
}
fn code(outcome: Result<Vec<f32>, Failure>) -> Option<String> {
outcome.err().map(|failure| failure.code)
}
#[test]
fn the_declared_models_one_vector_is_the_question() -> TestResult {
let asker = StandIn {
model: "stand-in",
answers: vec![Answer::Vector(vec![0.6, 0.8])],
};
assert_eq!(
question_for(&asker, "chest pain", &declaration()?)?,
[0.6_f32, 0.8]
);
Ok(())
}
#[test]
fn another_models_question_is_refused() -> TestResult {
let asker = StandIn {
model: "another",
answers: vec![Answer::Vector(vec![0.6, 0.8])],
};
let outcome = question_for(&asker, "chest pain", &declaration()?);
assert_eq!(code(outcome).as_deref(), Some("embed_model_mismatch"));
Ok(())
}
#[test]
fn two_vectors_for_one_question_are_refused() -> TestResult {
let asker = StandIn {
model: "stand-in",
answers: vec![
Answer::Vector(vec![0.6, 0.8]),
Answer::Vector(vec![0.8, 0.6]),
],
};
let outcome = question_for(&asker, "chest pain", &declaration()?);
assert_eq!(code(outcome).as_deref(), Some("embed_vector_count"));
Ok(())
}
#[test]
fn a_question_of_another_length_is_refused() -> TestResult {
let asker = StandIn {
model: "stand-in",
answers: vec![Answer::Vector(vec![0.6, 0.8, 0.0])],
};
let outcome = question_for(&asker, "chest pain", &declaration()?);
assert_eq!(code(outcome).as_deref(), Some("embed_vector_length"));
Ok(())
}
#[test]
fn a_question_longer_than_the_model_is_refused_by_name() -> TestResult {
let asker = StandIn {
model: "stand-in",
answers: vec![Answer::Longer(Longer {
tokens_total: 600,
tokens_limit: 512,
})],
};
let refused = question_for(&asker, "words", &declaration()?)
.err()
.ok_or("a longer question was searched")?;
assert_eq!(refused.code, "text_longer_than_model");
assert_eq!(
refused.message,
"the question is 600 tokens; the model's limit is 512"
);
Ok(())
}