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
+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.