From 096ddfe24e2c2ac466ff3df38c582ab4c0378d76 Mon Sep 17 00:00:00 2001 From: Cameron Cordes Date: Sun, 1 Nov 2026 18:24:36 -0500 Subject: [PATCH] Enhance: generate 32-bit seeds SeedVR2VideoUpscaler (the new Sharpen / Rescue templates) rejects seeds above 2^32 - 1 at validation, and the 52-bit seeds made every job fail with "Value ... bigger than max of 4294967295: seed". 32 bits is still plenty of variety for the Fibo templates. Co-Authored-By: Claude Opus 5.5 --- src/enhance/service.rs | 6 ++---- src/enhance/template.rs | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/enhance/service.rs b/src/enhance/service.rs index cda4202..c8c7997 100644 --- a/src/enhance/service.rs +++ b/src/enhance/service.rs @@ -16,7 +16,7 @@ use serde::{Deserialize, Serialize}; use super::comfy::{ApproveError, ComfyClient, ComfyError, HistoryEntry, Staged, SubmitError}; use super::config::{EnhanceConfig, Loaded, PromptMode}; use super::path_map::PathMap; -use super::template::Template; +use super::template::{Template, random_seed}; use crate::database::enhance_dao::{ EnhanceJobDao, EnhanceJobRow, JobUpdate, RecentPrompt, STATUS_APPROVED, STATUS_EXPIRED, STATUS_FAILED, STATUS_QUEUED, STATUS_READY, STATUS_RUNNING, STATUS_UNAVAILABLE, @@ -314,9 +314,7 @@ impl EnhanceService { None => None, }; let filled = template - .fill(&source, prompt, reference_path.as_deref(), &mut || { - rand::random::() >> 12 - }) + .fill(&source, prompt, reference_path.as_deref(), &mut random_seed) .map_err(|e| EnhanceError::BadRequest(e.0))?; if !self.probe_now().await { diff --git a/src/enhance/template.rs b/src/enhance/template.rs index 55c0e0d..4e788b8 100644 --- a/src/enhance/template.rs +++ b/src/enhance/template.rs @@ -285,6 +285,13 @@ fn links_to(v: &Value, id: &str) -> bool { matches!(v.as_array().map(Vec::as_slice), Some([Value::String(s), Value::Number(_)]) if s == id) } +/// A fresh seed for one job. 32 bits: some nodes cap their seed there +/// (SeedVR2VideoUpscaler's max is 2^32 - 1), and it's plenty of variety for +/// the ones that take 64. +pub fn random_seed() -> u64 { + u64::from(rand::random::()) +} + fn is_seed_input(key: &str) -> bool { key == "seed" || key == "noise_seed" || key.ends_with(".seed") } @@ -401,12 +408,22 @@ mod tests { assert!(a > 1000 && b > 1000 && a != b); assert_ne!(a, 774744764437710); // Two fills with a real RNG differ. - let mut rng = || rand::random::() >> 12; + let mut rng = random_seed; let x = t.fill("p", None, None, &mut rng).unwrap(); let y = t.fill("p", None, None, &mut rng).unwrap(); assert_ne!(input(&x.prompt, "5", "seed"), input(&y.prompt, "5", "seed")); } + #[test] + fn random_seeds_fit_seedvr2s_u32_limit() { + // SeedVR2VideoUpscaler rejects seeds above 2^32 - 1 at validation. + let t = load(RESTORE, PromptMode::Append).unwrap(); + for _ in 0..200 { + let f = t.fill("p", None, None, &mut random_seed).unwrap(); + assert!(input(&f.prompt, "5", "seed").as_u64().unwrap() <= u64::from(u32::MAX)); + } + } + #[test] fn force_approval_even_if_the_export_had_it_off() { let off = RESTORE.replace("\"require_approval\": true", "\"require_approval\": false"); -- 2.54.0