use crate::agent::CliError;
use saccade_core::wave7::{faces, models, vision::VisionImage, watermark};
use serde_json::{Value, json};
use std::path::PathBuf;
#[derive(clap::Args, Default)]
pub(crate) struct Checks {
#[arg(long)]
faces: bool,
#[arg(long)]
face_observations: Option<PathBuf>,
#[arg(long)]
model_registry: Option<PathBuf>,
#[arg(long)]
model_cache: Option<PathBuf>,
#[arg(long)]
runtime_library: Option<PathBuf>,
#[arg(long)]
watermark: bool,
#[arg(long, requires = "watermark")]
watermark_payload: Option<String>,
#[arg(long, requires = "faces", value_delimiter = ',', num_args = 1)]
face_crop: Option<Vec<f32>>,
}
impl Checks {
pub(crate) fn measure(&self, path: &std::path::Path) -> Result<Value, CliError> {
let mut value = json!({});
if !(self.faces || self.face_observations.is_some() || self.watermark) {
return Ok(value);
}
let image = VisionImage::load(path).map_err(crate::wave7_cmd::error)?;
if self.faces || self.face_observations.is_some() {
let report = if let Some(p) = &self.face_observations {
let mut r: faces::FaceReport = serde_json::from_slice(
&models::read_bounded(p, 1024 * 1024).map_err(crate::wave7_cmd::error)?,
)?;
r.validate(&image).map_err(crate::wave7_cmd::error)?;
r.provenance.runtime = "replay".into();
r.provenance.source_parity = false;
r
} else {
self.detect(&image)?
};
if let Some(crop) = &self.face_crop {
let c: [f32; 4] = crop
.clone()
.try_into()
.map_err(|_| CliError::usage("face crop needs X,Y,W,H"))?;
value["crop_check"] = serde_json::to_value(
faces::crop_check(
&image,
&report,
&[faces::CropSpec::Rectangle {
rect: saccade_core::wave7::vision::Rect {
x: c[0],
y: c[1],
width: c[2],
height: c[3],
},
}],
None,
)
.map_err(crate::wave7_cmd::error)?,
)?;
}
value["faces"] = serde_json::to_value(report)?;
}
if self.watermark {
let legacy = self
.watermark_payload
.as_deref()
.map(crate::wave7_cmd::unhex)
.transpose()?
.map(|expected_payload| watermark::DwtConfig {
expected_payload,
quantization_step: 36.,
minimum_agreement: 0.9,
});
value["watermark"] = serde_json::to_value(
watermark::inspect(&image, None, legacy.as_ref())
.map_err(crate::wave7_cmd::error)?,
)?;
}
Ok(value)
}
#[cfg(feature = "local-models")]
fn detect(&self, image: &VisionImage) -> Result<faces::FaceReport, CliError> {
let registry = crate::wave7_cmd::registry(self.model_registry.as_deref())?;
let cache = crate::wave7_cmd::cache(self.model_cache.as_deref())?;
let library = saccade_core::wave7::runtime_install::resolve(
crate::wave7_cmd::runtime_flag(self.runtime_library.as_deref())?.as_deref(),
&cache,
)
.map_err(crate::wave7_cmd::error)?;
let mut runtime = saccade_core::wave7::runtime::OnnxModel::load(
registry
.model("yunet-2026may")
.map_err(crate::wave7_cmd::error)?,
&cache,
&library,
false,
)
.map_err(crate::wave7_cmd::error)?;
faces::detect(image, &mut runtime).map_err(crate::wave7_cmd::error)
}
#[cfg(not(feature = "local-models"))]
fn detect(&self, _image: &VisionImage) -> Result<faces::FaceReport, CliError> {
Err(CliError::new(
"feature_unavailable",
"face inference requires local-models",
))
}
}