hf2q 0.1.7

Pure Rust CLI for converting HuggingFace models to hardware-optimized formats and serving them over an OpenAI-compatible API on Apple Silicon
use std::path::Path;

use serde_json::Value;

use crate::core::integrity::ShardIntegrity;

use super::hf_reference::HfModelReference;
use super::integrity::VerifiedSourceManifest;
use super::model_recipe::{
    embedded_qwen38_recipe, recipe_for_reference, ModelRecipe, ModelRecipeError,
    RecipeArtifactRole, RecipeQuantization, SourceRetentionChoice, MAX_MODEL_RECIPE_BYTES,
    QWEN38_ACCEPTED_REVISION, QWEN38_RECIPE_ID,
};

const RECIPE_BYTES: &[u8] = include_bytes!("../../data/model-recipes/qwen38-27b-official-v1.json");
const RECIPE_SHA256: &str = "8d62322eb7860b75fe4d26661354f9890d73e5b3f037a86ebcd60d6ea954fb3c";

fn mutated(mutator: impl FnOnce(&mut Value)) -> Vec<u8> {
    let mut value: Value = serde_json::from_slice(RECIPE_BYTES).unwrap();
    mutator(&mut value);
    let mut bytes = serde_json::to_vec(&value).unwrap();
    bytes.push(b'\n');
    bytes
}

#[test]
fn embedded_recipe_is_exact_canonical_and_binds_accepted_outputs() {
    let recipe = embedded_qwen38_recipe().expect("embedded recipe");
    assert_eq!(recipe.recipe_id(), QWEN38_RECIPE_ID);
    assert_eq!(recipe.recipe_sha256().unwrap(), RECIPE_SHA256);
    assert_eq!(recipe.producer_version(), "hf2q 0.1.6");
    assert_eq!(recipe.source().repository_id(), "Qwen/Qwen3.8-27B");
    assert_eq!(recipe.source().revision(), QWEN38_ACCEPTED_REVISION);
    assert_eq!(
        recipe.source().bundle_sha256(),
        "73ded708c49c2d0a47c790ce1d6181e848ac7591dab741de83dbb57218cc6873"
    );
    assert_eq!(recipe.source().files().len(), 29);

    let text = recipe.artifact(RecipeArtifactRole::Text).unwrap();
    assert_eq!(text.quantization(), RecipeQuantization::Q4KM);
    assert_eq!(text.filename(), "Qwen3.8-27B-Q4_K_M.gguf");
    assert_eq!(text.size(), 16_810_714_752);
    assert_eq!(
        text.sha256(),
        "0fa8acc661d0edc60276c43705619fd848682dbf768ced9fe46cd8a572b8043d"
    );
    let projector = recipe
        .artifact(RecipeArtifactRole::VisionProjector)
        .unwrap();
    assert_eq!(projector.quantization(), RecipeQuantization::F16Mmproj);
    assert_eq!(projector.filename(), "Qwen3.8-27B-mmproj-F16.gguf");
    assert_eq!(projector.size(), 927_606_848);
    assert_eq!(
        projector.sha256(),
        "6fa039b75244c0a28a013da30b92b1d221c61029acc19f9efa882b75a495b0d0"
    );

    assert_eq!(
        recipe.interactive_retention_default(),
        SourceRetentionChoice::Keep
    );
    assert!(recipe.non_interactive_retention_requires_explicit());
}

#[test]
fn recipe_rejects_a_changed_or_unbounded_output_producer_version() {
    let changed = mutated(|value| {
        value["conversion"]["producer_version"] = Value::String("hf2q 0.1.7".into());
    });
    assert!(matches!(
        ModelRecipe::parse(&changed),
        Err(ModelRecipeError::Invalid { .. })
    ));

    let unknown = mutated(|value| {
        value["conversion"]["unexpected"] = Value::Bool(true);
    });
    assert!(matches!(
        ModelRecipe::parse(&unknown),
        Err(ModelRecipeError::Json(_))
    ));
}

