A delta that does not fit its target module cannot apply, and applying only
the layers that do fit leaves the model in a state nothing was trained for,
so try_load_chain drops the whole file when any family reports a mismatch.
Bias deltas were never checked against the target bias and could only surface
at apply time; a module with no bias stays a non-mismatch, since whole
architectures are built bias=False.
- check bias deltas against the module bias in the lora, norm and full loaders
- carry the mismatch count on the network so the chain can refuse the file
- record refused writes in the infotext so a partial apply is not read as clean
- point the krea2 full-diff test at a module that has a bias
network_add_weights defaulted its base tensor to self.weight for the bias
delta as well, so in fuse mode a diff_b was added to the weight matrix and
the result written into the bias. Layers where in and out differ threw a
shape error and had the weight matrix installed as their bias, square layers
broadcast silently, and either way the summary still counted the delta as
applied.
- pick the base tensor from the bias flag
- name the layer, target and both shapes in the mismatch error
- return which of (weight, bias) took a write, count the rest as refused
- report refused= on partially applied and partially removed networks
- cover both apply paths in test/test-lora-apply.py
The triton autotune hijack draws its own rich progress bar on the
logger console during sweeps; a second live display on the same
terminal tramples the benchmark's live tables and leaves stale panel
border lines in scrollback. Disarm the bar in the benchmark process
and keep the hijack's bookkeeping.
Write the text and json reports by default, named to the archive
convention <gpu>-t<torch>-<date> with a collision suffix for the pair,
into $SDNQ_BENCH_DIR, --outdir, or benchmarks/ under the sdnext root.
'none' disables either output and explicit paths behave as before.
Its 2.x sibling always did, so only the 0.9 path hit a cpu weight against cuda
latents. Also logs why a run is refused, which until now travelled only in the
raised error.
TAEHV.decode_video already returns [-1,1] and framepack applied its own x2-1 on
top, landing near [-3,1] where the worker documents [-1,1]. The path was
unreachable until the taesd loader began honoring an explicit variant.
run_ltx reported failure by yielding a string, which is why LTX had no API.
run() is the core underneath: keyword arguments named as video_run.run names
them, a VideoResult back, VideoError out with 499 for an interrupt. The lock,
progress and summary stay in the adapter, whose signature is unchanged since
callers bind to it by keyword. Failure now closes the processing object and
deactivates networks, which abort never did.
The gallery branch took an upload handle or a PIL image, so an encoded string
fell through to .convert and was logged as a failed condition. Strings now go
to the api decoder, which reads base64 and upload refs rather than paths.
Both runners round the requested resolution and the two-stage LTX path derives
its own, so the request never recorded what came out. Read it off the decoded
frames instead.
Add accum configs, value-side stress rows, a recommendation row, block
specs, and a triton-mm variant for the new use_fp16_accum surface. The
unsafe unquantized-pv mode is measured but never starred or recommended;
accum rows skip on sdnq builds without the kwarg. Also move the dequant
compile verdict off the removed sdnq_dequantize_compile option to a note
keyed on SDNQ_USE_TORCH_COMPILE, fixing the crash after 2250d8052.
check_av returned the module on success and False on failure, so the two
callers testing for None treated a failed import as a working av and reached
into it anyway. It now returns None, and the guard that had to test for both
tests for one.
Tiny decode lost its call site when the video vae hijack was replaced by the
shared one, which has no tiny branch, so selecting it on the video tab quietly
decoded through the full vae for every engine. The decode hijack now takes the
tiny path when the run asked for it, falling back to the full vae whenever
there is no tiny counterpart to use.
The class test also spelled Wan in capitals and matched none of the four Wan
pipeline classes. Alongside that:
- the requested type travels on the pipe rather than a module global, so the
hijack reads the same value the run set
- a latent whose channel count taehv cannot take is reported and falls back
instead of failing inside the first convolution
- decode_video already returns the range the pipelines expect, so the second
normalization that followed it is gone
get_model discarded the variant its caller asked for and re-derived one from
the loaded model type. None of the video model types appear in those sets, so
every video caller got None back and no tiny decoder at all, while kandinsky5
matched the flux group and would have been handed an image decoder.
The derivation now runs only when the caller named nothing. Callers that name
nothing are unaffected, and the one video type that resolved before keeps the
same variant and cache key.
Pins the sentinel contract, row uniqueness, and the equivalence between
dispatch_mode and the ladder it replaced. A row that declares neither a name
marker nor a mapped pipeline class now fails here instead of reaching a runner
that would generate it as text to video.
LTX generates 8n+1 frames and floors anything else internally, so a request
for 120 frames on the shared path silently produced 113. The tab has always
snapped the count before requesting it; the shared path now applies the same
rule, next to the width and height rounding it already did.
Both load buttons matched the dropdown value against the raw row list, so the
separators the list uses for grouping resolved to a row with no repo. The
loader returned early on that row without loading or reporting, and the ltx
tab went on to record load and offload timings for a load that never ran.
Selection now goes through models_def.find, which knows a separator names no
model, and both buttons say what happened. The ltx button had no output bound
at all, so its message went nowhere.
Two ladders decided how a model's inputs get wired, and they had drifted: the
api reported an unrecognized row as t2v while run() fell through to a branch
that wires nothing. Eight LTX condition rows sat in that gap, advertised as
text to video and generating without their conditioning.
dispatch_mode answers once from the row, reading name markers before the
pipeline class because one class serves several modes: six LTXConditionPipeline
rows are named T2V or I2V and generate as such. Rows that declare nothing now
resolve as condition, and the api reports that instead of guessing t2v.
Pins the cases that separate a complete slice from a truncated one, including
the zero-argument script whose empty slice is complete, and asserts no hook
runner slices the vector on its own.
The selectable run and after paths sliced the incoming vector behind an
attribute check alone, so a short vector reached the script as too few
positionals and raised a TypeError out of the runner. Both now resolve the
slice the same way the alwayson hooks do and report the mismatch instead.
The alwayson hook runners checked that a script declared an argument range
but never that the processing object's vector could fill it, so a caller
that did not bootstrap script args splatted fewer positionals than the hook
signature takes. Every registered script raised a TypeError per hook, and
postprocess_image runs per image, so one video generation produced a
traceback per script per frame.
resolve_script_args now answers with the slice or None, and each runner
skips on None. A script that declares no arguments still runs, since its
empty slice is complete rather than truncated.
The FP VAE dropdown defaulted to Local, which is not among its choices, so
the control rendered with an out-of-list value. The decode dispatch treats
anything unrecognized as Full, so Full is the value the default already
meant.
The nVidia Cosmos engine carried two byte-identical Predict2 2B I2V rows,
so the dropdown showed the name twice. The surviving row's url now points
at the Video2World repo it loads; it pointed at the Text2Image image model.
The tab held the only code that built video and audio references, sniffed the
file type itself, and dropped anything it did not recognize: an unknown
extension, a file that had gone missing, and any decode failure were all skipped
without a word, leaving a request that generated from fewer references than were
uploaded.
Reference marshalling now goes through the same funnel the api path uses, and
runs before the load, so a rejected file costs nothing and says which file and
why. The workflow comes from the registry row, which is where the loader reads
it from as well.
- a rejected input returns its reason to the output box, since the general
handler only reaches the log
- references uploaded against a keyframe workflow warn instead of vanishing:
the accordion hides on a row change but the files it held do not
- guard p.close() in the finally, which the model-not-loaded return has always
reached before p exists