use ort::inputs;
use ort::value::Tensor;
use super::engine::{EMBEDDING_DIMS, EmbeddingEngine};
use crate::errors::Error;
pub(crate) fn encode_and_infer(
engine: &mut EmbeddingEngine,
input: &str,
) -> Result<Vec<f32>, Error> {
let encoding = engine
.tokenizer
.encode(input, true)
.map_err(|e| Error::Tokenization(format!("inference tokenizer: {e}").into()))?;
let input_ids = encoding.get_ids();
let attention_mask = encoding.get_attention_mask();
if input_ids.is_empty() {
return Ok(vec![0.0f32; EMBEDDING_DIMS]);
}
let seq_len = input_ids.len();
let input_ids_vec: Vec<i64> = input_ids.iter().map(|&id| id as i64).collect();
let attention_mask_vec: Vec<i64> = attention_mask.iter().map(|&m| m as i64).collect();
let input_ids_tensor = Tensor::from_array(([1usize, seq_len], input_ids_vec))?;
let attention_mask_tensor = Tensor::from_array(([1usize, seq_len], attention_mask_vec))?;
let outputs = if engine.requires_token_type_ids {
let token_type_ids_vec: Vec<i64> = vec![0i64; seq_len]; let token_type_ids_tensor = Tensor::from_array(([1usize, seq_len], token_type_ids_vec))?;
let inputs = inputs![
"input_ids" => input_ids_tensor,
"attention_mask" => attention_mask_tensor,
"token_type_ids" => token_type_ids_tensor
];
engine.session.run(inputs?)?
} else {
let inputs = inputs![
"input_ids" => input_ids_tensor,
"attention_mask" => attention_mask_tensor
];
engine.session.run(inputs?)?
};
let last_hidden_state = outputs
.get("last_hidden_state")
.or_else(|| outputs.get("token_embeddings"))
.ok_or_else(|| {
Error::Inference(
"Output tensor 'last_hidden_state' or 'token_embeddings' not found".to_string(),
)
})?
.try_extract_tensor::<f32>()?;
let shape = last_hidden_state.shape();
let data = last_hidden_state.as_slice().unwrap();
if shape.len() != 3 {
return Err(Error::Inference(format!(
"Expected 3D output (batch, seq_len, hidden), got {:?}",
shape
)));
}
let batch_size = shape[0];
let hidden_dim = shape[2];
if batch_size != 1 || hidden_dim != EMBEDDING_DIMS {
return Err(Error::Inference(format!(
"Unexpected output shape: {:?}, batch=1, hidden=384 expected",
shape
)));
}
let mut pooled = vec![0.0f32; EMBEDDING_DIMS];
for (token_idx, chunk) in data.chunks(hidden_dim).take(seq_len).enumerate() {
let mask_value = attention_mask.get(token_idx).copied().unwrap_or(0) as f32;
for (dim, pooled_value) in pooled.iter_mut().enumerate() {
*pooled_value += chunk[dim] * mask_value;
}
}
let mask_sum: f32 = attention_mask
.iter()
.take(seq_len)
.map(|&m| m as f32)
.sum::<f32>()
.max(1e-9);
for value in pooled.iter_mut() {
*value /= mask_sum;
}
let normalized = l2_normalize(&pooled);
Ok(normalized)
}
pub(crate) fn l2_normalize(vec: &[f32]) -> Vec<f32> {
let norm: f32 = vec.iter().map(|&x| x * x).sum::<f32>().sqrt();
let norm = norm.max(1e-9);
vec.iter().map(|&x| x / norm).collect()
}