mirror of
https://github.com/ruvnet/RuView
synced 2026-07-22 17:23:19 +00:00
1c9727f9cf
Bind the ADR-024 MAT (Mass Casualty Assessment Tool) disaster-survivor detection + START triage surface into the wheel behind a gated [mat] extra / Cargo `mat` feature, mirroring the upstream disaster/ML gating. Also adds the [sota] superset extra (aether+meridian+mat). Surface (bound against the REAL code at HEAD, not the ADR wishlist): - DisasterType (9 variants) / TriageStatus (5, START) enums - DisasterConfig (builder-backed, continuous_monitoring forced off) - DisasterResponse: initialize_event / add_zone / push_csi_data / scan_once / survivors / survivors_by_triage - Survivor (id, triage_status, confidence, location, latest_vitals) - VitalSignsReading (breathing/heartbeat rate, movement, confidence) - ScanZone.rectangle / ScanZone.circle push_csi_data + scan_once are GIL-released. Honest deviations from ADR section 3.4 (documented in module header): - ADR proposed adding a Rust-side sync scan_once() (section 11.3). That was UNNECESSARY: the public async start_scanning() runs exactly one scan_cycle and returns when continuous_monitoring == false. The binding forces that flag off and drives one cycle on a private current-thread tokio runtime -- NO change to wifi-densepose-mat. - scan_cycle requires an active event + Active zone, which the ADR surface omitted; initialize_event + add_zone are bound as required additions. - Survivor.vital_signs is a *history* in the real code; bound as Survivor.latest_vitals -> Optional[VitalSignsReading]. - DisasterType has 9 variants at HEAD (adds Landslide/MineCollapse/ Industrial/TunnelCollapse); all bound. Parity (section 4.1, release-blocking): committed fixture mat_input.json (synthetic breathing-modulated CSI stream) -> native Rust reference (tests/mat_parity.rs, drives DisasterResponse directly) locks tests/golden/mat_result.sha256 over a canonical `count=<K>;triage_priorities=<sorted>` string (survivor UUIDs/timestamps excluded as non-deterministic); pytest (tests/test_mat.py) runs the same stream through the binding and asserts the identical hash. Both detect exactly 1 survivor, triage Delayed. Honest: synthetic fixture proves binding==native path equality, NOT live detection accuracy. Verified: cargo test --features mat --test mat_parity -> 2/2 pass maturin develop --features mat + pytest tests/test_mat.py -> 7/7 pass default cargo build clean, 0 mat/tokio refs in the default dep graph. WHEEL-SIZE FINDING (ADR-185 section 9): default-features=false drops MAT's `api` (axum) and `ruvector` features, but MAT still carries NON-optional tokio (rt/sync/time), wifi-densepose-nn (ort/ONNX + reqwest/hyper), rustfft, geo, ndarray. So a [mat] wheel exceeds the ADR-117 section 5.4 <=5 MB budget -- same leaf-crate-hoist follow-up as AETHER/MERIDIAN. The default wheel is untouched (feature-gated).
120 lines
4.9 KiB
Rust
120 lines
4.9 KiB
Rust
//! ADR-117 — PyO3 bindings for the WiFi-DensePose Rust core.
|
|
//!
|
|
//! This crate is the compiled half of the `wifi-densepose` v2.x PyPI
|
|
//! wheel. The Python-facing facade lives in `python/wifi_densepose/`
|
|
//! and re-exports symbols from this module under their stable names.
|
|
//!
|
|
//! ## Phase status (per ADR-117 §6)
|
|
//!
|
|
//! - **P1 (scaffold) — this commit**: module loads, version constant
|
|
//! exposed, smoke test passes via maturin develop.
|
|
//! - **P2**: bind `CsiFrame`, `Keypoint`, `PoseEstimate` (next).
|
|
//! - **P3**: bind 4-stage vitals + signal DSP.
|
|
//! - **P4**: pure-Python `wifi_densepose.client` (WS/MQTT) — no Rust
|
|
//! surface needed; lives outside this crate.
|
|
//! - **P5**: cibuildwheel + PyPI publish.
|
|
|
|
use pyo3::prelude::*;
|
|
|
|
mod bindings {
|
|
#[cfg(feature = "aether")]
|
|
pub mod aether;
|
|
pub mod bfld;
|
|
#[cfg(feature = "mat")]
|
|
pub mod mat;
|
|
#[cfg(feature = "meridian")]
|
|
pub mod meridian;
|
|
pub mod keypoint;
|
|
pub mod pose;
|
|
pub mod privacy_gate;
|
|
pub mod vitals;
|
|
}
|
|
|
|
/// Version of the bound Rust core. Surfaced to Python as
|
|
/// `wifi_densepose.__rust_version__` so users can correlate wheel
|
|
/// behaviour with the exact `v2/crates/` HEAD it was built from.
|
|
const RUST_CORE_VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
|
|
/// Compile-time identifier for the Rust commit that produced this
|
|
/// wheel. Surfaced for diagnostics. Set via `CARGO_PKG_VERSION` for
|
|
/// now; P5 wires in the git SHA via `vergen`.
|
|
const RUST_BUILD_TAG: &str = env!("CARGO_PKG_VERSION");
|
|
|
|
/// One-line description of which feature flags were enabled at build
|
|
/// time. Helps users debug "is my wheel the slim one or the full one?".
|
|
fn build_features() -> Vec<&'static str> {
|
|
let mut feats: Vec<&'static str> = Vec::new();
|
|
feats.push("p1-scaffold");
|
|
feats.push("p2-keypoint-bindings"); // Keypoint + KeypointType
|
|
feats.push("p2-pose-bindings"); // BoundingBox + PersonPose + PoseEstimate
|
|
feats.push("p3-vitals-bindings"); // BreathingExtractor + HeartRateExtractor + VitalEstimate
|
|
feats.push("p3.5-bfld-bindings"); // BfldFrame + BfldReport + BfldKind (stub Rust)
|
|
#[cfg(feature = "aether")]
|
|
feats.push("p6-aether-bindings"); // ADR-185 P1 — AETHER contrastive embeddings
|
|
#[cfg(feature = "meridian")]
|
|
feats.push("p6-meridian-bindings"); // ADR-185 P2 — MERIDIAN domain generalization
|
|
#[cfg(feature = "mat")]
|
|
feats.push("p6-mat-bindings"); // ADR-185 P3 — MAT disaster survivor detection
|
|
feats
|
|
}
|
|
|
|
/// Quick smoke test exposed to Python. Returns "ok" — used by the
|
|
/// integration tests in `python/tests/test_smoke.py` to assert the
|
|
/// PyO3 module is importable and callable.
|
|
#[pyfunction]
|
|
fn hello() -> PyResult<&'static str> {
|
|
Ok("ok")
|
|
}
|
|
|
|
/// The `_native` module — re-exported in pure-Python as
|
|
/// `wifi_densepose._native`. End users should import the parent
|
|
/// package (`import wifi_densepose`) and never reach into `_native`
|
|
/// directly; the leading underscore is a Python convention marking
|
|
/// it as private.
|
|
///
|
|
/// The function name MUST match the `module-name` in pyproject.toml's
|
|
/// `[tool.maturin]` block — i.e. it must be `_native` because the
|
|
/// pyproject says `module-name = "wifi_densepose._native"`. PyO3
|
|
/// generates the `PyInit__native` symbol from this function name.
|
|
#[pymodule]
|
|
#[pyo3(name = "_native")]
|
|
fn wifi_densepose_native(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
|
m.add("__rust_version__", RUST_CORE_VERSION)?;
|
|
m.add("__rust_build_tag__", RUST_BUILD_TAG)?;
|
|
m.add("__build_features__", build_features())?;
|
|
m.add_function(wrap_pyfunction!(hello, m)?)?;
|
|
|
|
// P2 — Keypoint + KeypointType bindings.
|
|
bindings::keypoint::register(m)?;
|
|
// P2 — BoundingBox + PersonPose + PoseEstimate bindings.
|
|
bindings::pose::register(m)?;
|
|
// P3 — Vital sign extraction bindings.
|
|
bindings::vitals::register(m)?;
|
|
// P3.5 — BFLD bindings (stub Rust; future wifi-densepose-bfld crate
|
|
// will replace the stub without changing the Python API).
|
|
bindings::bfld::register(m)?;
|
|
// ADR-118 PrivacyClass + HAP/Matter eligibility gates (SOTA — backed by
|
|
// the published `wifi-densepose-bfld 0.3.0` crate, not the Python port).
|
|
// Closes ADR-125 §2.1.d at the binding boundary.
|
|
bindings::privacy_gate::register(m)?;
|
|
|
|
// ADR-185 P1 — AETHER contrastive CSI embedding bindings, compiled
|
|
// and registered only under the `aether` feature so the default
|
|
// wheel links none of the sensing-server dependency tree.
|
|
#[cfg(feature = "aether")]
|
|
bindings::aether::register(m)?;
|
|
|
|
// ADR-185 P2 — MERIDIAN cross-environment domain-generalization
|
|
// bindings (hardware normalization, geometry encoding, rapid
|
|
// adaptation, cross-domain eval). Gated behind `meridian`; tch-free.
|
|
#[cfg(feature = "meridian")]
|
|
bindings::meridian::register(m)?;
|
|
|
|
// ADR-185 P3 — MAT disaster-survivor detection + START triage. Gated
|
|
// behind `mat`, mirroring the upstream disaster/ML stack gating.
|
|
#[cfg(feature = "mat")]
|
|
bindings::mat::register(m)?;
|
|
|
|
Ok(())
|
|
}
|