Skip to main content

apr_format/
lib.rs

1// Tests legitimately use expect/unwrap/panic and exact-float asserts on known
2// values; mirror the workspace convention (aprender-core/src/lib.rs) of allowing
3// these — including the `.clippy.toml` disallowed-methods `.unwrap()` ban — in
4// test code only.
5#![cfg_attr(
6    test,
7    allow(
8        clippy::expect_used,
9        clippy::unwrap_used,
10        clippy::panic,
11        clippy::float_cmp,
12        clippy::disallowed_methods
13    )
14)]
15
16//! # apr-format — sovereign `.apr` model container format
17//!
18//! Minimal, dependency-light read/write for the `.apr` model container (v1
19//! `APRN` + v2 `APR\0`), extracted from `aprender-core` so that downstream
20//! consumers (realizar inference, xpile, external tooling) can read and write
21//! `.apr` files **without** pulling the full ML/GPU/tokenizer/quantization stack.
22//! See issue #2231 — "depend on the *format*, not the *framework*."
23//!
24//! ## Status: Stage 1 foundation + cut-feasibility spike
25//!
26//! This crate currently ships:
27//! - The sovereign error seam ([`error::AprFormatError`], wrapped by
28//!   `aprender-core` via `impl From`).
29//! - The single deduplicated [`crc32::crc32`] and [`f16`] conversions.
30//! - A representative v1 (`APRN`) slice — [`types`] (header/metadata/flags) and
31//!   [`core_io`] (save/load) — proving the error-seam compiles at a crate
32//!   boundary.
33//! - The byte-only structural validator split ([`validate`]), demonstrating the
34//!   Structure-vs-Physics separation.
35//!
36//! The bulk `git-mv` of the rest of `format/` (v2 container, mmap, spec, …) lands
37//! in Stage 2.
38//!
39//! ## Locked design decisions (issue #2231)
40//! 1. The GGUF/SafeTensors/ONNX **converter stays in `aprender-core`** (it needs
41//!    `f32` physics + the ML stack); only the container moves.
42//! 2. **std-only** for v1 (no `no_std` yet — the std surface is kept thin).
43//! 3. **Wrapper error seam**: the leaf owns `AprFormatError`; core From-wraps it.
44//! 4. **mmap is feature-gated** (`mmap` feature, off by default).
45
46pub mod core_io;
47pub mod crc32;
48pub mod error;
49pub mod f16;
50pub mod falsifiers;
51pub mod model_card;
52pub mod types;
53pub mod v2;
54pub mod validate;
55
56// --- Convenience re-exports (the public surface aprender-core re-exports) ---
57pub use core_io::{
58    inspect, inspect_bytes, load, load_auto, load_from_bytes, load_mmap, save, MMAP_THRESHOLD,
59};
60pub use crc32::crc32;
61pub use error::{AprFormatError, Result};
62pub use f16::{f16_to_f32, f32_to_f16};
63pub use model_card::{ModelCard, TrainingDataInfo};
64pub use types::{
65    Compression, DistillMethod, DistillationInfo, DistillationParams, Flags, Header, LayerMapping,
66    LicenseInfo, LicenseTier, Metadata, ModelInfo, ModelType, SaveOptions, TeacherProvenance,
67    TrainingInfo, FORMAT_VERSION, HEADER_SIZE, MAGIC, MAX_UNCOMPRESSED_SIZE,
68};
69pub use validate::{validate_structure, StructureCheck};
70
71#[cfg(test)]
72mod sovereignty_tests {
73    /// FALSIFY-APRF-SOV-STD-ONLY: the leaf is std-only (v1) — it links and runs
74    /// against std (fs/io save+load round-trip works), and carries no `#![no_std]`.
75    /// `no_std` is an explicit deferred decision; this pins that the std surface
76    /// stays available so the deferral cannot silently regress into a half-`no_std`
77    /// state. (A genuine `no_std` build would fail to link `std::fs::File` here.)
78    #[test]
79    fn test_std_only_surface_available() {
80        use crate::types::{ModelType, SaveOptions};
81        let dir = std::env::temp_dir();
82        let path = dir.join("apr_format_std_probe.apr");
83        // Uses std::fs via the leaf's save/load — proves the std surface links.
84        crate::save(
85            &vec![1.0_f32, 2.0],
86            ModelType::LinearRegression,
87            &path,
88            SaveOptions::default(),
89        )
90        .expect("std-only save must work");
91        let back: Vec<f32> =
92            crate::load(&path, ModelType::LinearRegression).expect("std-only load must work");
93        assert_eq!(back, vec![1.0, 2.0]);
94        let _ = std::fs::remove_file(&path);
95    }
96}