#[test]
fn source_inventory_and_disk_floor_are_derived_exactly() {
    let recipe = embedded_qwen38_recipe().unwrap();
    assert_eq!(
        recipe
            .source()
            .files()
            .iter()
            .map(|file| file.size())
            .sum::<u64>(),
        55_586_101_511
    );
    assert_eq!(
        recipe
            .artifacts()
            .iter()
            .map(|artifact| artifact.size())
            .sum::<u64>(),
        17_738_321_600
    );
    assert_eq!(recipe.minimum_free_bytes(), 81_914_357_703);
    recipe.require_free_space(81_914_357_703).unwrap();
    assert!(matches!(
        recipe.require_free_space(81_914_357_702),
        Err(ModelRecipeError::InsufficientDisk { .. })
    ));

    let names = recipe
        .source()
        .files()
        .iter()
        .map(|file| file.path())
        .collect::<Vec<_>>();
    assert!(names.windows(2).all(|pair| pair[0] < pair[1]));
    assert_eq!(
        names
            .iter()
            .filter(|name| name.ends_with(".safetensors"))
            .count(),
        18
    );
    assert!(names.contains(&"README.md"));
    assert!(names.contains(&"chat_template.jinja"));
    assert!(names.contains(&"preprocessor_config.json"));
    assert!(names.contains(&"video_preprocessor_config.json"));
}

#[test]
fn only_the_independently_proven_host_profile_is_selected() {
    let recipe = embedded_qwen38_recipe().unwrap();
    let exact = recipe
        .select_hardware_profile(
            "aarch64-apple-darwin",
            "Apple M5 Max",
            128 * 1024 * 1024 * 1024,
        )
        .unwrap();
    assert_eq!(exact.profile_id(), "qwen38-m5-max-128g-q4-k-m-v1");
    assert_eq!(exact.target(), "aarch64-apple-darwin");
    assert_eq!(exact.chip_model(), "Apple M5 Max");
    assert_eq!(
        exact.minimum_unified_memory_bytes(),
        128 * 1024 * 1024 * 1024
    );
    assert_eq!(exact.text_quantization(), RecipeQuantization::Q4KM);
    assert!(exact.runtime_calibration_required());

    for (target, chip, bytes) in [
        (
            "aarch64-apple-darwin",
            "Apple M5 Max",
            128 * 1024 * 1024 * 1024 - 1,
        ),
        (
            "aarch64-apple-darwin",
            "Apple M4 Max",
            128 * 1024 * 1024 * 1024,
        ),
        (
            "x86_64-apple-darwin",
            "Apple M5 Max",
            128 * 1024 * 1024 * 1024,
        ),
    ] {
        assert!(matches!(
            recipe.select_hardware_profile(target, chip, bytes),
            Err(ModelRecipeError::UnsupportedHardware { .. })
        ));
    }
}

#[test]
fn recipe_lookup_accepts_bare_or_exact_source_and_rejects_ambiguity() {
    let bare = HfModelReference::parse("Qwen/Qwen3.8-27B", None).unwrap();
    assert_eq!(
        recipe_for_reference(&bare).unwrap().unwrap().recipe_id(),
        QWEN38_RECIPE_ID
    );
    let exact =
        HfModelReference::parse("Qwen/Qwen3.8-27B", Some(QWEN38_ACCEPTED_REVISION)).unwrap();
    assert!(recipe_for_reference(&exact).unwrap().is_some());

    let mutable = HfModelReference::parse("Qwen/Qwen3.8-27B", Some("main")).unwrap();
    assert!(matches!(
        recipe_for_reference(&mutable),
        Err(ModelRecipeError::RevisionNotAccepted { .. })
    ));
    let wrong = HfModelReference::parse("Qwen/Qwen3.8-27B", Some(&"a".repeat(40))).unwrap();
    assert!(matches!(
        recipe_for_reference(&wrong),
        Err(ModelRecipeError::RevisionNotAccepted { .. })
    ));
    let file = HfModelReference::parse(
        &format!(
            "https://huggingface.co/Qwen/Qwen3.8-27B/blob/{QWEN38_ACCEPTED_REVISION}/config.json"
        ),
        None,
    )
    .unwrap();
    assert!(recipe_for_reference(&file).unwrap().is_none());
    let other = HfModelReference::parse("owner/model", None).unwrap();
    assert!(recipe_for_reference(&other).unwrap().is_none());
}

