* Keep per-item LATENT session refs when joining mapped outputs
Ordered phases bridge values between phases as remote session refs.
Joining mapped LATENT outputs ran _join_latent_batches on those refs,
which copied the first ref's fields and collapsed every item to item 0.
The next phase then broadcast item 0's latent to all items, so a second
KSampler fed by a mapped first KSampler always refined the first image.
Only merge LATENT values that are real latent mappings; otherwise keep
the ordered list. Log boundary inputs that are broadcast because they
cannot be split instead of dropping the error silently.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cap Modal containers at instance_count across parameter-set pools
Modal enforces max_containers per RemoteEngine parameter set. Remote
components without loaders (e.g. SaveImageAdvanced) had an empty
snapshot profile, so they opened a second pool of GPU containers beside
the samplers and doubled the container count past instance_count.
- Stamp the environment's most common loader snapshot profile onto
profile-less Comfy-pool components so they share one container pool.
- Hold one host-side slot per in-flight Modal call per deployment app,
capped at max_containers, so concurrent mapped components share the
configured budget.
- Lower the per-item unsplittable boundary input log to debug.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The per-item split of zipped mapped inputs only ran inside the Modal
invocation path. SSH and Vast clients received the whole list as one
value, so CLIPTextEncode failed with "'list' object has no attribute
'startswith'". The router now runs the shared mapped orchestration with
the provider client as its invoker, one item at a time on the assigned
worker.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Expect resolved STRING type for ModalMapInput boundaries
69a33e3 made _remote_output_io_type resolve wildcard "*" outputs through
their linked input, so a ModalMapInput boundary fed by a STRING list now
reports STRING, matching the mapped_input type the test already expects.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Add Remote Progressive Preview node
A local PreviewImage-style node that shows one empty slot per mapped item
when remote work starts and fills each slot as soon as that item's images
stream back, instead of waiting for ComfyUI to deliver the whole result.
- Planning records direct consumers as progressive_preview_target_node_ids
and exempts them from local bridge materializers.
- The remote shim streams boundary outputs for those targets; mapped item
payloads carry map_item_count and dispatch emits a start event.
- The host saves streamed images to temp, records them per prompt/node, and
sends modal_progressive_preview websocket events.
- The node reuses streamed slots on final execution and only materializes
bridge refs that were never streamed.
- web/progressive_preview.js renders the slot grid and hands off to the
native preview on the final executed event.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Execute every mapped item instead of reusing shared-session outputs
Mapped items share one remote session. The restored-session short-circuit,
meant for identical per-lane seed phases, looked outputs up by node id
only, so after the first item on a container every later item skipped
sampling and returned that earlier item's image under its own bridge key.
Payloads carrying map_item_index now always execute.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep finished progressive previews handed off to the native preview
Hide the panel with an inline display style so the frontend's DOM-widget
wrapper cannot override the hidden attribute, and ignore replayed events
for a prompt whose final images were already delivered.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Local mode runs rewritten components inside the local ComfyUI process and
exists only for developing the extension, yet it was the default, so users
who did not set the variable silently ran in it. settings now owns the
default (remote) through configured_execution_mode(), and every host-side
reader uses it. The Modal image environment pins local explicitly, as SSH
and Vast workers already do, so container code keeps its in-process
behaviour.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hybrid mapped execution rebuilt its static, mapped, and cleanup phase payloads
from a fixed field list that dropped modal_gpu and modal_max_containers. Those
payloads resolved default settings and expected a different runtime fingerprint
for the same Modal app, so the sibling-validation guard refused them with
ModalRemoteAppOutOfDateError.
Define the fingerprint-shaping payload fields once in settings and copy them at
every derived-payload site (mapped phases, warmup requests, speculative
prewarm targets).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>