63 KiB
Architecture
Runtime overview
ComfyUI startup
|
+-- prestartup_script.py
| `-- disables built-in Partner/API nodes for this distribution
|
`-- __init__.py
|-- imports public node classes from nodes/
|-- publishes NODE_CLASS_MAPPINGS and display names
|-- registers /o1key/* HTTP routes
|-- registers the parallel image-job manager
`-- exposes web/ through WEB_DIRECTORY
Node execution
nodes/ --> clients/ --> O1Key/provider HTTP APIs
| |
`----------> utils/ --> config, uploads, retries, media, polling, jobs
Browser UI
web/js/ --> /o1key/* routes --> ComfyUI input/output/temp storage
Dependency direction should remain one-way: frontend talks to registered routes; nodes orchestrate; clients own provider protocols; utilities own reusable infrastructure. Clients and utilities must not import node modules.
Repository boundaries
Plugin integration
__init__.py is the public integration surface. It owns:
- canonical node registration;
- display names;
- frontend exposure through
WEB_DIRECTORY; - server routes for configuration, cases, notes, history, chat, prompt optimization, element management, restart, safe updates, and image jobs.
Keep route registration guarded so an unavailable optional ComfyUI service does not make all node imports fail silently. When changing this file, run the plugin import smoke test.
Nodes
nodes/ contains a mixture of legacy V1 classes and V3 io.ComfyNode classes. A node is public only when it is exported from nodes/__init__.py and added to the root mappings. Module-local mappings are not sufficient.
Released node IDs and widget order form a persistence contract with saved workflows. Use web/js/migrateWorkflow.js when schema evolution changes positional widgets_values.
O1keyAutoRedCast remains a V1, deterministic local colour-correction node. Its native seed input is the final optional widget, enables ComfyUI's control-after-generate behavior, and changes the node's cache input; it does not add random sampling to the colour algorithm. Frontend migration appends default 0 to old six-widget workflows and moves the interim seven-widget layout's seed after both gray-card values.
O1keyPromptMultiFunction keeps its released node ID and its original 提示词 / 功能 widget positions. Its visible random mode is unified as 随机抽取n套; workflow migration rewrites legacy 随机抽取1套 to that value with count 1, and rewrites the interim 随机抽取多套 value while preserving its saved count. Backend aliases retain compatibility for API workflows that bypass frontend migration. Multi-selection remains append-only: 抽取数量 and 指定序号 occupy the next two positions, and the workflow migration supplies defaults 3 and 1,2,3 to older graphs. promptMultiFunctionDynamic.js changes only widget visibility: random mode shows the count, explicit mode shows the index field, and all mode hides both while retaining their serialized values. Random selection samples without replacement and restores source order before joining with standalone --- lines; explicit selection uses one-based indexes and preserves the order written by the user.
Provider clients
Omni Flash follows the SeedanceAutoPass native execution pattern. O1keyOmniFlashVideo.execute validates scalar and media inputs, uploads connected IMAGE/VIDEO values through the shared uploader, then uses clients/omni_flash_client.py to submit, poll, and download on the selected O1Key network route. Ordinary generation always submits omni_flash_10s; video editing selects the dedicated omni_flash_abra_edit model. The model is not a node widget. The frontend workflow migration removes the retired model value at widget index 2 from saved Omni Flash nodes before the new schema loads. The node returns a fresh fingerprint_inputs value for each queued execution so ComfyUI does not reuse a prior paid generation result when the same node is run again with unchanged inputs. Both upload and generation read the existing O1KEY_API_KEY from .config. The client reports the provider's task percentage to the node's native ComfyUI ProgressBar; repeated or older values cannot move it backwards, and 100 is reserved for a fully saved video. It returns only native VIDEO, with no node-local preview payload. The frontend changes visible media sockets with the generation mode, suspending Autogrow while removing inactive sockets; the 开始生成 button queues this output node through ComfyUI. No dedicated server route, result node, job history, or URL widget is involved.
The client normalizes top-level and nested task IDs, statuses, progress, and result URLs. It polls through unrecognized nonterminal statuses until the deadline, maps documented API error codes to user-facing messages, and inspects the content endpoint before streaming so JSON download links are not saved as video bytes. A result URL from the status response is used if the content endpoint cannot serve the file. Submission, poll, and text download response bodies are printed to the ComfyUI terminal with secret fields, URLs, and large media strings masked; binary video bodies are never printed. Credentials and signed URLs are removed from surfaced error text.
Only omni_flash_abra_edit task creation adds the X-No-Watermark: video header. Polling and content requests retain the regular authentication header.
clients/ owns request construction, provider endpoints, polling protocols, and response normalization. Its package exports are lazy so importing one provider does not initialize all providers. Video task polling uses the shared 2,000-second deadline unless a caller explicitly supplies a different value.
The MiniMaxH3Video node keeps its released node ID and original first four
inputs. Its append-only 模型 widget selects MiniMax-H3 or
MiniMax-H3-MAX, followed by an append-only native seed widget. Workflow
migration supplies MiniMax-H3 and seed 0 to older saved graphs. The seed is
validated as an integer and passed unchanged in the provider request. Backend
validation owns the authoritative model-specific resolution, duration, mode,
and aggregate reference-count rules, while the frontend guard updates the
visible resolution and duration constraints and prevents H3 Max from selecting
reference mode.
The MiniMax client creates tasks through /v1/video/generations and queries
them through /v1/videos/{task_id} every 10 seconds. New API's documented
post-submission unknown status is normalized as a pending state and remains
bounded by the shared 2,000-second polling deadline. Completed results prefer
result_url and retain compatibility fallbacks for wrapped gateway and official
V2 response shapes before the temporary CDN file is downloaded.
Grok Video uses separate create endpoints for generation, edit, and extension:
/grok/v1/videos/generations, /grok/v1/videos/edits, and
/grok/v1/videos/extensions. Every operation then polls
/grok/v1/videos/{request_id} and downloads video.url only after a done
state. clients/grok_video_client.py owns the operation-specific payload
whitelists and model capability checks. The V1 nodes validate with placeholder
media locators before uploading local IMAGE, AUDIO, or VIDEO values, so an
invalid model, duration, resolution, mode, media count, or edit clip length
cannot consume an upload or paid generation request. Saved workflows retain the
O1keyGrokVideo ID and are migrated according to ADR 0006.
Shared utilities
utils/ contains cross-provider infrastructure:
config.py: atomic.configreads and writes plus route resolution;http_error.py: retry classification and friendly errors;http2_client.py: HTTP/2 with an aiohttp fallback;image_utils.pyandfile_utils.py: media conversion and file pairing;r2_uploader.py: temporary public media upload;video_task.py: interruption-aware polling and downloads;nano_banana_async.py: Nano Banana asynchronous lifecycle;o1key_image_catalog.py: canonical capabilities for the unified image generator;o1key_image_jobs.py: isolated parallel job snapshots, model-family dispatch, and results.o1key_image_save.py: original-byte preservation, format conversion, workflow metadata, and output naming forO1keyImageSave.reference_color_correction.py: bounded reference-guided chroma correction retained by the GPT Image batch node.
Like clients, the utils package uses lazy exports to reduce startup work.
Frontend
Every JavaScript file in web/ is served as a ComfyUI extension. Major responsibilities include settings, chat, cases, notes, element management, workflow migration, upload helpers, previews, painting, trimming, and the panel-style image generator.
web/js/o1keyUpdateButton.js places an Update button directly below the Token Manager button in the left toolbar, before Restart. Opening the Update dialog immediately calls GET /o1key/update/check and asks for confirmation only when a newer version is available; confirmation calls POST /o1key/update. Both routes delegate to utils/updater.py and share a lock. The updater fetches the public main branch from https://git.o1key.com/publisher/comfyui_o1key.git without changing the user's origin. It reports an already current installation before checking local modifications, and only fast-forwards a clean local Git main when an update exists. Local tracked changes, divergent history, and file collisions receive structured error codes; the UI maps those codes to customer-facing messages without exposing the repository or Git details. It never resets or cleans the worktree. After a successful update with unchanged requirements, the frontend invokes the shared web/js/o1keyRestart.js flow, waits for a new process boot ID and a ready system endpoint, then reloads the page. A requirements change still asks for maintenance before restart; an automatic restart failure leaves the manual Restart button available.
The updater dialog keeps the check result, update confirmation, progress, and retry actions in one ComfyUI-styled modal. Opening it runs the check; a newer version changes the primary action to “立即更新”, while “稍后再说” closes without updating. Because an update runs in the old frontend and server process, installations upgrading from a version without automatic restart must restart manually once to load this behavior.
Because the directory is auto-loaded, unused or experimental JavaScript must not be left here.
O1keyVideoTrim keeps its released widget order and uses 视频路径 only as an
internal serialized value populated by the upload control. web/js/videoTrim.js
hides that backend widget through the supported Nodes 2.0 options.hidden
flag, without assigning a negative widget height. Uploaded files below the
configured ComfyUI input root are previewed through the native /view route;
other absolute paths retained by old workflows are never exposed through a
browser file route. Numeric widget callback wrappers must preserve ComfyUI's
receiver, argument list, and return value so Nodes 2.0 can render and edit the
controls safely.
O1keyImageSave preview invariant
O1keyImageSave uses ComfyUI's native image preview when every requested image succeeds. While a panel batch is active, a single DOM slot grid reserves the exact expected image positions. Each provider result is published to that grid immediately after its complete file has been written to ComfyUI temp; permanent promotion and native-output dispatch still wait for the terminal batch result. If the batch partially fails, that grid remains as the sole visible preview so successful images retain their request positions and failed positions remain individually actionable; the native preview is hidden during this state, never duplicated above or below it. Once all slots succeed, the temporary slot widget is removed from node.widgets and the native preview becomes the sole result renderer again. Removing it is required because ComfyUI treats every DOM widget row as expandable; merely hiding the grid element would leave node-widgets at flex: 1 and consume half of the node's extra height above the preview.
Ordinary image batches map slots by request_index, using the first returned image for each request while preserving every valid provider result in the native preview. A provider request that unexpectedly returns multiple images therefore cannot leave unrelated slots stuck in a running state. Result cardinality is treated as independent from request cardinality only when layer decomposition was explicitly enabled; it is never inferred solely because result_count exceeds request_count. Workflow loading reconciles stored result descriptors back into stale pending or running slots, which repairs state serialized by older frontend versions without starting another generation request.
The replacement regeneration control inherits the native preview button geometry and uses a white surface with a black refresh icon. Generation progress remains a thin absolute overlay without percentage text. The slot grid is the only layout-reserving addition and exists specifically to make batch cardinality and per-image failure explicit; its height is derived from its measured content box without duplicate bottom padding, and it scrolls for large prompt batches. Once native results are visible, the save node recomputes its initial preview height from the loaded image dimensions so landscape, square, and portrait results do not inherit the placeholder batch height. After that initial fit, ComfyUI's Vue NodeContent and ImagePreview remain the sole layout authorities: their native flex-auto, minimum preview height, element-size observation, responsive grid, and object-contain rules make the preview occupy the remaining node area during resize. The extension must not override those native flex/min-height rules or mutate the legacy canvas preview widget from a resize hook. Save-node sizing otherwise follows the native SaveImage node without an o1key-specific permanent minimum-size clamp.
The save node persists sanitized ComfyUI image descriptors in properties.o1keyImageSaveResults and bounded slot state in properties.o1keyImageSlots. Slot state contains request order, prompt text, sanitized ComfyUI input descriptors for that exact task, status, compact error text, and an optional sanitized output descriptor; it never contains credentials, Base64, signed URLs, or local paths. The input descriptors let a failed source/target pairing be retried after the panel manifests change or the workflow is reloaded. When a saved workflow is loaded or the page is refreshed, completed descriptors are replayed into ComfyUI's native executed-output store and an incomplete slot grid is restored without starting a generation request.
Provider results from panel-triggered background jobs are written to the root of folder_paths.get_temp_directory() with their detected PNG/JPEG/WebP extension. When original bytes are available they are written without pixel re-encoding. The /o1key/image/save route accepts only batch-bound type=temp descriptors, and O1keyImageSave alone promotes them into the configured permanent destination. A blank location uses folder_paths.get_output_directory(), a relative location stays below that root, and an absolute location is used as an explicit external destination. External saves return a path-free type=temp preview copy below o1key_external_preview/<uuid>/, because ComfyUI's native /view endpoint cannot serve arbitrary filesystem roots. A live job record replaces its provider descriptors with the final output or preview descriptors under a per-record save lock, making browser-refresh and concurrent recovery saves idempotent. Disk recovery prefers root-level output filenames over matching temp files, then checks the legacy output/o1key_parallel/<date>/<batch-id>/ layout.
Workflow-bearing saves follow ComfyUI's native SaveImage metadata contract: the execution prompt is stored under prompt, and the serialized graph supplied through extra_pnginfo is stored under workflow. Standard V3 execution reads both values from the executor-provided class hidden holder. Panel execution obtains both from one app.graphToPrompt() call so the API prompt and workflow describe the same graph snapshot. ComfyUI's image metadata loader restores PNG and WebP workflows but does not parse JPEG workflow metadata; JPEG EXIF also has a practical single-segment size ceiling. Therefore any JPEG target carrying a workflow is promoted to PNG and embeds the native text fields without truncation. When ComfyUI's global metadata switch disables metadata, format selection remains unchanged and no workflow is embedded. This compatibility decision is recorded in ADR 0004.
O1keyImageSave now has only its images input and forwarded IMAGE output. It remains the sole component that writes permanent files and renders results, but it receives save settings from its connected O1keyImageGenerator: direct execution carries a validated _o1key_save_settings tensor attribute, while panel jobs snapshot the same values in the server-side job record before generation. The generator owns append-only inputs 命名规则, filename_prefix, 格式, and 保存位置 at indexes 17 through 20. 命名规则 defaults to the serialized compatibility value 自定义前缀, displayed as 自定义; filename_prefix defaults to o1key. 和主图一致 uses the first reference-image stem, and 自然数字 allocates the first free integer filename. Every strategy checks under a process-wide lock and never overwrites an existing result. Save locations accept blank/output-root, safe output-relative subfolders, or normalized absolute directories; ambiguous drive-relative paths and relative parent traversal remain invalid.
The generator's local 格式 input defaults to 原始 and is visible only for Nano Banana models. Explicit PNG and WebP conversions are lossless; JPEG uses quality 100 and 4:4:4 subsampling but remains intrinsically lossy. 原始 preserves provider bytes when valid and falls back to PNG after pixel changes, when bytes are unavailable, or when a JPEG result must carry a ComfyUI-restorable workflow. GPT Image and Seedream ignore this local conversion input and otherwise promote their provider result as 原始; GPT's separate 输出格式 API parameter accepts jpeg / png / webp, while Seedream accepts jpeg / png. Transparent GPT backgrounds exclude JPEG before the paid request.
Unified image-generator model dispatch
O1keyImageGenerator is the stable public node ID for the panel-style multi-model generator. Its original nine input IDs and positions remain unchanged; GPT-specific inputs and the 缩放图片 widget are append-only, and the workflow migration fills their defaults without shifting old positional widgets_values. The existing 输出格式 widget remains at index 10, 背景 remains at index 13, and the retired 内容审查强度 widget is removed by an idempotent positional migration. Batch inputs occupy indexes 14 through 16. Generator-owned save inputs occupy indexes 17 through 20 in the stable order 命名规则, filename_prefix, 格式, 保存位置. New GPT panel selections default index 10 to png and smart resize; legacy workflows retain saved values. The standalone O1keyGPTImage and O1keyGPTImageBatch node IDs remain registered for saved-workflow compatibility.
The panel displays model-route labels as 特价 / 优质 / 企业, but serializes and submits the established internal values 畅速 / 直连 / 专线. This label/value separation is mandatory: changing the serialized values would require a workflow migration and provider-matrix compatibility work.
The panel's prompt remains a socketless, serialized widget value edited in the node. The removed external_prompt input is not part of the V3 schema or execution signature. Before loading an older workflow, the frontend migration removes that input and its exact graph link from the generator, the link table, and the source output while retaining the saved panel prompt text. It is idempotent and applies inside subgraph definitions as well as the root graph.
The unified generator accepts at most ten references in one provider request and offers GPT Image counts 1–8 (retaining saved 9-image jobs) and other-model counts 1 / 2 / 4 / 9. Batch source and target manifests may each contain up to fifty images when the active pairing mode sends only one source or target per request; group mode still caps its source manifest at nine because it appends one target to the same ten-reference request. A prompt field containing --- on a line by itself expands into prompt-major tasks. The unified boundary rejects more than 1000 tasks before any paid request, and background/direct GPT and Seedream execution keeps at most nine provider requests in flight per batch. Every GPT Image and Seedream request sends n=1; the outer scheduler owns concurrency and partial-result isolation.
The generator's canvas-image picker reads only image descriptors already exposed by nodes in the current graph through app.nodeOutputs, native preview URLs, or persisted O1keyImageSave results. A selected input, output, or temp descriptor is fetched through ComfyUI's /view route and immediately re-uploaded through the native /upload/image route as a new type=input reference. The picker never sends an output or temp path directly to the background-job API; therefore job validation, batch-owned snapshots, refresh recovery, filename collision handling, and input-root containment keep one shared transport contract. This is a frontend convenience and adds no node inputs or serialized workflow fields. Reference tracks use /o1key/image/thumbnail for bounded 256 × 256 WebP previews with at most two concurrent server-side decodes and browser caching. Pending uploads render a placeholder rather than decoding their local full-resolution blobs. The lightbox alone uses the original /view descriptor, and prompt optimization plus provider requests continue resolving the untouched original input file.
New reference, source, target, and mask uploads use ComfyUI's native /upload/image route with type=input and no subfolder, so their descriptors point directly at the configured input root. All o1key uploads in the browser share one serial queue: this lets the native non-overwrite allocator append its natural-number suffix for duplicate names without two concurrent requests racing for the same path. The frontend persists the actual name, empty subfolder, and type=input returned by the server. Resolvers must continue accepting non-empty subfolders so saved workflows that reference the legacy input/o1key_uploads/... layout remain valid; existing files are not migrated or deleted.
Reference, source, and target thumbnails expose a bottom-right replacement action that uploads one local image through the existing non-overwriting queue and swaps only the original manifest entry after success. The original image and order survive validation or upload failure. The same thumbnails expose the browser-only image editor from their top-left action. web/js/o1keyReferenceImageEditor.js owns its modal, crop geometry, pointer drawing, single upper sticker layer, vector-arrow annotations, undo history, and PNG composition. Fixed aspect-ratio presets create the largest centred crop and allow repositioning; free mode also allows a new crop or corner resizing. The visual mask brush, coloured annotation brush, and arrows are flattened into the exported pixels and never become a ComfyUI MASK value. An arrow stores its exact source-coordinate start and end points; its tip is the pointer-release position, and its filled head scales with line width. A sticker is read from a temporary browser object URL, initially fitted and centred, and represented in source-image coordinates by centre, dimensions, rotation, and opacity. Its selection border, corner scale handles, and rotation handle are interaction chrome only. The source object URL is revoked when the editor closes and is never serialized or uploaded independently; only the flattened final pixels leave the modal. Sticker pixels are composed above the base image, then arrows and brush annotations are composed above the sticker so positional guidance stays visible. Applying an edit creates a non-overwriting PNG through the existing serialized /upload/image queue and atomically replaces that exact manifest entry; it does not overwrite the source file, add a workflow field, or change provider transport. If the entry is removed while the modal is open, the exported file is not attached to another position.
Optional source/target batching adds a pairing dimension without changing normal-mode task expansion. The panel calls the two roles 素材图 and 目标图; these terms cover objects, elements, styles, materials, structures, or any other source content applied to a destination image. The legacy serialized value 一组搭配+多模特 treats all uploaded source references as one ordered group and appends exactly one target reference per pairing; because a provider request still accepts at most ten references, that mode allows nine source images plus one target image. The legacy value 全部搭配×全部模特 creates the source-major Cartesian product and sends exactly one source plus one target per task, allowing ten uploaded sources and ten uploaded targets to produce 100 pairings. The appended value 单图素材批量 creates one task per source image, sends that image as the sole provider reference, ignores the target manifest, and hides the target lane in the panel. This supports changing poses or expressions across several model images without an additional comparison reference. The complete order is prompt-major, then source/target pairing or source index, then copies for 每组生图数; the 1000-task ceiling applies after all dimensions are expanded. Background jobs snapshot only the manifests used by the selected mode, retain reference indexes in the task plan, and resolve the exact references immediately before each paid request. Direct V3 execution uses the same task-plan helper. GPT masks are rejected while batch generation is enabled because a single edit mask cannot safely describe multiple changing reference sets.
The panel exposes both manifest identity and provider-request position on every batch thumbnail. The group mode labels sources as request images 图1...图N and every target as 图N+1 in its own request; Cartesian mode always labels the current source as 图1 and current target as 图2; single-reference mode labels every source as 图1 because it is the sole reference in its request. Historical widget names and the first two batch-mode values remain unchanged for saved-workflow compatibility.
O1keyImageGenerator.execute is a native async V3 execution method. GPT Image and Seedream await their asynchronous clients directly, while the legacy synchronous Nano Banana adapter runs in a worker thread; none of these paths may create a nested event loop inside ComfyUI's executor. Before a standard top-level or selected-output queue is serialized, the frontend routes each unified generator to its first connected O1keyImageSave without persisted/native results and creates a new save node only when no blank destination exists. Other connected save branches are removed from that prompt payload without changing their workflow nodes, modes, stored result descriptors, or previews. Queue reservations are attached to the selected save node: global ComfyUI execution-start callbacks activate the save-node progress indicator only for that destination and keep the generator panel idle, matching panel-triggered background jobs. A selected downstream branch preserves its exact upstream save node so the new IMAGE output remains executable. The panel's own background queue uses the same blank-first allocation rule. Unified result downloads have no separate semaphore or concurrency ceiling: every ready task enters its download immediately, while the generation scheduler still bounds active provider tasks. Unified image jobs omit the retired moderation parameter, including when a legacy request still supplies it.
Panel-triggered batches use /o1key/image/jobs rather than ComfyUI's native prompt executor. The scheduler exposes a one-based position among waiting batches and emits queued, running, completed, failed, and cancelled states together with total/success/failed counts and structured failed request indexes. A frontend bridge merges those batch records into ComfyUI's public jobs API results so they appear as independent items in the native top-right task queue and completed history; each o1key row receives a total-count button that reuses ComfyUI's native secondary/medium button and asset-stack utility classes without a plugin-owned visual CSS implementation, while native single/bulk cancellation is routed to the matching o1key batch endpoint. Activating the count delegates to the native task-row result viewer. Terminal summaries are atomically indexed in <ComfyUI user directory>/o1key/image_job_history.json, capped at 200 entries, and exposed through GET/POST /o1key/image/jobs/history. On its first native-history request, the frontend hydrates up to 64 recent summaries into the bridge; native single deletion and clear-history operations update both the in-memory bridge and disk index, so removed entries do not reappear after restart. The persisted schema deliberately excludes prompts, manifests, provider payloads, absolute paths, credentials, Base64, and signed URLs. The generator panel stays idle and its primary action remains available for further submissions, preserving scheduler concurrency. Queue clearing cancels waiting o1key batches without interrupting already-running ones. POST /o1key/image/jobs/{batch_id}/cancel is idempotent for terminal records and cancels both semaphore-waiting and active local tasks when explicitly selected; cancellation cannot retract a provider request that was already accepted upstream. Batch executors keep normal-mode references and the bounded group-mode source set reusable, but load large Cartesian, single-reference, and group-target manifests only for the active task. Reference preprocessing is ordered and bounded before concurrent provider calls, and task-local PIL images/tensors are released as soon as encoding or provider retrieval no longer needs them; this changes lifetime only, never source pixels or request ordering.
The frontend batch registry stores immutable generator/save node IDs separately from live node objects. Workflow unload removes only the stale object references while polling and terminal details remain registered. Events are applied only when the matching IDs resolve to the exact node instances in app.graph; an existing registry entry is the batch-binding authority during concurrent failed-slot retries, while the serialized single batch property remains the restart-recovery fallback. A save node tracks active, saving, and terminal background batch IDs independently. Different failed slots can therefore submit against the same save node concurrently, update only their own slot, and finish independently; the node and generator remain busy until the last active retry batch terminates. Returning to the workflow rebinds terminal details to its current node instances, and a full browser refresh reconstructs the latest registration by querying /o1key/image/jobs/{batch_id} from the serialized save-node batch identity. This prevents off-screen nodes from receiving results or serializing metadata from the wrong active workflow. The native save preview uses the complete remaining node content area and recomputes its canvas-widget height on every resize; images retain object-fit: contain semantics.
Nano Banana reference images are submitted directly in the generation JSON as images[].inlineData. Each item contains raw base64 in data (without a data-URL prefix) and an explicit mimeType derived from the encoded PNG or JPEG byte signature. The unified generator must not upload these references to obtain a temporary public URL. Concurrent output requests reuse the same encoded payload.
Nano Banana has no output_format request parameter. Its completed-image byte signature is authoritative, so a PNG response remains PNG and a JPEG response remains JPEG until O1keyImageSave applies the generator's Banana-only local 格式 conversion. GPT Image's output_format is a provider API parameter accepting jpeg / png / webp. Seedream's output_format is also a provider parameter but accepts only jpeg / png; its request always includes watermark: false, with no serialized watermark widget. The local 格式 value is ignored for both provider-format model families.
Seedream references use the documented POST /v1/o1key/uploads endpoint on the same globally selected base URL as generation. The multipart request contains only the file field and bearer authentication; the returned HTTPS URLs retain manifest order and become Seedream's images string array. Generation submits dola-seedream-5-0-pro-260628-ep to POST /async/v1/generateImage, then polls GET /async/v1/tasks/{task_id}. o1key_image_catalog.py maps Seedream's explicit 1K / 2K choices and supported aspect ratios to documented exact WIDTHxHEIGHT values because the provider request has no separate aspect-ratio field. The default 智能 choice omits size and delegates sizing to the provider. Its panel capability exposes only the existing API 输出格式 control, restricted to png / jpeg; quality, background, moderation, mask, resize, and local 格式 stay hidden, and no serialized watermark widget is added. Seedream reuses the same idempotent task-query recovery, result validation, download retry, and node-wide error normalization as the other unified image families.
Seedream reference validation mirrors the current Volcengine per-image contract at both entry points. Normal image-generation references must have both dimensions greater than 14 px, an inclusive width/height ratio of 1/16~16, no more than 36,000,000 pixels, and an exact temporary-upload payload no larger than 30 MiB. Layer decomposition instead requires 262,144~36,000,000 pixels with the same ratio and byte ceiling. Browser-selected files are rejected before ComfyUI's native upload; direct execution validates every converted reference before creating concurrent provider tasks; background jobs validate all source and target manifests before making their immutable snapshots. SeedreamImageClient repeats the check immediately before /v1/o1key/uploads, so no caller can reach a reference upload or paid generation request with an out-of-contract image. The shared uploader normalizes unsupported source containers to PNG, so the provider receives only JPEG or PNG bytes while saved workflow descriptors and node inputs remain unchanged.
Seedream layer decomposition is an append-only mode on O1keyImageGenerator. Widget index 23 stores 图层拆分=false for old workflows. Enabled mode accepts exactly one reference, one provider request, optional prompt text, 智能 / 1K / 1.5K / 2K, and PNG output; batching is rejected before upload. Historical serialized auto values remain accepted and are normalized to the equivalent visible 智能 choice by the panel. The client sends layer_decomposition=true, sorts returned images by z_index, and retains only bounded z_index, size, output_format, bounding_box, name, and description metadata. Result URLs remain live transport data and never enter workflow or history metadata. Background jobs preserve every provider image byte-for-byte in temp storage and distinguish request count from result count because one successful request may yield a base image plus sixteen layers. The save route promotes all descriptors in one request; the frontend then routes the first descriptor to the IMAGE save node and the remaining descriptors to a paired, auto-created o1key 保存图层 node on LAYERS. The paired node stores only safe node-role and pairing IDs, so refresh recovery can rediscover the branch without duplicating provider work or saved files. Direct execution preserves the original first IMAGE port for the base and appends list-valued LAYERS, list-valued LAYER_MASKS, and JSON LAYER_INFO outputs; RGB plus a separate mask follows ComfyUI's IMAGE/MASK contract while allowing provider layers to have different dimensions. In layer mode the frontend normally exposes only IMAGE, LAYERS, and LAYER_MASKS; the less-used LAYER_INFO remains in backend position four but is hidden unless already connected. Outside layer mode, appended ports collapse to the highest connected output. This display-only policy cannot discard a saved-workflow link or change backend output order.
Completed Nano Banana and GPT Image task queries use two recovery layers. Transport interruptions and incomplete JSON retry the idempotent result GET; after a successful JSON parse, inline Base64 must pass strict alphabet/padding validation and the decoded image must load completely. The shared response reader counts bytes while streaming so an exception retains the partial byte count. For uncompressed responses with a valid Content-Length, the completed byte count must match exactly; content-encoded responses skip this direct comparison because aiohttp/httpx expose decoded bytes, and responses without a declared length rely on clean stream completion plus JSON/image validation. Successful task-query responses remain silent. HTTP failures, interrupted reads, length mismatches, invalid JSON, and explicit returned-task-ID mismatches emit a compact terminal trace containing requested/returned task IDs, HTTP status/version, declared and received sizes, encodings, length verdict, JSON verdict, and the existing Eagleid when available. It must never log response bodies, Base64, authorization data, or signed result URLs. An explicit returned task_id must match the requested ID; absence remains compatible with providers that omit it. A failed inline-image validation re-fetches the same task_id with bounded exponential backoff and stable jitter, but never repeats the paid generation POST. HTTP image URLs retain their independent download-and-decode retries. Large response bodies and Base64 payloads remain disabled in logs by default.
Error normalization belongs to the O1keyImageGenerator node boundary, not to a single provider model. Both standard execution and panel background jobs apply the same mapping after dispatching either GPT Image or any supported Nano Banana model. The unsafe-image response phrase content rejected: the image was flagged as unsafe by the content safety system maps to 内容被拒绝:该图像被内容安全系统标记为不安全。, Your request was rejected by the safety system maps to 您的请求已被安全系统拒绝, insufficient balance maps to 上游额度不足!, Image generation returned empty response maps to 图片生成过程中被内容审查机制拒绝!, and The provided prompt is considered unsafe and it cannot be used to generate content maps to 提供的提示被认为是不安全的,不能用于生成内容。. All unrelated errors retain their existing diagnostic text or status mapping. The frontend repeats these narrow matches as a compatibility fallback for already-running or restored jobs. For standard executions, it updates the current ComfyUI error overlay through the overlay's stable data-testid hooks after Vue rendering, retaining the core title, dismissal, and details actions while replacing the generic body copy; it does not emit a duplicate short-lived toast.
The exact compact UTF-8 request JSON for both Nano Banana and unified-node GPT Image has an 18 MiB local ceiling, leaving 2 MiB below the provider's 20 MiB boundary. 不缩放 rejects an oversized body before any paid request. 智能缩放 resamples the largest encoded references from their originals with aspect-preserving Lanczos until the exact serialized body fits; intermediate candidates are never resized from an earlier candidate. For GPT edits, the first reference and mask form one resize group so they retain identical dimensions. The UI must warn 可能发生像素偏移 whenever that mode is selected. Legacy standalone GPT client callers that omit the resize mode retain their existing automatic 20 MiB compatibility behavior.
O1keyImageGenerator, NanoBanana, BatchNanoBananaPro, and O1keyGPTImage no longer expose or apply reference-guided colour correction. The frontend migration removes their former serialized correction values before ComfyUI maps positional widget arrays, preserving the settings that followed them. O1keyGPTImageBatch retains its correction control and uses reference_color_correction.py for batch outputs.
GPT Image sizing is represented in the unified panel as separate resolution (智能 / 1K / 2K / 4K) and aspect-ratio controls. Resolution defaults to 智能; all three unified provider families omit size at that value. o1key_image_catalog.py owns the mapping from an explicit tier plus ratio to the exact GPT pixel size; 智能 aspect ratio deliberately selects the square size for the chosen explicit tier (1024x1024 / 2048x2048 / 2880x2880) rather than sending a bare tier label, while legacy standalone GPT nodes keep their combined size labels. Active user controls are rendered as a single vertical list, with a fixed label column on the left and the control column on the right, and filtered through a frontend model-capability matrix: thinking level and online search are Nano Banana 2-only; resize mode is available to Nano Banana and GPT Image; API output format is available to GPT Image and Seedream; and quality, background, moderation, plus mask are GPT Image-only. Switching models updates existing field visibility without recreating controls, so model-specific values survive a round trip. Online search is omitted by default and becomes the top-level provider field google_search: true only when enabled for Nano Banana 2. Transport-only manifest widgets remain hidden. Nodes 1.0 group conversion automatically injects a control_after_generate widget for inputs named seed or noise_seed; the unified panel hides that generated widget because its embedded seed randomizer is the sole visible authority, while retaining the released seed input ID and serialized value for workflow compatibility. GPT Image background values (auto / transparent / opaque) and output formats (png / webp / jpeg) are validated before a paid request and travel unchanged through both direct execution and background jobs; transparent is rejected with jpeg, and the frontend removes JPEG from the available formats while transparency is selected. Seedream validates png / jpeg and hides unsupported GPT-only controls. Moderation accepts only the UI values 自动 / 低; 自动 omits the provider parameter and 低 sends moderation: "low".
The unified image prompt editor's visible AI帮写 action calls the compatibility route POST /o1key/image/prompt-optimize. The browser sends only the current prompt and sanitized ComfyUI input descriptors; the server resolves those paths inside the input root, creates ordered analysis images, and calls gpt-5.6-sol with reasoning_effort=high through the configured O1Key route. The request is non-streaming and uses a dedicated system instruction that prioritizes binding visual attributes to concrete subjects before expressing preserve/change directives. Reference analysis images use exact image/jpeg data URLs and the compact request body is capped at 18 MiB. API credentials remain server-side, and neither request bodies nor base64 image data may be logged.
The released NanoBanana and BatchNanoBananaPro nodes no longer expose prompt optimization or colour correction. The unified O1keyImageGenerator retains its AI帮写 action. Both Nano nodes expose only 1K / 2K / 4K; saved 512 / 512px values are migrated idempotently to 1K before widget configuration. Their route combos use ComfyUI's display-only getOptionLabel hook to show 特价 / 优质 / 企业, while the widget values, saved workflows, and provider matrix remain 畅速 / 直连 / 专线. They share nano_banana_async.py for exact 18 MiB body enforcement, same-task polling recovery, inline-image validation, and download retries. The batch node represents JPEG/WebP compression quality as an integer input, and its frontend migration converts a serialized numeric string back to an integer before widget configuration. The batch node's final inputs are resize, output format, quality, naming rule, save path, and seed. The frontend migration removes the former correction value, appends 不缩放 when needed, and reorders the six trailing values while retaining saved path, quality, resize, and seed settings.
BatchNanoBananaPro no longer creates random image pools. Every filled folder path participates in the selected pairing mode. Its saved-workflow migration removes the retired dynamic 图片随机抽取 widget value after earlier layout migrations and removes any connected input and link while preserving later socket indices. Saved workflows with multiple paths and 不配对 must select a pairing mode before execution.
The released O1keyGPTImage and O1keyGPTImageBatch nodes reuse the same text-only prompt-optimization action and display-only route labels. The standalone node retains the order of remaining inputs and migrates its old 色彩纠正 and 内容审查强度 values out of the positional array; 缩放图片 and 背景 remain in order. The batch node retains all four controls, including colour correction. The batch node's operational inputs are visible rather than marked advanced. Both nodes pass an explicit resize mode to GptImageClient, selecting the same exact 18 MiB JSON ceiling as the unified generator while preserving the client's 20 MiB compatibility behavior for external callers that omit this argument. Background is validated before paid submission, and batch colour correction runs against each task's first reference before saving. Query retries remain restricted to the idempotent task GET; transient result-query statuses and result-download statuses use bounded backoff, and download diagnostics omit signed URLs.
Seedance execution-error normalization
SeedanceElementCreate is displayed as Seedance 创建素材 and exposes the neutral 照片 / 视频 / 音频 input names while preserving its released node ID, widget order, and output order. migrateWorkflow.js rewrites the former 真人照片 / 真人视频 / 真人音频 socket names before graph configuration, and the backend accepts those former kwargs as execution-time aliases for API-workflow compatibility. The node preserves HC as the default request-mode value and appends Doubao for saved-workflow compatibility. Both modes use the unified /v1/seedance/assets create/query boundary; the client submits and polls with the normalized lowercase API type (hc or doubao).
SeedanceAutoPass and SeedanceMultiModal share a narrowly scoped generation-error formatter at their node execution boundaries. When an upstream response contains The request failed because the output video may be related to copyright restriction, its plural restrictions form, or an OutputVideoSensitiveContentDetected.PolicyViolation: prefix, either node raises 输出视频触发版权审查被拒绝生成!; unrelated exceptions preserve their original type and text. The frontend execution-error listener recognizes only these two released node IDs and replaces ComfyUI's generic persistent-overlay body after Vue rendering while retaining the native title, dismissal, and details actions. SeedanceAutoPassBatch is intentionally outside this mapping.
SeedanceMultiModal uses V3 Autogrow.TemplateNames for its released numbered image, video, and audio inputs. The saved node ID and Autogrow leaf names remain stable; migrateWorkflow.js rewrites legacy flat input names such as 参考图片1 to 参考图片.参考图片1 and renames 真人素材IDn inputs to 图片素材IDn before graph configuration. Both migrations are safe to repeat, and the backend retains the former material-ID kwargs as aliases for API-workflow compatibility. Autogrow cannot retain editable string widgets because widget templates become connection-only, so the material-ID inputs remain ordinary append-order string widgets. seedanceMultiModalDynamic.js hides only the unused trailing widgets in each ID family, reveals one new empty row after the highest filled value, and recomputes the node height from the currently visible rows while preserving its width. This frontend visibility rule never reorders values, allowing legacy positional widgets_values to load unchanged. Before submission, the node prints its finalized request body to the ComfyUI log; the logged copy preserves ordinary parameters and asset:// IDs but folds credentials, Base64 media, binary data, and HTTP(S) temporary media URLs without changing the submitted body.
Seedance model capabilities are shared across the single, multimodal, and batch boundaries. Seedance 2.5 accepts 4–30 seconds, all four exposed resolutions (480p / 720p / 1080p / 4k), and 30 image, 10 video, and 10 audio content items; direct media and matching asset:// IDs count toward the same per-type limit. Seedance 2.0 variants remain capped at 4–15 seconds and 9/3/3 content items, while fast and mini retain the 480p / 720p resolution restriction. SeedanceMultiModal expands its Autogrow leaf-name lists to the 2.5 maxima. Its original 9 image, 3 video, and 3 audio ID widgets retain their exact positional order, and additional ID widgets are appended after that legacy block so saved positional widgets_values remain compatible; only the former image-widget name is migrated from 真人素材IDn to 图片素材IDn.
SeedanceMultiModal, SeedanceAutoPass, and SeedanceAutoPassBatch expose both 国内 and 海外 model routes and default newly created nodes to 国内; 海外 is the display-name replacement for the former 海外HC value. The domestic route maps the four existing base-model choices to doubao-seedance-2-0-260128-max, doubao-seedance-2-0-fast-260128-max, doubao-seedance-2-0-mini-260615-max, and doubao-seedance-2-5-260628-max; it reuses the same capability envelope and new-format request body as the corresponding overseas choices. SeedanceAutoPass exposes only 多模态 and 首尾帧: the backend resolves prompt-only multimodal calls to text, and resolves one or two frame images to first_frame or first_last_frame. Its 素材创建 branch mirrors the unified generator's auto/manual contract; automatic media is validated and converted into HC/Doubao assets, while manual IDs bypass upload and enter build_seedance_video_body through the assets fields. The node translates its Chinese widget values into o1key_video_catalog.py and submits through SeedanceClient, so the unified panel and graph node cannot drift in scalar parameters or request shape. migrateWorkflow.js performs an idempotent compatibility migration for the former four mode values, moved web-search widget, former route label, last-frame default, and newly added asset-creation selector.
Panel-driven unified video generation
O1keyVideoGenerator exposes append-only VIDEO and LAST_FRAME outputs while keeping paid generation exclusively behind POST /o1key/video/jobs. Every panel click creates and connects a native SaveVideo; when return_last_frame is enabled, it also creates and connects a native SaveImage. The completed safe descriptors are dispatched to those native nodes for preview and persisted on them for workflow reload recovery. The generator's native execute may resolve only the latest completed local descriptors and can never submit or retry a paid request. O1keyVideoResult remains registered as deprecated compatibility support for saved workflows but is never created by the current panel. ParallelVideoJobManager still starts every accepted job immediately without entering ComfyUI's native queue or imposing an internal concurrency ceiling. Provider-side quotas and rate limits remain authoritative.
Review-error normalization for O1keyVideoGenerator belongs to the ParallelVideoJobManager failure boundary, so every current and future provider adapter uses the same mapping. A case-insensitive copyright marker takes priority; audio, video, content, and real subject fields or keywords distinguish output-audio, output-video, prompt, and real-person failures. Other safety, moderation, rejection, and policy-violation messages use the same subject classification with review-specific Chinese text. Errors without those markers retain their original diagnostic text.
The video generator deliberately reuses the image generator's frontend grammar: a 560-pixel default width, prompt card, single-column label/control rows, custom dropdowns, 102-pixel media tiles, compact status text, and a light primary action. Reference-video tiles reuse the sanitized input descriptor through /view in a muted, non-playing native <video> element, seek to the first decodable instant after metadata loads, and retain the icon/name fallback when decoding fails. Re-rendering releases removed video sources so stale elements do not keep network or decoder resources. No canvas capture, Base64 poster, FFmpeg process, server route, or generated thumbnail file is involved. Mode-specific media sections are mounted above the prompt and hidden when irrelevant; the node height follows the active mode instead of reserving blank space.
Video image tiles call the shared o1keyReferenceImageEditor and replace the exact source descriptor with a newly uploaded, non-overwriting PNG only if that source is still present. Their header actions also call the image generator's namespaced canvas-image picker: candidate discovery and /view loading remain single-source, while the selected file is validated against the Seedance image envelope and copied through the normal non-overwriting ComfyUI input upload before entering the video manifest. This is available for the first-frame, last-frame, and multimodal image sections, but not for video or audio sections. Multi-item image, video, and audio tracks reorder their manifest arrays directly through drag-and-drop (with Alt+arrow keyboard parity); visible order badges therefore match provider submission order. Single first/last-frame slots remain replaceable and editable but are not reorderable.
The video prompt editor's AI帮写 action calls POST /o1key/video/prompt-write. It uses a video-only default system preset with gpt-5.6-sol, high reasoning, and non-streaming output; the preset emphasizes temporal continuity, subject/action binding, camera motion, visual consistency, first/last-frame transitions, and synchronized sound when audio generation is enabled. The browser submits scalar generation context plus sanitized input-image descriptors. The server derives image roles from the generation mode and analyzes them in manifest order under the input-root boundary. Reference video and audio content is not sent to the writing model; only bounded counts are supplied so the preset does not invent unseen media details. The API key remains server-side and the exact multimodal body retains the shared 18 MiB ceiling.
The first provider adapter is Seedance. o1key_video_catalog.py is the canonical public model/capability matrix; o1key_video_jobs.py validates model, route, mode, asset-creation policy, duration, resolution, media counts, descriptors, and save location before uploading media or submitting provider work. Direct first-frame, last-frame, and multimodal reference images follow the published Seedance bounds of 300~6000px per dimension and an inclusive 0.4~2.5 aspect ratio; no image total-pixel floor is invented locally. Reference videos use the same dimension and ratio bounds plus the official inclusive 407,696~8,295,044 total-pixel range. The frontend rejects readable invalid media before upload, while the authoritative PyAV backend check runs before snapshot copying, upload, or paid provider work; a browser codec limitation does not by itself reject an otherwise supported MOV/H.265 file. The append-only asset_creation_mode generator widget defaults to auto; migrateWorkflow.js appends that value to older positional workflows. Automatic mode stores browser-selected media as sanitized type=input descriptors and copies them into temp/o1key_video_jobs/<batch-id>/inputs before the background task starts, isolating repeated submissions from later file changes. It then uses the shared seedance_assets.py service for both routes: overseas HC maps to asset API type=hc, domestic maps to type=doubao, and video submission receives only asset:// references after every material reaches Active. Material preparation is bounded to three concurrent items and preserves manifest order. A route-and-media-type-scoped SHA-256 cache under <ComfyUI user directory>/o1key/seedance_asset_cache.json stores only content fingerprints, asset IDs, and timestamps; a hit is queried for Active before reuse and skips the upload. Completed material IDs are included in safe job summaries, allowing a failed video submission to retry as a manual-ID request without recreating material. Manual mode accepts validated image, video, and audio asset IDs from SeedanceElementCreate, excludes hidden/stale upload descriptors from the submitted request, and maps one or two image IDs to first-frame or first/last-frame roles when those generation modes are selected. Legacy requests that already combined direct references with the former persons ID key remain accepted and retain their combined capability limit. Upload URLs, local paths, credentials, and provider response bodies are never written to the cache or job history. Both unified image and video generator panels reserve the prompt editor as their single vertically flexible region: its minimum height remains fixed for compact layouts, while any user-added node height expands the editor instead of leaving blank space below the primary action.
Completed videos are atomically promoted into the configured output location with collision-safe names. An absolute external destination receives an additional path-free preview copy in ComfyUI temp. Auto-created native save nodes persist only the batch identity, generator association, safe output descriptors, compact terminal state, and the sanitized request required for recovery. Reloading a workflow restores their previews and resumes polling unfinished jobs. Terminal summaries are atomically bounded in <ComfyUI user directory>/o1key/video_jobs.json. See ADR 0008; ADR 0007 remains the historical job-scheduling decision.
State and storage
.config: plugin-local credentials and route settings; ignored by Git.ComfyUI/input/o1key-notes.json: persistent user notes.cases/*.json: bundled runtime case definitions.- ComfyUI
input/,output/, andtemp/: uploaded references, generated artifacts, previews, and job snapshots. - Saved workflows: node IDs and positional widget values; treat as long-lived external data.
Compatibility invariants
- Node IDs and mapping keys are stable APIs.
- Secrets never enter workflow JSON.
- Long operations remain interruptible.
- Retried requests respect non-retryable status codes and server retry hints.
- Nodes whose provider protocol requires temporary uploads must resolve them to HTTPS URLs before paid generation begins; Nano Banana image references use validated inline base64 instead.
- File paths from HTTP requests are resolved beneath their intended ComfyUI root.
- Frontend migrations are narrow and idempotent.
O1keyImageSaverenders results only through the ComfyUI native preview; dual previews are forbidden.- A saved
O1keyImageSaveresult must survive workflow reload and browser refresh through its serialized image descriptors. - Background generators produce temp descriptors; only
O1keyImageSavewrites new permanent results into ComfyUI's configured output root. O1keyImageSavehas no user-configurable widgets; it reads validated generator-owned save settings and remains the sole permanent writer.O1keyImageGeneratorappends命名规则,filename_prefix,格式, and保存位置at indexes 17–20; old connected save-node values migrate into those slots once and are then removed from the save node.- Local
格式is Banana-only. GPT Image saves its API-selected container as原始, and its API输出格式defaults to lowercasepngfor new GPT panel selections. - Seedream saves its API-selected
png / jpegcontainer as原始, uploads references through/v1/o1key/uploads, always sendswatermark=false, and has no watermark widget. Layer decomposition forces PNG and preserves every returned transparent layer. - Unified image-generation batch manifests hold at most fifty sources or targets, while every provider request still uses at most ten references and uses GPT counts 1–8 or the other-model values
1,2,4, and9. - GPT Image batches never send a provider request with
ngreater than1. - Seedream batches never send a provider request with
ngreater than1. - Seedream layer decomposition is one request with one reference; result cardinality is independent and may be 1–17 images.
- Standard and panel-triggered unified image runs reuse a connected blank
O1keyImageSavebefore creating another result node, and never overwrite populated sibling save nodes. - Unified GPT Image requests preserve the selected background and output format; transparent backgrounds are limited to PNG and WebP.
- Unified GPT Image requests omit the retired moderation parameter.
GPT Image 2.5 SunburstandGPT Image 2.5 Flareshare GPT Image 2's resolution/aspect-ratio matrix and capability controls. Their畅速 / 直连 / 专线route values resolve to API IDs ending in-sp / -sd /no suffix, respectively.- The GPT Image batch node pairs every populated folder through its selected pairing mode and runs the resulting tasks concurrently. It has no random-folder selector or concurrency widget; workflow migration removes both values from older saved nodes.
- The Nano Banana batch node's optional colour correction never resizes the generated image, uses only reference image 1, and runs exactly once after generation and before save; its standalone counterpart and
O1keyImageGeneratorhave no correction stage. - Route-label changes never alter the serialized values
畅速,直连, and专线. SeedanceAutoPassretains a visible素材创建模式combo with default关闭(automatic assets and hidden IDs) and打开(manual IDs shown below). Material IDs use ordinary numbered single-line widgets in image/video/audio order (30/10/10), revealing one empty successor after the highest filled row as inSeedanceMultiModal. Visibility never removes serialized widgets or clears IDs. The former素材创建input and自动创建 / 手动values migrate to this toggle without shifting its position; aggregate fields migrate into numbered rows, and legacy API kwargs remain accepted. Web-search, seed, and last-frame parameters remain at the bottom. The ordinary generation-mode combo drives actual media-socket removal/restoration in the frontend: only progressive reference-image/video/audio sockets in multimodal, only first/last-frame sockets in frame mode. Inactive Autogrow groups are suspended to prevent ghost sockets; switching disconnects removed media links but preserves unrelated widget sockets, and saved workflows are reconciled after configuration.