mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
feat(lora_randomizer): implement dual seed mechanism for batch queue synchronization, fixes #773
- Add execution_seed and next_seed parameters to support deterministic randomization across batch executions - Separate UI display generation from execution stack generation to maintain consistency in batch queues - Update LoraService to accept optional seed parameter for reproducible randomization - Ensure each execution with a different seed produces unique results without affecting global random state
This commit is contained in:
@@ -54,6 +54,9 @@ const props = defineProps<{
|
||||
// State management
|
||||
const state = useLoraRandomizerState(props.widget)
|
||||
|
||||
// Symbol to track if the widget has been executed at least once
|
||||
const HAS_EXECUTED = Symbol('HAS_EXECUTED')
|
||||
|
||||
// Track current loras from the loras widget
|
||||
const currentLoras = ref<LoraEntry[]>([])
|
||||
|
||||
@@ -190,6 +193,31 @@ onMounted(async () => {
|
||||
state.restoreFromConfig(props.widget.value as RandomizerConfig)
|
||||
}
|
||||
|
||||
// Add beforeQueued hook to handle seed shifting for batch queue synchronization
|
||||
// This ensures each execution uses the loras that were displayed before that execution
|
||||
;(props.widget as any).beforeQueued = () => {
|
||||
// Only process when roll_mode is 'always' (randomize on each execution)
|
||||
if (state.rollMode.value === 'always') {
|
||||
if ((props.widget as any)[HAS_EXECUTED]) {
|
||||
// After first execution: shift seeds (previous next_seed becomes execution_seed)
|
||||
state.generateNewSeed()
|
||||
} else {
|
||||
// First execution: just initialize next_seed (execution_seed stays null)
|
||||
// This means first execution uses loras from widget input
|
||||
state.initializeNextSeed()
|
||||
;(props.widget as any)[HAS_EXECUTED] = true
|
||||
}
|
||||
|
||||
// Update the widget value so the seeds are included in the serialized config
|
||||
const config = state.buildConfig()
|
||||
if ((props.widget as any).updateConfig) {
|
||||
;(props.widget as any).updateConfig(config)
|
||||
} else {
|
||||
props.widget.value = config
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override onExecuted to handle backend UI updates
|
||||
const originalOnExecuted = (props.node as any).onExecuted?.bind(props.node)
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ export interface RandomizerConfig {
|
||||
use_recommended_strength: boolean
|
||||
recommended_strength_scale_min: number
|
||||
recommended_strength_scale_max: number
|
||||
execution_seed?: number | null // Seed for execution_stack (previous next_seed)
|
||||
next_seed?: number | null // Seed for ui_loras (current)
|
||||
}
|
||||
|
||||
export interface LoraEntry {
|
||||
|
||||
@@ -21,6 +21,12 @@ export function useLoraRandomizerState(widget: ComponentWidget) {
|
||||
// Track last used combination (for backend roll mode)
|
||||
const lastUsed = ref<LoraEntry[] | null>(null)
|
||||
|
||||
// Dual seed mechanism for batch queue synchronization
|
||||
// execution_seed: seed for generating execution_stack (= previous next_seed)
|
||||
// next_seed: seed for generating ui_loras (= what will be displayed after execution)
|
||||
const executionSeed = ref<number | null>(null)
|
||||
const nextSeed = ref<number | null>(null)
|
||||
|
||||
// Build config object from current state
|
||||
const buildConfig = (): RandomizerConfig => ({
|
||||
count_mode: countMode.value,
|
||||
@@ -37,8 +43,24 @@ export function useLoraRandomizerState(widget: ComponentWidget) {
|
||||
use_recommended_strength: useRecommendedStrength.value,
|
||||
recommended_strength_scale_min: recommendedStrengthScaleMin.value,
|
||||
recommended_strength_scale_max: recommendedStrengthScaleMax.value,
|
||||
execution_seed: executionSeed.value,
|
||||
next_seed: nextSeed.value,
|
||||
})
|
||||
|
||||
// Shift seeds for batch queue synchronization
|
||||
// Previous next_seed becomes current execution_seed, and generate a new next_seed
|
||||
const generateNewSeed = () => {
|
||||
executionSeed.value = nextSeed.value // Previous next becomes current execution
|
||||
nextSeed.value = Math.floor(Math.random() * 2147483647)
|
||||
}
|
||||
|
||||
// Initialize next_seed for first execution (execution_seed stays null)
|
||||
const initializeNextSeed = () => {
|
||||
if (nextSeed.value === null) {
|
||||
nextSeed.value = Math.floor(Math.random() * 2147483647)
|
||||
}
|
||||
}
|
||||
|
||||
// Restore state from config object
|
||||
const restoreFromConfig = (config: RandomizerConfig) => {
|
||||
countMode.value = config.count_mode || 'range'
|
||||
@@ -185,6 +207,8 @@ export function useLoraRandomizerState(widget: ComponentWidget) {
|
||||
useRecommendedStrength,
|
||||
recommendedStrengthScaleMin,
|
||||
recommendedStrengthScaleMax,
|
||||
executionSeed,
|
||||
nextSeed,
|
||||
|
||||
// Computed
|
||||
isClipStrengthDisabled,
|
||||
@@ -195,5 +219,7 @@ export function useLoraRandomizerState(widget: ComponentWidget) {
|
||||
restoreFromConfig,
|
||||
rollLoras,
|
||||
useLastUsed,
|
||||
generateNewSeed,
|
||||
initializeNextSeed,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user