#[test]
fn parser_rejects_size_duplicate_unknown_trailing_depth_and_noncanonical_bytes() {
    let oversized = vec![b' '; MAX_MODEL_RECIPE_BYTES + 1];
    assert!(matches!(
        ModelRecipe::parse(&oversized),
        Err(ModelRecipeError::TooLarge { .. })
    ));

    let text = std::str::from_utf8(RECIPE_BYTES).unwrap();
    let duplicate = text.replacen(
        "\"schema_version\":1,",
        "\"schema_version\":1,\"schema_version\":1,",
        1,
    );
    assert!(ModelRecipe::parse(duplicate.as_bytes()).is_err());

    let unknown = mutated(|value| {
        value
            .as_object_mut()
            .unwrap()
            .insert("extra".into(), Value::Bool(true));
    });
    assert!(ModelRecipe::parse(&unknown).is_err());

    let mut trailing = RECIPE_BYTES.to_vec();
    trailing.extend_from_slice(b"x");
    assert!(ModelRecipe::parse(&trailing).is_err());

    let mut deep = serde_json::from_slice::<Value>(RECIPE_BYTES).unwrap();
    let mut nested = Value::Null;
    for _ in 0..150 {
        nested = Value::Array(vec![nested]);
    }
    deep.as_object_mut().unwrap().insert("extra".into(), nested);
    assert!(ModelRecipe::parse(&serde_json::to_vec(&deep).unwrap()).is_err());

    let pretty =
        serde_json::to_vec_pretty(&serde_json::from_slice::<Value>(RECIPE_BYTES).unwrap()).unwrap();
    assert!(matches!(
        ModelRecipe::parse(&pretty),
        Err(ModelRecipeError::NonCanonical)
    ));
}

#[test]
fn semantic_cross_bindings_fail_closed() {
    let cases: Vec<Box<dyn FnOnce(&mut Value)>> = vec![
        Box::new(|v| v["kind"] = Value::String("other".into())),
        Box::new(|v| v["schema_version"] = Value::from(2)),
        Box::new(|v| v["recipe_id"] = Value::String("other".into())),
        Box::new(|v| v["acceptance"]["accepted_at"] = Value::String("2026-08-18".into())),
        Box::new(|v| v["source"]["repository_id"] = Value::String("attacker/model".into())),
        Box::new(|v| v["source"]["revision"] = Value::String("a".repeat(40))),
        Box::new(|v| v["source"]["bundle_sha256"] = Value::String("a".repeat(64))),
        Box::new(|v| v["source"]["files"][0]["path"] = Value::String("../README.md".into())),
        Box::new(|v| v["source"]["files"][0]["sha256"] = Value::String("A".repeat(64))),
        Box::new(|v| v["source"]["files"][5]["hf_lfs_sha256"] = Value::String("b".repeat(64))),
        Box::new(|v| v["artifacts"][0]["quantization"] = Value::String("q5_k_m".into())),
        Box::new(|v| v["artifacts"][0]["size"] = Value::from(1)),
        Box::new(|v| {
            v["hardware_profiles"][0]["chip_model"] = Value::String("Apple M4 Max".into())
        }),
        Box::new(|v| v["disk"]["minimum_free_bytes"] = Value::from(1)),
        Box::new(|v| v["source_retention"]["deletion_scope"] = Value::String("any_cache".into())),
    ];
    for mutate in cases {
        let bytes = mutated(mutate);
        assert!(ModelRecipe::parse(&bytes).is_err());
    }
}

