46 KiB
PRD: Bottom Composer as Current Node Console
Version: 1.2
Last Updated: 2026-05-03
Status: Draft synced with NewApiWG gateway notes
Scope: popiart-node-canvas
1. Summary
The bottom composer should become the primary control surface for the currently focused node. For generation nodes it edits prompt/model/parameters; for supported processing nodes it acts as a node inspector with execution controls. It should not act as an implicit "next step" generator when an existing node is selected.
Core product rule:
The canvas owns workflow structure. The bottom composer owns the selected node's parameters and execution.
This means:
- Selecting a supported generation node opens its prompt, model, and parameters in the bottom composer.
- Selecting a supported processing node opens a node-specific inspector, not a prompt editor.
- Clicking the main generate button applies the current composer values to that selected node and reruns that node.
- Creating a next-step node must be expressed through canvas actions: dragging from an output handle, using a node plus action, or using the connection drop menu.
- When there is no selected node, the composer may create a new root generation node in the canvas center. This is the only creation mode owned by the composer.
2. Problem
The current prototype bottom composer visually matches the desired direction, but its behavior is still ambiguous:
- It currently supports only image generation nodes (
nanoBanana). - It can create a new image node or update a selected image node from the same button.
- It always opens the model browser with an image capability filter.
- It does not support all video-related node states, video model selection, processing-node inspection, or clear unsupported-node states.
- It blurs two product concepts: editing the selected node vs creating a downstream node.
For a node canvas product, implicit downstream creation from a bottom prompt box is risky because users expect graph topology to be visible and deliberate.
3. Current Architecture
3.1 Frontend Shell
- Framework: Next.js + React + TypeScript.
- Canvas:
@xyflow/reactviasrc/components/WorkflowCanvas.tsx. - State: Zustand store in
src/store/workflowStore.ts. - Bottom composer entry:
src/components/composer/GenerationComposer.tsx. - Existing right/side settings:
src/components/nodes/ControlPanel.tsx.
3.2 Graph Data Model
Node types are defined in src/types/nodes.ts.
Relevant generation node types:
nanoBanana: image generation.generateVideo: video generation.generate3d: 3D generation.generateAudio: audio generation.llmGenerate: text/LLM generation.
Relevant node data fields:
-
Image generation:
NanoBananaNodeDatainputPromptinputImagesaspectRatioresolutionmodelselectedModelparametersinputSchemaoutputImagestatuserror
-
Video generation:
GenerateVideoNodeDatainputPromptinputImagesselectedModelparametersinputSchemaoutputVideostatuserror
3.3 Store and Execution
Existing store APIs:
addNode(type, position, initialData?)updateNodeData(nodeId, data)addEdgeWithType(connection, edgeType)regenerateNode(nodeId)executeWorkflow(startNodeId?)executeSelectedNodes(nodeIds)
Execution already supports:
executeNanoBananafornanoBananaexecuteGenerateVideoforgenerateVideoexecuteGenerate3Dforgenerate3dexecuteGenerateAudioforgenerateAudio- batch paths for selected generation node types
3.4 Model Layer
The model browser is src/components/modals/ModelSearchDialog.tsx.
It already supports capability filtering:
allimagevideo3daudio
It fetches models through /api/models and maps filters to capabilities:
- image:
text-to-image,image-to-image - video:
text-to-video,image-to-video,audio-to-video - 3D:
text-to-3d,image-to-3d - audio:
text-to-audio
Default NewAPI models exist in src/store/utils/defaultImageModel.ts:
- image:
apiyi_nano_banana_2 - video:
viduq2-pro-fast
3.5 Canvas Next-Step Creation
WorkflowCanvas.tsx already supports dragging from handles and dropping on blank canvas. The ConnectionDropMenu can create downstream nodes such as:
nanoBananagenerateVideogenerateAudiooutputoutputGallery- media processing nodes
This should remain the canonical way to create visible workflow structure.
3.6 Node / Composer / Model API Relationship
flowchart LR
S["Zustand workflow store\nnodes, edges, selected state"] --> C["ComposerContext\nmode, node, nodeType, capability"]
C --> D["Composer draft\neditable local form state"]
D --> P["Adapter buildPatch\nnode-type-specific patch"]
P --> S
S --> R["regenerateNode(nodeId)"]
R --> E["Existing executor\nimage, video, 3D, audio"]
E --> A["/api/generate\nprovider gateway"]
A --> M["Model provider\nNewAPI / Gemini / fal / Replicate"]
M --> S
Ownership:
- Nodes and edges are the source of truth for creative data and workflow structure.
- Composer is a derived view plus a local draft, not a second source of truth.
selectedModel.capabilitiesdefines which node type and model filter are valid.inputSchemaandparametersdefine provider-specific controls.- Executors and API routes remain the only layer allowed to call model providers.
4. Product Principles
P1. One Focus
The bottom composer must represent exactly one target:
- no selected node: create a new root generation node
- one supported generation node selected: edit and run that node
- one supported processing node selected: inspect inputs/status and run that node
- unsupported node selected: show non-generative context or disabled state
- multiple nodes selected: show batch/selection state, not a single-node editor
P2. No Hidden Graph Mutation
If a node is selected, the main generate button must not silently create a downstream branch.
Next-step creation must happen through visible graph actions:
- drag an output handle to blank canvas
- choose a node type from the connection drop menu
- use a node-level plus action
- select the newly created node
- configure and run it from the bottom composer
P3. Selected Node Is the Source of Truth
For supported generation nodes, the composer reads from and writes to the selected node's data.
The composer should display:
- prompt
- selected model
- model capability
- aspect ratio / resolution / duration where applicable
- reference inputs
- advanced parameters when available
- status and error
P4. NewAPI Is a Provider, Not a Separate UX
The composer should use the existing selectedModel contract and model browser. It should not hardcode direct provider calls.
For NewAPI:
- model selection still writes
selectedModel.provider = "newapiwg" - generation still flows through existing node executors and API routes
- capability filtering determines which NewAPI models appear in each context
- gateway credentials are configured through
NEWAPIWG_API_KEY/NEWAPIWG_BASE_URLor project settings - UI components never call NewApiWG directly; they use
/api/models,/api/generate, and existing executors - provider-specific media mapping belongs in the NewApiWG provider adapter, not in the composer
P5. Upstream Graph Inputs Win
When a generation node has connected upstream prompt/media inputs, those graph connections are the authoritative source for execution. The composer may display them, but it must not hide or override them.
P6. Decisions Must Be Explicit
The implementation should not leave major product behavior to incidental code paths. Draft sync, prompt precedence, video parameter ownership, model switching, and selection ownership are recorded as decision documents under docs/decisions/.
5. Goals
| ID | Goal | Priority |
|---|---|---|
| G-01 | Make bottom composer a clear current-node control surface | Must |
| G-02 | Support editing and rerunning selected image generation nodes | Must |
| G-03 | Support editing and rerunning selected video generation nodes | Must |
| G-04 | Filter model browser by selected node capability | Must |
| G-05 | Allow no-selection composer to create a new root node from any model capability | Should |
| G-06 | Make canvas drag/drop the required path for downstream generation | Must |
| G-07 | Show references and existing node parameters without hiding graph topology | Should |
| G-08 | Preserve UI affordances for unsupported future capabilities without blocking MVP | Should |
6. Non-Goals
- Do not use the composer generate button to create downstream nodes when an existing node is selected.
- Do not replace the entire advanced node settings system in the first pass.
- Do not introduce a new model provider abstraction.
- Do not add new dependencies.
- Do not make real API calls during automated tests.
- Do not implement unsupported provider-specific controls unless the model schema already exposes them.
7. User Stories
US-01: Edit an Image Node
As a creator, I select an existing image generation node and see its prompt, image model, aspect ratio, resolution, references, and status in the bottom composer. When I click generate, the selected node reruns with the visible values.
US-02: Edit a Video Node
As a creator, I select an existing video generation node and see its prompt, video model, references, and video parameters in the bottom composer. When I click generate, the selected video node reruns.
US-03: Create a New Root Generation
As a creator, when no node is selected, I can type a prompt, choose any supported model, and click generate. The app creates a new root generation node matching the selected model capability and runs it.
US-04: Create a Next Step
As a creator, I drag from an image or video output to blank canvas, choose the next node type, then edit and run the newly created node from the bottom composer.
US-05: Avoid Accidental Branches
As a creator, if I select an old node and click generate, I expect the old node to rerun. I do not expect a hidden downstream branch to appear.
7.1 Composer Context and Draft Lifecycle
7.1.1 Context Source
ComposerContext is always derived from the workflow store. It is not manually stored as independent component state.
Inputs that can change the context:
- selected node ids
- selected node type
- selected node model capability
- selected node deletion
- node type support in the adapter registry
Inputs that update visible status but must not reset the draft:
- selected node
status - selected node
error - selected node
outputImage/outputVideo - execution start, completion, or failure
Model catalog async loading does not change the selected node's context by itself. If a stored selectedModel lacks capabilities, the adapter's node type determines the capability until the user selects a new model.
7.1.2 Draft Source
Composer draft is initialized from the selected node through the node adapter. The draft is reinitialized only when the composer identity changes:
- mode changes
- selected node id changes
- selected node type changes
- empty-create draft is explicitly reset after creating a node
Status and output updates must not wipe unsaved prompt or parameter edits.
7.1.3 Draft Sync Decision
The product decision is autosave.
Autosave rules:
- On blur, write dirty draft fields back to the selected node.
- Before generate, write dirty draft fields back to the selected node, then call
regenerateNode. - Before switching from selected node A to selected node B, write dirty draft fields back to node A, then load node B.
- If node A is deleted before autosave can apply, discard the draft because the target no longer exists.
- No confirmation dialog is required in MVP.
This avoids hidden data loss without interrupting canvas exploration.
7.1.4 Composer State Transition Triggers
| Trigger | Context Update | Draft Update | Notes |
|---|---|---|---|
| Select no nodes | empty-create |
keep existing empty draft unless previous target was a node | Creation draft is independent |
| Select one supported node | node-edit |
autosave previous dirty node draft, then read selected node | Adapter decides fields |
| Select one supported processing node | process-node |
autosave previous dirty node draft, then clear editable generation draft | Inspector reads directly from node data and connected inputs |
| Select one unsupported node | unsupported-node |
autosave previous dirty node draft, then clear editable draft | Show guidance only |
| Select multiple nodes | multi-select |
autosave previous dirty node draft, then clear editable draft | Batch edit disabled in MVP |
| Node data changes externally | same context | update non-dirty fields only | Prevent status refresh from wiping edits |
| Model selected in dialog | same context | update draft model and selected node model in edit mode | Capability must match current context |
| Generate clicked | same context | autosave draft before execution | Then regenerateNode |
| Generation starts | same context | no draft reset | Show running state |
| Generation succeeds | same context | no draft reset | Show output/status |
| Generation fails | same context | no draft reset | Show node error |
| Cancel/escape composer draft | same context | reload from selected node | Optional MVP action |
8. Composer States
| State | Trigger | Composer Target | Model Filter | Main Button |
|---|---|---|---|---|
| Empty create | no selected nodes | new root node draft | all | Create and generate |
| Image node edit | exactly one nanoBanana node selected |
selected node | image | Rerun selected image node |
| Video node edit | exactly one generateVideo node selected |
selected node | video | Rerun selected video node |
| Video stitch inspector | exactly one videoStitch node selected |
selected node + connected videos | none | Stitch selected processing node when at least 2 clips are connected |
| Ease curve inspector | exactly one easeCurve node selected |
selected node + connected video/settings | none | Apply selected processing node when a video is connected |
| 3D node edit | exactly one generate3d node selected |
selected node | 3d | Rerun selected 3D node, later phase |
| Audio node edit | exactly one generateAudio node selected |
selected node | audio | Rerun selected audio node, later phase |
| Unsupported node | one non-generation node selected | selected node info | none | Disabled |
| Multi-select | multiple nodes selected | selection summary | none or batch | Disabled in MVP |
9. Core Interaction Rules
9.1 No Selection
Default behavior:
- Composer shows a creation draft.
- Model browser opens with
initialCapabilityFilter = "all". - User selects an image/video/3D/audio model.
- Composer infers node type from selected model capability.
- User clicks generate.
- App creates a node at the viewport center.
- App selects the new node.
- App runs
regenerateNode(newNodeId).
Model to node mapping:
| Model Capability | Node Type |
|---|---|
text-to-image, image-to-image |
nanoBanana |
text-to-video, image-to-video, audio-to-video |
generateVideo |
text-to-3d, image-to-3d |
generate3d |
text-to-audio |
generateAudio |
9.2 Selected Image Generation Node
Default behavior:
- User selects a
nanoBanananode. - Composer reads:
inputPromptselectedModelaspectRatioresolutionparametersstatuserror
- Model browser opens with
initialCapabilityFilter = "image". - Edits write to the selected node, either immediately with debounce or on generate.
- Main button calls
regenerateNode(selectedNode.id).
The button label should be "生成" or "重新生成", but the tooltip must clarify: "重新生成当前图片节点".
9.3 Selected Video Generation Node
Default behavior:
- User selects a
generateVideonode. - Composer reads:
inputPromptselectedModelparametersstatuserror- connected image/text/audio references when available
- Model browser opens with
initialCapabilityFilter = "video". - Video controls render from known fields and model schema.
- Main button calls
regenerateNode(selectedNode.id).
The button tooltip must clarify: "重新生成当前视频节点".
9.4 Selected Video Stitch Processing Node
Default behavior:
- User selects a
videoStitchnode. - Composer switches to
process-node, notnode-edit. - Composer displays:
- connected video clip count
- audio input presence
- clip order summary from
clipOrder/ connected video edges loopCountcontrols- output readiness
statusanderror
- Composer hides prompt, model selector, reference image upload, and generation-only tools.
- Main button calls
regenerateNode(selectedNode.id)only when at least two connected videos resolve throughgetConnectedInputs.
The button tooltip must clarify: "拼接当前视频节点".
9.5 Selected Ease Curve Processing Node
Default behavior:
- User selects an
easeCurvenode. - Composer switches to
process-node, notnode-edit. - Composer displays:
- connected video input state
- inherited vs local curve settings
- current preset or custom bezier handles
outputDurationcontrol- output readiness
statusanderror
- Composer hides prompt, model selector, reference image upload, and generation-only tools.
- Main button calls
regenerateNode(selectedNode.id)only when at least one connected video resolves throughgetConnectedInputs.
The button tooltip must clarify: "应用当前缓动曲线节点".
9.6 Unsupported Node Selection
Examples:
imageInputvideoInputannotationpromptoutputoutputGallery
Behavior:
- Composer must not imply that generate will create the next step.
- Composer may show compact context:
- node type
- filename / media preview if available
- "从节点端口拖拽以创建下一步"
- Main generate button is disabled unless the node has an explicit supported processing inspector.
9.7 Multi-Selection
MVP behavior:
- Show selected count.
- Show a compact empty state: "Selected {count} nodes. Batch editing is not available in this version."
- Hide single-node parameter editing.
- Main generate button disabled.
Future behavior:
- Batch rerun selected supported generation nodes.
- Batch parameter edit only when all selected nodes share the same node type.
10. Next-Step Workflow
Next-step generation is a canvas operation, not a composer operation.
Preferred flow:
flowchart LR
A["User drags from source node output"] --> B["ConnectionDropMenu opens"]
B --> C["User selects Generate Image / Generate Video"]
C --> D["Canvas creates downstream node"]
D --> E["Canvas connects edge"]
E --> F["Canvas selects new node"]
F --> G["Bottom composer edits new node"]
G --> H["Generate button reruns selected new node"]
Engineering requirement:
- After
ConnectionDropMenucreates a new generation node, the app should select the new node automatically so the composer immediately targets it. - If the new node is not auto-selected, the user must manually click it, which breaks the intended flow.
- Selection ownership is centralized in the workflow store. React Flow selection is treated as a UI mirror of
nodes[].selected, not an independent source of truth. - Typed processing handles must not fall back to generic media targets. Dragging from an
easeCurveSettings output should offer Settings-compatible targets such aseaseCurveorrouter, notvideoInput.
11. Parameter Display
11.1 Image Controls
Required MVP controls:
- model selector
- prompt textarea
- aspect ratio
- resolution
- count display or selector
- reference thumbnail strip
- status/error indicator
- generate button
Current fields:
inputPromptselectedModelaspectRatioresolutioninputImagesparameters
11.2 Video Controls
Required MVP controls:
- model selector
- prompt textarea
- reference thumbnail strip
- generated video status/error
- generate button
- schema-driven advanced parameters when available
Semantic video controls are first-class product fields, even if the initial implementation stores them temporarily under parameters during migration.
First-class video fields to add before exposing fixed controls:
- aspect ratio
- resolution
- duration seconds
- fps
- sound enabled
Provider-specific controls remain schema-driven:
- camera/motion preset
- motion strength
- seed variants that do not map cleanly across providers
- provider-only quality or mode flags
Important constraint:
The current GenerateVideoNodeData does not define first-class fields such as duration, fps, or resolution. The decided direction is to add semantic first-class fields and let executors map them to provider payload names. Fixed video controls must not ship until they write to execution data.
11.3 Advanced Parameters
The composer should not hardcode every provider-specific field.
Use this precedence:
- Known stable first-class fields, such as image
aspectRatioandresolution. - Model schema via
inputSchema. - Raw
parameterseditor or compact settings popover. - UI-only disabled placeholders for future capabilities.
12. Data Ownership
12.1 Prompt
Generation nodes own their prompt:
- image:
NanoBananaNodeData.inputPrompt - video:
GenerateVideoNodeData.inputPrompt
Prompt nodes and connected text inputs still remain valid workflow inputs. Connected upstream text has priority over the node-local prompt.
Prompt precedence:
| Node-local prompt | Upstream text connection | Execution prompt | Composer behavior |
|---|---|---|---|
| empty | none | missing prompt | editable, generate disabled |
| empty | present | upstream text | read-only display of upstream text |
| present | none | node-local prompt | editable |
| present | present | upstream text | read-only display of upstream text, show local prompt as fallback/overridden |
When upstream text is connected, the composer prompt field must be disabled or visually read-only with a hint: "Prompt is provided by an upstream text node." This keeps the graph as the relationship source of truth.
12.2 References
The graph should remain the source of truth for relationships.
Composer should display references from:
- connected upstream media/text/audio nodes when possible
- selected generation node cached fields such as
inputImagesas fallback
Composer should not silently create new reference edges except in no-selection root creation, where there are no selected graph relationships.
MVP direct upload boundary:
- Image uploads in the composer may become node-local
inputImagesfor no-selection root creation, or fallback references on a selected generation node when no upstream image connection exists. - Upstream image connections remain authoritative. If a selected node has connected image inputs, composer uploads must not override those graph relationships.
- Video file uploads are not stored directly on generation nodes in this PRD. Until
GenerationInputand model input schemas support video inputs, uploaded videos remain explicitvideoInputnodes connected through the canvas.
12.3 Model
Selected generation nodes own their model:
selectedModel.providerselectedModel.modelIdselectedModel.displayNameselectedModel.capabilitiesselectedModel.pricing
For image nodes, the legacy model field must still be maintained for backward compatibility.
Model switching rules:
- Same capability switch is allowed.
- Cross-capability switch is blocked in node-edit mode. For example, an image node cannot become a video node through the composer model browser.
- Empty-create mode allows any capability because there is no existing node data structure.
- Same capability switch preserves common fields such as prompt and references.
- Same capability switch clears provider-specific
parameters,fallbackParameters, andinputSchema. - Image node switches must continue to update the legacy
modelfield when applicable.
13. Generate Button Contract
13.1 Enabled Conditions
| State | Enabled When |
|---|---|
| Empty create | prompt is non-empty and selected model has a supported capability |
| Image node edit | selected node exists, prompt is non-empty, model is an image model, not running |
| Video node edit | selected node exists, prompt is non-empty, model is a video model, not running |
| Unsupported node | never |
| Multi-select | never in MVP |
13.2 Click Behavior
No selected node:
- Infer node type from selected model capability.
- Build initial node data from composer draft.
addNode(nodeType, viewportCenter, initialData).- Select the newly created node.
regenerateNode(newNodeId).
Selected supported generation node:
- Build a node patch from composer state.
updateNodeData(selectedNode.id, patch).regenerateNode(selectedNode.id).
Selected unsupported node:
- No-op.
- Button disabled.
Multiple selected nodes:
- No-op in MVP.
- Button disabled.
13.3 Running State
While isRunning is true:
- composer controls remain visible
- generate button shows spinner
- generate button is disabled
- status text should identify the current running node when possible
13.4 Error Handling
Errors are stored on the node, not in composer-only state:
- image:
NanoBananaNodeData.error - video:
GenerateVideoNodeData.error - status:
NodeStatus = "idle" | "loading" | "complete" | "error" | "skipped"
Composer behavior:
- If the selected node has
status = "error", show the error message above the footer controls. - The generate button remains enabled when the prompt/model requirements are valid and
isRunningis false. - Button tooltip changes to "Retry current node" when selected node status is
error. - Clearing or changing prompt/model/parameters does not clear the error immediately; the error is cleared when execution starts through the existing executor update.
Expected user-facing error categories:
- missing API key or provider configuration
- invalid model selection
- provider timeout or polling failure
- rate limit or quota
- safety/policy rejection
- invalid provider-specific parameters
14. Technical Implementation Plan
Phase 1: Composer Context Derivation
Create a small context derivation layer in GenerationComposer.tsx or a sibling utility.
Suggested types:
type ComposerMode =
| "empty-create"
| "node-edit"
| "process-node"
| "unsupported-node"
| "multi-select";
type ComposerCapability = "all" | "image" | "video" | "3d" | "audio";
interface ComposerContext {
mode: ComposerMode;
node: WorkflowNode | null;
capability: ComposerCapability;
nodeType: NodeType | null;
}
Acceptance:
- no selection resolves to
empty-create+all nanoBananaresolves tonode-edit+imagegenerateVideoresolves tonode-edit+videovideoStitchresolves toprocess-node+videoeaseCurveresolves toprocess-node+video- unsupported single selection resolves to
unsupported-node - multiple selection resolves to
multi-select - status/output/error updates do not reset dirty draft fields
Phase 2: Adapter-Based Node Read/Write
Avoid adding one-off conditionals throughout the composer.
Suggested shape:
interface ComposerAdapter<TData> {
nodeType: NodeType;
capability: Exclude<ComposerCapability, "all">;
defaultModel: () => SelectedModel;
readDraft: (node: WorkflowNode) => ComposerDraft;
buildPatch: (draft: ComposerDraft) => Partial<TData>;
getModelDialogTitle: () => string;
}
MVP adapters:
imageComposerAdapterfornanoBananavideoComposerAdapterforgenerateVideo
Adapter registration:
const COMPOSER_ADAPTERS = {
nanoBanana: imageComposerAdapter,
generateVideo: videoComposerAdapter,
} satisfies Partial<Record<NodeType, ComposerAdapter<WorkflowNodeData>>>;
Do not place adapters in Zustand. They are deterministic UI/domain mapping and should stay as testable module-level code.
Acceptance:
- selected image node loads existing prompt/model/ratio/resolution
- selected video node loads existing prompt/model/parameters
- switching between selected nodes updates composer values correctly
- adding a future 3D/audio adapter requires registering one adapter entry
Phase 3: Model Selection Rules
Update composer model browser usage:
- empty-create:
initialCapabilityFilter = "all" - image node:
initialCapabilityFilter = "image" - video node:
initialCapabilityFilter = "video" - 3D/audio later: use existing filters
For composer usage, ModelSearchDialog should receive onModelSelected so it selects a model but does not immediately create a node.
Acceptance:
- selected image node only shows image-capable models by default
- selected video node only shows video-capable models by default
- no-selection state can browse all models
- selecting a model updates composer state and selected node data when editing
- selecting a cross-capability model is impossible in node-edit mode because the dialog is filtered by current capability
- same-capability model changes clear provider-specific parameters and schema
Phase 4: Generate Button Refactor
Refactor handleSubmit so it branches by ComposerContext, not by selected image references.
Acceptance:
- no selection + image model creates
nanoBanana - no selection + video model creates
generateVideo - selected image node reruns itself
- selected video node reruns itself
- selected input/media/output node does not create hidden downstream nodes
Phase 5: Canvas Next-Step Auto-Select
When ConnectionDropMenu creates a new generation node:
- connect edge as today
- select the new node
- clear prior selection
Implementation options:
- add
selectSingleNode(nodeId)toworkflowStore
Decision:
- add
selectSingleNode(nodeId)andclearSelection()toworkflowStore - use those actions after
ConnectionDropMenucreates a node - treat React Flow's selection callbacks as sync events into the store, not as the composer source of truth
Acceptance:
- drag image output to blank canvas, choose Generate Video, new video node is selected
- composer immediately switches to video editing mode
- generate button reruns the new video node
Phase 6: Video Parameter UI
Add video controls only for parameters already available in schema or node data.
MVP:
- prompt
- model
- reference previews
- raw/advanced parameter popover if schema exists
- status/error
Future:
- first-class
GenerateVideoNodeDatafields for semantic controls - duration
- resolution
- aspect ratio
- fps
- audio
- seed
Acceptance:
- video node can select NewAPI video models
- video node can rerun through
regenerateNode - UI does not show fake controls that do not write to execution data
Phase 7: Test and Visual Verification
Add focused tests instead of broad snapshots.
Targets:
- composer context derivation
- model filter behavior
- selected image node rerun
- selected video node rerun
- unsupported selection disables generation
- connection drop creation selects new generation node
Manual validation:
- open local app
- select image node
- select video node
- drag image output to Generate Video
- verify bottom composer mode changes
- verify no hidden downstream branch is created by the main button
15. Acceptance Criteria
| ID | Requirement | Acceptance |
|---|---|---|
| AC-01 | Composer has one target | Every state resolves to exactly one composer mode |
| AC-02 | Selected image node edit | Selecting nanoBanana loads its prompt/model/ratio/resolution |
| AC-03 | Selected image node run | Main button calls regenerateNode on the selected image node |
| AC-04 | Selected video node edit | Selecting generateVideo loads its prompt/model/parameters |
| AC-05 | Selected video node run | Main button calls regenerateNode on the selected video node |
| AC-06 | No hidden next step | With a node selected, main button never calls addNode |
| AC-07 | No selection creation | With no selection, main button creates a new node based on model capability |
| AC-08 | Model filters | Image nodes open image models, video nodes open video models, empty state opens all models |
| AC-09 | Unsupported node state | Selecting imageInput, prompt, output, etc. disables generate |
| AC-10 | Canvas next step | Drag/drop creation selects the new node so composer edits it |
| AC-11 | NewAPI compatibility | NewAPI model selection writes selectedModel.provider = "newapiwg" and uses existing executors |
| AC-12 | Tests | Component/store tests cover core state transitions and generate button behavior |
| AC-13 | Draft safety | Dirty draft fields are autosaved on selection change, blur, and before generate |
| AC-14 | Prompt precedence | Connected upstream text overrides node-local prompt and makes composer prompt read-only |
| AC-15 | Model switching | Node-edit mode prevents cross-capability model selection |
| AC-16 | Error display | Selected node error appears in composer and retry reruns the same node |
| AC-17 | NewApiWG gateway payloads | Video generation sends gateway-compatible images, videos, and audios fields, and model-specific exceptions are handled in the provider adapter |
| AC-18 | Video stitch inspector | Selecting videoStitch shows clip/input/loop/status controls and reruns the same node without requiring a prompt |
| AC-19 | Ease curve inspector | Selecting easeCurve shows video/settings/duration/status controls and reruns the same node without requiring a prompt |
| AC-20 | Settings handle menu | Dragging from an easeCurve Settings handle shows only Settings-compatible connection targets and sources |
16. UX Copy
Suggested mode labels:
- Empty create: "新建生成"
- Image node edit: "图片节点"
- Video node edit: "视频节点"
- Video stitch inspector: "视频拼接"
- Ease curve inspector: "缓动曲线"
- Unsupported node: "当前节点"
- Multi-select: "已选择 {count} 个节点"
Suggested button tooltips:
- Empty create: "创建并生成"
- Image edit: "重新生成当前图片节点"
- Video edit: "重新生成当前视频节点"
- Video stitch: "拼接当前视频节点"
- Ease curve: "应用当前缓动曲线节点"
- Unsupported: "从节点端口拖拽以创建下一步"
- Multi-select: "多选暂不支持单节点生成"
Suggested unsupported hint:
下一步需要从节点端口拖拽创建,底部框只编辑生成节点或显示受支持处理节点的检查器。
17. Risks and Constraints
R-01: Video Fields Are Not First-Class Yet
GenerateVideoNodeData currently stores provider-specific controls mostly under parameters. Fixed duration/fps/resolution controls require a data-model migration before they can become reliable UI.
Mitigation:
- MVP uses prompt/model/references/status plus schema-driven controls.
- Add first-class semantic fields before exposing fixed video controls.
- Executors map semantic fields to provider payload names.
R-02: Composer and ControlPanel Duplicate Responsibilities
The existing ControlPanel already edits generation nodes. The composer should become the primary surface for common generation parameters, while ControlPanel can remain advanced/legacy during transition.
Mitigation:
- Do not delete
ControlPanelin MVP. - Move only high-frequency controls first.
R-03: Selection State Must Not Split
addNode does not appear to select newly created nodes by default.
Mitigation:
- Add explicit store selection helpers.
- Use workflow store as the composer source of truth.
- Test connection drop flows.
R-04: Count UI May Exceed Executor Semantics
The composer prototype shows image count. Existing executor behavior may not support native multi-count for every provider.
Mitigation:
- MVP may keep count as UI for image only if wired.
- Otherwise default to
1and mark multi-count as future.
R-05: NewApiWG Video Models Do Not Share One Media Schema
NewApiWG exposes several upstream video families through one gateway. Some models accept top-level images, videos, and audios, while others require provider-native fields under metadata.
Mitigation:
- Composer emits semantic node fields and connected graph inputs only.
- The NewApiWG provider adapter normalizes common aliases into gateway fields.
- Model-family exceptions, such as Kling metadata media fields or Jimeng base64 constraints, must stay in the provider adapter or model schema layer.
- Do not expose provider-specific composer controls until they write to a tested execution payload.
18. Metrics
Product success metrics:
- A user can select an image node, edit prompt/model, and rerun it without opening the side control panel.
- A user can select a video node, select a video model, and rerun it from the composer.
- A user can create image-to-video by dragging from image output to blank canvas, choosing Generate Video, and running the new selected node.
- No user test participant interprets the selected-node generate button as "create next step".
Engineering success metrics:
- Composer behavior is covered by unit/component tests.
- No direct provider calls are added to composer UI.
- Existing
regenerateNodeexecution path remains the single run path. - Existing NewAPI image and video tests keep passing.
19. Final Product Decision
The bottom composer should be implemented as a current-node console.
It is acceptable for the composer to create a new root node only when there is no current node selection. Once a node is selected, the composer must only edit and run that selected node. All next-step workflow creation must remain visible on the canvas through dragging, node plus actions, and explicit connections.
20. Related Decision Records
docs/decisions/0001-composer-draft-sync.mddocs/decisions/0002-prompt-source-precedence.mddocs/decisions/0003-video-parameter-ownership.mddocs/decisions/0004-model-switching-boundaries.mddocs/decisions/0005-selection-source-of-truth.md
21. Appendix: NewApiWG Gateway Documentation Summary
21.1 Product Boundary
NewApiWG is the unified provider gateway for model discovery and multimodal generation. In this PRD it is a provider implementation behind the existing app APIs, not a separate composer UX.
The intended call path is:
flowchart LR
UI["Composer / Node UI"] --> E["regenerateNode(nodeId)"]
E --> A["/api/generate"]
A --> P["NewApiWG provider adapter"]
P --> G["NewApiWG gateway"]
The composer must not build gateway-specific payloads directly. It writes selected model, prompt, references, semantic video fields, and provider parameters into the selected node. Executors and the provider adapter own request construction.
21.2 Configuration and Local API Surface
Configuration:
- Environment:
NEWAPIWG_API_KEY - Environment:
NEWAPIWG_BASE_URL - Project settings override: provider
newapiwg.apiKeyandnewapiwg.baseUrl - Request headers used internally:
X-NewApiWG-Key,X-NewApiWG-Base-URL
Local routes:
| App Route | Gateway Route | Purpose |
|---|---|---|
GET /api/models?provider=newapiwg |
GET {baseUrl}/models |
Fetch gateway model catalog and infer app capabilities |
POST /api/generate with image capability |
POST {baseUrl}/images/generations, POST {baseUrl}/chat/completions, or Gemini-native image route |
Generate or edit images depending on model family |
POST /api/generate with video capability |
POST {baseUrl}/video/generations |
Submit async video generation task |
| provider polling | GET {baseUrl}/video/generations/{task_id} |
Poll video task until success/failure/timeout |
POST /api/generate with audio capability |
POST {baseUrl}/audio/speech |
Generate speech/audio |
| media playback / frame extraction | /api/proxy-media?url=... |
Avoid remote video CORS/range failures in browser media pipelines |
21.3 Model Discovery and Capability Inference
NewApiWG model objects may expose capabilities explicitly through capabilities, capability, tags, modalities, or metadata fields. If the gateway does not provide explicit capability tags, the app infers capabilities from model id/name/description.
Supported app capabilities:
text-to-imageimage-to-imagetext-to-videoimage-to-videoaudio-to-videotext-to-3dimage-to-3dtext-to-audio
Known inference keywords:
- image:
image,img,vision,flux,sdxl,gpt-image,imagen,seedream,banana - video:
video,vidu,veo,sora,kling,hailuo,seedance,dreamactor,motion - audio:
audio,speech,voice,tts,music - 3D:
3d,mesh,tripo,hunyuan3d
Chat-only models should not appear in image/video composer filters unless the gateway marks them with a compatible generation capability.
Default app models when NewApiWG is preferred:
| Capability | Default Model |
|---|---|
| Image | apiyi_nano_banana_2 / APIYI Nano Banana 2 |
| Video | viduq2-pro-fast / Vidu Q2 Pro Fast |
21.4 Common Generation Payload Contract
Shared fields:
model: gateway model idprompt: node-local prompt or upstream text prompt, with upstream graph text taking precedenceparameters: provider-specific fields that do not have first-class node fieldsdynamicInputs: schema-derived inputs
Image generation:
- Standard route uses
POST /images/generations. - Image references use OpenAI-compatible
imagefor image/edit models. - Gemini-native image models route through the Gemini-compatible generation endpoint.
- Chat image models such as
gpt-image-2-allroute through/chat/completions; URL-only responses are accepted and may be converted to data URLs when fetchable.
Video generation:
- Standard route uses
POST /video/generations. - Common gateway media fields:
images: string[]videos: string[]audios: string[]
- Stale or provider-specific aliases should be normalized by the NewApiWG provider adapter. Examples:
image,image_url,first_frame_url,last_frame_url,reference_image_urls->imagesvideo,video_url,reference_video_urls->videosaudio,audio_url,reference_audio_urls->audios
parametersmay include fields such asduration,seconds,size,ratio,metadata,seed,frames,generate_audio, or model-family options.- Connected graph inputs are authoritative. Composer uploads are only fallback references when no upstream media connection exists.
21.5 Video Model Family Summary
| Family | Gateway Model Examples | Input Rules | Product Notes |
|---|---|---|---|
| Sora / VEO-like OpenAI video | sora-2, veo-3.1-landscape-fl |
Text prompt plus optional or required reference image depending on model | Some modes use multipart input_reference; do not expose single-file controls until provider adapter supports them |
| Hailuo / MiniMax | MiniMax-Hailuo-2.3, I2V-01, S2V-01 |
Gateway prefers images; 0 image = text-to-video, 1 image = image-to-video, 2 images = first/tail for supported models |
metadata.action can force task type; model-specific duration/resolution validation belongs in adapter/schema |
| Doubao / Seedance | doubao-seedance-2-0-260128, doubao-seedance-2-0-fast-260128 |
Top-level images, videos, audios; 0 image = text-to-video, 1 = first frame, 2 = first/tail, 3+ = references |
Seedance 2.0 supports video/audio references; expected runtime can be several minutes |
| Jimeng | jimeng_t2v_v30, jimeng_i2v_first_v30, jimeng_dreamactor_m20_gen_video |
Top-level images / videos; some base64 inputs require pure base64 without data:image/... prefix |
Base64 normalization must be handled below the composer |
| Kling | kling-v2-6, kling-v3, kling-video-o1 |
Media should be sent in Kling-native metadata fields such as metadata.image, metadata.image_tail, metadata.image_list, metadata.video_list |
This is a schema exception. Generic top-level images is not enough for all Kling modes |
| Vidu | viduq3, viduq3-mix, viduq2-pro-fast, vidu2.0 |
Top-level images; 0 = text-to-video, 1 = image-to-video, 2 = first/tail, 3+ = reference video; metadata.subjects for subject reference |
Current default video model is Vidu Q2 Pro Fast |
21.6 Response and Polling Contract
Video submission response may include:
idortask_idstatuscreated_at- immediate
url,video_url,result_url, or nestedmetadata.url
Polling response may include:
status: pending/queued/processing/in_progress/completed/SUCCESS/failed/FAILUREresult_urlmetadata.urlmetadata.last_frame_url
Current app behavior:
- Polls NewApiWG video tasks through the provider adapter.
- Treats returned remote video URLs as valid outputs.
- Uses
/api/proxy-mediafor browser playback and frame extraction when remote media would otherwise fail CORS or range requests.
Gateway timing guidance:
- Video generation is async.
- First poll should not be immediate for slow models.
- Seedance can take around 6 minutes; other families are often around 1 to 2 minutes, depending on mode and load.
- The app should keep timeout and retry behavior in provider/executor code, not in composer UI.
21.7 Error Handling Contract
Errors should be stored on the node and surfaced by the composer.
Expected NewApiWG error categories:
- missing
NEWAPIWG_API_KEYor project key - invalid model id or missing selected model metadata
- missing prompt or required media
- invalid model-specific duration/resolution/aspect ratio
- quota or insufficient credits
- upstream task failure
- polling timeout
- transient gateway/provider overload
Current adapter behavior includes transient retry normalization for system disk overloaded. User-facing copy should recommend retrying or switching models instead of exposing raw provider infrastructure text.
21.8 PRD Consequences
- NewApiWG is the recommended unified gateway when configured.
- The model browser should prefer NewApiWG over Gemini when a NewApiWG key exists.
- Composer mode and filters remain capability-driven, not provider-driven.
- Provider-specific media schema exceptions must be implemented in
src/app/api/generate/providers/newapiwg.tsor model schema extraction, not inGenerationComposer. - Tests must cover gateway field normalization for connected media, including
images,videos,audios, and stale aliases such asfirst_frame_url.