The LTX tab already collected a last-frame image but anchored every condition at index 0, so it never acted as a last frame. Build a separate condition for it at the final frame: index -1 for the 2.x family (latent index, negatives wrap) and num_frames-1 for 0.9 (pixel index). The Last image input now shows only for Condition models, the pipelines that accept multi-frame conditioning.
The I2V path forwards a last-frame image to the pipeline when one is supplied and the loaded pipeline accepts it, turning the run into first-last-frame interpolation. supports_last_frame() gates on the pipeline taking a last_image argument and not running expand_timesteps, which conditions on the first frame only, so a model that cannot use a last frame logs a warning instead of silently ignoring it.
Wan 2.2 A14B ships a per-model boundary_ratio (0.9 I2V, 0.875 T2V) that selects the high- or low-noise expert per step. The video and base-model image loaders both load the shipped value; the slider override is applied at generation time in set_pipeline_args, the one point both paths pass through before invoking the pipeline.
The denoising loop reads config.boundary_ratio each call, so tuning takes effect with no reload for video and base-model images alike. The slider defaults to -1, meaning use the model's value; 0 to 1 set the boundary explicitly. Single-expert stages stay load-time because they drop a transformer to free VRAM.
A single handler bound to the AR dropdown, width and height (in the
shared create_resolution_inputs and the resize section) wrote both
sliders on every change. With a ratio selected it locked one axis and
snapped it back on each edit, the two sliders looped, and the math ran
server-side one round-trip per keystroke, echoing a value back into the
field being typed in and yanking it.
Move aspect-ratio linking to the browser (ui/resolutionLock.ts): a
debounced edit writes only the partner axis, never the field being
edited, and commits immediately on blur, enter, or slider release. Keep
the kanvas notify on notifyKanvasResize wired to the resize sliders'
gradio .change, so it still fires on programmatic size updates (detect,
paste, swap) that client-side listeners miss. Drop the per-change AR
wiring, res_apply, and the resolutionChange* helpers.
The nsfw boolean leaks Mature+ content (nsfw=false returned levels up to
XXX) and /models rejects server-side level filtering, so filter search
results on each model's aggregate nsfwLevel: nsfw off keeps SFW (None +
Soft), nsfw on keeps everything.
Civitai returns a null username for deleted or anonymous creators. The
field default only applies when the key is absent, so an explicit null
failed str validation and rejected the entire CivitSearchResponse,
dropping every model on that page.
Coerce null or empty usernames to the default in a pre-validator so one
bad creator no longer empties the whole search result.
Replace base-model and model-type discovery that scraped ZodError
responses with the documented GET /api/v1/enums endpoint. The /images
baseModels probe (unreliable since /models stopped validating it) is now
a fallback only; sort and period still come from the /models 400 response
since /enums does not carry them.
Add the model fields the API returns but CivitModel was dropping:
supportsGeneration, mode, poi, minor, sfwOnly, userId, cosmetic. The
poi/minor/sfwOnly flags serialize as null on some endpoints, so coerce
null to False to keep parsing resilient.
clip_skip and the uni_pc_* opts live in opts.data without an OptionInfo in
data_labels (compatibility_opts). Options.set() read data_labels[key].onchange
unconditionally, so setting clip_skip via /sdapi/v1/options raised KeyError and
returned 500; the override_settings restore path had the same unguarded
data_labels[k] access for falsy-valued compat opts.
Guard the onchange lookup and read the stored value via getattr(opts, k), which
already falls back through data then data_labels.
The vendored encode_prompt copies in the PAG, APG, ControlNet-XS and differential
diffusion pipelines dereference the .text_model wrapper that transformers 5.6
removed from CLIPTextModel, so their clip-skip path crashes on SD1.5 and SDXL TE1.
Apply the same getattr(te, 'text_model', te) fix as the core parser.
Mirrors upstream diffusers, which still carries this deref in pipeline
encode_prompt; only the single-file loader was fixed there.
transformers 5.6 flattened CLIPTextModel, removing the .text_model wrapper that
compel_hijack and the xhinker parser dereference on the normalized clip-skip path.
On SD1.5 at clip-skip >= 2 this raised AttributeError, which processing_prompt
caught and silently fell back to fixed-attention encoding, dropping textual
inversion and prompt weighting.
Resolve the submodule via getattr(te, 'text_model', te), correct for flattened
CLIPTextModel, CLIPTextModelWithProjection (still nested), and transformers < 5.6.
transformers 5 defaults tokenizer.add_tokens() to normalized=True, so the CLIP
tokenizer lowercases added embedding names. tokenizer.tokenize() then returns the
lowercased surface, maybe_convert_prompt never matches mixed-case names in
added_tokens_encoder, and multi-vector expansion is skipped. Every mixed-case
multi-vector embedding collapsed to its first vector and looked ignored.
Add embedding tokens as AddedToken(name, normalized=False) so they stay
case-sensitive and tokenize() surfaces them verbatim. Valid on transformers 4.x
and 5.x; convert_tokens_to_ids and encoding are unaffected.