diff --git a/crates/engine/src/cmd/face_models.rs b/crates/engine/src/cmd/face_models.rs index de4a8d225..1ba0db0f4 100644 --- a/crates/engine/src/cmd/face_models.rs +++ b/crates/engine/src/cmd/face_models.rs @@ -88,7 +88,6 @@ fn installed_models(dir: &Path) -> Vec { } fn row(m: &ModelManifest, installed: bool, selected: bool, accepted: &Value) -> Value { - let bundled = known::BUNDLED.contains(&m.id.as_str()); json!({ "id": m.id, "name": m.name, @@ -99,8 +98,7 @@ fn row(m: &ModelManifest, installed: bool, selected: bool, accepted: &Value) -> "sizeBytes": m.size_bytes, "sha256": m.sha256, "known": known::all().iter().any(|k| k.id == m.id), - "bundled": bundled, - "installed": installed || bundled, + "installed": installed, "selected": selected, "accepted": accepted, }) @@ -192,9 +190,6 @@ fn install(s: &mut Session, p: &Value) -> Result { Suggestion::Known(m) | Suggestion::Draft { manifest: m, .. } => m, Suggestion::Unsupported(why) => return Err(bad(C, format!("this model cannot be used yet: {why}"))), }; - if known::BUNDLED.contains(&m.id.as_str()) { - return Err(bad(C, "this model is already included with LightCraft")); - } if m.role != Role::Embedder { return Err(bad(C, "only face recognition models can be installed for now")); } @@ -228,9 +223,6 @@ fn remove(s: &mut Session, p: &Value) -> Result { if !manifest::valid_id(id) { return Err(bad(C, "not a model id")); } - if known::BUNDLED.contains(&id) { - return Err(bad(C, "this model is part of LightCraft and cannot be removed")); - } let home = dir.join(id); // only a folder that is a model (it has a manifest) is ever deleted if !home.join("face-model.json").is_file() { @@ -274,7 +266,7 @@ fn enable(s: &mut Session, p: &Value) -> Result { pub fn specs() -> Vec { vec![ - cmd!(query "faces.models.list", "Face Models", [], None, "{} → {dir, enabled, embedder, runtime, models: [{id, name, role, licence{name, commercial, url, notice}, provenance, source, sizeBytes, known, bundled, installed, selected}]}", always, list), + cmd!(query "faces.models.list", "Face Models", [], None, "{} → {dir, enabled, embedder, runtime, models: [{id, name, role, licence{name, commercial, url, notice}, provenance, source, sizeBytes, known, installed, selected}]}", always, list), cmd!(query "faces.models.inspect", "Inspect Face Model File", [], None, "{path} → what a .onnx file is: {kind: known | draft | unsupported, model, assumptions, reason, alreadyInstalled}; installs nothing", always, inspect), cmd!(query "faces.models.install", "Install Face Model", [], None, "{path, acknowledged: true} — copy a .onnx face recognition model into the models folder. `acknowledged` must be true: the user has been shown its licence (see inspect) and accepted it", always, install), cmd!(query "faces.models.remove", "Remove Face Model", [], None, "{id} — delete an installed model", always, remove), diff --git a/crates/engine/src/tests_face_models.rs b/crates/engine/src/tests_face_models.rs index 1296ac882..e002b5c62 100644 --- a/crates/engine/src/tests_face_models.rs +++ b/crates/engine/src/tests_face_models.rs @@ -37,8 +37,7 @@ fn the_list_knows_the_known_models_and_is_honest_about_the_runtime() { let l = s.execute("faces.models.list", &json!({})).unwrap(); assert_eq!(l["enabled"], false); assert_eq!(l["runtime"], false); - assert_eq!(find(&l, "yunet-2023mar")["bundled"], true); - assert_eq!(find(&l, "yunet-2023mar")["installed"], true); + assert_eq!(find(&l, "yunet-2023mar")["installed"], false, "nothing ships with LightCraft"); assert_eq!(find(&l, "auraface-v1")["installed"], false); assert_eq!(find(&l, "auraface-v1")["licence"]["commercial"], "yes"); assert_eq!(find(&l, "sface-2021dec")["licence"]["commercial"], "unknown"); diff --git a/crates/faces/src/known.rs b/crates/faces/src/known.rs index a3e563ede..b5c73d5fc 100644 --- a/crates/faces/src/known.rs +++ b/crates/faces/src/known.rs @@ -1,17 +1,13 @@ //! The models LightCraft recognises by their SHA-256, with what is honestly known about each. //! //! The licence and provenance texts here are what the user reads before enabling a model, so they say what -//! is *not* known too. Weights are never bundled except where the bundling decision says so (the YuNet -//! detector, 232 KB, MIT); everything else is an opt-in the user installs themselves. +//! is *not* known too. Weights are never part of LightCraft: every model is an opt-in the user installs. use crate::manifest::{Colour, Commercial, InputSpec, Licence, ModelManifest, OutputSpec, Resize, Role, Thresholds}; /// Detector output decoders built into LightCraft. pub const DECODERS: &[&str] = &["yunet-v2"]; -/// Models that ship inside LightCraft (they are not installed by the user). -pub const BUNDLED: &[&str] = &["yunet-2023mar"]; - pub const YUNET_SHA256: &str = "8f2383e4dd3cfbb4553ea8718107fc0423210dc964f9f4280604804ed2552fa4"; pub const SFACE_SHA256: &str = "0ba9fbfa01b5270c96627c4ef784da859931e02f04419c829e83484087c34e79"; pub const AURAFACE_SHA256: &str = "a7933ea5330113b01c9b60351d8f4c33003f145d8470ac5f0e52ee2effe25c60"; @@ -20,7 +16,7 @@ fn licence(name: &str, commercial: Commercial, url: &str, notice: &str) -> Licen Licence { name: name.into(), commercial, url: Some(url.into()), notice: notice.into() } } -/// YuNet 2023mar (OpenCV Zoo): the face detector LightCraft bundles. +/// YuNet 2023mar (OpenCV Zoo): a small face detector. pub fn yunet() -> ModelManifest { ModelManifest { id: "yunet-2023mar".into(), @@ -31,7 +27,7 @@ pub fn yunet() -> ModelManifest { "MIT", Commercial::Yes, "https://github.com/opencv/opencv_zoo/blob/main/models/face_detection_yunet/LICENSE", - "MIT licence, copyright Shiqi Yu. Bundled with LightCraft.", + "MIT licence, copyright Shiqi Yu.", ), source: Some("https://github.com/opencv/opencv_zoo/tree/main/models/face_detection_yunet".into()), sha256: Some(YUNET_SHA256.into()), diff --git a/crates/ui-egui/src/panels/faces.rs b/crates/ui-egui/src/panels/faces.rs index ab321affa..5dad9bfcf 100644 --- a/crates/ui-egui/src/panels/faces.rs +++ b/crates/ui-egui/src/panels/faces.rs @@ -145,13 +145,11 @@ fn model_row(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens, m: &Value) }); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { if installed { - if m["bundled"] != true { - let r = ui.button("Remove"); - register(ui.ctx(), format!("faces:remove:{id}"), r.rect); - if r.clicked() { - let _ = app.run("faces.models.remove", json!({"id": id})); - app.caches.faces_epoch += 1; - } + let r = ui.button("Remove"); + register(ui.ctx(), format!("faces:remove:{id}"), r.rect); + if r.clicked() { + let _ = app.run("faces.models.remove", json!({"id": id})); + app.caches.faces_epoch += 1; } if !selected { let r = ui.button("Use");