87 lines
3.6 KiB
Rust
87 lines
3.6 KiB
Rust
//! Genuine Google CPU proof for background prepared-graph publication.
|
|||
|
|
use anyhow::Result;
|
||
|
|
use searchi_core::SearchIndex;
|
||
|
|
use searchi_service::{HybridSearch, PreparedGraphs, SearchOptions};
|
||
|
|
use std::fs;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[ignore = "Requires verified Google models and normal worker; CPU only"]
|
||
|
|
fn prepared_publication_keeps_warmed_query_reader_hot() -> Result<()> {
|
||
|
|
assert_eq!(std::env::var("SEARCHI_FORCE_CPU")?, "1");
|
||
|
|
let model = std::env::var_os("SEARCHI_GEMMA_MODEL_DIR").expect("Verified models required");
|
||
|
|
let worker = std::env::var_os("SEARCHI_GEMMA_WORKER").expect("Normal worker required");
|
||
|
|
let temp = tempfile::tempdir()?;
|
||
|
|
let root = temp.path().join("documents");
|
||
|
|
fs::create_dir(&root)?;
|
||
|
|
let file = root.join("supplier.txt");
|
||
|
|
fs::write(
|
||
|
|
&file,
|
||
|
|
"The supplier agreement ends with sixty days written notice.",
|
||
|
|
)?;
|
||
|
|
let db = temp.path().join("index.db");
|
||
|
|
let mut lexical = SearchIndex::open(&db)?;
|
||
|
|
lexical.index_folder(&root)?;
|
||
|
|
let graphs = PreparedGraphs::default();
|
||
|
|
let mut indexer = HybridSearch::open(&db, &model, &worker, 1)?;
|
||
|
|
indexer.share_prepared_graphs(&graphs)?;
|
||
|
|
indexer.index_embeddings()?;
|
||
|
|
let mut reader = HybridSearch::open(&db, &model, &worker, 1)?;
|
||
|
|
reader.share_prepared_graphs(&graphs)?;
|
||
|
|
reader.warmup()?;
|
||
|
|
let query = "How can we end our supplier partnership?";
|
||
|
|
reader.search(query, 10, &SearchOptions::default())?;
|
||
|
|
fs::write(
|
||
|
|
&file,
|
||
|
|
"The supplier agreement ends with ninety days written notice.",
|
||
|
|
)?;
|
||
|
|
lexical.index_folder(&root)?;
|
||
|
|
indexer.index_embeddings()?;
|
||
|
|
drop(indexer); // The shared graph outlives the background model worker.
|
||
|
|
let response = reader.search_measured(query, 10, &SearchOptions::default(), true)?;
|
||
|
|
assert_eq!(response.results[0].filename, "supplier.txt");
|
||
|
|
assert!(response.results[0].snippet.contains("ninety days"));
|
||
|
|
let trace = response.metrics.trace.expect("Detailed trace required");
|
||
|
|
assert!(
|
||
|
|
trace
|
||
|
|
.iter()
|
||
|
|
.any(|span| span.counters.get("query_cache_hit") == Some(&1)),
|
||
|
|
"The retained query model must preserve its real-vector LRU across publication"
|
||
|
|
);
|
||
|
|
let loads: Vec<_> = trace
|
||
|
|
.iter()
|
||
|
|
.filter(|span| span.name == "semantic.ann_load")
|
||
|
|
.collect();
|
||
|
|
assert!(!loads.is_empty());
|
||
|
|
assert!(loads
|
||
|
|
.iter()
|
||
|
|
.all(|span| span.counters.get("cache_hit") == Some(&1)));
|
||
|
|
assert!(!trace.iter().any(|span| matches!(
|
||
|
|
span.name.as_str(),
|
||
|
|
"semantic.ann_blob_read" | "semantic.ann_checksum" | "semantic.ann_deserialize"
|
||
|
|
)));
|
||
|
|
let unseen = reader.search_measured(
|
||
|
|
"What notice is required for the contract to end?",
|
||
|
|
10,
|
||
|
|
&SearchOptions::default(),
|
||
|
|
true,
|
||
|
|
)?;
|
||
|
|
assert!(unseen.results[0].snippet.contains("ninety days"));
|
||
|
|
let unseen_trace = unseen.metrics.trace.expect("Unseen query trace required");
|
||
|
|
assert!(
|
||
|
|
unseen_trace
|
||
|
|
.iter()
|
||
|
|
.any(|span| span.counters.get("query_cache_hit") == Some(&0)),
|
||
|
|
"A distinct foreground input must execute genuine query inference"
|
||
|
|
);
|
||
|
|
assert!(unseen_trace
|
||
|
|
.iter()
|
||
|
|
.filter(|span| span.name == "semantic.ann_load")
|
||
|
|
.all(|span| span.counters.get("cache_hit") == Some(&1)));
|
||
|
|
assert!(!unseen_trace.iter().any(|span| matches!(
|
||
|
|
span.name.as_str(),
|
||
|
|
"semantic.ann_blob_read" | "semantic.ann_checksum" | "semantic.ann_deserialize"
|
||
|
|
)));
|
||
|
|
eprintln!("Prepared reader CPU fixture: retained_query_ms={:.3}, unseen_query_ms={:.3}; one document, not a scale benchmark",
|
||
|
|
response.metrics.total_ms, unseen.metrics.total_ms);
|
||
|
|
Ok(())
|
||
|
|
}
|