use haem_frames::embedding::Kind;
use super::{Answer, Embedded, Embedder};
use crate::serve::storage::Failure;
use crate::serve::storage::embedded::{Declaration, ModelName};
pub trait Ask {
fn question(&self, text: &str) -> Result<Embedded, Failure>;
}
impl Ask for Embedder {
fn question(&self, text: &str) -> Result<Embedded, Failure> {
self.embed(Kind::Query, &[text.to_owned()])
}
}
pub fn question_for(
asker: &dyn Ask,
text: &str,
declaration: &Declaration,
) -> Result<Vec<f32>, Failure> {
let embedded = asker.question(text)?;
let count = embedded.answers.len();
let model = ModelName {
name: embedded.model.name,
revision: embedded.model.revision,
manifest_sha256: embedded.model.manifest_sha256,
};
let mut answers = embedded.answers.into_iter();
let (Some(answer), None) = (answers.next(), answers.next()) else {
return Err(Failure::new(
"embed_vector_count",
format!("one question was sent and {count} vectors came back"),
));
};
if model != declaration.model {
return Err(Failure::new(
"embed_model_mismatch",
format!(
"the question worker runs {}; the field is declared for {}",
model.name, declaration.model.name
),
));
}
let vector = match answer {
Answer::Vector(vector) => vector,
Answer::Windows(_) => {
return Err(Failure::new(
"embed_vector_count",
"the question worker answered windows for one question",
));
}
Answer::Longer(longer) | Answer::OverCap(longer) => {
return Err(Failure::new(
"text_longer_than_model",
format!(
"the question is {} tokens; the model's limit is {}",
longer.tokens_total, longer.tokens_limit
),
));
}
};
let dimensions = declaration.dimensions.get();
if vector.len() != dimensions {
return Err(Failure::new(
"embed_vector_length",
format!(
"a question vector of {} members came back; {dimensions} are declared",
vector.len()
),
));
}
Ok(vector)
}
#[cfg(test)]
#[path = "question_tests.rs"]
mod tests;