divisor.keybinds

Choice registry decorator.

Usage:

@choice("g", "Guidance")
def change_guidance(controller: ManualTimestepController,
state: MenuState,
clear_prediction_cache: Callable[[], None],) -> MenuState:

    ...

    return state

Returns:

{"fn": callable, "desc": description}
  1# SPDX-License-Identifier: MPL-2.0 AND LicenseRef-Commons-Clause-License-Condition-1.0
  2# <!-- // /*  d a r k s h a p e s */ -->
  3
  4"""
  5Choice registry decorator.
  6
  7Usage:
  8```
  9@choice("g", "Guidance")
 10def change_guidance(controller: ManualTimestepController,
 11state: MenuState,
 12clear_prediction_cache: Callable[[], None],) -> MenuState:
 13
 14    ...
 15
 16    return state
 17```
 18
 19Returns:
 20```
 21{"fn": callable, "desc": description}
 22```
 23
 24"""
 25
 26from collections import OrderedDict
 27from typing import Any, Callable, Dict
 28
 29from nnll.console import nfo
 30from nnll.helpers import generate_valid_resolutions
 31
 32from divisor.cli_input import (
 33    get_float_input,
 34    get_int_input,
 35    handle_float_setting,
 36    handle_toggle,
 37)
 38from divisor.controller import ManualTimestepController, update_state_and_cache
 39from divisor.interaction_context import InteractionContext
 40from divisor.noise import prepare_4d_noise_for_3d_model
 41from divisor.state import MenuState
 42
 43
 44def choice(key: str, description: str) -> Callable[[Callable], Callable]:
 45    """Decorator that registers a function as a menu choice.\n
 46    :param key: Single-character option the user will type (e.g. ``"g"``).Use ``""`` for the *Enter* (advance) option.
 47    :param description: Short human-readable description that will be shown in the prompt and self-document the registry"""
 48
 49    def wrapper(fn: Callable) -> Callable:
 50        normalized_key = key.lower()
 51        _CHOICE_REGISTRY[normalized_key] = {"fn": fn, "desc": description}  # case‑insensitive
 52        return fn
 53
 54    return wrapper
 55
 56
 57_CHOICE_REGISTRY: "OrderedDict[str, Dict[str, Any]]" = OrderedDict()
 58
 59
 60@choice("", "Advance (Enter)")
 61def _advance(
 62    controller: ManualTimestepController,
 63    state: MenuState,
 64    interaction_context: InteractionContext,
 65) -> MenuState:
 66    """Advance one step (default action when Enter is pressed)."""
 67    nfo("Advancing...")
 68    interaction_context.clear_prediction_cache()
 69    controller.step()
 70    return controller.current_state
 71
 72
 73@choice("g", "Guidance")
 74def change_guidance(
 75    controller: ManualTimestepController,
 76    state: MenuState,
 77    interaction_context: InteractionContext,
 78) -> MenuState:
 79    """Handle guidance value change.\n"""
 80    new_guidance = get_float_input(
 81        f"Enter new guidance value (current: {state.guidance:.2f}): ",
 82        state.guidance,
 83        allow_empty=False,
 84    )
 85    if new_guidance is not None:
 86        return update_state_and_cache(
 87            controller,
 88            controller.set_guidance,
 89            new_guidance,
 90            interaction_context,
 91            f"Guidance set to {new_guidance}",
 92        )
 93    nfo("Invalid guidance value, keeping current value")
 94    return state
 95
 96
 97@choice("l", "Layer Dropout")
 98def change_layer_dropout(
 99    controller: ManualTimestepController,
100    state: MenuState,
101    interaction_context: InteractionContext,
102) -> MenuState:
103    """Handle layer dropout change.\n
104    :param controller: ManualTimestepController instance
105    :param state: Current input state
106    :param interaction_context: InteractionContext arguments
107    :returns: Updated input state"""
108    try:
109        dropout_input = input("Enter layer indices to drop (comma-separated, or 'none' to clear): ").strip()
110        if dropout_input.lower() == "none" or dropout_input == "":
111            layer_indices = None
112        else:
113            layer_indices = [int(x.strip()) for x in dropout_input.split(",")]
114
115        controller.set_layer_dropout(layer_indices)
116        interaction_context.clear_prediction_cache()
117        state = controller.current_state
118        nfo(f"Layer dropout set to: {layer_indices}")
119        return state
120    except ValueError:
121        nfo("Invalid layer indices, keeping current value")
122    return controller.current_state
123
124
125@choice("r", "Resolution")
126def change_resolution(
127    controller: ManualTimestepController,
128    state: MenuState,
129    interaction_context: InteractionContext,
130) -> MenuState:
131    """Handle resolution change.\n
132    :param controller: ManualTimestepController instance
133    :param state: Current input state
134    :param clear_prediction_cache: Function to clear prediction cache
135    :returns: Updated input state"""
136
137    try:
138        if state.width is None or state.height is None:
139            nfo("Cannot generate resolutions: width or height not set")
140        else:
141            valid_resolutions = generate_valid_resolutions(state.width, state.height)
142            nfo("Valid resolutions (same patch count):")
143            for i, (w, h) in enumerate(valid_resolutions):
144                current_marker = ""
145                if state.width == w and state.height == h:
146                    current_marker = " (current)"
147                nfo(f"  {i}: {w}x{h}{current_marker}")
148            resolution_input = input(f"\nEnter resolution index (0-{len(valid_resolutions) - 1}) or 'custom' for custom: ").strip()
149            if resolution_input.lower() == "custom":
150                width_input = input("Enter width: ").strip()
151                height_input = input("Enter height: ").strip()
152                new_width = int(width_input)
153                new_height = int(height_input)
154            else:
155                resolution_idx = int(resolution_input)
156                if 0 <= resolution_idx < len(valid_resolutions):
157                    new_width, new_height = valid_resolutions[resolution_idx]
158                else:
159                    nfo("Invalid resolution index, keeping current value")
160                    return state
161            if new_width is not None and new_height is not None:
162                controller.set_resolution(new_width, new_height)
163                interaction_context.clear_prediction_cache()
164                state = controller.current_state
165                nfo(f"Resolution set to: {new_width}x{new_height}")
166                return state
167    except (ValueError, IndexError):
168        nfo("Invalid resolution input, keeping current value")
169    return state
170
171
172@choice("s", "Seed")
173def change_seed(
174    controller: ManualTimestepController,
175    state: MenuState,
176    interaction_context: InteractionContext,
177) -> MenuState:
178    """Handle seed change.\n
179    :param controller: ManualTimestepController instance
180    :param state: Current menu state
181    :param rng: Random number generator instance
182    :param clear_prediction_cache: Function to clear prediction cache
183    :param t5: Optional T5 embedder instance (required for Flux1/XFlux1 to prepare sample)
184    :param clip: Optional CLIP embedder instance (required for Flux1/XFlux1 to prepare sample)
185    :returns: Updated menu state"""
186
187    current_seed = state.seed if state.seed is not None else 0
188    if new_seed := get_int_input(
189        f"Enter new seed number (current: {current_seed}, or press Enter for random): ",
190        current_seed,
191        generate_random=lambda: interaction_context.rng.next_seed(),
192    ):
193        controller.set_seed(new_seed)
194        new_sample = prepare_4d_noise_for_3d_model(
195            height=state.height,  # type: ignore
196            width=state.width,  # type: ignore
197            seed=new_seed,
198            t5=interaction_context.t5,
199            clip=interaction_context.clip,
200            prompt=state.prompt,
201        )
202
203        controller.current_sample = new_sample
204        interaction_context.clear_prediction_cache()
205        updated_state = controller.current_state
206        nfo(f"Seed set to: {new_seed}")
207        return updated_state
208    nfo("Invalid seed value, keeping current seed")
209    return state
210
211
212@choice("b", "Buffer Mask")
213def toggle_buffer_mask(
214    controller: ManualTimestepController,
215    state: MenuState,
216    interaction_context: InteractionContext,
217) -> MenuState:
218    """Handle buffer mask toggle.\n
219    :param controller: ManualTimestepController instance
220    :param state: Current input state
221    :returns: Updated input state"""
222
223    return handle_toggle(
224        controller,
225        state,
226        state.use_previous_as_mask,
227        controller.set_use_previous_as_mask,
228        enabled_msg="Previous step tensor mask: ENABLED",
229        disabled_msg="Previous step tensor mask: DISABLED",
230    )
231
232
233@choice("a", "Autoencoder Offset")
234def change_vae_offset(
235    controller: ManualTimestepController,
236    state: MenuState,
237    interaction_context: InteractionContext,
238) -> MenuState:
239    """Handle VAE shift/scale offset change.\n
240    :param controller: ManualTimestepController instance
241    :param state: Current input state
242    :param ae: Optional AutoEncoder instance
243    :param clear_prediction_cache: Function to clear prediction cache
244    :returns: Updated input state"""
245
246    if interaction_context.ae is None:
247        nfo("AutoEncoder not available, cannot set VAE offset")
248        return state
249
250    vae_input = input("\nChoose [S]hift or [C]scale: ").strip().lower()
251    if vae_input == "c":
252        return handle_float_setting(
253            controller,
254            state,
255            f"Enter VAE scale offset (current: {state.vae_scale_offset:.4f}, or press Enter to reset to 0.0): ",
256            state.vae_scale_offset,
257            controller.set_vae_scale_offset,
258            interaction_context.clear_prediction_cache,
259            default_value=0.0,
260            value_name="VAE scale offset",
261        )
262    elif vae_input == "s":
263        return handle_float_setting(
264            controller,
265            state,
266            f"Enter VAE shift offset (current: {state.vae_shift_offset:.4f}, or press Enter to reset to 0.0): ",
267            state.vae_shift_offset,
268            controller.set_vae_shift_offset,
269            interaction_context.clear_prediction_cache,
270            default_value=0.0,
271            value_name="VAE shift offset",
272        )
273    return state
274
275
276@choice("d", "Deterministic")
277def toggle_deterministic(
278    controller: ManualTimestepController,
279    state: MenuState,
280    interaction_context: InteractionContext,
281) -> MenuState:
282    """Handle deterministic mode toggle.\n
283    :param controller: ManualTimestepController instance
284    :param state: Current input state
285    :param clear_prediction_cache: Function to clear prediction cache
286    :returns: Updated input state"""
287
288    deterministic = not state.deterministic
289    interaction_context.rng.random_mode(reproducible=deterministic)
290    interaction_context.variation_rng.random_mode(reproducible=deterministic)
291
292    return handle_toggle(
293        controller,
294        state,
295        state.deterministic,
296        controller.set_deterministic,
297        interaction_context.clear_prediction_cache,
298        enabled_msg="Deterministic mode: ENABLED",
299        disabled_msg="Deterministic mode: DISABLED",
300    )
301
302
303@choice("p", "Prompt")
304def change_prompt(
305    controller: ManualTimestepController,
306    state: MenuState,
307    interaction_context: InteractionContext,
308) -> MenuState:
309    """Handle prompt change.\n
310    :param controller: ManualTimestepController instance
311    :param state: Current input state
312    :param clear_prediction_cache: Function to clear prediction cache
313    :param recompute_text_embeddings: Optional function to recompute text embeddings
314    :returns: Updated input state"""
315
316    current_prompt = state.prompt if state.prompt is not None else ""
317    new_prompt = input(f"Enter new prompt (current: {current_prompt}): ").strip()
318
319    if new_prompt:
320        controller.set_prompt(new_prompt)
321        if interaction_context.recompute_text_embeddings is not None:
322            interaction_context.recompute_text_embeddings(new_prompt)
323        else:
324            interaction_context.clear_prediction_cache()
325        state = controller.current_state
326        nfo(f"Prompt set to: {new_prompt}")
327    else:
328        nfo("Prompt unchanged")
329
330    return state
331
332
333@choice("e", "Edit Mode")
334def edit_mode(
335    controller: ManualTimestepController,
336    state: MenuState,
337    interaction_context: InteractionContext,
338) -> None:
339    """Handle edit mode (debugger breakpoint).\n
340    :param clear_prediction_cache: Function to clear prediction cache"""
341    nfo("Entering edit mode (use c/cont to exit)...")
342    breakpoint()
343    interaction_context.clear_prediction_cache()
344
345
346@choice("j", "Jump to Step")
347def jump_to_step(
348    controller: ManualTimestepController,
349    state: MenuState,
350    interaction_context: InteractionContext,
351) -> MenuState:
352    """Run the controller forward until a user‑specified step index.\n
353    :param controller: ManualTimestepController instance
354    :param state: Current input state (unused – we return the controller’s final state)
355    :param clear_prediction_cache: Routine to nvalidate cache prediction.
356    :returns: Input state post jump (or the current state if the target is out of range)."""
357
358    target_str = input(f"Jump to step (0‑{controller.current_index}{len(controller.timesteps) - 1}): ").strip()
359    if not target_str.isdigit():
360        nfo("Invalid step number – aborting jump.")
361        return controller.current_state
362
363    target = int(target_str)
364
365    if target <= controller.current_index:
366        nfo("Target step is before or equal to the current step – nothing to do.")
367        return controller.current_state
368    if target >= len(controller.timesteps):
369        nfo("Target step exceeds the schedule – jumping to the end.")
370        target = len(controller.timesteps) - 1
371
372    interaction_context.clear_prediction_cache()
373    while controller.current_index < target and not controller.is_complete:
374        controller.step()
375
376    nfo(f"Jumped to step {controller.current_index}/{len(controller.timesteps) - 1}")
377    return controller.current_state
378
379
380@choice("v", "Variation")
381def change_variation(
382    controller: ManualTimestepController,
383    state: MenuState,
384    interaction_context: InteractionContext,
385) -> MenuState:
386    """Handle variation seed/strength change.\n
387    :param controller: ManualTimestepController instance
388    :param state: Current input state
389    :param variation_rng: Variation random number generator instance
390    :param clear_prediction_cache: Function to clear prediction cache
391    :returns: Updated input state"""
392    try:
393        var_input = input(
394            f"Variation (current integer seed: {state.variation_seed}, float strength: {state.variation_strength:.3f}. type a number, leave empty for random seed, or use 0.0 to disable): "
395        ).strip()
396
397        if not var_input or "." not in var_input:
398            # Try to parse as integer (seed)
399            try:
400                if var_input != "":
401                    variation_seed = interaction_context.variation_rng.next_seed(int(var_input))
402                else:
403                    variation_seed = interaction_context.variation_rng.next_seed()
404                controller.set_variation_seed(variation_seed)
405                interaction_context.clear_prediction_cache()
406                state = controller.current_state
407                nfo(f"Variation seed set to: {state.variation_seed}")
408            except ValueError:
409                nfo("Invalid integer seed value, keeping current value")
410        else:
411            # Try to parse as float (strength)
412            try:
413                strength_value = float(var_input)
414                if strength_value < 0.0 or strength_value > 1.0:
415                    state = controller.current_state
416                    nfo("Variation strength must be between 0.0 and 1.0, keeping current value")
417                else:
418                    controller.set_variation_strength(strength_value)
419                    interaction_context.clear_prediction_cache()
420                    state = controller.current_state
421                    nfo(f"Variation strength set to: {strength_value:.3f}")
422            except ValueError:
423                nfo("Invalid float strength value, keeping current value")
424    except (ValueError, KeyboardInterrupt):
425        nfo("Invalid variation value, keeping current value")
426    return state
427
428
429@choice("w", "Rewind")
430def rewind(
431    controller: ManualTimestepController,
432    state: MenuState,
433    interaction_context: InteractionContext,
434) -> MenuState:
435    """Rewind the controller by a specified number of steps.\n
436    :param controller: ManualTimestepController instance
437    :param state: Current input state
438    :param clear_prediction_cache: Function to clear prediction cache
439    :returns: Updated input state"""
440    import random as prng
441
442    num_steps = get_int_input(
443        f"Enter number of steps to rewind (current: {controller.rewind_steps}): ",
444        controller.rewind_steps,
445        generate_random=lambda: prng.randint(0, controller.timesteps.index(state.timestep_index)),
446    )
447    if num_steps is not None:
448        controller.rewind(num_steps)
449        interaction_context.clear_prediction_cache()
450        state = controller.current_state
451        nfo(f"Rewind step {num_steps} to {controller.current_index}")
452        return state
453    nfo("Invalid number of steps, keeping current value")
454    return state
455
456
457# Optionally recompute the *remaining* schedule with a different compression
458# remaining = controller.timesteps[controller.current_index + 1 :]
459# new_tail = time_shift(
460#    schedule_mu=controller.mu,
461#    schedule_sigma=controller.sigma,
462#    original_timestep_tensor=torch.tensor(remaining),
463#    desired_step_count=len(remaining),
464#    compression_factor=0.9,               # e.g. stretch the tail a bit
465# )
466# controller.timesteps[controller.current_index + 1 :] = new_tail.tolist()
def choice(key: str, description: str) -> Callable[[Callable], Callable]:
45def choice(key: str, description: str) -> Callable[[Callable], Callable]:
46    """Decorator that registers a function as a menu choice.\n
47    :param key: Single-character option the user will type (e.g. ``"g"``).Use ``""`` for the *Enter* (advance) option.
48    :param description: Short human-readable description that will be shown in the prompt and self-document the registry"""
49
50    def wrapper(fn: Callable) -> Callable:
51        normalized_key = key.lower()
52        _CHOICE_REGISTRY[normalized_key] = {"fn": fn, "desc": description}  # case‑insensitive
53        return fn
54
55    return wrapper

