Publish current ComfyUI O1Key code baseline
Replace the prior release tree with the current plugin, frontend, tests, and documentation. Document retired node IDs and the public Gitea update source.
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
# Architecture
|
||||
|
||||
## Runtime overview
|
||||
|
||||
```text
|
||||
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](decisions/0006-grok-video-api-and-workflow-migration.md).
|
||||
|
||||
### Shared utilities
|
||||
|
||||
`utils/` contains cross-provider infrastructure:
|
||||
|
||||
- `config.py`: atomic `.config` reads and writes plus route resolution;
|
||||
- `http_error.py`: retry classification and friendly errors;
|
||||
- `http2_client.py`: HTTP/2 with an aiohttp fallback;
|
||||
- `image_utils.py` and `file_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 for `O1keyImageSave`.
|
||||
- `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` registers an O1Key tab in the left sidebar. Its panel calls `/o1key/update`, which delegates to `utils/updater.py`. The updater fetches the public `main` branch from `https://git.o1key.com/publisher/comfyui_o1key.git` without changing the user's `origin`. It only fast-forwards a clean local Git `main`; local tracked changes, divergent history, and file collisions receive structured error codes and guidance. It never resets or cleans the worktree. A completed update requires a ComfyUI restart to load new Python and JavaScript code.
|
||||
|
||||
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](decisions/0004-native-recoverable-image-workflow-metadata.md).
|
||||
|
||||
`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](decisions/0008-native-video-save-outputs.md); [ADR 0007](decisions/0007-panel-driven-video-jobs.md) 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/`, and `temp/`: 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.
|
||||
- `O1keyImageSave` renders results only through the ComfyUI native preview; dual previews are forbidden.
|
||||
- A saved `O1keyImageSave` result must survive workflow reload and browser refresh through its serialized image descriptors.
|
||||
- Background generators produce temp descriptors; only `O1keyImageSave` writes new permanent results into ComfyUI's configured output root.
|
||||
- `O1keyImageSave` has no user-configurable widgets; it reads validated generator-owned save settings and remains the sole permanent writer.
|
||||
- `O1keyImageGenerator` appends `命名规则`, `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 lowercase `png` for new GPT panel selections.
|
||||
- Seedream saves its API-selected `png / jpeg` container as `原始`, uploads references through `/v1/o1key/uploads`, always sends `watermark=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`, and `9`.
|
||||
- GPT Image batches never send a provider request with `n` greater than `1`.
|
||||
- Seedream batches never send a provider request with `n` greater than `1`.
|
||||
- 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 `O1keyImageSave` before 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 Sunburst` and `GPT Image 2.5 Flare` share 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 `O1keyImageGenerator` have no correction stage.
|
||||
- Route-label changes never alter the serialized values `畅速`, `直连`, and `专线`.
|
||||
- `SeedanceAutoPass` retains 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 in `SeedanceMultiModal`. 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.
|
||||
Reference in New Issue
Block a user