open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-loader-gguf/examples/load_openvino.rs
Claude fd25cb8b70 jouleclaw-loader-gguf: apply clippy's own suggestions, 81 -> 40
`cargo clippy --fix` only applies MachineApplicable suggestions, and most
of this crate's remaining lints carry MaybeIncorrect ones. Those are still
exact byte spans with exact replacement text; "maybe incorrect" means
clippy will not promise the semantics, not that the text is wrong. So
scripts/../apply-suggestions.py reads the JSON and applies them, and the
crate's 343 tests decide.

96 spans across 26 files: collapsible_if, redundant_guards,
needless_late_init, unnecessary_cast, manual_is_multiple_of,
manual_repeat_n, unnecessary_sort_by, manual_slice_size_calculation,
unused_mut, needless_lifetimes, needless_borrows_for_generic_args,
double_parens.

One suggestion class must never be applied this way, and it cost a
revert to learn: `needless_range_loop` emits a *template*, not a
replacement --

    for (i, <item>) in w.iter_mut().enumerate().take(total)

-- leaving both the binding name and the body's `w[i]` uses for a human.
Applied literally it writes `<item>` into the source, which does not
parse; four files failed to compile. They were reverted to HEAD and the
applier now refuses any replacement containing `<` and `>`.

The lesson that actually matters is the ordering: the first batch of 69
was verified green and then left uncommitted while a riskier batch ran on
top, so reverting the breakage could not be done without also losing
verified work. Commit verified progress before the next batch.

Verified: 0 errors, 343 tests pass.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-21 20:30:31 -04:00

94 lines
2.8 KiB
Rust

//! Real-model bring-up for OpenVINO IR files. Pass the
//! `.xml` path — the sibling `.bin` is auto-resolved.
//!
//! ```bash
//! cargo run --release --example load_openvino -- path/to/model.xml
//! ```
use jouleclaw_loader_gguf::openvino::read_openvino_ir;
use std::collections::BTreeMap;
use std::path::PathBuf;
use std::time::Instant;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("usage: load_openvino <path.xml>");
std::process::exit(1);
}
let path = PathBuf::from(&args[1]);
println!("xml: {}", path.display());
let t0 = Instant::now();
let m = read_openvino_ir(&path).map_err(|e| format!("parse: {:?}", e))?;
let load_secs = t0.elapsed().as_secs_f64();
println!("bin: {}", m.bin_path.display());
println!();
println!("=== load ===");
println!(" load_wall_clock_secs: {:.3}", load_secs);
println!(" net_name: {}", m.net_name);
println!(" ir_version: {}", m.ir_version);
println!(" const_count: {}", m.tensors.len());
println!(" bin_size: {} ({})", m.bin_mmap.len(), humanize_bytes(m.bin_mmap.len() as u64));
let total_data: u64 = m.tensors.iter().map(|t| t.bin_size).sum();
println!();
println!("=== tensor data ===");
println!(
" total_const_bytes: {} ({})",
total_data, humanize_bytes(total_data),
);
// Dtype distribution.
let mut counts: BTreeMap<String, (u32, u64)> = BTreeMap::new();
for t in &m.tensors {
let key = format!("{:?}", t.dtype);
let e = counts.entry(key).or_insert((0, 0));
e.0 += 1;
e.1 += t.bin_size;
}
println!();
println!("=== dtype distribution ===");
for (k, (c, b)) in &counts {
println!(" {:<14} count={:>5} size={}", k, c, humanize_bytes(*b));
}
// Top 10 tensors.
let mut sorted: Vec<&_> = m.tensors.iter().collect();
sorted.sort_by_key(|a| std::cmp::Reverse(a.bin_size));
println!();
println!("=== top 10 tensors by byte size ===");
for t in sorted.iter().take(10) {
println!(
" {:?} {:<60} {:>14} ({})",
t.dtype,
truncate(&t.name, 60),
t.bin_size,
humanize_bytes(t.bin_size),
);
}
Ok(())
}
fn truncate(s: &str, max: usize) -> String {
if s.len() <= max {
s.to_string()
} else {
format!("…{}", &s[s.len() - max + 1..])
}
}
fn humanize_bytes(n: u64) -> String {
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
if n >= GB {
format!("{:.2} GiB", n as f64 / GB as f64)
} else if n >= MB {
format!("{:.2} MiB", n as f64 / MB as f64)
} else if n >= KB {
format!("{:.2} KiB", n as f64 / KB as f64)
} else {
format!("{} B", n)
}
}