Decorator that registers a function as a menu choice.

Parameters
  • key: Single-character option the user will type (e.g. "g").Use "" for the Enter (advance) option.
  • description: Short human-readable description that will be shown in the prompt and self-document the registry
@choice('g', 'Guidance')
def change_guidance( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
74@choice("g", "Guidance")
75def change_guidance(
76    controller: ManualTimestepController,
77    state: MenuState,
78    interaction_context: InteractionContext,
79) -> MenuState:
80    """Handle guidance value change.\n"""
81    new_guidance = get_float_input(
82        f"Enter new guidance value (current: {state.guidance:.2f}): ",
83        state.guidance,
84        allow_empty=False,
85    )
86    if new_guidance is not None:
87        return update_state_and_cache(
88            controller,
89            controller.set_guidance,
90            new_guidance,
91            interaction_context,
92            f"Guidance set to {new_guidance}",
93        )
94    nfo("Invalid guidance value, keeping current value")
95    return state

Handle guidance value change.

@choice('l', 'Layer Dropout')
def change_layer_dropout( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
 98@choice("l", "Layer Dropout")
 99def change_layer_dropout(
100    controller: ManualTimestepController,
101    state: MenuState,
102    interaction_context: InteractionContext,
103) -> MenuState:
104    """Handle layer dropout change.\n
105    :param controller: ManualTimestepController instance
106    :param state: Current input state
107    :param interaction_context: InteractionContext arguments
108    :returns: Updated input state"""
109    try:
110        dropout_input = input("Enter layer indices to drop (comma-separated, or 'none' to clear): ").strip()
111        if dropout_input.lower() == "none" or dropout_input == "":
112            layer_indices = None
113        else:
114            layer_indices = [int(x.strip()) for x in dropout_input.split(",")]
115
116        controller.set_layer_dropout(layer_indices)
117        interaction_context.clear_prediction_cache()
118        state = controller.current_state
119        nfo(f"Layer dropout set to: {layer_indices}")
120        return state
121    except ValueError:
122        nfo("Invalid layer indices, keeping current value")
123    return controller.current_state

Handle layer dropout change.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • interaction_context: InteractionContext arguments :returns: Updated input state
@choice('r', 'Resolution')
def change_resolution( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
126@choice("r", "Resolution")
127def change_resolution(
128    controller: ManualTimestepController,
129    state: MenuState,
130    interaction_context: InteractionContext,
131) -> MenuState:
132    """Handle resolution change.\n
133    :param controller: ManualTimestepController instance
134    :param state: Current input state
135    :param clear_prediction_cache: Function to clear prediction cache
136    :returns: Updated input state"""
137
138    try:
139        if state.width is None or state.height is None:
140            nfo("Cannot generate resolutions: width or height not set")
141        else:
142            valid_resolutions = generate_valid_resolutions(state.width, state.height)
143            nfo("Valid resolutions (same patch count):")
144            for i, (w, h) in enumerate(valid_resolutions):
145                current_marker = ""
146                if state.width == w and state.height == h:
147                    current_marker = " (current)"
148                nfo(f"  {i}: {w}x{h}{current_marker}")
149            resolution_input = input(f"\nEnter resolution index (0-{len(valid_resolutions) - 1}) or 'custom' for custom: ").strip()
150            if resolution_input.lower() == "custom":
151                width_input = input("Enter width: ").strip()
152                height_input = input("Enter height: ").strip()
153                new_width = int(width_input)
154                new_height = int(height_input)
155            else:
156                resolution_idx = int(resolution_input)
157                if 0 <= resolution_idx < len(valid_resolutions):
158                    new_width, new_height = valid_resolutions[resolution_idx]
159                else:
160                    nfo("Invalid resolution index, keeping current value")
161                    return state
162            if new_width is not None and new_height is not None:
163                controller.set_resolution(new_width, new_height)
164                interaction_context.clear_prediction_cache()
165                state = controller.current_state
166                nfo(f"Resolution set to: {new_width}x{new_height}")
167                return state
168    except (ValueError, IndexError):
169        nfo("Invalid resolution input, keeping current value")
170    return state

Handle resolution change.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • clear_prediction_cache: Function to clear prediction cache :returns: Updated input state
@choice('s', 'Seed')
def change_seed( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
173@choice("s", "Seed")
174def change_seed(
175    controller: ManualTimestepController,
176    state: MenuState,
177    interaction_context: InteractionContext,
178) -> MenuState:
179    """Handle seed change.\n
180    :param controller: ManualTimestepController instance
181    :param state: Current menu state
182    :param rng: Random number generator instance
183    :param clear_prediction_cache: Function to clear prediction cache
184    :param t5: Optional T5 embedder instance (required for Flux1/XFlux1 to prepare sample)
185    :param clip: Optional CLIP embedder instance (required for Flux1/XFlux1 to prepare sample)
186    :returns: Updated menu state"""
187
188    current_seed = state.seed if state.seed is not None else 0
189    if new_seed := get_int_input(
190        f"Enter new seed number (current: {current_seed}, or press Enter for random): ",
191        current_seed,
192        generate_random=lambda: interaction_context.rng.next_seed(),
193    ):
194        controller.set_seed(new_seed)
195        new_sample = prepare_4d_noise_for_3d_model(
196            height=state.height,  # type: ignore
197            width=state.width,  # type: ignore
198            seed=new_seed,
199            t5=interaction_context.t5,
200            clip=interaction_context.clip,
201            prompt=state.prompt,
202        )
203
204        controller.current_sample = new_sample
205        interaction_context.clear_prediction_cache()
206        updated_state = controller.current_state
207        nfo(f"Seed set to: {new_seed}")
208        return updated_state
209    nfo("Invalid seed value, keeping current seed")
210    return state

Handle seed change.

Parameters
  • controller: ManualTimestepController instance
  • state: Current menu state
  • rng: Random number generator instance
  • clear_prediction_cache: Function to clear prediction cache
  • t5: Optional T5 embedder instance (required for Flux1/XFlux1 to prepare sample)
  • clip: Optional CLIP embedder instance (required for Flux1/XFlux1 to prepare sample) :returns: Updated menu state
@choice('b', 'Buffer Mask')
def toggle_buffer_mask( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
213@choice("b", "Buffer Mask")
214def toggle_buffer_mask(
215    controller: ManualTimestepController,
216    state: MenuState,
217    interaction_context: InteractionContext,
218) -> MenuState:
219    """Handle buffer mask toggle.\n
220    :param controller: ManualTimestepController instance
221    :param state: Current input state
222    :returns: Updated input state"""
223
224    return handle_toggle(
225        controller,
226        state,
227        state.use_previous_as_mask,
228        controller.set_use_previous_as_mask,
229        enabled_msg="Previous step tensor mask: ENABLED",
230        disabled_msg="Previous step tensor mask: DISABLED",
231    )

Handle buffer mask toggle.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state :returns: Updated input state
@choice('a', 'Autoencoder Offset')
def change_vae_offset( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
234@choice("a", "Autoencoder Offset")
235def change_vae_offset(
236    controller: ManualTimestepController,
237    state: MenuState,
238    interaction_context: InteractionContext,
239) -> MenuState:
240    """Handle VAE shift/scale offset change.\n
241    :param controller: ManualTimestepController instance
242    :param state: Current input state
243    :param ae: Optional AutoEncoder instance
244    :param clear_prediction_cache: Function to clear prediction cache
245    :returns: Updated input state"""
246
247    if interaction_context.ae is None:
248        nfo("AutoEncoder not available, cannot set VAE offset")
249        return state
250
251    vae_input = input("\nChoose [S]hift or [C]scale: ").strip().lower()
252    if vae_input == "c":
253        return handle_float_setting(
254            controller,
255            state,
256            f"Enter VAE scale offset (current: {state.vae_scale_offset:.4f}, or press Enter to reset to 0.0): ",
257            state.vae_scale_offset,
258            controller.set_vae_scale_offset,
259            interaction_context.clear_prediction_cache,
260            default_value=0.0,
261            value_name="VAE scale offset",
262        )
263    elif vae_input == "s":
264        return handle_float_setting(
265            controller,
266            state,
267            f"Enter VAE shift offset (current: {state.vae_shift_offset:.4f}, or press Enter to reset to 0.0): ",
268            state.vae_shift_offset,
269            controller.set_vae_shift_offset,
270            interaction_context.clear_prediction_cache,
271            default_value=0.0,
272            value_name="VAE shift offset",
273        )
274    return state

Handle VAE shift/scale offset change.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • ae: Optional AutoEncoder instance
  • clear_prediction_cache: Function to clear prediction cache :returns: Updated input state
@choice('d', 'Deterministic')
def toggle_deterministic( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
277@choice("d", "Deterministic")
278def toggle_deterministic(
279    controller: ManualTimestepController,
280    state: MenuState,
281    interaction_context: InteractionContext,
282) -> MenuState:
283    """Handle deterministic mode toggle.\n
284    :param controller: ManualTimestepController instance
285    :param state: Current input state
286    :param clear_prediction_cache: Function to clear prediction cache
287    :returns: Updated input state"""
288
289    deterministic = not state.deterministic
290    interaction_context.rng.random_mode(reproducible=deterministic)
291    interaction_context.variation_rng.random_mode(reproducible=deterministic)
292
293    return handle_toggle(
294        controller,
295        state,
296        state.deterministic,
297        controller.set_deterministic,
298        interaction_context.clear_prediction_cache,
299        enabled_msg="Deterministic mode: ENABLED",
300        disabled_msg="Deterministic mode: DISABLED",
301    )

Handle deterministic mode toggle.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • clear_prediction_cache: Function to clear prediction cache :returns: Updated input state
@choice('p', 'Prompt')
def change_prompt( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
304@choice("p", "Prompt")
305def change_prompt(
306    controller: ManualTimestepController,
307    state: MenuState,
308    interaction_context: InteractionContext,
309) -> MenuState:
310    """Handle prompt change.\n
311    :param controller: ManualTimestepController instance
312    :param state: Current input state
313    :param clear_prediction_cache: Function to clear prediction cache
314    :param recompute_text_embeddings: Optional function to recompute text embeddings
315    :returns: Updated input state"""
316
317    current_prompt = state.prompt if state.prompt is not None else ""
318    new_prompt = input(f"Enter new prompt (current: {current_prompt}): ").strip()
319
320    if new_prompt:
321        controller.set_prompt(new_prompt)
322        if interaction_context.recompute_text_embeddings is not None:
323            interaction_context.recompute_text_embeddings(new_prompt)
324        else:
325            interaction_context.clear_prediction_cache()
326        state = controller.current_state
327        nfo(f"Prompt set to: {new_prompt}")
328    else:
329        nfo("Prompt unchanged")
330
331    return state

Handle prompt change.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • clear_prediction_cache: Function to clear prediction cache
  • recompute_text_embeddings: Optional function to recompute text embeddings :returns: Updated input state
@choice('e', 'Edit Mode')
def edit_mode( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> None:
334@choice("e", "Edit Mode")
335def edit_mode(
336    controller: ManualTimestepController,
337    state: MenuState,
338    interaction_context: InteractionContext,
339) -> None:
340    """Handle edit mode (debugger breakpoint).\n
341    :param clear_prediction_cache: Function to clear prediction cache"""
342    nfo("Entering edit mode (use c/cont to exit)...")
343    breakpoint()
344    interaction_context.clear_prediction_cache()

Handle edit mode (debugger breakpoint).

Parameters
  • clear_prediction_cache: Function to clear prediction cache
@choice('j', 'Jump to Step')
def jump_to_step( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
347@choice("j", "Jump to Step")
348def jump_to_step(
349    controller: ManualTimestepController,
350    state: MenuState,
351    interaction_context: InteractionContext,
352) -> MenuState:
353    """Run the controller forward until a user‑specified step index.\n
354    :param controller: ManualTimestepController instance
355    :param state: Current input state (unused – we return the controller’s final state)
356    :param clear_prediction_cache: Routine to nvalidate cache prediction.
357    :returns: Input state post jump (or the current state if the target is out of range)."""
358
359    target_str = input(f"Jump to step (0‑{controller.current_index}{len(controller.timesteps) - 1}): ").strip()
360    if not target_str.isdigit():
361        nfo("Invalid step number – aborting jump.")
362        return controller.current_state
363
364    target = int(target_str)
365
366    if target <= controller.current_index:
367        nfo("Target step is before or equal to the current step – nothing to do.")
368        return controller.current_state
369    if target >= len(controller.timesteps):
370        nfo("Target step exceeds the schedule – jumping to the end.")
371        target = len(controller.timesteps) - 1
372
373    interaction_context.clear_prediction_cache()
374    while controller.current_index < target and not controller.is_complete:
375        controller.step()
376
377    nfo(f"Jumped to step {controller.current_index}/{len(controller.timesteps) - 1}")
378    return controller.current_state

Run the controller forward until a user‑specified step index.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state (unused – we return the controller’s final state)
  • clear_prediction_cache: Routine to nvalidate cache prediction. :returns: Input state post jump (or the current state if the target is out of range).
@choice('v', 'Variation')
def change_variation( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
381@choice("v", "Variation")
382def change_variation(
383    controller: ManualTimestepController,
384    state: MenuState,
385    interaction_context: InteractionContext,
386) -> MenuState:
387    """Handle variation seed/strength change.\n
388    :param controller: ManualTimestepController instance
389    :param state: Current input state
390    :param variation_rng: Variation random number generator instance
391    :param clear_prediction_cache: Function to clear prediction cache
392    :returns: Updated input state"""
393    try:
394        var_input = input(
395            f"Variation (current integer seed: {state.variation_seed}, float strength: {state.variation_strength:.3f}. type a number, leave empty for random seed, or use 0.0 to disable): "
396        ).strip()
397
398        if not var_input or "." not in var_input:
399            # Try to parse as integer (seed)
400            try:
401                if var_input != "":
402                    variation_seed = interaction_context.variation_rng.next_seed(int(var_input))
403                else:
404                    variation_seed = interaction_context.variation_rng.next_seed()
405                controller.set_variation_seed(variation_seed)
406                interaction_context.clear_prediction_cache()
407                state = controller.current_state
408                nfo(f"Variation seed set to: {state.variation_seed}")
409            except ValueError:
410                nfo("Invalid integer seed value, keeping current value")
411        else:
412            # Try to parse as float (strength)
413            try:
414                strength_value = float(var_input)
415                if strength_value < 0.0 or strength_value > 1.0:
416                    state = controller.current_state
417                    nfo("Variation strength must be between 0.0 and 1.0, keeping current value")
418                else:
419                    controller.set_variation_strength(strength_value)
420                    interaction_context.clear_prediction_cache()
421                    state = controller.current_state
422                    nfo(f"Variation strength set to: {strength_value:.3f}")
423            except ValueError:
424                nfo("Invalid float strength value, keeping current value")
425    except (ValueError, KeyboardInterrupt):
426        nfo("Invalid variation value, keeping current value")
427    return state

Handle variation seed/strength change.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • variation_rng: Variation random number generator instance
  • clear_prediction_cache: Function to clear prediction cache :returns: Updated input state
@choice('w', 'Rewind')
def rewind( controller: divisor.controller.ManualTimestepController, state: divisor.state.MenuState, interaction_context: divisor.interaction_context.InteractionContext) -> divisor.state.MenuState:
430@choice("w", "Rewind")
431def rewind(
432    controller: ManualTimestepController,
433    state: MenuState,
434    interaction_context: InteractionContext,
435) -> MenuState:
436    """Rewind the controller by a specified number of steps.\n
437    :param controller: ManualTimestepController instance
438    :param state: Current input state
439    :param clear_prediction_cache: Function to clear prediction cache
440    :returns: Updated input state"""
441    import random as prng
442
443    num_steps = get_int_input(
444        f"Enter number of steps to rewind (current: {controller.rewind_steps}): ",
445        controller.rewind_steps,
446        generate_random=lambda: prng.randint(0, controller.timesteps.index(state.timestep_index)),
447    )
448    if num_steps is not None:
449        controller.rewind(num_steps)
450        interaction_context.clear_prediction_cache()
451        state = controller.current_state
452        nfo(f"Rewind step {num_steps} to {controller.current_index}")
453        return state
454    nfo("Invalid number of steps, keeping current value")
455    return state

Rewind the controller by a specified number of steps.

Parameters
  • controller: ManualTimestepController instance
  • state: Current input state
  • clear_prediction_cache: Function to clear prediction cache :returns: Updated input state