use std::future::Future;
use std::time::Duration;
#[derive(Debug, thiserror::Error)]
pub enum ProbeError<E> {
#[error("embedding probe timed out after {0:?}")]
Timeout(Duration),
#[error("{0}")]
Embed(E),
}
#[tracing::instrument(
name = "memory.embed_probe.probe_vector_size",
skip_all,
err,
level = "debug"
)]
pub async fn probe_vector_size<Fut, E>(
embed_fut: Fut,
timeout: Option<Duration>,
) -> Result<u64, ProbeError<E>>
where
Fut: Future<Output = Result<Vec<f32>, E>>,
E: std::fmt::Display,
{
let vector = match timeout {
Some(d) => tokio::time::timeout(d, embed_fut)
.await
.map_err(|_| ProbeError::Timeout(d))?
.map_err(ProbeError::Embed)?,
None => embed_fut.await.map_err(ProbeError::Embed)?,
};
Ok(vector.len() as u64)
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::Infallible;
#[tokio::test]
async fn probe_vector_size_returns_length() {
let size = probe_vector_size(async { Ok::<_, Infallible>(vec![0.0_f32; 128]) }, None)
.await
.unwrap();
assert_eq!(size, 128);
}
#[tokio::test]
async fn probe_vector_size_propagates_embed_error() {
let result = probe_vector_size(async { Err::<Vec<f32>, _>("boom") }, None).await;
assert!(matches!(result, Err(ProbeError::Embed("boom"))));
}
#[tokio::test]
async fn probe_vector_size_times_out() {
let result = probe_vector_size(
async {
tokio::time::sleep(Duration::from_millis(50)).await;
Ok::<_, Infallible>(vec![0.0_f32; 4])
},
Some(Duration::from_millis(1)),
)
.await;
assert!(matches!(result, Err(ProbeError::Timeout(_))));
}
#[tokio::test]
async fn probe_vector_size_no_timeout_awaits_unconditionally() {
let size = probe_vector_size(
async {
tokio::time::sleep(Duration::from_millis(20)).await;
Ok::<_, Infallible>(vec![0.0_f32; 7])
},
None,
)
.await
.unwrap();
assert_eq!(size, 7);
}
}