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:
Jony
2026-09-24 19:56:48 +08:00
parent 3e337722ab
commit ba920f2b66
183 changed files with 49496 additions and 9934 deletions
+33
View File
@@ -0,0 +1,33 @@
# Maintainer and agent documentation
This directory is the structured knowledge base for maintaining `comfyui_o1key`. Keep executable rules concise in `AGENTS.md`; keep explanations, diagrams, and procedures here.
## Start here
- [Architecture](architecture.md): runtime boundaries, startup flow, storage, and dependency direction.
- [Development](development.md): how to add or change nodes, clients, routes, and frontend extensions.
- [Testing](testing.md): isolated offline suite, smoke tests, and validation matrix.
- [Configuration](configuration.md): supported settings, storage, precedence, and security rules.
- [Architecture decisions](decisions/README.md): durable decisions and the ADR template.
- [Maintenance history](maintenance/cleanup-2026-08-29.md): the deep-cleanup baseline for the current layout.
## Knowledge ownership
| Change | Required documentation |
| --- | --- |
| User installation or visible behavior | `README.md` |
| Runtime boundary or data flow | `docs/architecture.md` |
| Configuration key or precedence | `docs/configuration.md` |
| Development/test procedure | `docs/development.md` or `docs/testing.md` |
| Compatibility-sensitive design choice | `docs/decisions/NNNN-title.md` |
| One-time repository maintenance | `docs/maintenance/YYYY-MM-DD-topic.md` |
## AI handoff checklist
Before ending a non-trivial change, leave the repository in a state where the next maintainer or agent can answer:
1. What runtime boundary changed?
2. Which invariant protects backward compatibility?
3. Which offline test proves the behavior?
4. Which document is now the source of truth?
5. Are any follow-up risks or decisions still open?
+261
View File
@@ -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 18 (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/1616`, no more than `36,000,000` pixels, and an exact temporary-upload payload no larger than 30 MiB. Layer decomposition instead requires `262,14436,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 430 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 415 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 `3006000px` per dimension and an inclusive `0.42.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,6968,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 1720; 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 18 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 117 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.
+59
View File
@@ -0,0 +1,59 @@
# Configuration
## Source of truth
Runtime credential and route behavior is implemented in `utils/config.py`. Update this document and `README.md` whenever supported keys or precedence changes.
## Configuration file
The plugin stores settings in a UTF-8 `.config` file at the plugin root:
```text
O1KEY_API_KEY=replace-with-your-key
O1KEY_NETWORK_ROUTE=全球加速
```
Use the ComfyUI sidebar's「令牌管理」window to write settings atomically. `.config` is ignored by Git and must never be copied into docs, fixtures, logs, screenshots, or issue reports.
Supported file keys:
| Key | Purpose |
| --- | --- |
| `O1KEY_API_KEY` | Authentication token used by provider clients |
| `O1KEY_NETWORK_ROUTE` | Global named route selected by the settings UI |
| `O1KEY_API_BASE_URL` | Custom synchronous fallback base URL |
| `O1KEY_ASYNC_API_BASE_URL` | Custom asynchronous fallback base URL |
Omni Flash reads `O1KEY_API_KEY` through the same configuration helper as image generation. Connected media uploads and generation use the selected O1Key network route; the key never becomes a workflow widget value.
Named routes and defaults are defined in `utils/config.py`; do not duplicate endpoint constants in nodes.
## Resolution behavior
- A recognized global named route resolves to its configured endpoint.
- Direct base-URL helpers fall back to the custom URL and then the built-in default when no named route is stored.
- Route-aware callers using `get_base_url_by_route()` use the explicit route when supplied, otherwise the global route.
- Clients that cache configuration should key that cache with `get_runtime_config_signature()` so UI changes take effect without restarting.
## Diagnostic environment flags
Environment variables are currently used for logging behavior, not as the primary credential store:
| Variable | Effect |
| --- | --- |
| `O1KEY_DEBUG_LOG=1` | Enables Nano Banana debug logging |
| `O1KEY_VERBOSE_LOG=1` | Enables verbose transport logging |
| `O1KEY_RESPONSE_LOG=1` | Enables response logging where supported |
Verbose response logging can expose sensitive metadata, so keep it disabled by default and never enable it in committed test configuration. Complete base64 media payloads are always redacted from logs, including when verbose logging is enabled.
## Security rules
- Mask API keys in UI responses and error reports.
- Do not serialize credentials into ComfyUI workflows.
- Do not log authorization headers, signed URLs, or complete user media payloads.
- Keep `.config` during plugin upgrades and exclude it from cleanup scripts except for explicit user-requested credential removal.
## Runtime history storage
Completed, failed, and cancelled panel-image jobs are indexed at `<ComfyUI user directory>/o1key/image_job_history.json`. This is runtime UI state rather than configuration and is capped at 200 records. It contains only batch and node IDs, terminal state, bounded counts/timestamps/error summaries, and sanitized ComfyUI image descriptors. Prompts, reference manifests, local absolute save paths, credentials, Base64 payloads, and signed URLs must never enter this file. Native single-item history deletion and clear-history actions update the same index atomically.
+25
View File
@@ -0,0 +1,25 @@
# NNNN: Decision title
- Status: Proposed
- Date: YYYY-MM-DD
- Owners: Maintainers
## Context
What problem, constraint, or compatibility risk requires a durable decision?
## Decision
What will the project do?
## Consequences
What becomes easier, harder, required, or intentionally unsupported?
## Alternatives considered
Which realistic alternatives were rejected, and why?
## Validation
Which tests, measurements, or operational checks prove the decision works?
@@ -0,0 +1,39 @@
# 0001: AI-native repository guidance and documentation
- Status: Accepted
- Date: 2026-08-29
- Owners: Maintainers
## Context
The plugin accumulated implementation notes, local tool state, root-level tests, and stale scripts. Important knowledge was scattered between code and temporary reports, making it difficult for a new maintainer or coding agent to identify runtime boundaries, compatibility constraints, and the correct validation commands.
Codex supports layered `AGENTS.md` files from repository root to working directory. ComfyUI also auto-loads frontend files, so generic repository assumptions can be unsafe for `web/` and node-schema work.
## Decision
- Use a concise root `AGENTS.md` for repository-wide executable rules.
- Use scoped `AGENTS.md` files only in `nodes/`, `web/`, and `tests/`, where local constraints materially differ.
- Use `docs/` as the structured maintainer knowledge base.
- Keep user documentation in root `README.md` and durable design choices in numbered ADRs.
- Keep one-off cleanup reports under `docs/maintenance/`.
- Keep tests in `tests/` and run them in isolated processes through `tests/run_all.py`.
## Consequences
- Agents receive the right constraints close to the files they edit without overloading the root instruction file.
- Architecture and operational knowledge becomes reviewable and versioned.
- Changes that affect configuration, compatibility, or runtime boundaries must update documentation in the same work unit.
- Maintainers must keep links and commands synchronized as the portable layout evolves.
## Alternatives considered
- One large root instruction file: rejected because it mixes executable rules with background knowledge and approaches instruction-size limits as the project grows.
- Documentation only in README: rejected because user guidance and maintainer internals have different audiences and change rates.
- Tool-specific hidden configuration directories: rejected because they are not portable across agents and should not be runtime project state.
## Validation
- Verify root and scoped instruction files are discoverable from their directories.
- Verify every path linked from `docs/README.md` exists.
- Run the full isolated test suite and the plugin import smoke test after structural changes.
@@ -0,0 +1,37 @@
# 0002: Save-node ownership of generated image formats
- Status: Superseded by [0003](0003-generator-owned-image-save-configuration.md)
- Date: 2026-08-30
- Owners: Maintainers
## Context
The unified background generator previously decoded provider results and permanently re-encoded Nano Banana output as PNG. This obscured whether `output_format` was a provider capability or a local save choice, and discarded the original PNG/JPEG container returned by Nano Banana. The released `O1keyImageSave` node also delegated to ComfyUI's PNG-only helper.
## Decision
Generation paths retain provider bytes and detected format. Panel-triggered jobs place those bytes in ComfyUI temp storage and return `type=temp` descriptors. `O1keyImageSave` is the only component that promotes them into output storage, through its append-only `格式` widget and the batch-bound `/o1key/image/save` route.
`格式=原始` preserves PNG/JPEG/WebP compressed image data; workflow metadata may be inserted into the container without recompressing pixels. Explicit PNG and WebP use lossless encoding. Explicit JPEG uses quality 100 with 4:4:4 subsampling. If pixels were modified after generation or original bytes are unavailable, `原始` falls back to PNG.
Nano Banana payloads never contain `output_format`. GPT Image keeps its API-level `output_format`, which determines provider output but does not bypass the save node.
## Consequences
- Nano Banana PNG and JPEG responses remain distinguishable and recoverable.
- API output options and local save options have separate owners and labels.
- Background generation must retain temp results until the save route succeeds.
- Original-format preservation requires private in-memory tensor metadata on the direct execution path; ordinary downstream tensor operations may invalidate it and trigger the PNG fallback.
- Existing workflows gain only one appended save-node widget and migrate to `原始` idempotently.
## Alternatives considered
- Continue saving every background result as PNG. Rejected because it destroys the provider container and implies a Nano Banana capability that does not exist.
- Let the generator select the permanent local format. Rejected because it duplicates the output responsibility and leaves the save node semantically misleading.
- Store original bytes inside workflow JSON. Rejected because large binary payloads do not belong in workflows and would make saved graphs unsafe and impractical.
## Validation
- `tests/test_o1key_image_save.py` verifies original JPEG/PNG preservation, metadata injection, explicit conversions, and PNG fallback.
- `tests/test_o1key_image_jobs.py` verifies Nano Banana ignores GPT-only fields, jobs produce temp descriptors, and save-route path binding.
- `tests/test_o1key_image_generator_frontend.mjs` verifies temp promotion, workflow metadata submission, result restoration, and idempotent widget migration.
@@ -0,0 +1,40 @@
# 0003: Generator-owned image save configuration
- Status: Accepted
- Date: 2026-09-02
- Owners: Maintainers
- Supersedes: [0002](0002-save-node-image-format-ownership.md)
## Context
The unified image workflow exposed naming, local format conversion, and output location on every `O1keyImageSave` result node. A single generator may create several result nodes, so this duplicated settings and made model-level behavior hard to understand. GPT Image's API `output_format` is also semantically different from Nano Banana's optional local save conversion.
Moving released positional widgets between nodes risks silently changing saved workflows. Multiple old save nodes may also carry different settings even though the new design has one shared generator configuration.
## Decision
`O1keyImageGenerator` owns four append-only save inputs at indexes 1922: `命名规则`, `filename_prefix`, `格式`, and `保存位置`. `命名规则` keeps the serialized value `自定义前缀` for compatibility but displays `自定义`. `O1keyImageSave` removes all configuration widgets and remains the sole permanent file writer and result renderer.
Direct execution attaches validated save settings to the output tensor. Panel jobs snapshot them in the server-side job record at submission, and the save route prefers that immutable snapshot. The frontend migration finds the generator connected to each legacy save node, moves the first connected save node's positional settings into the generator, clears migrated save-node values, and is safe to repeat.
Local `格式` is visible and effective only for Banana models. GPT Image always uses local `原始`; its independent API `输出格式` displays `JPEG / PNG / WebP`, stores lowercase values, and defaults new nodes to `jpeg`. Legacy generators retain saved API-format values and use the historical `png` default only when migrating workflows that predate that field.
## Consequences
- One generator controls naming and destination for all of its current and future result nodes.
- Multiple legacy save nodes with different settings cannot all be represented; migration deterministically uses the first connected save node.
- Existing node IDs, image ports, and the first nineteen generator widget positions remain unchanged.
- Save-node preview and retry behavior remains independent from save configuration.
- GPT provider format and Banana local conversion have separate controls and cannot accidentally override one another.
## Alternatives considered
- Keep configuration duplicated on every save node. Rejected because it conflicts with generator-wide settings and obscures provider capabilities.
- Keep only `filename_prefix` on the save node. Rejected because the default custom naming rule would then be split across two nodes.
- Rename the serialized `自定义前缀` value to `自定义`. Rejected because the visible label can change without breaking saved workflows or backend validation.
## Validation
- `tests/test_o1key_image_generator.py` verifies schema positions, defaults, tensor-carried settings, GPT lowercase `jpeg`, and the parameter-free save node.
- `tests/test_o1key_image_jobs.py` verifies immutable job save settings and GPT's forced local `原始` behavior.
- `tests/test_o1key_image_generator_frontend.mjs` verifies model-dependent visibility, temp promotion settings, zero-widget result nodes, and idempotent cross-node migration.
@@ -0,0 +1,36 @@
# 0004: Native-recoverable image workflow metadata
- Status: Accepted
- Date: 2026-09-04
- Owners: Maintainers
## Context
`O1keyImageSave` already serialized ComfyUI's `prompt` and `extra_pnginfo`, but original JPEG provider results were kept as JPEG and stored those values in EXIF. The current ComfyUI frontend restores embedded workflows from PNG and WebP only; its file metadata parser has no JPEG workflow branch. Large workflows can also exceed JPEG's APP1 segment length, causing the EXIF payload to be omitted even though the image itself still saves.
Panel-triggered generation bypasses the native prompt executor. It previously obtained the API prompt from `app.graphToPrompt()` but serialized the workflow through a separate `graph.serialize()` call, allowing the two metadata values to describe different graph snapshots.
## Decision
Use ComfyUI's native `SaveImage` field names and PNG container behavior for every JPEG target that carries a workflow. Store the API prompt as PNG text key `prompt` and the serialized graph as PNG text key `workflow`. This conversion applies to original JPEG provider bytes and to an explicit JPEG save choice. It is skipped when ComfyUI's global metadata setting disables metadata or when no workflow is present.
PNG and WebP sources retain their existing metadata paths. Panel saves take both `output` and `workflow` from one `app.graphToPrompt()` result, with direct graph serialization retained only as a compatibility fallback for older frontends.
## Consequences
- Saved o1key output images can be loaded or dragged into ComfyUI to restore their workflow using the native parser.
- Workflow-bearing JPEG requests produce a `.png` permanent artifact, so recoverability takes precedence over preserving the requested JPEG container.
- Large workflow JSON is no longer constrained by JPEG APP1 length.
- JPEG bytes remain untouched when no workflow metadata is being written.
- Existing JPEG files that were saved without readable workflow metadata cannot be repaired retroactively.
## Alternatives considered
- Add a custom JPEG workflow parser to the frontend. Rejected because it would create an o1key-only recovery path and would still require nonstandard chunking for large metadata.
- Keep JPEG and silently omit oversized EXIF. Rejected because the saved image appears successful but cannot restore its workflow.
- Convert every result format to PNG. Rejected because ComfyUI already restores WebP metadata and non-workflow saves should keep their requested container.
## Validation
- `tests/test_o1key_image_save.py` verifies that a JPEG carrying a workflow larger than 64 KiB becomes PNG with complete native `prompt` and `workflow` fields, while a metadata-free JPEG retains its exact provider bytes.
- `tests/test_o1key_image_generator_frontend.mjs` verifies that panel save metadata uses the prompt and workflow returned by one `app.graphToPrompt()` call without invoking the fallback serializer.
@@ -0,0 +1,35 @@
# 0005: Persistent image-job history summaries
- Status: Accepted
- Date: 2026-09-04
- Owners: Maintainers
## Context
Panel-triggered o1key image jobs do not pass through ComfyUI's native prompt executor. Their native task-queue rows are virtual records assembled by the frontend from the process-local `ParallelImageJobManager`. Restarting ComfyUI clears both that manager and the browser bridge, so completed o1key rows disappear even though their saved images remain.
Output-directory scanning cannot rebuild a complete history: users may choose custom prefixes, natural-number naming, subfolders, or absolute destinations. Persisting entire job snapshots would retain prompts and manifests that the history UI does not need.
## Decision
Persist at most 200 terminal job summaries in `<ComfyUI user directory>/o1key/image_job_history.json` using atomic replacement. A summary contains only batch and node IDs, terminal state, counts, millisecond timestamps, bounded error/warning fields, failed request indexes, and sanitized ComfyUI image descriptors.
Expose the summaries through `GET /o1key/image/jobs/history`. The frontend loads up to 64 recent records before returning its first merged native history page. `POST /o1key/image/jobs/history` synchronizes single deletion and clear-history actions. History write failures are non-fatal to generation and image saving.
## Consequences
- Completed, failed, and cancelled o1key rows survive ComfyUI and browser restarts.
- History remains available independently of the currently loaded workflow and output naming rule.
- Prompts, reference manifests, provider bodies, absolute paths, credentials, Base64 data, and signed URLs are intentionally unrecoverable from the history index.
- Existing rows created before this decision cannot be reconstructed reliably and begin appearing only after the first new terminal job is indexed.
## Alternatives considered
- Scan output filenames at startup. Rejected because supported naming and destination choices do not preserve batch identity.
- Persist complete `JobRecord` objects. Rejected because prompts, manifests, and transient execution data exceed the history UI's needs and security boundary.
- Store only in browser local storage. Rejected because it is browser-profile-specific and does not survive browser data clearing or serve multiple connected clients consistently.
## Validation
- `tests/test_o1key_image_jobs.py` recreates the store from disk, verifies terminal manager persistence, and checks that prompt, Base64, and signed URL fields are absent.
- `tests/test_o1key_image_generator_frontend.mjs` verifies first-page hydration into native history and independent concurrent retry-batch lifetime.
@@ -0,0 +1,46 @@
# 0006: Grok Video API and workflow migration
- Status: Accepted
- Date: 2026-09-05
- Owners: Maintainers
## Context
The released `O1keyGrokVideo` node used the legacy `/v1/videos` API, retired model names, and a positional widget layout containing a per-node network route. The current O1Key Grok Video API has separate generation, edit, and extension endpoints, new model identifiers, different duration and resolution limits, and one shared task-status endpoint.
ComfyUI stores widget values by position. Replacing the schema without migrating `widgets_values` would assign old prompts, models, durations, and resolutions to the wrong controls. Renaming the optional reference-image sockets would also make old connections harder to restore reliably.
## Decision
- Keep the released `O1keyGrokVideo` node ID and the existing `VIDEO` output position.
- Use `O1keyGrokVideo` for text, image, and multi-reference generation, and `O1keyGrokVideoEdit` for edit and extension operations.
- Keep API transport, payload validation, polling, and response parsing in `clients/grok_video_client.py`.
- Validate all user-controlled parameters before temporary uploads or paid generation calls.
- Migrate legacy generation workflows before ComfyUI maps positional widget values:
- remove the legacy per-node network route through the shared route migration;
- map `grok-imagine-video-1.5-preview` to `grok-imagine-video-1.5`;
- map `grok-imagine-1.0-video` to `grok-imagine-video`;
- infer multi-reference mode when a legacy reference-image socket is connected, otherwise use text mode;
- rename `参考图1` through `参考图7` sockets to `图片1` through `图片7`;
- clamp legacy durations above the new API maximum to 15 seconds;
- append new controls rather than shifting values in workflows already saved with the intermediate schema.
- Never write temporary upload or result URLs to logs or workflows.
## Consequences
Existing workflow node IDs and output links remain valid, while old parameter values are converted to the new API contract. A legacy 16- or 20-second selection becomes 15 seconds because the replacement generation API has a hard 115 second range.
The frontend migration remains required runtime code as long as pre-migration workflows are supported. Future Grok widget additions must remain append-only or include another idempotent migration and regression test.
## Alternatives considered
- Register entirely new node IDs and leave the old node untouched. Rejected because it would strand saved workflows on an obsolete API.
- Reinterpret old widget arrays in backend execution only. Rejected because ComfyUI assigns widget values before execution, so the visible controls and saved values would still be corrupted.
- Preserve retired model names as aliases in the node dropdown. Rejected because those identifiers are not valid for the current endpoints and would allow avoidable failed requests.
## Validation
- `tests/test_grok_video.py` protects payload construction, capability limits, schema defaults, media locators, edit duration preflight, and secret-safe error formatting.
- `tests/test_o1key_image_generator_frontend.mjs` protects legacy Grok widget and socket migration, including idempotency.
- `tests/test_temp_media_uploads.py` verifies that temporary upload URLs are returned to callers without being printed.
- `tests/run_all.py`, compile checks, and the package import smoke test validate the bundled Windows environment.
@@ -0,0 +1,33 @@
# 0007: Panel-driven independent video jobs
- Status: Superseded by [ADR 0008](0008-native-video-save-outputs.md)
- Date: 2026-09-07
- Owners: Maintainers
## Context
Video generation is asynchronous and long-running. The unified video node must support repeated clicks that run independently, while saved workflows still need a durable node that can preview a completed artifact and feed native ComfyUI video consumers. The user explicitly does not want these generation requests represented by ComfyUI's native queue.
## Decision
`O1keyVideoGenerator` is a panel-only V3 node with no outputs and a side-effect-free native `execute`. Every click creates an `O1keyVideoResult` node and submits an independent server-side job through `/o1key/video/jobs`. Every accepted job starts immediately; the plugin does not impose a semaphore, concurrency ceiling, or internal waiting slot on video-job scheduling. A job may still bound its own prerequisite media preparation. Provider-side quotas and rate limits remain authoritative.
The result node stores only safe ComfyUI file descriptors and bounded status/request metadata. Once a job completes, its native execution resolves the already-downloaded file and emits `VIDEO` plus optional `LAST_FRAME`; it cannot initiate or retry generation. Existing released video nodes and their mapping keys remain registered unchanged.
For Seedance automatic mode, both domestic and overseas routes use the same material-service boundary as `SeedanceElementCreate`; the route selects the Doubao or HC namespace. Material preparation is bounded to three concurrent items without limiting independent video jobs. Safe resolved IDs and content fingerprints may be persisted so retries and repeated content reuse existing Active material, but temporary upload URLs and local paths may not be persisted.
Prompt assistance is a separate, non-job operation at `/o1key/video/prompt-write`. It uses a dedicated video default preset rather than the image preset. Only sanitized input-image descriptors and scalar generation context cross the browser/server boundary; reference video and audio are represented by counts, not media payloads. This operation does not create a result node or enter either job system.
## Consequences
Repeated submissions remain available while earlier videos run, and every job has its own visible result node, status, cancellation, and retry lifecycle. Native queue controls do not display or control these jobs. A burst of clicks can therefore create the same number of simultaneous provider requests and may encounter upstream rate limits. Cancelling after provider acceptance cannot retract the remote request. New providers must be added through the shared catalog/job adapter boundary rather than by adding paid work to the generator node's `execute` method.
## Alternatives considered
- Represent jobs in ComfyUI's native queue: rejected because it conflicts with the required click-driven independent workflow.
- Return `VIDEO` directly from the generator: rejected because native execution would either duplicate paid work or require a blocking queue run.
- Reuse one result node for all clicks: rejected because concurrent completions could overwrite one another and obscure per-request state.
## Validation
`tests/test_o1key_video_generator.py` verifies the side-effect-free schema, Seedance matrix and validation, request-body roles, safe descriptors, and immediate unbounded independent execution. `tests/test_o1key_video_generator_frontend.mjs` verifies one-result-per-click wiring, the jobs API/event bridge, repeated-submit behavior, and absence of native queue submission calls.
@@ -0,0 +1,30 @@
# 0008: Native save nodes for panel video outputs
- Status: Accepted
- Date: 2026-09-12
- Owners: Maintainers
- Supersedes: [ADR 0007](0007-panel-driven-video-jobs.md) for result presentation
## Context
The dedicated `O1keyVideoResult` made every panel submission visible and recoverable, but duplicated capabilities already provided by ComfyUI's native save nodes and occupied a large result card. Users expect the generator's outputs to be visible through ordinary graph connections. Saved workflows containing the released result node must remain loadable, and native graph execution must never submit a second paid generation request.
## Decision
`O1keyVideoGenerator` exposes `VIDEO` followed by `LAST_FRAME`. Every panel submission creates and connects one native `SaveVideo`; it additionally creates and connects one native `SaveImage` when the submitted `return_last_frame` value is enabled. The independent background job keeps saving the provider result atomically, then dispatches its safe file descriptor to the exact native save node for preview. Batch identity, generator association, terminal state, and safe descriptors are stored on the native node so workflow reload can restore completed previews or resume polling unfinished jobs.
The generator's appended result-manifest widgets allow native execution to resolve the latest completed local video and image without contacting the provider. `O1keyVideoResult` remains registered and executable as a deprecated compatibility node for existing workflows, but the frontend no longer creates it.
## Consequences
New jobs use familiar native save nodes and visible typed connections. Returning a last frame produces two clearly separated native outputs. Repeated clicks remain independent and may create multiple save-node pairs. The panel job has already persisted the artifact before its preview is dispatched, so executing a native save node again is unnecessary and may create another copy of the generator's latest completed output.
## Alternatives considered
- Remove the released result-node registration: rejected because saved workflows would fail to load.
- Route paid video generation through ComfyUI's native queue: rejected because it would remove immediate independent submissions and risk duplicate provider calls.
- Reuse one native save node across every click: rejected because concurrent jobs would overwrite each other's visible result association.
## Validation
`tests/test_o1key_video_generator.py` verifies append-only manifest inputs, typed generator outputs, side-effect-free local resolution, and deprecated result compatibility. `tests/test_o1key_video_generator_frontend.mjs` verifies native node creation, conditional last-frame saving, typed connections, descriptor dispatch, recovery, and the absence of new `O1keyVideoResult` creation or native queue submission.
@@ -0,0 +1,25 @@
# 0009: Remove the unified image generator's external prompt input
- Status: Accepted
- Date: 2026-09-24
- Owners: Maintainers
## Context
`O1keyImageGenerator` offered both an in-panel prompt editor and a separate `external_prompt` STRING socket. The socket added a second prompt source, changed panel behavior when connected, and required a separate execution path when the panel button was used. The requested node interaction uses the in-panel editor only.
## Decision
Keep the serialized `prompt` widget and its existing index. Remove `external_prompt` from the V3 schema, execution signature, and frontend panel behavior. On workflow load, remove an old `external_prompt` input and its specific graph link while retaining the saved in-panel prompt and unrelated links.
## Consequences
Older workflows that relied on an upstream STRING value must place their prompt text in the panel. The upstream node remains in the graph, and any other connections from it remain intact. Standard ComfyUI execution and panel jobs now read the same prompt widget.
## Alternatives considered
Hiding the socket while retaining the backend override would leave an invisible second prompt source in saved workflows. Keeping the socket solely for old workflows would preserve the interaction the user requested to remove.
## Validation
The image generator schema and execution tests verify the single prompt source. Frontend tests verify empty-prompt validation and idempotent removal of old socket links while preserving unrelated graph links.
@@ -0,0 +1,23 @@
# 0010: Sync unified GPT Image controls and remove moderation
- Status: Accepted
- Date: 2026-09-24
- Owners: Maintainers
## Context
The standalone GPT Image node supports two additional GPT Image 2.5 quality levels and counts 18. The unified panel used four quality levels, counts 1/2/4/9, and exposed moderation after the background control. Removing a middle widget would otherwise shift saved batch and save settings.
## Decision
`O1keyImageGenerator` uses the standalone GPT Image quality resolver for GPT Image 2 and 2.5. The panel offers counts 18 for GPT models and retains the older 9-image value when loading a saved workflow. New GPT panel selections default to PNG and smart resize; existing saved output and resize values remain intact. Other model families keep their count and default behavior.
The unified generator no longer exposes or submits `内容审查强度` / `moderation`. Its old positional widget at index 14 is removed after the existing old-layout migrations finish, preserving batch and save widget values at their new positions. Legacy panel job payloads that contain `moderation` are ignored. The provider client retains optional low-level support for callers outside the unified node.
## Compatibility
The node ID, earlier widget positions, model-to-route mapping, and provider request scheduler stay stable. The workflow migration is idempotent for both old 24-value arrays and new 23-value arrays. GPT Image continues to send one provider request per selected output image with `n=1`.
## Validation
Offline node, job, standalone GPT, and frontend tests cover quality conversion, count options, omission of moderation, and the saved-workflow migration.
@@ -0,0 +1,21 @@
# 0011: Remove Nano Banana prompt optimization and batch colour correction
- Status: Accepted
- Date: 2026-09-24
- Owners: Maintainers
## Context
The standalone and batch Nano Banana nodes added a browser-side prompt optimization button. The batch node also offered optional colour correction after generation. The batch control was the last serialized widget, so removing it requires migration for saved workflows.
## Decision
Remove the Nano-specific prompt optimization extension and the batch node's colour correction input and post-processing. Keep the unified image generator's `AI帮写` action and the GPT Image batch node's colour correction behavior.
## Compatibility
`migrateWorkflow.js` removes the retired batch correction value before ComfyUI configures widgets. It then adds the `不缩放` default only when an old workflow lacks a resize value. Running the migration twice leaves the same values. Existing batch image quality, naming, and resize values stay in place.
## Validation
Node schema and execution tests cover the absent correction input and unchanged generated image. Frontend tests cover removed extension and old batch workflow values with both default and smart resize.
@@ -0,0 +1,21 @@
# 0012: Place batch Nano Banana save settings and seed last
- Status: Accepted
- Date: 2026-09-24
- Owners: Maintainers
## Context
The batch Nano Banana node displayed seed before file-save controls and displayed resize after them. Saved workflows serialize these widgets by position, so changing their order without migration would assign the old values to the wrong inputs.
## Decision
Place resize after the dynamic reference inputs, followed by output format, quality, naming rule, and save path. Place seed last. Keep all input IDs, defaults, and execution behavior.
## Compatibility
The frontend first applies earlier colour-correction removal, missing-resize defaults, and string-quality conversion. It then recognizes the old six-value tail and rewrites it into the new order. The new tail is not changed on later loads. The rule uses the tail pattern so the number of dynamic folder paths does not matter.
## Validation
Schema and frontend tests cover final widget order, old workflows without resize, saved string quality, smart resize, three folder paths, preserved save settings, and repeat migration.
@@ -0,0 +1,21 @@
# 0013: Remove batch Nano Banana random image selection
- Status: Accepted
- Date: 2026-09-24
- Owners: Maintainers
## Context
The batch Nano Banana path selector exposed `图片随机抽取`. A selected path became a random image pool instead of participating in ordinary image pairing. Removing the control changes both serialized dynamic widgets and task assembly.
## Decision
Remove the random-selection widget and its pool-loading and image-draw logic. All filled folder paths now participate in the selected pairing mode. Keep the node ID, path selector, pairing choices, fixed references, save controls, and seed.
## Compatibility
The frontend drops the former widget value after applying older layout migrations. It removes a connected random-selection input and its graph link and shifts later target slot indices. API workflows that still send the retired value have it ignored. An old workflow with multiple paths and `不配对` must select a pairing mode before running; the node reports this rather than silently choosing one.
## Validation
Offline node tests cover schema and execution forwarding. Frontend tests cover saved one-path and multi-path widget arrays, idempotence, and connected-input link cleanup.
@@ -0,0 +1,25 @@
# 0014: Publish the current code as a new release baseline
- Status: Accepted
- Date: 2026-09-24
- Owners: Maintainers
## Context
The public Gitea release tree still reflects the older GitHub package. The maintainer requested that the current working code replace it as the new published baseline. The current registry contains 39 node IDs, while the previous release contains 37. Twelve old IDs have no implementation in the current code.
## Decision
Publish the current worktree as a new commit on top of the existing Gitea `main` history. Keep the current node registry and do not restore retired implementations solely for this release. The retired IDs are `KlingVideo`, `KlingFirstLastFrame`, `KlingMotionControlTest`, `AspectRatioPreset`, `Seedance`, `KVideoFirstLast`, `KVideoImage2Video`, `K3VideoFirstLast`, `K3MotionVideoCheck`, `NanoBananaV2`, `NanoBananaV2Batch`, and `O1keyColorRemoveBG`.
## Consequences
Saved workflows containing those retired IDs will report missing nodes. Users should keep a copy of such workflows and either use the previous release in a separate installation or replace the missing nodes manually. Existing IDs retained in the new registry remain unchanged; migrations for their widget changes remain in `web/js/migrateWorkflow.js`. Later releases should not remove additional published IDs without a separate compatibility decision.
## Alternatives considered
Restoring all retired providers and nodes would reintroduce code the current package intentionally removed. A semantic mapping from those IDs to newer nodes is not established, so an automatic migration could silently change workflow behavior.
## Validation
Compare the old and new `NODE_CLASS_MAPPINGS` keys, run the offline tests, compile the Python files, and import the package in the bundled ComfyUI environment before publication.
+28
View File
@@ -0,0 +1,28 @@
# Architecture decision records
Use ADRs for decisions that constrain future changes: node ID compatibility, package boundaries, frontend loading strategy, persistent storage, dependencies, and supported migration paths.
## Process
1. Copy `0000-template.md` to the next available four-digit number.
2. Use a short kebab-case title, for example `0002-split-server-routes.md`.
3. Set status to `Proposed` while discussion is open.
4. Change status to `Accepted`, `Superseded`, or `Rejected` when decided.
5. Link superseding ADRs in both directions; do not rewrite old decisions as if history changed.
## Current decisions
- [0001: AI-native repository guidance and documentation](0001-ai-native-repository-guidance.md)
- [0002: Save-node ownership of generated image formats](0002-save-node-image-format-ownership.md)
- [0003: Generator-owned image save configuration](0003-generator-owned-image-save-configuration.md)
- [0004: Native-recoverable image workflow metadata](0004-native-recoverable-image-workflow-metadata.md)
- [0005: Persistent image-job history summaries](0005-persistent-image-job-history-summaries.md)
- [0006: Grok Video API and workflow migration](0006-grok-video-api-and-workflow-migration.md)
- [0007: Panel-driven independent video jobs](0007-panel-driven-video-jobs.md)
- [0008: Native save nodes for panel video outputs](0008-native-video-save-outputs.md)
- [0009: Remove the unified image generator's external prompt input](0009-remove-unified-image-external-prompt.md)
- [0010: Sync unified GPT Image controls and remove moderation](0010-unified-gpt-parameters-and-moderation-removal.md)
- [0011: Remove Nano Banana prompt optimization and batch colour correction](0011-remove-nano-prompt-optimization-and-colour-correction.md)
- [0012: Place batch Nano Banana save settings and seed last](0012-batch-nano-save-and-seed-widget-order.md)
- [0013: Remove batch Nano Banana random image selection](0013-remove-batch-nano-random-image-selection.md)
- [0014: Publish the current code as a new release baseline](0014-new-release-code-baseline.md)
+108
View File
@@ -0,0 +1,108 @@
# Development workflow
## Environment
The primary development target is the portable Windows installation containing this plugin. Run commands from the plugin root and use its embedded interpreter:
```powershell
..\..\..\python_embeded\python.exe --version
..\..\..\python_embeded\python.exe tests\run_all.py
```
Do not install packages into a system Python when validating the portable build.
The left sidebar update panel uses `utils/updater.py` through `/o1key/update`. Its release source is `https://git.o1key.com/publisher/comfyui_o1key.git`, independent of the installation's `origin`. Keep its Git operation fast-forward only, preserve local files, and return an actionable `code`, `error`, and `suggestion` for expected failures. Run `tests/test_updater.py` and `tests/test_o1key_update_button.mjs` after changing this path; restart ComfyUI to load backend route changes.
## Add or change a node
1. Choose the closest module in `nodes/`; create a new module only for a distinct responsibility.
2. Define a stable, globally unique node ID. For V3 nodes, make `Schema.node_id` match the root mapping key.
3. Keep input validation before uploads and paid API requests.
4. Move reusable provider logic into `clients/` and reusable infrastructure into `utils/`.
5. Export the class from `nodes/__init__.py`.
6. Add it to root `NODE_CLASS_MAPPINGS` and `NODE_DISPLAY_NAME_MAPPINGS`.
7. If widget order changes, add an idempotent migration to `web/js/migrateWorkflow.js`.
8. Add an offline test and update user/architecture docs when behavior is visible.
For `O1keyPromptMultiFunction`, keep the original first two widgets unchanged and retain backend aliases for the legacy `随机抽取1套` / `随机抽取多套` values. The visible mode remains `随机抽取n套`; migration must map old single-random workflows to count `1`. Its dynamic extension may hide inactive selection widgets but must retain their values and serialization. Future selection controls must remain append-only, and every multi-prompt result must use a standalone `---` line so downstream batch nodes retain the same parsing contract.
## Add or change a provider client
For Omni Flash, keep `O1keyOmniFlashVideo` as the sole node ID. Its `execute` validates all modes and media before upload or paid submission, then returns a native `VIDEO`. Ordinary generation hard-codes `omni_flash_10s`; edit mode uses `omni_flash_abra_edit`. If changing the widget schema, keep the migration that removes the former model value at index 2 so saved workflows remain aligned. The button only queues the current node using ComfyUI's native queue. O1Key's JSON `/v1/videos` gateway requires `input_reference` to be a string for one image; send several references as repeated multipart fields, never a JSON array under `input_reference`. Keep provider URLs and credentials out of widgets, workflow metadata, and logs. Use `tests/test_omni_flash.py` and `tests/test_omni_flash_frontend.mjs` for offline regression coverage.
- Normalize authentication and base URL selection through `utils/config.py`.
- Use shared retry, interruption, polling, upload, and download helpers before creating provider-local variants.
- Convert provider-specific failures into actionable messages without printing secrets or full payloads.
- If the class is part of the package API, add it to the lazy `_EXPORTS` table in `clients/__init__.py`.
- Test payload construction, success envelopes, failure envelopes, retryability, and cancellation offline.
## Add a server route
- Keep `/o1key/*` route names unique and explicit.
- Validate JSON types, identifiers, filenames, and root containment before accessing files.
- Return structured JSON errors with meaningful HTTP status codes.
- Never return the stored API key; existing configuration routes expose only presence/masked state.
- Register reusable route groups from a utility module when they grow beyond a cohesive section.
## Add a frontend extension
- Remember that adding a `.js` file under `web/` activates it automatically.
- Prefer one extension per coherent UI feature.
- Make setup idempotent across frontend reloads.
- Prefix extension names, DOM IDs, CSS IDs, and events with `o1key`.
- Use the existing ComfyUI `app` and `api` modules; do not add a bundler for a small extension.
- Add or extend a Node-based test for logic that can run without a browser.
- For display-only Combo aliases, set `widget.options.getOptionLabel` in an idempotent frontend extension; keep the actual option values unchanged so prompt payloads and saved workflows remain compatible.
Panel-triggered image batches must be represented in ComfyUI's native task queue, not by task-state text or a cancel action inside `O1keyImageGenerator`. Keep the jobs API bridge idempotent, retain a distinct queue item and expected-output count per batch, route native single and bulk cancellation to `/o1key/image/jobs/{batch_id}/cancel`, and leave the generator action available for consecutive submissions. Preserve native queue/history records returned by ComfyUI when merging o1key records, hydrate persisted terminal summaries before returning the first history page, and keep queue clearing semantics limited to waiting batches. History persistence must remain bounded, atomic, credential-free, and synchronized with native delete/clear actions; a persistence failure must never change the result of a paid generation or successful image save.
`O1keyVideoGenerator` is intentionally different from the image generator: its panel jobs must not enter or imitate ComfyUI's native queue. Keep paid work exclusively behind `/o1key/video/jobs`; native generator execution may resolve completed local descriptors but must never submit or retry generation. One click must create one connected native `SaveVideo`, plus one connected native `SaveImage` only when `return_last_frame` is enabled, and one independent server job record. Dispatch completed safe descriptors to those exact native nodes and persist enough safe association state to restore previews or resume polling after workflow reload. Keep the released `O1keyVideoResult` registered as deprecated compatibility support, but never create it for new panel submissions. Re-enable the action as soon as submission returns so later clicks remain independent. Provider adapters belong behind `o1key_video_catalog.py` and `o1key_video_jobs.py`: add the capability entry, scalar/media validation, request-body builder, executor dispatch, frontend controls, and offline tests together. Seedance automatic material creation must go through `seedance_assets.py`, map the video route to the matching HC or Doubao asset namespace, wait for `Active`, and submit `asset://` references only. Keep material concurrency bounded independently of video-job concurrency, preserve input order after concurrent preparation, and let terminal results retain only safe resolved IDs so retries do not recreate assets. Content reuse may persist fingerprints and IDs, but never upload URLs, local paths, credentials, or complete provider envelopes. Validate all limits before uploads or paid requests, snapshot only sanitized Comfy input descriptors, and persist neither credentials, Base64, signed URLs, nor absolute input paths.
`SeedanceAutoPass` is the graph-connected counterpart to the panel generator. Preserve its released Chinese input IDs or provide an explicit idempotent `migrateWorkflow.js` rewrite when a user-requested layout change moves positional widgets. Translate displayed values into `normalize_seedance_parameters`, use the shared reference-dimension/count constants, delegate request-body construction to `build_seedance_video_body`, and submit through `SeedanceClient`. Its two displayed modes must resolve to the canonical `text / multimodal / first_frame / first_last_frame` values from the connected media count. Automatic assets validate every connected tensor or media source before the bounded three-item HC/Doubao preparation stage; manual assets skip upload and pass normalized IDs through the shared body's `assets` fields.
In automatic multimodal mode, render reference images, videos, and audio cards in one shared horizontal material track. Keep one mixed-media upload control in the track header, and do not render trailing type-specific image, video, or audio add cards. Detect each selected file's media type before upload, retain type-specific validation and type-local ordering, and preserve the existing `reference_images` / `reference_videos` / `reference_audios` workflow fields; the compact layout is presentation-only and must not merge or reorder those serialized manifests.
The video generator defaults new nodes to multimodal mode. Its custom panel is created before saved widget values may be applied, so restore every visible control and material track from the configured node state in `onConfigure`; `loadedGraphNode` must repeat that restoration after graph loading. Never let panel initialization write defaults over a returning workflow's prompt, parameters, manual asset IDs, or `o1keyVideoMedia` properties.
Keep source/target batching behind the append-only `批量出图` switch. The visible panel must use the industry-neutral names `素材图`, `目标图`, `整组素材 → 多个目标`, `全匹配(素材 × 目标)`, and `单图批量(每张素材独立)`; the historical widget and first two mode values remain compatibility identifiers. Render references as a `102 × 102` horizontal card track with upload and canvas-picker actions in the track header. Every track must retain a trailing add card after its uploaded and pending thumbnails; both that card and the complete track accept external image-file drops. Stop accepted file-drop propagation before ComfyUI's canvas handler while leaving internal thumbnail sorting drags untouched. A drop handled by a nested upload card must clear the `drag` class from both that card and its parent track; the track must not retain a coloured background or outline after the file is released. Track images must use the bounded `/o1key/image/thumbnail` representation, must not decode pending local blobs, and must retain the original `/view` descriptor only for the lightbox; never substitute thumbnail bytes into manifests, prompt optimization, or provider requests. Normal mode shows one `参考图` track; paired batch modes show separate `素材图` and `目标图` tracks, while single-reference mode hides the target track. Header hints must include the current manifest count and the active participation rule. Make the complete thumbnail the pointer drag target rather than rendering a separate handle. During a drag, dim and scale the source card, label it `移动中`, highlight its track, and use a thick insertion edge plus target displacement; suppress the immediate post-drag preview click. Keep `Alt + Arrow/Home/End` ordering on the focused thumbnail as the keyboard path. With the switch off, ignore the stored target manifest and preserve the historical reference/prompt path. With it on, use `expand_image_generation_tasks` as the shared ordering authority for direct and background execution: prompts first, then pairings or source indexes, then per-pair copies. Batch manifests cap at fifty images, but provider requests remain capped at ten references: group mode therefore allows nine sources plus one target, while its target manifest may hold fifty; Cartesian and single-reference source manifests may hold fifty because each task selects one source. Snapshot only manifests used by the active mode, never serialize file paths into a workflow, and reconstruct task-specific references from validated indexes. Load large batch manifests only for active tasks, serialize reference preprocessing to bound full-resolution decode pressure while preserving request order, and release task-local images/tensors immediately after their last consumer. A failed-slot retry must flatten only that slot's exact references into a one-task normal-mode request. Keep request-position thumbnail badges derived from the same ordering. Treat ordered manifests as the source of truth for sorting and persist moves immediately. Uploaded thumbnails must reuse ComfyUI's `MediaLightbox` interaction contract; do not expose signed URLs or open a browser tab. Keep the batch summary hidden until all inputs required by the active mode are present, then show the exact task count, per-item count, prompt multiplier, and total.
Keep reference-image editing frontend-only and descriptor-based. The top-left thumbnail action may crop and flatten visual mask/brush/arrow marks plus one transformed upper sticker layer into a new PNG, but it must not create a `MASK` value or mutate the GPT mask widget. Keep sticker placement and arrow endpoints in source-image coordinates so preview resizing cannot alter the exported geometry; the arrow tip must remain the exact pointer-release position. Compose annotations above the sticker and never export selection handles. Revoke local sticker object URLs on close and never serialize them. Save through the shared serialized native upload queue with `overwrite=false`, replace the original descriptor only after upload succeeds, and leave the source file untouched. Keep crop math, sticker fit/hit geometry, arrow-head geometry, and filename normalization independently testable in `tests/test_o1key_reference_image_editor.mjs`; run that test together with `test_o1key_image_generator_frontend.mjs` after editor changes.
When changing `O1keyImageSave`, preserve the mutually-exclusive preview rules documented in `architecture.md`: the bounded slot-grid DOM widget exists only while a batch is active or has incomplete slots, and the native preview is hidden in that state; after all slots succeed, remove the temporary widget from `node.widgets` and restore the native preview. Publish a partial slot result only after its complete file is atomically present in ComfyUI `temp`, and send only sanitized descriptors; do not promote it to permanent output or dispatch native execution results before the batch reaches a terminal state. Do not leave an invisible DOM widget behind: ComfyUI assigns expandable DOM widget rows an `auto` grid track and `flex: 1`, which steals resize space from the image preview. Never stack both renderers. Leave ComfyUI's Vue `NodeContent`/`ImagePreview` flex, minimum-height, element-size observer, responsive grid, and `object-contain` behavior intact so the native preview fills the remaining node area during resize. Keep structured request indexes through temp promotion, preserve exact failed-slot prompts for one-image retries, and serialize only sanitized slot/output metadata. Failed-slot buttons must be disabled by their own slot state rather than the save node's aggregate busy flag; registrations, saving guards, and terminal guards must be keyed by batch ID so concurrent retries cannot overwrite or finish one another. The save node has no configuration widgets and remains the sole permanent writer; direct execution reads generator settings attached to the IMAGE tensor, while panel jobs use the immutable settings snapshot in their job record. Preserve provider bytes when possible, keep custom locations relative to the configured output root, and use the native prompt/`extra_pnginfo` metadata convention. Obtain panel prompt and workflow metadata from the same `app.graphToPrompt()` result. Never save a workflow-bearing JPEG: ComfyUI does not restore workflows from JPEG, and EXIF may silently exceed its segment limit; promote that result to PNG and verify the complete `prompt` and `workflow` text fields instead.
When adding a model to `O1keyImageGenerator`, update `utils/o1key_image_catalog.py`, the frontend model descriptions/capability switch, the job payload validator, and the model-family executor together. Preserve the existing route values even when their labels change. Provider-specific multi-image parameters must not bypass the unified scheduler: each selected output image is one concurrent provider request, and GPT Image always receives `n=1`. Keep GPT-only controls appended after the original generator widgets so old workflow arrays do not shift.
For Seedream, keep the stable workflow value `Seedream 5.0 Pro` separate from API model ID `dola-seedream-5-0-pro-260628-ep`. Treat the unified panel's default `智能` resolution as an omitted provider `size`; resolve explicit `1K / 2K` plus aspect ratio through the catalog's exact size matrix, submit one image per async task with `n=1`, pass the selected `png / jpeg` as `output_format`, and always send `watermark=false` without adding a widget. Upload ordered references through `{base_url}/v1/o1key/uploads` and submit only the returned HTTPS URL strings; never log those temporary URLs. Layer decomposition is the exception to the normal size/prompt rules: accept exactly one reference, optional prompt text, `智能 / 1K / 1.5K / 2K`, forced PNG, and no batching. Keep all returned images in `z_index` order, preserve transparent provider bytes, expose alpha as MASK in direct execution, and sanitize metadata before it reaches descriptors, history, or workflows.
Keep batch-prompt behavior model-independent. A `---` line is the only prompt separator, task order is prompt-major, and both direct execution and panel background jobs must calculate `valid prompt segments × pairing count × images per pair` and reject totals above the shared 1000-task ceiling before provider calls. Pairing count is one in normal mode. Panel background jobs and direct execution limit each batch to nine in-flight provider requests.
Do not add a download semaphore or an independent download-concurrency limit to `O1keyImageGenerator`. Ready result URLs must download immediately; the active provider-task scheduler is the only natural bound. The shared Nano transport may retain its optional semaphore for other released nodes, but unified direct and background paths must pass no limiter.
For GPT Image, keep resolution/aspect-ratio pairs in the catalog size matrix rather than duplicating exact pixel labels in the panel. `智能` is the first and default unified resolution for every model; it must become `None` at the provider boundary so Nano Banana, GPT Image, and Seedream request builders omit `size` entirely. Explicit tiers retain their existing mappings. Keep active user controls in one persistent vertical list with labels on the left and controls on the right, but apply the model capability matrix when synchronizing the model: thinking level and online search are Nano Banana 2-only, resize mode is shared by Nano and GPT Image, and quality, background, API output format, plus mask are GPT Image-only. Online search defaults to `关闭`; omit `google_search` unless it is `打开`, then submit top-level `google_search: true`. The local `格式` field is visible only for Banana models; `命名规则`, `filename_prefix`, and `保存位置` are shared. Hide unsupported fields in place instead of rebuilding them so values and listeners survive model switching. Display `JPEG` but serialize and submit lowercase `jpeg`; new GPT panel selections use `png` while saved values remain intact. Reject `transparent + jpeg` before any paid request and keep JPEG out of the frontend list while transparency is active.
Keep prompt writing behind dedicated server routes. The unified image and video prompt actions must retain both the magic-wand icon and the visible `AI帮写` label so their purpose is understandable without hover; the image compatibility route remains `/o1key/image/prompt-optimize`, while video uses `/o1key/video/prompt-write` and a separate video-only system preset. The frontend must submit input descriptors rather than API credentials or filesystem paths. The server must enforce the input-root boundary, preserve reference order, cap the exact multimodal request body, use `gpt-5.6-sol` with high reasoning, and return only the optimized prompt. Tests must mock the upstream completion call and generate reference images at runtime; never add encoded reference fixtures or log complete data URLs.
Keep unified-generator post-processing, batch, and save widgets in their established order; migrate any removed middle widget before ComfyUI restores saved values. The retired `色彩纠正` value is removed from legacy index 13 before later values are mapped; `背景` occupies index 13; the retired `内容审查强度` value is removed from legacy index 14 after the old-layout migrations. Batch fields occupy indexes 1416, generator save fields 1720, `在线搜索` index 21, and Seedream `图层拆分` index 22. `migrateWorkflow.js` must first remove the retired value and fill older generator defaults, then move the connected legacy save node's four positional values into those slots, append later defaults, and clear the old save-node array idempotently. The original `IMAGE` output remains first; new provider-specific outputs must be appended. Display-only collapsing of provider-specific outputs must restore them when their mode is enabled and must never hide a connected output. Layer-decomposition background generation must save the returned descriptors once, then route the base descriptor to the output-0 save node and the remaining descriptors to its paired output-1 layer save node. The frontend, direct V3 execution path, and background job payload must use the same save settings; GPT forces local `格式=原始`.
Keep the unified generator's canvas-image picker descriptor-based. Discover current-graph images from public execution outputs and native preview descriptors, fetch them through `/view`, and reuse the serialized `/upload/image` queue before adding them to a reference role. Do not pass `output` or `temp` descriptors to the image-job API, do not serialize picker state, and keep candidate text and filenames out of `innerHTML`.
## Documentation and decisions
- Keep `README.md` focused on users.
- Update the matching file in `docs/` in the same change as an architectural or operational behavior change.
- Record decisions that constrain future work as ADRs. Copy `docs/decisions/0000-template.md`, choose the next number, and describe consequences rather than meeting history.
`SeedanceAutoPass` keeps its mode and asset-policy selectors as ordinary combos for frontend compatibility. `seedanceAutoPassDynamic.js` removes inactive media sockets through `removeInput`: multimodal exposes only the three native Autogrow reference groups, while frame mode exposes only `首帧图片` and `尾帧图片`. Cache and suspend the reference Autogrow configurations before structural edits, bypass their connection callbacks during those edits, and restore them on return to multimodal; socket names remain unchanged and ordinary converted-widget inputs are untouched. Reconcile saved sockets again in `loadedGraphNode`. Switching modes disconnects removed sockets and does not restore their links. Its `素材创建模式` selector must remain visible and default to `关闭`; only `打开` reveals and uses the numbered single-line material-ID widgets (30/10/10). The extension uses the same progressive one-empty-row rule as `SeedanceMultiModal`, without removing widgets, clearing values, or changing serialization order. Migrate the former `素材创建` name and `自动创建 / 手动` values idempotently, expand the previous aggregate ID strings, and preserve the trailing ordinary web-search, seed, and last-frame parameters. The backend accepts old names and aggregate ID kwargs as legacy aliases.
`SeedanceAutoPass` temporarily hides the `联网搜索` and `返回末帧图片` controls with the same zero-height widget mechanism. Keep both schema entries and serialized values/order for compatibility; do not force-reset existing workflow values. Apply visibility on creation, graph loading, and material-ID/mode updates, before recalculating height. `seed` and the asset-mode selector remain visible. This restriction is scoped to the single all-in-one node, not the batch node or unified video panel.
## Completion checklist
- Direct references searched with `rg`.
- Registration and frontend migration synchronized.
- Offline regression test added or updated.
- Relevant isolated test passes.
- Full isolated suite passes for cross-cutting changes.
- Import smoke test passes for startup or dependency changes.
- `git diff --check` reports no content errors.
- Generated caches and local credentials remain untracked.
+74
View File
@@ -0,0 +1,74 @@
# 文件清理与结构优化说明
清理日期:2026-08-29
本次仅整理当前工作区,没有创建 Git 提交,也没有覆盖清理前已经存在的业务代码修改。
## 清理结果
- 插件文件(不含 `.git`)由约 5.00 MB 降至约 1.86 MB。
- 删除所有 Python 字节码和 `__pycache__`;测试验证后生成的缓存也已再次清除。
- 根目录仅保留插件入口、配置、说明、依赖、更新工具和源码目录。
- 保留 `.git``.config``cases/` 中的有效案例,以及所有已注册节点和前端运行资源。
## 已删除的临时与工具文件
- 误生成文件:`45deea2``=1.2.0``screen_capture.png`
- 本地工具状态:`.agents/``.claude/``.codex/``.playwright-mcp/`
- 编辑器专用旧配置:`.cursorrules`
- 无效占位文件:`cases/.gitkeep`
- 所有 `__pycache__/``*.pyc`
## 已删除的历史资料
- `notes/` 中的阶段性重构记录和旧版笔记迁移样本。
- `mockups/note-sidebar-sketch.html`
- `V3_DEV_GUIDE.py`
- `SEEDANCE_ELEMENT_QUICKSTART.md``SEEDANCE_ELEMENT_README.md``SEEDANCE_NEW_FORMAT.md`
笔记面板仍会使用前端内置样本初始化,并将用户数据保存到 ComfyUI 的 `input/o1key-notes.json`,不再从插件目录迁移旧文件。
## 已删除的死代码
- 旧更新通知链:`utils/update_checker.py``version.txt``web/js/updateNotifier.js`
- 已移除颜色去背节点的遗留工具:`utils/color_key.py`
- 未被活动节点调用的客户端:
- `clients/base_async_provider.py`
- `clients/gemini_async_provider.py`
- `clients/kling_client.py`
- `clients/openai_client.py`
- 未注册且没有可用入口的节点:`nodes/seedance_firstlast.py`
相关的包导出、前端工作流线路集合和旧笔记迁移逻辑已同步清理,没有留下悬空引用。
## 测试目录整理
- 将仍有价值的离线测试从根目录移动到 `tests/`
- 删除带硬编码旧路径或会调用真实接口的手工脚本:
- `test_autopass_live.py`
- `test_upload.py`
- `test_upload_real.py`
- 删除已被新测试覆盖、断言已经过期的 `test_seedance_element.py`
- 新增 `tests/run_all.py`,让每个测试文件在独立进程中运行,避免 ComfyUI 测试桩污染其他测试模块。
运行全部离线测试:
```powershell
..\..\..\python_embeded\python.exe tests\run_all.py
```
## 代码结构优化
- `clients/__init__.py` 改为延迟导入,加载单个客户端时不再初始化所有模型供应商客户端。
- `utils/__init__.py` 改为延迟导入,减少插件启动期间不必要的图像和文件工具初始化。
- `.gitignore` 增加本地 AI 工具、Playwright、pytest 和覆盖率缓存规则。
- `README.md` 删除对不存在的配置、Mac 更新和编码修复脚本的说明,并改为当前可用的令牌管理界面与 `update.bat` 使用方法。
- 保留 `WEB_DIRECTORY = "./web"`;除确认失效的更新通知脚本外,所有前端扩展继续由 ComfyUI 自动加载。
## 验证结果
- Python 静态编译检查通过。
- 14 个隔离测试文件全部通过,共 107 个 Python 测试用例。
- `test_o1key_image_generator_frontend.mjs` 前端测试通过。
- 使用便携版 ComfyUI Python 成功导入插件。
- 插件成功注册 36 个节点,`WEB_DIRECTORY``./web`
+53
View File
@@ -0,0 +1,53 @@
# Testing
## Why tests run in isolation
Several tests replace ComfyUI modules in `sys.modules` with lightweight stubs. A single `unittest discover` process allows those stubs to leak into later test modules. `tests/run_all.py` therefore launches every Python file in a fresh interpreter and runs the frontend test separately when Node.js is available.
## Full offline suite
```powershell
..\..\..\python_embeded\python.exe tests\run_all.py
```
The suite must not contact real O1Key/provider endpoints or read the developer's `.config`.
When Node.js is available, `run_all.py` also runs every `tests/test_*.mjs`
frontend regression test in filename order.
## Focused tests
```powershell
..\..\..\python_embeded\python.exe tests\test_nano_banana_dynamic_inputs.py
..\..\..\python_embeded\python.exe tests\test_seedance_autopass_v3.py
node tests\test_o1key_image_generator_frontend.mjs
node tests\test_o1key_reference_image_editor.mjs
node tests\test_video_trim_frontend.mjs
```
## Static and import checks
```powershell
..\..\..\python_embeded\python.exe -m compileall -q __init__.py prestartup_script.py models_config.py clients nodes utils tests
..\..\..\python_embeded\python.exe -c "import sys; sys.path.insert(0, '..'); import comfyui_o1key; print('registered_nodes=', len(comfyui_o1key.NODE_CLASS_MAPPINGS)); print('web_directory=', comfyui_o1key.WEB_DIRECTORY)"
git diff --check
```
`compileall` creates ignored `__pycache__` directories. They do not belong in Git.
## Validation matrix
| Change | Minimum validation |
| --- | --- |
| Node schema or mapping | focused node test, full suite, import smoke |
| Client payload or response parsing | focused client test, retry/failure cases |
| Upload/download/polling | cancellation, size limit, retry, and failure tests |
| `__init__.py`, requirements, package exports | compile, full suite, import smoke |
| `web/js/o1keyImageGenerator.js` | frontend Node test |
| `web/js/o1keyReferenceImageEditor.js` | editor and image-generator frontend Node tests |
| `web/js/videoTrim.js` | video-trim frontend Node test |
| Workflow widget order | migration regression test and manual old-workflow check |
| Documentation only | link/path review and `git diff --check` |
## Manual checks
Use a real ComfyUI session only when behavior cannot be proven offline, such as canvas interaction, sidebar layout, or native preview rendering. Never turn a credit-consuming live request into a default automated test.