Preserve explicit code candidates and expose verified model asset milestones

This commit is contained in:
karl committed 2026-10-13 08:47:41 +02:00
1 parent 6e05227f53
commit e28e20af38
9 files changed
+475 -16

No files matched your search

+304 -6
View File
@@ -17,6 +17,31 @@ use std::{
/// Exact bytes of the pinned text model plus its multimedia projection.
pub const TOTAL_BYTES: u64 = 864_676_480;
const ATTEMPTS: usize = 3;
const BACKBONE_FILE: &str = "embeddinggemma-2-Q8_0.gguf";
const PROJECTOR_FILE: &str = "mmproj-embeddinggemma-2-Q8_0.gguf";
/// One verified pinned artifact. This does not imply the full bundle or an
/// inference session is ready; a later artifact can still fail provisioning.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VerifiedAsset {
TextBackbone,
MediaProjector,
}
fn verified_asset(artifact: &Artifact) -> Option<VerifiedAsset> {
match artifact.path.as_str() {
BACKBONE_FILE => Some(VerifiedAsset::TextBackbone),
PROJECTOR_FILE => Some(VerifiedAsset::MediaProjector),
_ => None, // Private HTTP fixtures may use arbitrary artifact names.
}
}
fn validate_artifact_order(files: &[Artifact]) -> Result<()> {
if files.len() != 2 || files[0].path != BACKBONE_FILE || files[1].path != PROJECTOR_FILE {
bail!("bundled model manifest must provision the text backbone before the media projector");
}
Ok(())
}
#[derive(Deserialize)]
struct Manifest {
@@ -66,6 +91,7 @@ fn manifest() -> Result<Manifest> {
if manifest.files[0].path == manifest.files[1].path {
bail!("duplicate model manifest artifact");
}
validate_artifact_order(&manifest.files)?;
Ok(manifest)
}
@@ -158,28 +184,60 @@ fn verified(path: &Path, artifact: &Artifact) -> Result<bool> {
/// is completed verified bytes plus bytes of the current attempt. On failure it
/// resets to verified completed bytes; cached files never trigger network access.
pub fn provision(directory: &Path, progress: &AtomicU64) -> Result<()> {
provision_with_verified_assets(directory, progress, |_| {})
}
/// Provision the complete pinned pair, notifying after each artifact's exact
/// cached SHA verification or successful verified atomic installation. The
/// callback is synchronous and has no error return: callers must keep their
/// inference/setup errors separate from download errors. Byte progress alone
/// never triggers a notification. Success still requires both artifacts.
pub fn provision_with_verified_assets(
directory: &Path,
progress: &AtomicU64,
on_verified: impl FnMut(VerifiedAsset),
) -> Result<()> {
let manifest = manifest()?;
let client = Client::builder()
.connect_timeout(Duration::from_secs(20))
.timeout(Duration::from_secs(900))
.user_agent(concat!("Searchi/", env!("CARGO_PKG_VERSION")))
.build()?;
provision_with(directory, progress, &manifest, &client, |artifact| {
format!(
"https://huggingface.co/{}/resolve/{}/{}",
manifest.export_repository, manifest.revision, artifact.path
)
})
provision_with_callback(
directory,
progress,
&manifest,
&client,
|artifact| {
format!(
"https://huggingface.co/{}/resolve/{}/{}",
manifest.export_repository, manifest.revision, artifact.path
)
},
on_verified,
)
}
// URL injection is private so tests can use a local HTTP fixture. Production
// exposes no arbitrary download source or unverified executable provisioner.
#[cfg(test)]
fn provision_with(
directory: &Path,
progress: &AtomicU64,
manifest: &Manifest,
client: &Client,
url: impl Fn(&Artifact) -> String,
) -> Result<()> {
provision_with_callback(directory, progress, manifest, client, url, |_| {})
}
fn provision_with_callback(
directory: &Path,
progress: &AtomicU64,
manifest: &Manifest,
client: &Client,
url: impl Fn(&Artifact) -> String,
mut on_verified: impl FnMut(VerifiedAsset),
) -> Result<()> {
fs::create_dir_all(directory).context("create model cache directory")?;
progress.store(0, Ordering::Release);
@@ -190,6 +248,9 @@ fn provision_with(
if verified(&destination, artifact)? {
completed += artifact.bytes;
progress.store(completed, Ordering::Release);
if let Some(asset) = verified_asset(artifact) {
on_verified(asset);
}
continue;
}
let mut last_error = None;
@@ -225,6 +286,9 @@ fn provision_with(
}
completed += artifact.bytes;
progress.store(completed, Ordering::Release);
if let Some(asset) = verified_asset(artifact) {
on_verified(asset);
}
}
Ok(())
}
@@ -564,4 +628,238 @@ mod tests {
.unwrap()
.ends_with("models/embeddinggemma2"));
}
#[test]
fn pinned_backbone_must_precede_projector() {
let mut files = manifest().unwrap().files;
validate_artifact_order(&files).unwrap();
files.swap(0, 1);
assert!(validate_artifact_order(&files).is_err());
}
#[test]
fn cached_pair_emits_verified_callbacks_without_network() {
let directory = tempfile::tempdir().unwrap();
let manifest = test_manifest(vec![
artifact(BACKBONE_FILE, b"backbone"),
artifact(PROJECTOR_FILE, b"projection"),
]);
fs::write(directory.path().join(BACKBONE_FILE), b"backbone").unwrap();
fs::write(directory.path().join(PROJECTOR_FILE), b"projection").unwrap();
let progress = AtomicU64::new(0);
let mut observed = Vec::new();
provision_with_callback(
directory.path(),
&progress,
&manifest,
&client(),
|_| panic!("Cached pair attempted network"),
|asset| {
observed.push(asset);
let bytes = match asset {
VerifiedAsset::TextBackbone => 8,
VerifiedAsset::MediaProjector => 18,
};
assert_eq!(progress.load(Ordering::Acquire), bytes);
},
)
.unwrap();
assert_eq!(
observed,
[VerifiedAsset::TextBackbone, VerifiedAsset::MediaProjector]
);
assert_eq!(progress.load(Ordering::Acquire), 18);
}
#[test]
fn failed_projector_retains_verified_text_callback_and_progress() {
let directory = tempfile::tempdir().unwrap();
fs::write(directory.path().join(PROJECTOR_FILE), b"oldproject").unwrap();
let manifest = test_manifest(vec![
artifact(BACKBONE_FILE, b"backbone"),
artifact(PROJECTOR_FILE, b"projection"),
]);
let mut responses = vec![response(b"backbone", Some(8))];
responses.extend(vec![response(b"corrupted!", Some(10)); ATTEMPTS]);
let (url, server) = server(responses);
let progress = AtomicU64::new(0);
let mut observed = Vec::new();
let result = provision_with_callback(
directory.path(),
&progress,
&manifest,
&client(),
|_| url.clone(),
|asset| {
assert_eq!(asset, VerifiedAsset::TextBackbone);
assert_eq!(
fs::read(directory.path().join(BACKBONE_FILE)).unwrap(),
b"backbone"
);
observed.push(asset);
},
);
assert!(
result.is_err(),
"The incomplete bundle must not be successful"
);
assert_eq!(server.join().unwrap(), ATTEMPTS + 1);
assert_eq!(observed, [VerifiedAsset::TextBackbone]);
assert_eq!(progress.load(Ordering::Acquire), 8);
assert_eq!(
fs::read(directory.path().join(PROJECTOR_FILE)).unwrap(),
b"oldproject"
);
assert!(!verified(&directory.path().join(PROJECTOR_FILE), &manifest.files[1]).unwrap());
assert_no_part_files(directory.path());
}
#[test]
fn corrupt_backbone_never_emits_a_verified_callback() {
let directory = tempfile::tempdir().unwrap();
fs::write(directory.path().join(BACKBONE_FILE), b"corrupt!").unwrap();
let manifest = test_manifest(vec![
artifact(BACKBONE_FILE, b"backbone"),
artifact(PROJECTOR_FILE, b"projection"),
]);
let (url, server) = server(vec![response(b"corrupt!", Some(8)); ATTEMPTS]);
let progress = AtomicU64::new(0);
let mut observed = Vec::new();
assert!(provision_with_callback(
directory.path(),
&progress,
&manifest,
&client(),
|_| url.clone(),
|asset| observed.push(asset)
)
.is_err());
assert_eq!(server.join().unwrap(), ATTEMPTS);
assert!(observed.is_empty());
assert_eq!(progress.load(Ordering::Acquire), 0);
assert!(!directory.path().join(PROJECTOR_FILE).exists());
assert_no_part_files(directory.path());
}
#[test]
fn partial_backbone_download_never_emits_a_verified_callback() {
let directory = tempfile::tempdir().unwrap();
let manifest = test_manifest(vec![
artifact(BACKBONE_FILE, b"backbone"),
artifact(PROJECTOR_FILE, b"projection"),
]);
let (url, server) = server(vec![response(b"back", Some(8)); ATTEMPTS]);
let progress = AtomicU64::new(0);
let mut observed = Vec::new();
assert!(provision_with_callback(
directory.path(),
&progress,
&manifest,
&client(),
|_| url.clone(),
|asset| observed.push(asset)
)
.is_err());
assert_eq!(server.join().unwrap(), ATTEMPTS);
assert!(observed.is_empty());
assert_eq!(progress.load(Ordering::Acquire), 0);
assert!(!directory.path().join(BACKBONE_FILE).exists());
assert_no_part_files(directory.path());
}
#[test]
fn text_callback_precedes_gated_projector_and_full_completion() {
use std::sync::mpsc;
let directory = tempfile::tempdir().unwrap();
fs::write(directory.path().join(BACKBONE_FILE), b"backbone").unwrap();
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let url = format!("http://{}/fixture", listener.local_addr().unwrap());
let (release, wait) = mpsc::channel();
let (headers_sent, headers) = mpsc::channel();
listener.set_nonblocking(true).unwrap();
let server = thread::spawn(move || {
let deadline = Instant::now() + Duration::from_secs(5);
let mut stream = loop {
match listener.accept() {
Ok((stream, _)) => break stream,
Err(error)
if error.kind() == std::io::ErrorKind::WouldBlock
&& Instant::now() < deadline =>
{
thread::sleep(Duration::from_millis(2))
}
Err(error) => panic!("Gated local fixture accept failed: {error}"),
}
};
stream.set_nonblocking(false).unwrap();
stream
.set_read_timeout(Some(Duration::from_secs(2)))
.unwrap();
let mut request = Vec::new();
let mut buffer = [0u8; 1024];
while !request.windows(4).any(|w| w == b"\r\n\r\n") {
let n = stream.read(&mut buffer).unwrap();
assert!(n > 0);
request.extend_from_slice(&buffer[..n]);
assert!(request.len() <= 8192);
}
stream
.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 10\r\nConnection: close\r\n\r\n")
.unwrap();
headers_sent.send(()).unwrap();
wait.recv_timeout(Duration::from_secs(2)).unwrap();
stream.write_all(b"projection").unwrap();
});
let (events, observed) = mpsc::channel();
let (done, completion) = mpsc::channel();
let path = directory.path().to_path_buf();
let provisioning = thread::spawn(move || {
let manifest = test_manifest(vec![
artifact(BACKBONE_FILE, b"backbone"),
artifact(PROJECTOR_FILE, b"projection"),
]);
let progress = AtomicU64::new(0);
let result = provision_with_callback(
&path,
&progress,
&manifest,
&client(),
|_| url.clone(),
|asset| {
let artifact = match asset {
VerifiedAsset::TextBackbone => &manifest.files[0],
VerifiedAsset::MediaProjector => &manifest.files[1],
};
assert!(verified(&path.join(&artifact.path), artifact).unwrap());
events.send(asset).unwrap();
},
);
done.send(result).unwrap();
});
assert_eq!(
observed.recv_timeout(Duration::from_secs(2)).unwrap(),
VerifiedAsset::TextBackbone
);
headers.recv_timeout(Duration::from_secs(2)).unwrap();
assert!(
observed.try_recv().is_err(),
"Projector is not yet verified"
);
assert!(
completion.try_recv().is_err(),
"Bundle must remain incomplete"
);
assert!(!directory.path().join(PROJECTOR_FILE).exists());
release.send(()).unwrap();
assert_eq!(
observed.recv_timeout(Duration::from_secs(2)).unwrap(),
VerifiedAsset::MediaProjector
);
completion
.recv_timeout(Duration::from_secs(2))
.unwrap()
.unwrap();
provisioning.join().unwrap();
server.join().unwrap();
assert_no_part_files(directory.path());
}
}
+13 -1
View File
@@ -22,7 +22,7 @@ impl SemanticOrigin {
}
/// A conservative intent hint, not a language understanding model. Explicit
/// document/visual cues veto it; ordinary topic and instrument queries do not
/// document/visual/code cues veto it; ordinary topic and instrument queries do not
/// silently restrict modality. Word boundaries avoid filename substrings.
fn preferred_origin(query: &str) -> Option<SemanticOrigin> {
if query.split_whitespace().count() == 1 && Path::new(query).extension().is_some() {
@@ -49,6 +49,12 @@ fn preferred_origin(query: &str) -> Option<SemanticOrigin> {
"screenshot",
"dokument",
"textdatei",
"code",
"function",
"implementation",
"quellcode",
"funktion",
"implementierung",
]) {
return None;
}
@@ -211,6 +217,12 @@ mod tests {
"a document that someone mentioned",
"speech.png",
"a screenshot of an audio waveform",
"code that chooses audio search when a query mentions speech",
"an implementation for identifying music",
"a function that decodes audio samples",
"Quellcode für die Suche nach gesprochenen Themen",
"eine Funktion für die Verarbeitung von Musik",
"Implementierung für die Erkennung von Geräuschen",
] {
assert_eq!(preferred_origin(query), None, "{query}");
}
@@ -0,0 +1,56 @@
use anyhow::Result;
use searchi_core::SearchIndex;
use searchi_service::{FfmpegTools, HybridSearch, MediaLimits, SearchOptions, SemanticOrigin};
use std::{fs, path::PathBuf};
#[test]
#[ignore = "Requires verified Google CPU worker/model and FFmpeg fixtures"]
fn explicit_code_intent_preserves_code_among_real_audio_candidates() -> Result<()> {
assert_eq!(std::env::var("SEARCHI_FORCE_CPU")?, "1");
let model = std::env::var_os("SEARCHI_GEMMA_MODEL_DIR").expect("Set model directory");
let worker = std::env::var_os("SEARCHI_GEMMA_WORKER").expect("Set worker path");
let tools = FfmpegTools {
ffmpeg: std::env::var_os("SEARCHI_TEST_FFMPEG")
.expect("Set FFmpeg")
.into(),
ffprobe: std::env::var_os("SEARCHI_TEST_FFPROBE")
.expect("Set FFprobe")
.into(),
};
let fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../fixtures");
let temp = tempfile::tempdir()?;
let corpus = temp.path().join("corpus");
fs::create_dir(&corpus)?;
// Preserve the unchanged diagnostic implementation and actual licensed sound.
fs::copy(
fixture.join("mixed/sources/crates__searchi-service__src__routing.rs"),
corpus.join("routing.rs"),
)?;
fs::copy(
fixture.join("media/assets/sound02.mp3"),
corpus.join("sound.mp3"),
)?;
let database = temp.path().join("index.db");
let mut lexical = SearchIndex::open(&database)?;
lexical.index_folder(&corpus)?;
let mut indexer = HybridSearch::open(&database, &model, &worker, 1)?;
let report = indexer.index_embeddings_with_media(&tools, &MediaLimits::default())?;
assert!(report.failures.is_empty(), "{report:?}");
assert_eq!(report.media_files, 1);
drop(indexer);
let mut search = HybridSearch::open_query(&database, &model, &worker, 1)?;
let query = "code that chooses audio search when a query mentions speech";
let options = SearchOptions::default();
let audio = search.search_semantic_with_origin(query, 20, &options, SemanticOrigin::Audio)?;
assert!(
!audio.is_empty(),
"The old exclusive audio lane must qualify"
);
assert!(audio.iter().all(|hit| hit.filename == "sound.mp3"));
let hits = search.search_semantic(query, 20, &options)?;
assert!(
hits.iter().any(|hit| hit.filename == "routing.rs"),
"Explicit code intent lost its semantic implementation: {hits:?}"
);
Ok(())
}