You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

6.3 KiB

多张图片生成技术实现记录

背景

本功能在 TEST-s 分支中为底部生成对话框增加类似 libTV 的图片张数选择能力。用户可以在图片或视频生成模式下选择 124,一次生成得到多张候选图。

产品语义采用“单节点多候选图”:一次多图生成只创建或更新一个 nanoBanana 图片节点,多张结果进入该节点的轮播历史,当前选中的图片作为节点输出。

核心文件:

  • src/components/composer/GenerationComposer.tsx
  • src/store/execution/nanoBananaExecutor.ts
  • src/types/nodes.ts
  • src/store/utils/nodeDefaults.ts
  • src/components/__tests__/GenerationComposer.test.tsx
  • src/store/execution/__tests__/nanoBananaExecutor.test.ts
  • src/store/utils/__tests__/nodeDefaults.test.ts

当前功能范围

已实现:

  • 底部生成对话框在图片模式下显示数量选择。
  • 张数选项固定为 124
  • 新建图片节点时写入 batchSize
  • 编辑已有图片节点时可以修改 batchSize
  • nanoBanana 执行器按 batchSize 顺序请求现有 /api/generate
  • 多张结果写入同一个节点的 imageHistory
  • 本次生成的第一张图写入 outputImage,并作为当前选中输出。
  • 每张成功结果都会进入全局图片历史。
  • 每张成功结果都会追加到下游 outputGallery
  • 自动保存路径启用时,每张成功结果都会独立保存。
  • 如果批次中前几张成功、后续失败,保留已成功候选图并把错误记录在节点数据中。

未实现:

  • 不使用 provider 原生 n 参数。
  • 不自动创建多个图片节点。
  • 不把当前切换到的候选图自动作为下一次生成的参考图。
  • 不提供“展开为多个图片节点”动作。
  • 不提供“以当前图继续生成”动作。

状态模型

生成数量由 NanoBananaNodeData.batchSize 表示:

export type GenerationBatchSize = 1 | 2 | 4;

export interface NanoBananaNodeData extends BaseNodeData {
  batchSize?: GenerationBatchSize;
}

字段为可选字段,旧工作流没有该字段时按 1 处理。

新建 nanoBanana 节点时默认写入:

batchSize: 1

底部对话框内部草稿 ComposerDraft 同步维护同名字段,避免用户在提交前切换节点或刷新草稿时丢失选择。

底部对话框

GenerationComposer 在图片或视频能力模式下显示三个控制:

模型 / 参数 / 数量

数量选择定义为:

const BATCH_SIZE_OPTIONS: GenerationBatchSize[] = [1, 2, 4];

提交新节点时,buildInitialDataForNode("nanoBanana", draft) 会把 draft.batchSize 写入节点数据。

编辑已有图片节点时,imageComposerAdapter.readDraft() 从节点读取 batchSizebuildPatch() 在字段变脏时回写 batchSize

运行逻辑

executeNanoBanana() 不改变 provider 请求协议。执行器会根据节点的 batchSize 做顺序循环:

batchSize = 1 -> 请求 1 次
batchSize = 2 -> 请求 2 次
batchSize = 4 -> 请求 4 次

每次请求仍然使用现有 /api/generate payload,包括:

  • images
  • prompt
  • aspectRatio
  • resolution
  • selectedModel
  • parameters
  • dynamicInputs

这样可以兼容当前 PopiArt Nano Pro / NewApiWG / Gemini native 链路,因为该链路目前稳定返回单张图片。

结果写入

每张成功结果会生成一个历史项:

{
  id,
  image,
  timestamp,
  prompt,
  aspectRatio,
  model,
  modelDisplayName,
  modelProvider,
}

本次批量结果按生成顺序插入到 imageHistory 前面:

[本次第1张, 本次第2张, 本次第3张, 本次第4张, ...旧历史]

节点输出规则:

  • outputImage 使用本次第 1 张。
  • selectedHistoryIndex 设为 0
  • 节点内已有轮播控件负责切换候选图。
  • 切换候选图时,下游读取的是当前 outputImage

输出画廊

如果图片生成节点连接到 outputGallery,本次成功的每一张候选图都会调用:

appendOutputGalleryImage(target.id, image)

因此画廊可以直接看到整批候选图,而普通下游节点仍然只读取当前选中的 outputImage

失败处理

执行器的失败策略:

  • 第一张就失败:节点进入 error,如果有 fallback model,则走现有 fallback 流程。
  • 前几张成功、后续失败:节点进入 complete,保留成功候选图,并把错误写入 error
  • 用户取消:继续沿用 AbortError 逻辑,不提交未完成批次。

第一版没有做并发生成,顺序生成更容易保证取消、fallback、历史顺序和保存逻辑一致。

参考图语义

多张候选图不会改变参考图来源。重新生成仍然使用:

  • 上游连接图片
  • 节点已有 inputImages
  • 当前 prompt
  • 当前参数
  • 当前 batchSize

用户切换到第 2/4 张候选图,不会自动把这张图变成下一次生成的参考图。后续如需迭代能力,应单独设计显式动作,例如“以当前图继续生成”。

测试覆盖

当前相关测试:

src/components/__tests__/GenerationComposer.test.tsx
src/store/execution/__tests__/nanoBananaExecutor.test.ts
src/store/utils/__tests__/nodeDefaults.test.ts

覆盖点:

  • 新建图片节点会保存用户选择的 batchSize
  • 默认 nanoBanana 节点数据包含 batchSize: 1
  • batchSize=4 时执行器请求 4 次。
  • 多张结果写入同一个节点历史。
  • 多张结果中的第一张成为 outputImage
  • 批次中后续失败时保留已成功候选图。
  • 下游 outputGallery 会收到每张候选图。

建议验证命令:

npm run test:run -- src/components/__tests__/GenerationComposer.test.tsx src/store/execution/__tests__/nanoBananaExecutor.test.ts src/store/utils/__tests__/nodeDefaults.test.ts

维护注意点

  • 不要把 batchSize 扩展为任意数字,当前产品选项只允许 1 | 2 | 4
  • 如果未来接入 provider 原生多图参数,需要保留当前循环实现作为兼容 fallback。
  • 如果未来新增“展开为多个图片节点”,应作为显式用户动作,不应改变默认生成行为。
  • 如果未来新增“以当前图继续生成”,应明确写入 inputImages,不要让普通轮播切换隐式改变参考图。
  • 自动保存和输出画廊需要继续按“每张成功图片一次写入”维护。