It read the prompt length from transformer_options['c_crossattn'], which
ComfyUI never sets, so every block ran the stock forward. A model wrapper now
records it per call. Per-token mod rows (any pinned frame) no longer disable
it, and the step window reads the real schedule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Studio gains an enhanced_prompt output; the link wires to it. Enhancer off or failed sends prompt + postfix unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ComfyUI tokenizers drop an unknown system_prompt kwarg, and Gemma skips its chat template by default. Instructions now go in the user turn of the model's own template, which also places the image.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- region lock: layout patch matched the old PackedLayout signature and crashed every H3 run; interior pins now use upstream's native any-frame placement
- REINS/Q-steer/AV decouple/probes: masks cover the target video/audio rows only, not pins, refs or vision pads
- video detail: works with the PDD FinalLayer signature; now in Engine Settings
- Q-steer: steers queries before RoPE, not after (old banks are not reused)
- ALG anchor: blurs the frame-0 pin on H3
- temporal style: hidden on H3
- shadow negative: main-window chip names what it switches off without compose
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Own tab with its full sampling settings (temperature, top-p, top-k, min-p,
repetition/presence penalty, seed, greedy) and a last-run before/after view.
Studio's source_image now reaches the enhancer on the per-scene path too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sel.onchange only passed the filename, missing the "detail_upsampler" key
setSamplerInputNow(name, value) expects -- so it wrote sampler_inputs["<the
filename>"] = undefined instead of sampler_inputs.detail_upsampler, and the
next render recomputed the visible value from the untouched (still unset)
field and fell back to "auto". Matches the working sibling call one line
below (identity_projector).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
These were force-disabled for a gate bug, not a measured failure: they
all rode _make_steer_ramp's last-half window, which measured 0-of-4 and
0-of-12 real step coverage on H3's shift-based schedules because it read
sigma as schedule position, which is false there. That gate was already
rewritten to recover the true position from the schedule's own base grid
and is wired into all three call sites -- their correction steps are
also self-normalizing (a fraction of the live tensor/embedding's own
norm, not an LTXAV-tuned constant), and trajectory_guidance's buckets
were built for H3's schedules from the start. Re-enabled to actually
test them now that the thing that broke them is fixed.
embed_guidance/taste_nearest_prompt stay off: different value function,
and phrase emphasis's own manipulation check directly measured
near-invisible pushes from it at H3's calibration -- an actual negative
result, not a stale gate.
Unvalidated on real hardware -- this removes a stale reason for
disabling them, not a claim that any of the three help.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous fix (67b1324) put mediaUpload straight into the mediabrowser
zone's fingerprint, so every XHR progress tick did a full rebuild of the
media grid, thumbnails, and project list -- and closed any open <select>
in there (e.g. "Sort by"), the same class of bug just fixed in Engine
settings. Only the file boundary (which file, 1-of-how-many) is in the
fingerprint now; byte-level progress goes through a quiet
updateMediaUploadProgress + a funpack-media-upload-progress event that
mediabrowser.js uses to patch the bar/percent in place, mirroring the
existing updateGenProgress/funpack-gen-progress pattern for the player.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A <select> was excluded from the field-editing protection that shields
text inputs from autosave/notify-driven rebuilds. Any unrelated store
notify while the dropdown was open (autosave tick, a generation poll,
another zone's change) tore down and rebuilt the pane underneath it,
which closes a native dropdown immediately. Selects are now protected
the same way while focused; their own change event still applies right
away so dependent controls don't wait for blur.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mediaUpload changes on every XHR tick but wasn't part of the mediabrowser
zone's ViewBus fingerprint, so real ticks hashed identically to the last
paint and were skipped -- the bar only ever appeared to update via some
unrelated repaint (e.g. clicking a thumbnail changes mediaPreviewId, which
IS tracked).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Uploads now go through XHR instead of fetch so byte-level progress is
visible; the drop zone shows a bar, percent, and filename instead of
looking stale while the backend receives the file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The media bin's grid always requested the FULL original file for a
thumbnail (an <img>/video-frame decode of the whole thing, only
cropped down visually via CSS/canvas afterward). A 4K photo or a
multi-MB clip got fully downloaded and decoded just to show a 96px
square, every render.
Added GET /api/media/{mid}/thumb: generates a small JPEG on first
request (Pillow resize for images, one ffmpeg frame-grab for video),
caches it under media/thumbnails/, and serves that instead. Deleting
a media item removes its cached thumbnail too. mediabrowser.js now
loads from this endpoint for both image and video kinds -- the same
canvas-drawing path as before, just fed a file that's already small
instead of the original. Measured live: a 1.57MB test clip's cached
thumbnail is 1.6KB.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
generate()/generateMontage()/generateSelected() awaited a full project
save (and a separate models save) before doing anything visible --
both are plain network round-trips that can retry for tens of seconds
on a flaky connection (_retryOnTunnel), and nothing set a gen state
until they finished. Clicking Generate on bad internet looked exactly
like the app had frozen: no disabled button, no message, no feedback,
for however long those saves took.
The wait existed because /api/projects/{pid}/generate read the
project BY ID from disk (_project_or_404) -- if the disk copy was
stale, Generate would silently build a graph from old data. Fixed at
the actual source instead of papering over the wait: the endpoint now
also accepts the current project inline in the request body (the same
shape /api/projects/{pid}'s own PUT already takes) and persists
exactly that payload before generating from it, atomically, in one
step. The client sends its live in-memory snapshot instead of relying
on whatever the last autosave wrote. body["project"] stays optional
so any other caller keeps the old disk-read behavior.
Adversarial review (two rounds) found this reopened a narrower race:
the generate handler's own existing tail already saved the project a
SECOND time afterward (to record the queued prompt's hash), so an
ambient autosave landing between this request's two internal writes
would get silently clobbered by the second one. Closed by suspending
the client's ambient autosave (suspendSave/resumeSave, an existing
primitive) for the exact span of the generate request, and by waiting
out any autosave that was already in flight before generate started.
Also moved _markGenInFlight to fire right before the actual request
instead of before that wait, so a scene deleted during a brief
already-in-flight wait isn't ghosted for a request that hadn't
actually been sent yet.
Verified live end-to-end: an edit made only in memory (never
autosaved) reached disk through the generate request itself and
survived a concurrent pending edit through the suspend/resume window,
confirmed via direct reads against the running dev server.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Image thumbnails appended the raw <img> (full-resolution decoded
bitmap held in memory for the card's lifetime, just to be shown at
56-96px via CSS crop). Video thumbnails already snapshotted to a
canvas but sized it at the source's native resolution. Neither ever
downscaled anything -- a bin of real photos/4K clips kept every one
of them fully decoded in memory at once.
Both paths now draw down to a capped 320px long edge before caching
(_thumbScale), same visual result, far smaller resident bitmaps. Also
drops the img.draggable=false workaround the old <img>-based image
thumb needed to stop the browser's native image drag from hijacking
card drag-and-drop -- a <canvas> was never draggable to begin with.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
_select_best_seed called value_fn.forward()/compress() directly on a
candidate's captured prediction with no device handling -- the value
function loads CPU-only (map_location='cpu'), but the candidate tensor
lives on whatever device the model ran on (cuda in production). Every
candidate failed to score on every real GPU generation since this
feature shipped; _select_best_seed always fell back to the unchanged
seed, silently a no-op the entire time despite passing all tests
(the suite is CPU-only, so a mismatch never had two devices to hit).
Every other value_function.py call site (gradient/ascend/search) moves
its input onto next(self.parameters()).device first -- this was the
one path that skipped that. Mirrored it: compress on the candidate's
own device (cheap reduction), then move the small compressed vector
onto the value function's device before scoring.
Added a regression test using a real second torch device (mps on this
box, cuda in CI/production) to reproduce the exact cross-device
failure, and fixed three pre-existing value_fn test doubles that were
missing parameters() and had silently started hitting this same
crash branch once the fix started calling it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Clear references only ever sat above the grid, so clearing refs on a
large bin meant scrolling all the way back to the top first. Made
.media-bin-actions sticky within the bin's scroll container, same
pattern already used for the (currently unused) bin-tabs-sticky class.
z-index bumped to 4 to clear each card's kind badge (z-index 2), which
otherwise ties and wins on DOM order, painting over the sticky bar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
parseComboSweepConfig in store.js already parses an av:penalty segment
(h3_av_decouple attention penalty) but the panel's own hint text and
placeholder never mentioned it -- looked like an undocumented, maybe
broken feature.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Compose-mode message and Editor tooltip were stating a false remedy
(turn REINS down to passive-capture-only/strength 0 to free blocks) --
REINS claims all 50 blocks the moment it's on at all, strength only
gates injection not installation. Corrected to say only fully
disabling REINS frees any blocks.
_select_best_seed had three silent failure paths (no usable
prediction, a scoring exception, zero candidates scored) that burned
real GPU compute and reported nothing. Added prints to all three.
Added _output_value_fn_sample_count so "not ready yet" messages show
the actual banked sample count instead of leaving the user blind on
how close they are to the threshold.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adversarial review of 77cf37e found a real, cheaply-closable test gap: this
file already calls node.sample() end-to-end with a monkeypatched value
function for the explore_first_step tests, so the same pattern (plus
spoofing minimax_h3.is_h3_model) proves the snapshot save is no longer
skipped for H3.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
explore_first_step reads the same output-space value function
output_guidance trains, but training/snapshotting was gated off for H3
entirely -- a decision made before explore_first_step existed, when
output_guidance (still correctly forced off there) was its only consumer.
Result: the value function could never reach MIN_SAMPLES on H3 no matter how
many generations were rated, so explore_first_step printed "not ready yet"
forever. Un-gate the snapshot save and the training call for H3; leave
output_guidance's own application (and the separate conditioning-payload VF
for embed_guidance/value_guidance) forced off as before -- that decision is
unaffected.
No new automated test: no existing fixture exercises this rating-commit code
path (FunPackVideoRefiner.run's post-scene training block) cheaply enough to
extend honestly; verified by full suite pass (1491/1491) and direct tracing
that no other call site re-introduces the gate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
h3_shadow_negative's non-composing install was silently starving REINS/
Q-steer of their own per-block capture, so ratings never trained them while
shadow negative was on (0/0 liked/disliked no matter how much was rated).
Add h3_shadow_negative_compose ("Allow when REINS/Q-steer are active"): when
on, shadow negative skips any block another dit-patch mechanism already
claimed, leaving it to keep capturing/steering there, and only pushes on the
rest. Structural coexistence, not per-block math blending -- disclosed as
such in the docstring and tooltip. Off by default, unchanged behavior.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Native port of the community "H3 Shadow Negative" pack's Basic scope: runs the
negative CONDITIONING (already threaded through the sampler, otherwise dead
weight since H3's CFG is fixed at 1.0) through H3's DiT blocks as a second
shadow stream and pushes the real stream's attention output away from it,
NAG-style. Non-composing with REINS/Q-steer/av_decouple/block-repeat/
attn_temperature by design -- replaces a block's forward outright and prints
which existing patches it superseded.
Full Editor (Cutting Room) UI exposure lands in this same change, along with
explore_first_step's own missing Editor UI from an earlier session.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Tries N candidate seeds with one throwaway denoising step each, scores
each with the trained output-space value function, commits to the
best-scoring seed for the real full pass. Candidates are scored fully
isolated from the scene's own guidance stack (model_function_wrapper
chain, context_windows, v2a_grad_scale, identity_overlap -- each torn
down and reinstalled around the candidate-scoring window) against the
scene's fully-finalized conditioning. Any failure in the value
function, schedule, or the isolation cycle itself degrades to a
silent no-op, declared in the run report, never a crash.
Also hardens _install_context_windows/_install_v2a_scale to roll back
partial state on failure (a leaked context_handler key was silently
re-enabling windowing for later scenes) and fixes the original
non-explore v2a install call site to degrade like its sibling
mechanisms instead of aborting the render.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Corrected the actual design: encode two FULL conditionings ($style
SHORTCUT1 $postfix and $style SHORTCUT2 $postfix) and average them row by
row -- (1-strength)*A + strength*B per position, strength=0.5 giving
exactly (A+B)/2 as originally specified. The prior mean-vector version
collapsed phraseB's whole sentence into one flat direction applied
identically everywhere, discarding its own word-by-word content.
Resamples the shorter side (linear interpolation along the token axis)
when the two shortcuts tokenize to different lengths, so both sides line
up for the average without changing the output's token count.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Confirmed on the rental: the previous version (phrase-scoped shift) showed
zero visible effect even at strength 1.5. Qwen's rows are contextual, so
every word in "a cat sits on a mat" already carries "sits", not just the
verb's own row -- editing 1-4 rows out of the sentence left the rest still
pointing at phraseA's action. Now shifts the whole prompt-token range toward
the alt sentence's own whole-prompt mean.
Multiple [a|b] spans in one prompt are additive: each span's delta is
measured against the original conditioning, not against a prior span's
already-shifted result, so they don't compound order-dependently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Default 0.5 shift left phraseA dominating the video. [phraseA|phraseB:N]
overrides strength per blend. Fixed a shared regex gap along the way: every
weight/strength markup in this file (not just blend) silently rejected
leading-dot decimals like ".8" -- fell through to literal text with no
error, reproducing the exact "override did nothing" bug this feature exists
to fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A real shortcut expanding to "wide shot: character walks" made [a|b]
never match at all, no error, because the previous fix banned ':' and
'@' anywhere in either phrase to stop one dangerous shape (an alt
phrase ending in ":1.5"). Two narrower shape-guards were tried and each
let a different shape through (colon-then-@, an em dash) while still
blocking ordinary prose. Reverted to fully permissive: a markup-shaped
alt phrase just reaches Qwen as literal punctuation in its own separate
re-encode, the same accepted trade-off this file already makes for a
weighted phrase's own stray parentheses -- a quality footnote, not a
leak, confirmed by tracing the encode path and its existing exception
handling.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adversarial review of the previous commit found two real leaks and fixed
them: a colon in an alt phrase reached the real text encoder unstripped
(no bracket survives to re-catch it after substitution), and a wired
conditioning re-blended a second time because the re-tag guard only
checked for tag absence. Also reports per-span placement failures instead
of only counting successes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Measurement only, opt-in from Settings > Refinement & Taste. Reading map is free
(attention share on the phrase's keys per block); response map costs one extra
forward per phrase per model call. Phrases are numbered, never quoted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A latent unsharp on H3's 24-channel latent does not translate to picture
sharpness. Wrong mechanism, not wrong number; removed rather than tuned.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The parser existed but nothing called it; the syntax went to Qwen as text.
One pass strips both markups so timed and weighted spans share one clean text;
both encode paths carry them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>