#[test]
fn artifact_and_source_evidence_cannot_be_cross_bound() {
    let recipe = embedded_qwen38_recipe().unwrap();
    let text = recipe.artifact(RecipeArtifactRole::Text).unwrap();
    recipe
        .verify_artifact_facts_for_test(RecipeArtifactRole::Text, text.size(), text.sha256())
        .unwrap();
    assert!(recipe
        .verify_artifact_facts_for_test(RecipeArtifactRole::Text, text.size() - 1, text.sha256(),)
        .is_err());
    assert!(recipe
        .verify_artifact_facts_for_test(RecipeArtifactRole::Text, text.size(), &"a".repeat(64),)
        .is_err());
    assert!(recipe
        .verify_artifact_facts_for_test(
            RecipeArtifactRole::VisionProjector,
            text.size(),
            text.sha256(),
        )
        .is_err());

    let empty = VerifiedSourceManifest::for_test(Vec::new());
    assert!(matches!(
        recipe.verify_source(Path::new("/unused"), empty),
        Err(ModelRecipeError::SourceMismatch { .. })
    ));
}

#[test]
fn live_recipe_metadata_matches_the_official_immutable_revision() {
    if std::env::var("HF2Q_NETWORK_TESTS").ok().as_deref() != Some("1") {
        eprintln!("skipping network test (set HF2Q_NETWORK_TESTS=1 to run)");
        return;
    }
    use hf_hub::api::sync::ApiBuilder;
    use hf_hub::{Repo, RepoType};

    let recipe = embedded_qwen38_recipe().unwrap();
    let api = ApiBuilder::new()
        .with_endpoint("https://huggingface.co".to_owned())
        .with_progress(false)
        .build()
        .expect("build exact-origin Hub client");
    let repo = api.repo(Repo::with_revision(
        recipe.source().repository_id().to_owned(),
        RepoType::Model,
        recipe.source().revision().to_owned(),
    ));
    let local = tempfile::tempdir().unwrap();
    let mut records = Vec::with_capacity(recipe.source().files().len());
    for expected in recipe.source().files() {
        let metadata = api
            .metadata(&repo.url(expected.path()))
            .unwrap_or_else(|error| panic!("metadata {}: {error}", expected.path()));
        assert_eq!(metadata.commit_hash(), recipe.source().revision());
        assert_eq!(metadata.size() as u64, expected.size());
        assert_eq!(metadata.etag().to_ascii_lowercase(), expected.hub_etag());
        let record =
            ShardIntegrity::from_metadata(expected.path(), metadata.etag(), metadata.size() as u64);
        if !record.is_lfs {
            let cached = repo
                .get(expected.path())
                .unwrap_or_else(|error| panic!("download support {}: {error}", expected.path()));
            std::fs::copy(cached, local.path().join(expected.path())).unwrap();
        }
        records.push(record);
    }
    let verified = recipe
        .verify_source(
            local.path(),
            VerifiedSourceManifest::for_test(records.clone()),
        )
        .expect("exact official source matches checked-in recipe");
    assert_eq!(verified.recipe_id(), QWEN38_RECIPE_ID);
    assert_eq!(verified.recipe_sha256(), RECIPE_SHA256);
    assert_eq!(verified.local_dir(), local.path());
    assert_eq!(verified.source_manifest().records(), records);

    std::fs::write(local.path().join("config.json"), b"tampered").unwrap();
    assert!(matches!(
        recipe.verify_source(local.path(), VerifiedSourceManifest::for_test(records)),
        Err(ModelRecipeError::SourceMismatch { .. })
    ));
}

#[test]
fn local_adr044_artifacts_match_when_explicitly_requested() {
    let Some(root) = std::env::var_os("HF2Q_QWEN38_ACCEPTED_ARTIFACT_ROOT") else {
        eprintln!("skipping artifact test (set HF2Q_QWEN38_ACCEPTED_ARTIFACT_ROOT to run)");
        return;
    };
    let root = Path::new(&root);
    let recipe = embedded_qwen38_recipe().unwrap();
    for role in [
        RecipeArtifactRole::Text,
        RecipeArtifactRole::VisionProjector,
    ] {
        let artifact = recipe.artifact(role).unwrap();
        let verified = recipe
            .verify_artifact_path(role, &root.join(artifact.filename()))
            .unwrap();
        assert_eq!(verified.role(), role);
        assert_eq!(verified.sha256(), artifact.sha256());
    }
}