Migrate embedding pipeline, EP session building, and model download to bread-onnx

embed.rs's OrtEmbedder now delegates its tokenize -> tensor build ->
mean-pool -> L2-normalize pipeline to bread_onnx::embedding::EmbeddingSession
(near-byte-identical to breadarrd's own OrtEmbedder — same duplication, now
shared, path dependency for now, see the TODO in breadmill/Cargo.toml), and
its per-EP session builders (npu_session/rocm_session/cuda_session/
openvino_session) collapse into a single to_provider() mapping onto
bread_onnx::Provider, which this crate's own breadmill/src/session.rs
counterpart now handles generically. This crate's Backend enum, cargo
feature gates (npu/rocm/cuda/openvino/full - unchanged, still control which
onnxruntime EPs actually link/load), and NPU vaip_config.json discovery all
stay local since they're genuinely breadsearch-specific. main.rs's Backend
construction and CLI flag handling are untouched.

This is also the reference implementation the MIGraphX-not-ROCm default in
bread-onnx's provider module was promoted from (see this machine's own
breadsearch-gpu-backends operator notes) — breadpad's ONNX migration, which
had the actual silent-fallback bug, follows in a later commit.

download_if_missing is replaced with bread_onnx::download::ensure_file
(same sync/ureq approach, now shared with breadarrd's downloader).

Builds clean with default features AND --features full (npu+rocm+cuda+
openvino all compiling together, matching how this crate already combined
them). All existing tests pass across the whole workspace.
This commit is contained in:
Breadway 2026-07-17 09:41:58 +08:00
parent d01a3841d9
commit 0990969722
4 changed files with 88 additions and 231 deletions

View file

@ -1,5 +1,4 @@
use std::{
io::Read,
path::{Path, PathBuf},
sync::{Arc, atomic::Ordering},
};
@ -226,29 +225,10 @@ fn download_if_missing(url: &str, dest: &Path) -> Result<(), String> {
eprintln!(" already present: {}", dest.display());
return Ok(());
}
eprintln!(" downloading {} ...", url);
let agent = ureq::AgentBuilder::new()
.timeout(std::time::Duration::from_secs(300))
.build();
let response = agent.get(url).call().map_err(|e| e.to_string())?;
let mut bytes = Vec::new();
response
.into_reader()
.read_to_end(&mut bytes)
.map_err(|e| e.to_string())?;
if bytes.is_empty() {
return Err(format!("empty download from {}", url));
}
// Write atomically via temp file
let tmp = dest.with_extension("tmp");
std::fs::write(&tmp, &bytes).map_err(|e| e.to_string())?;
std::fs::rename(&tmp, dest).map_err(|e| e.to_string())?;
eprintln!(" saved {} ({:.1} MB)", dest.display(), bytes.len() as f64 / 1_048_576.0);
// Shared with breadarrd's own (previously reqwest/async, now also this
// same sync/ureq implementation) model downloader — see
// bread_onnx::download's doc comment.
bread_onnx::download::ensure_file(url, dest, None).map_err(|e| e.to_string())?;
Ok(())
}