From 1a7fe2317c737a8c11df5deea51b618addfb7435 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Mon, 3 Aug 2026 11:56:09 -0500 Subject: [PATCH] NEWS/test cleanup: correct the dontrun count, add a bf16 source check Review follow-ups, all documentation or test scope. No code changes, so no version bump. NEWS.md said nine \dontrun{} examples remain while cran-comments.md said ten. Ten is right: the 0.2.0 review pass left nine, and resident_load() added one afterwards. Also drops a doubled blank line left by consolidating the dev sections under 0.2.2, and a trailing blank line at the end of test_recommend.R. The bf16 route had only structural coverage -- that .flux_model_dir() routes bf16 somewhere other than "bf16", and that an unknown model errors. Nothing checked the thing that actually carries risk: whether .flux_source_dir() lands on a directory the loader can open. test_bf16_source.R now asserts the cache layout (config.json plus a shard), that the router resolves to exactly that directory, that every tensor in the shard really is BF16 (read from the safetensors JSON header, so no tensors are materialized), and that flux_open_checkpoint() opens it and reports no format -- which is what selects the "full" bf16 path in flux_load_transformer(). Deliberately not a forward pass. bf16 names the storage dtype and reading it is CPU work, but computing in bf16 is GPU-only here (txt2img_flux2 upcasts to float32 on cpu), and materializing the 7.8 GB transformer is not a test-suite job. at_home() gates the file, so check machines skip it. Verified GPU-untouched: 11 assertions in 1.6 s with a 0 MiB delta in nvidia-smi. Full suite with CUDA hidden: 1081 pass. --- NEWS.md | 6 +-- inst/tinytest/test_bf16_source.R | 67 ++++++++++++++++++++++++++++++++ inst/tinytest/test_recommend.R | 1 - 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 inst/tinytest/test_bf16_source.R diff --git a/NEWS.md b/NEWS.md index 128313e..c78e13c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -22,7 +22,6 @@ License and FLUX.1-schnell is gated, so hosting would cover half the catalog and leave two models on a different workflow. - * Model residency: `resident_load()`, `resident_activate()`, `resident_deactivate()`, `resident_generate()`, `resident_status()` and `resident_unload()` keep a pipeline's weights page-locked on the @@ -54,8 +53,9 @@ Addressing the CRAN review of the 0.2.0 submission: for the FLUX, FLUX.2, Z-Image, LTX-2.3 and Gemma3 ports. * Examples: 14 of the 23 `\dontrun{}` blocks now run during check, and were rewritten to be self-contained instead of referencing undefined - objects. The 9 that remain need model weights on disk and are - itemised in `cran-comments.md`. + objects. The 10 that remain need model weights on disk and are + itemised in `cran-comments.md` (the nine left from that pass, plus + `resident_load()`, added below). * `ddim_scheduler_create()` was uncallable at its documented defaults: `beta_schedule` was never passed through `match.arg()`, so `switch()` errored on the length-3 default, and the `device` default was a diff --git a/inst/tinytest/test_bf16_source.R b/inst/tinytest/test_bf16_source.R new file mode 100644 index 0000000..bea9cba --- /dev/null +++ b/inst/tinytest/test_bf16_source.R @@ -0,0 +1,67 @@ +# The bf16 tier reads the unquantized HuggingFace source instead of a +# built artifact, so the risk it carries is cache layout: does +# .flux_source_dir() land on a directory the loader can actually open, +# and is what it finds really bf16? +# +# Scope, deliberately: this checks resolution, on-disk dtype, and that +# flux_open_checkpoint() reports the "full" format that routes to the +# bf16 load path. It does NOT run a forward pass. bf16 names the storage +# dtype, and reading it is CPU work, but *computing* in bf16 is GPU-only +# here -- txt2img_flux2() upcasts to float32 when device == "cpu" -- and +# materializing the 7.8 GB transformer is not a test-suite job. +# +# at_home() gates the whole file: R CMD check machines have no such +# cache. + +library(tinytest) +library(diffuseR) + +if (!at_home()) { + exit_file("bf16 source checks need a populated hfhub cache") +} +if (!requireNamespace("safetensors", quietly = TRUE)) { + exit_file("safetensors not installed") +} + +src <- tryCatch(diffuseR:::.flux_source_dir("flux2"), error = function(e) NULL) +if (is.null(src)) { + exit_file("FLUX.2 bf16 source not in the hfhub cache") +} + +# --- resolution lands somewhere the loader can use -------------------------------- + +expect_true(dir.exists(src)) +expect_true(file.exists(file.path(src, "config.json"))) +expect_true(length(list.files(src, pattern = "[.]safetensors$")) > 0L) + +# The precision router must send bf16 here and NOT to "bf16", +# which is what it did before and why the tier was unreachable. +prefix <- file.path(tools::R_user_dir("diffuseR", "data"), "flux2-klein-4b-") +expect_equal(diffuseR:::.flux_model_dir("flux2", "bf16", prefix), src) +expect_equal(diffuseR:::.flux_model_dir("flux2", "nf4", prefix), + paste0(prefix, "nf4")) + +# --- what is on disk is actually bf16 --------------------------------------------- + +# Read only the safetensors JSON header (8-byte little-endian length, +# then that many bytes), so this stays cheap and loads no tensors. +shard <- list.files(src, pattern = "[.]safetensors$", full.names = TRUE)[1] +con <- file(shard, "rb") +n <- readBin(con, "integer", n = 1L, size = 8L, endian = "little") +hdr <- rawToChar(readBin(con, "raw", n = n)) +close(con) +meta <- jsonlite::fromJSON(hdr) +meta <- meta[names(meta) != "__metadata__"] +dtypes <- unique(vapply(meta, function(x) x$dtype, character(1))) +expect_equal(dtypes, "BF16") + +# --- the checkpoint opens and reports the full (unquantized) format --------------- + +ckpt <- flux_open_checkpoint(src) +expect_inherits(ckpt, "ltx23_checkpoint") +# flux_load_transformer() branches on `format %||% "full"`; a source +# checkpoint carries no format, which is what selects the bf16 path. +expect_null(ckpt$format) +expect_true(length(ckpt$keys) > 0L) +expect_equal(length(ckpt$keys), length(meta)) +expect_equal(ckpt$config[["_class_name"]], "Flux2Transformer2DModel") diff --git a/inst/tinytest/test_recommend.R b/inst/tinytest/test_recommend.R index 1398723..ac8f318 100644 --- a/inst/tinytest/test_recommend.R +++ b/inst/tinytest/test_recommend.R @@ -216,4 +216,3 @@ expect_error(diffuseR:::.flux_source_dir("nosuch"), pattern = "No bf16 source") expect_equal(recommend("ltx", vram_gb = 16)$precision, "nf4") expect_true("precision" %in% names(formals(download_ltx2))) expect_equal(eval(formals(download_ltx2)$precision), c("nf4", "fp8")) -