482 lines
16 KiB
YAML
482 lines
16 KiB
YAML
app:
|
|
description: 'Laser card AI 镭射卡 5-variant 生成器。前端已抠图,传入 cutout_url 直接合成。'
|
|
icon: 🤖
|
|
icon_background: '#FFEAD5'
|
|
mode: workflow
|
|
name: laser_card_variants_v1
|
|
use_icon_as_answer_icon: false
|
|
kind: app
|
|
version: 0.1.4
|
|
workflow:
|
|
conversation_variables: []
|
|
environment_variables:
|
|
- description: 'laser-compositor 合成服务地址'
|
|
name: LASER_COMPOSITOR_HOST
|
|
value: 'http://host.docker.internal:7000'
|
|
value_type: string
|
|
- description: 'MiniMax API 密钥'
|
|
name: MINIMAX_API_KEY
|
|
value: ''
|
|
value_type: secret
|
|
features:
|
|
file_upload:
|
|
image:
|
|
enabled: false
|
|
number_limits: 3
|
|
transfer_methods:
|
|
- local_file
|
|
- remote_url
|
|
opening_statement: ''
|
|
retriever_resource:
|
|
enabled: false
|
|
sensitive_word_avoidance:
|
|
enabled: false
|
|
speech_to_text:
|
|
enabled: false
|
|
suggested_questions: []
|
|
suggested_questions_after_answer:
|
|
enabled: false
|
|
text_to_speech:
|
|
enabled: false
|
|
language: ''
|
|
voice: ''
|
|
graph:
|
|
edges:
|
|
# start → 参数展开
|
|
- data:
|
|
isInIteration: false
|
|
isInLoop: false
|
|
sourceType: start
|
|
targetType: code
|
|
id: start-source-code-param-target
|
|
source: start
|
|
sourceHandle: source
|
|
target: code-param
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 0
|
|
# 参数展开 → 循环
|
|
- data:
|
|
isInIteration: false
|
|
isInLoop: false
|
|
sourceType: code
|
|
targetType: iteration
|
|
id: code-param-source-loop-variants-target
|
|
source: code-param
|
|
sourceHandle: source
|
|
target: loop-variants
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 0
|
|
# 循环 → 聚合
|
|
- data:
|
|
isInIteration: false
|
|
isInLoop: false
|
|
sourceType: iteration
|
|
targetType: code
|
|
id: loop-variants-source-code-agg-target
|
|
source: loop-variants
|
|
sourceHandle: source
|
|
target: code-agg
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 0
|
|
# 聚合 → 结束
|
|
- data:
|
|
isInIteration: false
|
|
isInLoop: false
|
|
sourceType: code
|
|
targetType: end
|
|
id: code-agg-source-end-target
|
|
source: code-agg
|
|
sourceHandle: source
|
|
target: end
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 0
|
|
# === 循环内部 ===
|
|
# iter-start → MiniMax 背景
|
|
- data:
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
isInLoop: false
|
|
sourceType: start
|
|
targetType: http-request
|
|
id: iter-start-source-http-bg-target
|
|
source: iter-start
|
|
sourceHandle: source
|
|
target: http-bg
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 1000
|
|
# MiniMax 背景 → MiniMax 装饰
|
|
- data:
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
isInLoop: false
|
|
sourceType: http-request
|
|
targetType: http-request
|
|
id: http-bg-source-http-overlay-target
|
|
source: http-bg
|
|
sourceHandle: source
|
|
target: http-overlay
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 1000
|
|
# MiniMax 装饰 → compositor
|
|
- data:
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
isInLoop: false
|
|
sourceType: http-request
|
|
targetType: http-request
|
|
id: http-overlay-source-http-compositor-target
|
|
source: http-overlay
|
|
sourceHandle: source
|
|
target: http-compositor
|
|
targetHandle: target
|
|
type: custom
|
|
zIndex: 1000
|
|
nodes:
|
|
# ================================================================
|
|
# 开始节点
|
|
# ================================================================
|
|
- data:
|
|
desc: '接收抠图后的 cutout_url 和 generation 参数'
|
|
selected: false
|
|
title: 开始
|
|
type: start
|
|
variables:
|
|
- label: cutout_url
|
|
max_length: 2048
|
|
options: []
|
|
required: false
|
|
type: text-input
|
|
variable: cutout_url
|
|
- default: '["dream","classic","holoFull","ice","sunset"]'
|
|
label: preset_codes
|
|
max_length: 2048
|
|
options: []
|
|
required: false
|
|
type: text-input
|
|
variable: preset_codes
|
|
- label: render_configs
|
|
max_length: 20480
|
|
options: []
|
|
required: true
|
|
type: text-input
|
|
variable: render_configs
|
|
height: 150
|
|
id: start
|
|
position:
|
|
x: 30
|
|
y: 300
|
|
positionAbsolute:
|
|
x: 30
|
|
y: 300
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
# ================================================================
|
|
# 代码节点 — 参数展开
|
|
# ================================================================
|
|
- data:
|
|
code: "import json\n\ndef main(preset_codes: str, render_configs: str) -> dict:\n preset_list = []\n if preset_codes and preset_codes.strip():\n try:\n parsed = json.loads(preset_codes)\n if isinstance(parsed, list):\n preset_list = parsed\n except:\n pass\n if not preset_list:\n preset_list = [\"dream\", \"classic\", \"holoFull\", \"ice\", \"sunset\"]\n configs = []\n if render_configs and render_configs.strip():\n try:\n parsed = json.loads(render_configs)\n if isinstance(parsed, list):\n configs = parsed\n except:\n pass\n config_map = {}\n for rc in configs:\n if isinstance(rc, dict) and \"preset_id\" in rc:\n config_map[rc[\"preset_id\"]] = rc\n variants = []\n for pc in preset_list:\n if pc in config_map:\n cfg = config_map[pc]\n variants.append({\n \"preset_id\": pc,\n \"grating_config\": cfg.get(\"grating_config\", {}),\n \"bg_prompt\": cfg.get(\"bg_prompt\", \"\"),\n \"overlay_prompt\": cfg.get(\"overlay_prompt\", \"\"),\n })\n return {\n \"variants\": variants,\n \"variant_count\": len(variants),\n }\n"
|
|
code_language: python3
|
|
dependencies: []
|
|
desc: '解析 JSON 字符串 → 构建 variants 列表'
|
|
outputs:
|
|
variant_count:
|
|
children: null
|
|
type: number
|
|
variants:
|
|
children: null
|
|
type: array[object]
|
|
selected: false
|
|
title: 参数展开
|
|
type: code
|
|
variables:
|
|
- value_selector:
|
|
- start
|
|
- preset_codes
|
|
variable: preset_codes
|
|
- value_selector:
|
|
- start
|
|
- render_configs
|
|
variable: render_configs
|
|
height: 84
|
|
id: code-param
|
|
position:
|
|
x: 320
|
|
y: 300
|
|
positionAbsolute:
|
|
x: 320
|
|
y: 300
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
# ================================================================
|
|
# 循环节点
|
|
# ================================================================
|
|
- data:
|
|
desc: '对每个 variant 分别生成背景图、装饰图、合成最终镭射卡'
|
|
error_handle_mode: terminated
|
|
height: 351
|
|
iterator_selector:
|
|
- code-param
|
|
- variants
|
|
is_parallel: false
|
|
output_selector:
|
|
- http-compositor
|
|
- body
|
|
output_type: array[string]
|
|
parallel_nums: 1
|
|
selected: false
|
|
startNodeType: start
|
|
start_node_id: iter-start
|
|
title: 遍历 Variants
|
|
type: iteration
|
|
width: 953
|
|
height: 351
|
|
id: loop-variants
|
|
position:
|
|
x: 610
|
|
y: 300
|
|
positionAbsolute:
|
|
x: 610
|
|
y: 300
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 953
|
|
zIndex: 1
|
|
# ================================================================
|
|
# 循环内部 — 开始
|
|
# ================================================================
|
|
- data:
|
|
desc: 当前 variant_item
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
selected: false
|
|
title: 循环项
|
|
type: start
|
|
variables: []
|
|
extent: parent
|
|
height: 54
|
|
id: iter-start
|
|
parentId: loop-variants
|
|
position:
|
|
x: 87
|
|
y: 120
|
|
positionAbsolute:
|
|
x: 697
|
|
y: 420
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
zIndex: 1001
|
|
# ================================================================
|
|
# 循环内部 — MiniMax 背景
|
|
# ================================================================
|
|
- data:
|
|
authorization:
|
|
config:
|
|
api_key: '{{#env.MINIMAX_API_KEY#}}'
|
|
type: bearer-api-key
|
|
body:
|
|
data: '{"model": "image-01", "prompt": "{{#loop-variants.item.bg_prompt#}}", "aspect_ratio": "3:4", "n": 1, "prompt_optimizer": true, "response_format": "url"}'
|
|
type: json
|
|
desc: '根据 bg_prompt 生成 3:4 背景'
|
|
headers: 'Content-Type: application/json'
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
method: post
|
|
params: ''
|
|
selected: false
|
|
timeout:
|
|
max_connect_timeout: 10
|
|
max_read_timeout: 60
|
|
max_write_timeout: 20
|
|
title: MiniMax 背景图
|
|
type: http-request
|
|
url: 'https://api.minimaxi.com/v1/image_generation'
|
|
variables: []
|
|
extent: parent
|
|
height: 93
|
|
id: http-bg
|
|
parentId: loop-variants
|
|
position:
|
|
x: 377
|
|
y: 120
|
|
positionAbsolute:
|
|
x: 987
|
|
y: 420
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
zIndex: 1001
|
|
# ================================================================
|
|
# 循环内部 — MiniMax 装饰
|
|
# ================================================================
|
|
- data:
|
|
authorization:
|
|
config:
|
|
api_key: '{{#env.MINIMAX_API_KEY#}}'
|
|
type: bearer-api-key
|
|
body:
|
|
data: '{"model": "image-01", "prompt": "{{#loop-variants.item.overlay_prompt#}}", "aspect_ratio": "3:4", "n": 1, "prompt_optimizer": true, "response_format": "url"}'
|
|
type: json
|
|
desc: '根据 overlay_prompt 生成透明装饰层'
|
|
headers: 'Content-Type: application/json'
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
method: post
|
|
params: ''
|
|
selected: false
|
|
timeout:
|
|
max_connect_timeout: 10
|
|
max_read_timeout: 60
|
|
max_write_timeout: 20
|
|
title: MiniMax 装饰图
|
|
type: http-request
|
|
url: 'https://api.minimaxi.com/v1/image_generation'
|
|
variables: []
|
|
extent: parent
|
|
height: 93
|
|
id: http-overlay
|
|
parentId: loop-variants
|
|
position:
|
|
x: 667
|
|
y: 120
|
|
positionAbsolute:
|
|
x: 1277
|
|
y: 420
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
zIndex: 1001
|
|
# ================================================================
|
|
# 循环内部 — compositor 合成
|
|
# ================================================================
|
|
- data:
|
|
authorization:
|
|
type: no-auth
|
|
body:
|
|
data: '{"background_url": "{{#http-bg.body.data.image_urls[0]#}}", "cutout_url": "{{#start.cutout_url#}}", "overlay_url": "{{#http-overlay.body.data.image_urls[0]#}}", "grating_config": {{#loop-variants.item.grating_config#}}, "export_width": 450, "export_height": 600, "output_oss_key": "laser-card/dify/{{#loop-variants.item.preset_id#}}"}'
|
|
type: json
|
|
desc: '调用 laser-compositor /compose 6层合成'
|
|
headers: 'Content-Type: application/json'
|
|
isInIteration: true
|
|
iteration_id: loop-variants
|
|
method: post
|
|
params: ''
|
|
selected: false
|
|
timeout:
|
|
max_connect_timeout: 10
|
|
max_read_timeout: 60
|
|
max_write_timeout: 20
|
|
title: 合成 6 层镭射卡
|
|
type: http-request
|
|
url: '{{#env.LASER_COMPOSITOR_HOST#}}/compose'
|
|
variables: []
|
|
extent: parent
|
|
height: 93
|
|
id: http-compositor
|
|
parentId: loop-variants
|
|
position:
|
|
x: 957
|
|
y: 120
|
|
positionAbsolute:
|
|
x: 1567
|
|
y: 420
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
zIndex: 1001
|
|
# ================================================================
|
|
# 代码节点 — 聚合输出
|
|
# ================================================================
|
|
- data:
|
|
code: "import json\n\ndef main(loop_output: list) -> dict:\n variants = []\n warnings = []\n for i, item in enumerate(loop_output or []):\n if isinstance(item, str):\n try:\n item = json.loads(item)\n except:\n pass\n if isinstance(item, dict) and item.get(\"status\") == \"succeeded\":\n variants.append({\n \"preset_id\": item.get(\"preset_id\", f\"variant_{i}\"),\n \"oss_key\": item.get(\"oss_key\", \"\"),\n \"signed_url\": item.get(\"signed_url\", \"\"),\n \"width\": item.get(\"width\", 450),\n \"height\": item.get(\"height\", 600),\n })\n else:\n warnings.append(f\"variant_{i} 合成失败\")\n return {\n \"status\": \"succeeded\" if len(variants) > 0 else \"failed\",\n \"variants\": json.dumps(variants),\n \"warnings\": json.dumps(warnings),\n }\n"
|
|
code_language: python3
|
|
dependencies: []
|
|
desc: '收集循环结果,输出统一 JSON'
|
|
outputs:
|
|
status:
|
|
children: null
|
|
type: string
|
|
variants:
|
|
children: null
|
|
type: string
|
|
warnings:
|
|
children: null
|
|
type: string
|
|
selected: false
|
|
title: 聚合输出
|
|
type: code
|
|
variables:
|
|
- value_selector:
|
|
- loop-variants
|
|
- output
|
|
variable: loop_output
|
|
height: 84
|
|
id: code-agg
|
|
position:
|
|
x: 1610
|
|
y: 300
|
|
positionAbsolute:
|
|
x: 1610
|
|
y: 300
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|
|
# ================================================================
|
|
# 结束节点
|
|
# ================================================================
|
|
- data:
|
|
desc: '返回 5 张镭射卡 signed URL'
|
|
outputs:
|
|
- value_selector:
|
|
- code-agg
|
|
- status
|
|
variable: status
|
|
- value_selector:
|
|
- code-agg
|
|
- variants
|
|
variable: variants
|
|
- value_selector:
|
|
- code-agg
|
|
- warnings
|
|
variable: warnings
|
|
selected: false
|
|
title: 结束
|
|
type: end
|
|
height: 90
|
|
id: end
|
|
position:
|
|
x: 1900
|
|
y: 300
|
|
positionAbsolute:
|
|
x: 1900
|
|
y: 300
|
|
selected: false
|
|
sourcePosition: right
|
|
targetPosition: left
|
|
type: custom
|
|
width: 244
|