use crate::memory_core::retrieval::{PalaceHandle, shared_embedder};
use crate::memory_core::store::vector::VectorStore;
use std::sync::Arc;
pub(super) const BENCHMARK_QUERIES: &[&str] = &[
"cargo build and test commands for the workspace",
"how are crates organized and shared in the workspace",
"error handling patterns with thiserror and anyhow",
"how to create a git tag and publish a crate",
"unit test patterns and mock usage in Rust tests",
"daemon startup and graceful shutdown with SIGTERM",
"HNSW vector search performance and batch embedding",
"how to trace and debug slow recall queries",
];
pub(super) async fn run_benchmark(handle: &Arc<PalaceHandle>) -> Option<f64> {
if handle.drawers.read().is_empty() {
tracing::debug!(
palace = %handle.id,
"dream recall benchmark: palace empty, skipping"
);
return None;
}
let embedder = match shared_embedder().await {
Ok(e) => e,
Err(e) => {
tracing::warn!(
palace = %handle.id,
"dream recall benchmark: embedder unavailable, skipping: {e:#}"
);
return None;
}
};
let mut total_score: f64 = 0.0;
let mut total_hits: usize = 0;
for &query in BENCHMARK_QUERIES {
let vectors = match embedder.embed_batch(&[query.to_string()]).await {
Ok(v) => v,
Err(e) => {
tracing::warn!(
palace = %handle.id,
query,
"dream recall benchmark: embed failed, skipping query: {e:#}"
);
continue;
}
};
let Some(query_vec) = vectors.into_iter().next() else {
continue;
};
let hits = match handle.vector_store.search(&query_vec, 3).await {
Ok(h) => h,
Err(e) => {
tracing::warn!(
palace = %handle.id,
query,
"dream recall benchmark: search failed, skipping query: {e:#}"
);
continue;
}
};
for hit in &hits {
total_score += hit.score as f64;
total_hits += 1;
}
}
if total_hits == 0 {
tracing::debug!(
palace = %handle.id,
"dream recall benchmark: no hits across all queries (palace may be empty)"
);
return None;
}
let mean = total_score / total_hits as f64;
tracing::debug!(
palace = %handle.id,
mean_score = mean,
total_hits,
"dream recall benchmark complete"
);
Some(mean)
}