Replace the prior release tree with the current plugin, frontend, tests, and documentation. Document retired node IDs and the public Gitea update source.
24 KiB
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:
..\..\..\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
- Choose the closest module in
nodes/; create a new module only for a distinct responsibility. - Define a stable, globally unique node ID. For V3 nodes, make
Schema.node_idmatch the root mapping key. - Keep input validation before uploads and paid API requests.
- Move reusable provider logic into
clients/and reusable infrastructure intoutils/. - Export the class from
nodes/__init__.py. - Add it to root
NODE_CLASS_MAPPINGSandNODE_DISPLAY_NAME_MAPPINGS. - If widget order changes, add an idempotent migration to
web/js/migrateWorkflow.js. - 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
_EXPORTStable inclients/__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
.jsfile underweb/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
appandapimodules; 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.getOptionLabelin 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 14–16, generator save fields 17–20, 在线搜索 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.mdfocused 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 --checkreports no content errors.- Generated caches and local credentials remain untracked.