From c8388758a574d404ad42ca117bc0b631ba51f34c Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 11 Aug 2026 06:44:25 -0500 Subject: [PATCH 1/6] Fetch hosted prebuilt NF4 artifacts for flux2 and zimage download_flux2_klein() and download_zimage_turbo() gain prebuilt = TRUE: when the resolved precision is nf4 and no local artifact exists, the artifact is fetched from the cornball-ai/-R HuggingFace dataset (hard-linked out of the hfhub cache) instead of downloading the 7.8 GB / 24.6 GB source and quantizing locally. Any fetch failure falls back to the source + quantize path unchanged. Only the two redistributable models are hosted; flux1 (gated) and ltx (license) build locally as before. Also updates the stale Klein 13 s README timing to the 0.2.2 measured 9.1 s (fp8). --- NEWS.md | 15 +++++ R/download_flux2.R | 21 ++++-- R/download_prebuilt.R | 91 ++++++++++++++++++++++++++ R/download_zimage.R | 18 ++++- README.md | 21 +++--- inst/tinytest/test_download_prebuilt.R | 21 ++++++ 6 files changed, 172 insertions(+), 15 deletions(-) create mode 100644 R/download_prebuilt.R create mode 100644 inst/tinytest/test_download_prebuilt.R diff --git a/NEWS.md b/NEWS.md index e345f0c..b524db3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,18 @@ +# diffuseR 0.2.2.2 + +* Prebuilt NF4 artifacts for flux2 (2.3 GB) and zimage (3.6 GB) are now + hosted on the cornball-ai HuggingFace org, and + `download_flux2_klein()` / `download_zimage_turbo()` fetch them by + default when the resolved precision is nf4 (`prebuilt = FALSE` forces + a local build). This narrowly reverses 0.2.2's no-hosting decision: + the CRAN safetensors cannot read any of the multi-GB upstream sources + (the fix is merged upstream, mlverse/safetensors#14, but unreleased), + which left a stock CRAN install unable to build a quantized artifact + at all. Hosting the two redistributable models (both Apache-2.0, + ungated) gives a plain `install.packages("diffuseR")` setup something + to generate with right away. FLUX.1-schnell (gated repo) and LTX-2.3 + (LTX-2 Community License) still download sources and build locally. + # diffuseR 0.2.2.1 * `txt2img()` and `img2img()` now `match.arg()` their `model_name`, so diff --git a/R/download_flux2.R b/R/download_flux2.R index 52e4a2b..bc139e9 100644 --- a/R/download_flux2.R +++ b/R/download_flux2.R @@ -2,7 +2,8 @@ #' #' Downloads FLUX.2-klein-4B from HuggingFace (Apache-2.0, ungated) and #' quantizes the 4B transformer to a local fp8 (~4 GB) or NF4 (~2.3 GB) -#' artifact. +#' artifact. For nf4 the prebuilt hosted artifact is fetched instead of +#' building, when reachable (see \link{download_prebuilt}). #' #' @name download_flux2 NULL @@ -34,10 +35,15 @@ NULL #' @param quantize Logical. Build the quantized artifact. #' @param precision "auto" (default: fp8 when safetensors supports #' float8, else nf4), "fp8" (~4 GB, GPU-resident; near-bf16 quality), -#' or "nf4" (~2.3 GB). +#' or "nf4" (~2.3 GB, fetched prebuilt from the cornball-ai +#' HuggingFace dataset when available). #' @param output_dir Directory for the quantized artifact. #' @param text_encoders Logical. Also fetch the Qwen3 text encoder, #' tokenizer, VAE, and scheduler config (~8.3 GB). +#' @param prebuilt Logical. When the resolved precision is nf4, fetch +#' the hosted prebuilt artifact (~2.3 GB) instead of downloading the +#' 7.8 GB bf16 source and quantizing locally. \code{FALSE} forces a +#' local build. #' @param verbose Logical. #' #' @return Invisibly, a list with \code{transformer_dir}, @@ -47,7 +53,7 @@ NULL download_flux2_klein <- function(quantize = TRUE, precision = c("auto", "fp8", "nf4"), output_dir = NULL, text_encoders = TRUE, - verbose = TRUE) { + prebuilt = TRUE, verbose = TRUE) { precision <- match.arg(precision) precision <- .flux_resolve_precision(precision, file.path(tools::R_user_dir("diffuseR", "data"), "flux2-klein-4b-")) @@ -69,6 +75,13 @@ download_flux2_klein <- function(quantize = TRUE, all(file.exists(file.path(output_dir, m$shards))) } + fetched <- FALSE + if (prebuilt && quantize && !have_artifact && + identical(precision, "nf4")) { + fetched <- .flux_fetch_prebuilt("flux2", output_dir, verbose) + have_artifact <- have_artifact || fetched + } + if (!have_artifact || !quantize) { cached <- tryCatch( hfhub::hub_download(.flux2_repo, .flux2_transformer_files[[2]], @@ -115,7 +128,7 @@ download_flux2_klein <- function(quantize = TRUE, ) } } - } else if (verbose) { + } else if (verbose && !fetched) { message(toupper(precision), " artifact already present: ", output_dir) } diff --git a/R/download_prebuilt.R b/R/download_prebuilt.R new file mode 100644 index 0000000..14b9392 --- /dev/null +++ b/R/download_prebuilt.R @@ -0,0 +1,91 @@ +#' Hosted prebuilt NF4 artifacts +#' +#' diffuseR hosts prebuilt NF4 artifacts for the two models whose +#' licenses permit redistribution: FLUX.2-klein-4B and Z-Image-Turbo +#' (both Apache-2.0, ungated). The artifacts are the exact output of +#' \code{\link{flux_quantize}} (sub-2 GB shards, bf16 residents), so +#' stock CRAN safetensors reads them. Nothing else in the catalog is +#' hosted: FLUX.1-schnell sits behind a HuggingFace license gate and +#' LTX-2.3's license does not permit redistribution, so both still +#' download their sources and quantize locally. +#' +#' @name download_prebuilt +#' @keywords internal +NULL + +# model_name -> hosted dataset repo + artifact directory name. The repo +# follows the cornball-ai/-R dataset convention (see +# hf_download_pt); the base mirrors the local artifact directory name so +# a fetched artifact is indistinguishable from a locally built one. +.prebuilt_nf4_spec <- list( + flux2 = list(repo = "cornball-ai/flux2-R", base = "flux2-klein-4b-nf4"), + zimage = list(repo = "cornball-ai/zimage-R", base = "zimage-turbo-nf4") +) + +# Fetch a hosted NF4 artifact into output_dir. Returns TRUE when the +# artifact is complete there, FALSE when the model has no hosted +# artifact or the fetch failed (callers fall back to source + +# quantize). Files are hard-linked out of the hfhub cache when the +# filesystem allows it, copied otherwise. +.flux_fetch_prebuilt <- function(model, output_dir, verbose = TRUE) { + spec <- .prebuilt_nf4_spec[[model]] + if (is.null(spec)) { + return(FALSE) + } + manifest_cache <- tryCatch( + hfhub::hub_download(spec$repo, paste0(spec$base, "/manifest.json"), + repo_type = "dataset"), + error = function(e) NULL) + if (is.null(manifest_cache)) { + if (verbose) { + message("No hosted NF4 artifact reachable for '", model, + "'; building locally instead.") + } + return(FALSE) + } + manifest <- jsonlite::fromJSON(manifest_cache) + if (verbose) { + message("Downloading the prebuilt NF4 artifact from ", spec$repo, + " (", length(manifest$shards), " shards)...") + } + shard_cache <- vapply(manifest$shards, function(s) { + tryCatch(hfhub::hub_download(spec$repo, paste0(spec$base, "/", s), + repo_type = "dataset"), + error = function(e) NA_character_) + }, character(1)) + if (anyNA(shard_cache)) { + if (verbose) { + message("Prebuilt artifact fetch incomplete; ", + "building locally instead.") + } + return(FALSE) + } + dir.create(output_dir, recursive = TRUE, showWarnings = FALSE) + src <- c(manifest_cache, shard_cache) + dst <- file.path(output_dir, c("manifest.json", manifest$shards)) + for (i in seq_along(src)) { + .link_or_copy(normalizePath(src[[i]]), dst[[i]]) + } + ok <- file.exists(file.path(output_dir, "manifest.json")) + if (ok && verbose) { + message("NF4 artifact ready: ", output_dir) + } + ok +} + +# Hard link (free on one filesystem), falling back to a copy. An +# existing destination is replaced. +.link_or_copy <- function(from, to) { + if (file.exists(to)) { + unlink(to) + } + ok <- suppressWarnings(file.link(from, to)) + if (!ok) { + ok <- file.copy(from, to, overwrite = TRUE) + } + if (!ok) { + stop("Could not place ", basename(to), " in ", dirname(to), + call. = FALSE) + } + invisible(ok) +} diff --git a/R/download_zimage.R b/R/download_zimage.R index 66e2c02..c23e7f0 100644 --- a/R/download_zimage.R +++ b/R/download_zimage.R @@ -40,10 +40,15 @@ NULL #' @param quantize Logical. Build the quantized artifact. #' @param precision "auto" (default: fp8 when safetensors supports #' float8, else nf4), "fp8" (~6.3 GB, GPU-resident; near-bf16 -#' quality), or "nf4" (~3.6 GB). +#' quality), or "nf4" (~3.6 GB, fetched prebuilt from the cornball-ai +#' HuggingFace dataset when available). #' @param output_dir Directory for the quantized artifact. #' @param text_encoders Logical. Also fetch the Qwen3-4B text encoder, #' tokenizer, VAE, and scheduler config (~8.2 GB). +#' @param prebuilt Logical. When the resolved precision is nf4, fetch +#' the hosted prebuilt artifact (~3.6 GB) instead of downloading the +#' 24.6 GB float32 source and quantizing locally. \code{FALSE} forces +#' a local build. #' @param verbose Logical. #' #' @return Invisibly, a list with \code{transformer_dir}, @@ -53,7 +58,7 @@ NULL download_zimage_turbo <- function(quantize = TRUE, precision = c("auto", "fp8", "nf4"), output_dir = NULL, text_encoders = TRUE, - verbose = TRUE) { + prebuilt = TRUE, verbose = TRUE) { precision <- match.arg(precision) precision <- .flux_resolve_precision(precision, file.path(tools::R_user_dir("diffuseR", "data"), "zimage-turbo-")) @@ -75,6 +80,13 @@ download_zimage_turbo <- function(quantize = TRUE, all(file.exists(file.path(output_dir, m$shards))) } + fetched <- FALSE + if (prebuilt && quantize && !have_artifact && + identical(precision, "nf4")) { + fetched <- .flux_fetch_prebuilt("zimage", output_dir, verbose) + have_artifact <- have_artifact || fetched + } + if (!have_artifact || !quantize) { cached <- tryCatch( hfhub::hub_download(.zimage_repo, .zimage_transformer_files[[3]], @@ -121,7 +133,7 @@ download_zimage_turbo <- function(quantize = TRUE, ) } } - } else if (verbose) { + } else if (verbose && !fetched) { message(toupper(precision), " artifact already present: ", output_dir) } diff --git a/README.md b/README.md index 17e2b2e..0d18c7d 100644 --- a/README.md +++ b/README.md @@ -150,10 +150,13 @@ torch::cuda_empty_cache() ### FLUX and Z-Image FLUX.1-schnell (12B), FLUX.2 Klein (4B), and Z-Image-Turbo (6B) are -step-distilled models: 4-8 denoising steps, no guidance. All are -quantized locally once at download time and fit comfortably on a 16GB -GPU (measured 1024x1024 on an RTX 5060 Ti: FLUX.1 ~55s at 9.6GB peak; -FLUX.2 Klein ~13s at 12.5GB; Z-Image-Turbo ~24s at 13.1GB). +step-distilled models: 4-8 denoising steps, no guidance. All fit +comfortably on a 16GB GPU (measured 1024x1024 on an RTX 5060 Ti: +FLUX.1 ~55s at 9.6GB peak; FLUX.2 Klein ~9s at fp8; Z-Image-Turbo +~24s at 13.1GB). Klein and Z-Image ship prebuilt NF4 artifacts (2.3 +and 3.6 GB, hosted on the cornball-ai HuggingFace org), so a stock +CRAN install downloads a few GB and generates right away; FLUX.1 +downloads its gated source and quantizes locally. ```r library(diffuseR) @@ -165,14 +168,16 @@ download_flux1() txt2img_flux("An astronaut riding a horse on Mars, photorealistic", seed = 7) -# FLUX.2 Klein 4B: ungated. ~16GB download, one-time fp8 quantize to -# a 3.9GB artifact. +# FLUX.2 Klein 4B: ungated. On stock safetensors this fetches the +# prebuilt NF4 artifact (~2.3GB); a float8-capable safetensors builds +# fp8 from the 7.8GB source instead. download_flux2_klein() txt2img_flux2("a red fox sitting in a snowy forest, digital art", seed = 42) # Z-Image-Turbo: ungated, strong at legible text in images (EN + CN). -# ~33GB download, one-time fp8 quantize to a 5.9GB artifact. +# Prebuilt NF4 (~3.6GB) on stock safetensors, or fp8 from the 24.6GB +# float32 source. download_zimage_turbo() txt2img_zimage(paste("A storefront with a large wooden sign that reads", "\"DIFFUSER\" in bold carved letters"), seed = 42) @@ -256,7 +261,7 @@ Same prompt, same seed, 1024x1024, measured on an RTX 5060 Ti 16GB: | Model | Settings | Load | Warm generation | Peak VRAM | |---|---|---|---|---| | SDXL | 50 steps, CFG 7.5 | 45 s | 20 s | **12.7 GB** | -| FLUX.2 Klein 4B | 4 steps, guidance-free | 32 s | **13 s** | 12.5 GB | +| FLUX.2 Klein 4B | 4 steps, guidance-free, fp8 | 32 s | **9.1 s** | 12.5 GB | FLUX.2 is now faster per image (since the allocator gc-gate fix), and its prompt adherence and coherence are in a different class — SDXL diff --git a/inst/tinytest/test_download_prebuilt.R b/inst/tinytest/test_download_prebuilt.R new file mode 100644 index 0000000..e3ad4cf --- /dev/null +++ b/inst/tinytest/test_download_prebuilt.R @@ -0,0 +1,21 @@ +# Hosted-artifact policy: exactly the redistributable pair (Apache-2.0, +# ungated), nothing else. flux1 (gated) and ltx (LTX-2 Community +# License) must never appear here. +expect_equal(sort(names(diffuseR:::.prebuilt_nf4_spec)), + c("flux2", "zimage")) + +# Unhosted models refuse without touching the network. +expect_false(diffuseR:::.flux_fetch_prebuilt("flux1", tempfile(), + verbose = FALSE)) +expect_false(diffuseR:::.flux_fetch_prebuilt("ltx", tempfile(), + verbose = FALSE)) + +# .link_or_copy places a file and replaces an existing destination. +src <- tempfile() +writeLines("x", src) +dst <- tempfile() +diffuseR:::.link_or_copy(normalizePath(src), dst) +expect_true(file.exists(dst)) +diffuseR:::.link_or_copy(normalizePath(src), dst) +expect_equal(readLines(dst), "x") +unlink(c(src, dst)) From 47f8a6a82e96ac75f49d9b7119b1dc590d3de90d Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 11 Aug 2026 06:45:05 -0500 Subject: [PATCH 2/6] rformat + document --- R/download_flux2.R | 3 +-- R/download_prebuilt.R | 10 +++++----- R/download_zimage.R | 3 +-- man/download_flux2.Rd | 3 ++- man/download_flux2_klein.Rd | 9 ++++++++- man/download_zimage_turbo.Rd | 9 ++++++++- 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/R/download_flux2.R b/R/download_flux2.R index bc139e9..8ef2847 100644 --- a/R/download_flux2.R +++ b/R/download_flux2.R @@ -76,8 +76,7 @@ download_flux2_klein <- function(quantize = TRUE, } fetched <- FALSE - if (prebuilt && quantize && !have_artifact && - identical(precision, "nf4")) { + if (prebuilt && quantize && !have_artifact && identical(precision, "nf4")) { fetched <- .flux_fetch_prebuilt("flux2", output_dir, verbose) have_artifact <- have_artifact || fetched } diff --git a/R/download_prebuilt.R b/R/download_prebuilt.R index 14b9392..7f92263 100644 --- a/R/download_prebuilt.R +++ b/R/download_prebuilt.R @@ -18,8 +18,8 @@ NULL # hf_download_pt); the base mirrors the local artifact directory name so # a fetched artifact is indistinguishable from a locally built one. .prebuilt_nf4_spec <- list( - flux2 = list(repo = "cornball-ai/flux2-R", base = "flux2-klein-4b-nf4"), - zimage = list(repo = "cornball-ai/zimage-R", base = "zimage-turbo-nf4") + flux2 = list(repo = "cornball-ai/flux2-R", base = "flux2-klein-4b-nf4"), + zimage = list(repo = "cornball-ai/zimage-R", base = "zimage-turbo-nf4") ) # Fetch a hosted NF4 artifact into output_dir. Returns TRUE when the @@ -33,9 +33,9 @@ NULL return(FALSE) } manifest_cache <- tryCatch( - hfhub::hub_download(spec$repo, paste0(spec$base, "/manifest.json"), - repo_type = "dataset"), - error = function(e) NULL) + hfhub::hub_download(spec$repo, paste0(spec$base, "/manifest.json"), + repo_type = "dataset"), + error = function(e) NULL) if (is.null(manifest_cache)) { if (verbose) { message("No hosted NF4 artifact reachable for '", model, diff --git a/R/download_zimage.R b/R/download_zimage.R index c23e7f0..e4ede63 100644 --- a/R/download_zimage.R +++ b/R/download_zimage.R @@ -81,8 +81,7 @@ download_zimage_turbo <- function(quantize = TRUE, } fetched <- FALSE - if (prebuilt && quantize && !have_artifact && - identical(precision, "nf4")) { + if (prebuilt && quantize && !have_artifact && identical(precision, "nf4")) { fetched <- .flux_fetch_prebuilt("zimage", output_dir, verbose) have_artifact <- have_artifact || fetched } diff --git a/man/download_flux2.Rd b/man/download_flux2.Rd index 6d84f95..c9a9a82 100644 --- a/man/download_flux2.Rd +++ b/man/download_flux2.Rd @@ -5,5 +5,6 @@ \description{ Downloads FLUX.2-klein-4B from HuggingFace (Apache-2.0, ungated) and quantizes the 4B transformer to a local fp8 (~4 GB) or NF4 (~2.3 GB) -artifact. +artifact. For nf4 the prebuilt hosted artifact is fetched instead of +building, when reachable (see \link{download_prebuilt}). } diff --git a/man/download_flux2_klein.Rd b/man/download_flux2_klein.Rd index af6b7c6..7d0d1df 100644 --- a/man/download_flux2_klein.Rd +++ b/man/download_flux2_klein.Rd @@ -8,6 +8,7 @@ download_flux2_klein( precision = c("auto", "fp8", "nf4"), output_dir = NULL, text_encoders = TRUE, + prebuilt = TRUE, verbose = TRUE ) } @@ -16,13 +17,19 @@ download_flux2_klein( \item{precision}{"auto" (default: fp8 when safetensors supports float8, else nf4), "fp8" (~4 GB, GPU-resident; near-bf16 quality), -or "nf4" (~2.3 GB).} +or "nf4" (~2.3 GB, fetched prebuilt from the cornball-ai +HuggingFace dataset when available).} \item{output_dir}{Directory for the quantized artifact.} \item{text_encoders}{Logical. Also fetch the Qwen3 text encoder, tokenizer, VAE, and scheduler config (~8.3 GB).} +\item{prebuilt}{Logical. When the resolved precision is nf4, fetch +the hosted prebuilt artifact (~2.3 GB) instead of downloading the +7.8 GB bf16 source and quantizing locally. \code{FALSE} forces a +local build.} + \item{verbose}{Logical.} } \value{ diff --git a/man/download_zimage_turbo.Rd b/man/download_zimage_turbo.Rd index 323ec6f..c4d1275 100644 --- a/man/download_zimage_turbo.Rd +++ b/man/download_zimage_turbo.Rd @@ -8,6 +8,7 @@ download_zimage_turbo( precision = c("auto", "fp8", "nf4"), output_dir = NULL, text_encoders = TRUE, + prebuilt = TRUE, verbose = TRUE ) } @@ -16,13 +17,19 @@ download_zimage_turbo( \item{precision}{"auto" (default: fp8 when safetensors supports float8, else nf4), "fp8" (~6.3 GB, GPU-resident; near-bf16 -quality), or "nf4" (~3.6 GB).} +quality), or "nf4" (~3.6 GB, fetched prebuilt from the cornball-ai +HuggingFace dataset when available).} \item{output_dir}{Directory for the quantized artifact.} \item{text_encoders}{Logical. Also fetch the Qwen3-4B text encoder, tokenizer, VAE, and scheduler config (~8.2 GB).} +\item{prebuilt}{Logical. When the resolved precision is nf4, fetch +the hosted prebuilt artifact (~3.6 GB) instead of downloading the +24.6 GB float32 source and quantizing locally. \code{FALSE} forces +a local build.} + \item{verbose}{Logical.} } \value{ From 23d1c4e27d4c1470959a3de67a7cbec8958d59e3 Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 11 Aug 2026 06:48:17 -0500 Subject: [PATCH 3/6] Bump version to 0.2.2.2 --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 548c464..d59960d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: diffuseR Title: Functional Interface to Diffusion Models in R -Version: 0.2.2.1 +Version: 0.2.2.2 Authors@R: c( person("Troy", "Hernandez", email = "troy@cornball.ai", role = c("aut", "cre"), comment = c(ORCID = "0009-0005-4248-604X")), From caf736de6ca1100261795d5c2a17c515fdc9422d Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 11 Aug 2026 06:48:30 -0500 Subject: [PATCH 4/6] Add generated download_prebuilt.Rd --- man/download_prebuilt.Rd | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 man/download_prebuilt.Rd diff --git a/man/download_prebuilt.Rd b/man/download_prebuilt.Rd new file mode 100644 index 0000000..aa5e105 --- /dev/null +++ b/man/download_prebuilt.Rd @@ -0,0 +1,15 @@ +% tinyrox says don't edit this manually, but it can't stop you! +\name{download_prebuilt} +\alias{download_prebuilt} +\title{Hosted prebuilt NF4 artifacts} +\description{ +diffuseR hosts prebuilt NF4 artifacts for the two models whose +licenses permit redistribution: FLUX.2-klein-4B and Z-Image-Turbo +(both Apache-2.0, ungated). The artifacts are the exact output of +\code{\link{flux_quantize}} (sub-2 GB shards, bf16 residents), so +stock CRAN safetensors reads them. Nothing else in the catalog is +hosted: FLUX.1-schnell sits behind a HuggingFace license gate and +LTX-2.3's license does not permit redistribution, so both still +download their sources and quantize locally. +} +\keyword{internal} From 9628ca8876f695db307df2851fdc8ac9f5e2d7ab Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 11 Aug 2026 22:09:45 -0500 Subject: [PATCH 5/6] Consent-gate the prebuilt fetch Every model download is consent-gated by contract (NEWS 0.2.0); the prebuilt fetch was bypassing the gate. Ask before any network fetch, skip the ask when the artifact is already fully hub-cached, and fall through to the source path (with its own larger ask) on decline. --- NEWS.md | 2 +- R/download_prebuilt.R | 32 ++++++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index 266bebc..6eea834 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # diffuseR 0.2.2.3 -* Prebuilt NF4 artifacts for flux2 (2.3 GB) and zimage (3.6 GB) are now +* Prebuilt NF4 artifacts for flux2 (2.1 GB) and zimage (3.5 GB) are now hosted on the cornball-ai HuggingFace org, and `download_flux2_klein()` / `download_zimage_turbo()` fetch them by default when the resolved precision is nf4 (`prebuilt = FALSE` forces diff --git a/R/download_prebuilt.R b/R/download_prebuilt.R index 7f92263..13dade6 100644 --- a/R/download_prebuilt.R +++ b/R/download_prebuilt.R @@ -18,10 +18,30 @@ NULL # hf_download_pt); the base mirrors the local artifact directory name so # a fetched artifact is indistinguishable from a locally built one. .prebuilt_nf4_spec <- list( - flux2 = list(repo = "cornball-ai/flux2-R", base = "flux2-klein-4b-nf4"), - zimage = list(repo = "cornball-ai/zimage-R", base = "zimage-turbo-nf4") + flux2 = list(repo = "cornball-ai/flux2-R", base = "flux2-klein-4b-nf4", + size = "~2.1 GB"), + zimage = list(repo = "cornball-ai/zimage-R", base = "zimage-turbo-nf4", + size = "~3.5 GB") ) +# TRUE when the hosted artifact is already fully in the hfhub cache, so +# fetching it needs no network and therefore no consent. +.prebuilt_all_cached <- function(spec) { + cached <- function(f) { + !is.null(tryCatch( + hfhub::hub_download(spec$repo, paste0(spec$base, "/", f), + repo_type = "dataset", local_files_only = TRUE), + error = function(e) NULL)) + } + if (!cached("manifest.json")) { + return(FALSE) + } + m <- jsonlite::fromJSON(hfhub::hub_download(spec$repo, + paste0(spec$base, "/manifest.json"), repo_type = "dataset", + local_files_only = TRUE)) + all(vapply(m$shards, cached, logical(1))) +} + # Fetch a hosted NF4 artifact into output_dir. Returns TRUE when the # artifact is complete there, FALSE when the model has no hosted # artifact or the fetch failed (callers fall back to source + @@ -32,6 +52,14 @@ NULL if (is.null(spec)) { return(FALSE) } + # Every model download is consent-gated (see download_ltx2). A + # declined fetch returns FALSE so the caller falls through to the + # source + quantize path, which carries its own (larger) consent ask. + if (!.prebuilt_all_cached(spec) && + !.ltx23_consent(sprintf("the prebuilt NF4 artifact (%s, %s)", + spec$size, spec$repo))) { + return(FALSE) + } manifest_cache <- tryCatch( hfhub::hub_download(spec$repo, paste0(spec$base, "/manifest.json"), repo_type = "dataset"), From ce8c226fc796d82f5755f78f2277fab54a69cf9a Mon Sep 17 00:00:00 2001 From: TroyHernandez Date: Tue, 11 Aug 2026 22:10:30 -0500 Subject: [PATCH 6/6] rformat + document --- R/download_prebuilt.R | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/R/download_prebuilt.R b/R/download_prebuilt.R index 13dade6..0bf8d09 100644 --- a/R/download_prebuilt.R +++ b/R/download_prebuilt.R @@ -21,7 +21,7 @@ NULL flux2 = list(repo = "cornball-ai/flux2-R", base = "flux2-klein-4b-nf4", size = "~2.1 GB"), zimage = list(repo = "cornball-ai/zimage-R", base = "zimage-turbo-nf4", - size = "~3.5 GB") + size = "~3.5 GB") ) # TRUE when the hosted artifact is already fully in the hfhub cache, so @@ -30,15 +30,16 @@ NULL cached <- function(f) { !is.null(tryCatch( hfhub::hub_download(spec$repo, paste0(spec$base, "/", f), - repo_type = "dataset", local_files_only = TRUE), + repo_type = "dataset", + local_files_only = TRUE), error = function(e) NULL)) } if (!cached("manifest.json")) { return(FALSE) } m <- jsonlite::fromJSON(hfhub::hub_download(spec$repo, - paste0(spec$base, "/manifest.json"), repo_type = "dataset", - local_files_only = TRUE)) + paste0(spec$base, "/manifest.json"), repo_type = "dataset", + local_files_only = TRUE)) all(vapply(m$shards, cached, logical(1))) }