fix: align canvas model options with backend

This commit is contained in:
2026-05-25 18:55:33 +08:00
parent 103907ca3a
commit 84d9de6b30
15 changed files with 181 additions and 397 deletions

View File

@@ -102,17 +102,6 @@
<span class="model-group-title">图片模型</span>
<n-tag size="tiny" type="success">{{ allImageModels.length }} </n-tag>
</div>
<div class="model-input-row">
<n-input
v-model:value="newImageModel"
placeholder="输入模型名称,如 dall-e-3"
size="small"
@keyup.enter="handleAddImageModel"
/>
<n-button size="small" type="primary" @click="handleAddImageModel" :disabled="!newImageModel">
添加
</n-button>
</div>
<div class="model-tags">
<n-tag
v-for="model in allImageModels"
@@ -133,17 +122,6 @@
<span class="model-group-title">视频模型</span>
<n-tag size="tiny" type="warning">{{ allVideoModels.length }} </n-tag>
</div>
<div class="model-input-row">
<n-input
v-model:value="newVideoModel"
placeholder="输入模型名称,如 sora-2"
size="small"
@keyup.enter="handleAddVideoModel"
/>
<n-button size="small" type="primary" @click="handleAddVideoModel" :disabled="!newVideoModel">
添加
</n-button>
</div>
<div class="model-tags">
<n-tag
v-for="model in allVideoModels"
@@ -235,8 +213,6 @@ const formData = reactive({
// New model inputs | 新模型输入
const newChatModel = ref('')
const newImageModel = ref('')
const newVideoModel = ref('')
// 初始化或切换渠道时,更新 API 配置
const updateFormApiConfig = () => {
@@ -273,20 +249,6 @@ const handleAddChatModel = () => {
}
}
const handleAddImageModel = () => {
if (newImageModel.value.trim()) {
modelStore.addCustomImageModel(newImageModel.value.trim())
newImageModel.value = ''
}
}
const handleAddVideoModel = () => {
if (newVideoModel.value.trim()) {
modelStore.addCustomVideoModel(newVideoModel.value.trim())
newVideoModel.value = ''
}
}
// Handle remove models | 处理删除模型
const handleRemoveChatModel = (modelKey) => {
modelStore.removeCustomChatModel(modelKey)

View File

@@ -166,7 +166,7 @@ import { useImageGeneration } from '../../hooks'
import { updateNode, addNode, addEdge, nodes, edges, duplicateNode, removeNode } from '../../stores/canvas'
import NodeHandleMenu from './NodeHandleMenu.vue'
import { useModelStore } from '../../stores/pinia'
import { getModelSizeOptions, getModelQualityOptions, getModelConfig, DEFAULT_IMAGE_MODEL } from '../../stores/models'
import { getModelSizeOptions, getModelQualityOptions, getModelConfig, DEFAULT_IMAGE_MODEL, DEFAULT_IMAGE_SIZE } from '../../stores/models'
import { parseMentions } from '../../hooks/useNodeRef'
// 使用 Pinia store 获取模型选项(根据渠道过滤)
@@ -189,7 +189,7 @@ const { loading, error, images: generatedImages, generate } = useImageGeneration
// Local state | 本地状态
const showHandleMenu = ref(false)
const localModel = ref(props.data?.model || DEFAULT_IMAGE_MODEL)
const localSize = ref(props.data?.size || '2048x2048')
const localSize = ref(props.data?.size || DEFAULT_IMAGE_SIZE)
const localQuality = ref(props.data?.quality || 'standard')
// Label editing state | Label 编辑状态
@@ -288,7 +288,8 @@ onMounted(() => {
if (!localModel.value || !isModelAvailable) {
// 使用 store 中的默认模型或第一个可用模型
localModel.value = modelStore.selectedImageModel || availableModels[0]?.key || DEFAULT_IMAGE_MODEL
const selected = availableModels.find(m => m.key === modelStore.selectedImageModel)?.key
localModel.value = selected || availableModels[0]?.key || DEFAULT_IMAGE_MODEL
updateNode(props.id, { model: localModel.value })
}
})
@@ -486,8 +487,7 @@ const handleModelSelect = (key) => {
let defaultSize = config?.defaultParams?.size
if (!defaultSize && newSizeOptions.length > 0) {
// 备用逻辑:查找 2048 或最接近的尺寸
defaultSize = newSizeOptions.find(o => o.key === '2048x2048')?.key
defaultSize = newSizeOptions.find(o => o.key === DEFAULT_IMAGE_SIZE)?.key
|| newSizeOptions.find(o => o.key.includes('1024'))?.key
|| newSizeOptions[0].key
}
@@ -508,7 +508,7 @@ const handleQualitySelect = (quality) => {
// Update size to first option of new quality | 更新尺寸为新画质的第一个选项
const newSizeOptions = getModelSizeOptions(localModel.value, quality)
if (newSizeOptions.length > 0) {
const defaultSize = quality === '4k' ? newSizeOptions.find(o => o.key.includes('4096'))?.key || newSizeOptions[4]?.key : newSizeOptions[4]?.key
const defaultSize = newSizeOptions.find(o => o.key === DEFAULT_IMAGE_SIZE)?.key
localSize.value = defaultSize || newSizeOptions[0].key
updateNode(props.id, { quality, size: localSize.value })
} else {

View File

@@ -417,8 +417,8 @@ const handleSelect = (item) => {
// Create imageConfig node
const configNodeId = addNode('imageConfig', { x: nodeX + 900, y: nodeY }, {
model: 'doubao-seedream-4-5-251128',
size: '2048x2048',
model: 'auto',
size: '1024x1536',
label: '生图配置'
})
@@ -626,8 +626,8 @@ const createInpaintWorkflow = () => {
// Create imageConfig node for inpainting | 创建图生图配置节点
const configNodeId = addNode('imageConfig', { x: nodeX + 600, y: nodeY }, {
model: 'doubao-seedream-4-5-251128',
size: '2048x2048',
model: 'auto',
size: '1024x1536',
label: '局部重绘',
inpaintMode: true
})
@@ -848,8 +848,8 @@ const handleImageGen = () => {
// Create imageConfig node for generation | 创建生图配置节点
const configNodeId = addNode('imageConfig', { x: nodeX + 900, y: nodeY }, {
model: 'doubao-seedream-4-5-251128',
size: '2048x2048',
model: 'auto',
size: '1024x1536',
label: '生图配置'
})

View File

@@ -417,7 +417,7 @@ const handleSelect = (item) => {
const nodeY = currentNode?.position?.y || 0
const defaultData = {
imageConfig: { model: 'doubao-seedream-4-5-251128', size: '2048x2048', label: '文生图' },
imageConfig: { model: 'auto', size: '1024x1536', label: '文生图' },
videoConfig: { label: '视频生成' },
text: { content: '', label: '文本输入' }
}
@@ -1003,8 +1003,8 @@ const handleSplitToTextWithImage = () => {
position: { x: baseX + colSpacing, y: segY },
data: {
label: `图片 ${i + 1}`,
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
}
nodeSpecs.push(imageConfigSpec)

View File

@@ -440,7 +440,7 @@ const handleSelect = (item) => {
const nodeY = currentNode?.position?.y || 0
const defaultData = {
imageConfig: { model: 'doubao-seedream-4-5-251128', size: '2048x2048', label: '文生图' },
imageConfig: { model: 'auto', size: '1024x1536', label: '文生图' },
videoConfig: { label: '视频生成' },
llmConfig: { label: 'LLM文本生成' }
}
@@ -695,8 +695,8 @@ const handleImageGen = () => {
// Create imageConfig node | 创建text生图配置节点
const configNodeId = addNode('imageConfig', { x: nodeX + 400, y: nodeY }, {
model: 'doubao-seedream-4-5-251128',
size: '2048x2048',
model: 'auto',
size: '1024x1536',
label: '文生图'
})

View File

@@ -489,7 +489,8 @@ onMounted(() => {
if (!localModel.value || !isModelAvailable) {
// 使用 store 中的默认模型或第一个可用模型
localModel.value = modelStore.selectedVideoModel || availableModels[0]?.key || DEFAULT_VIDEO_MODEL
const selected = availableModels.find(m => m.key === modelStore.selectedVideoModel)?.key
localModel.value = selected || availableModels[0]?.key || DEFAULT_VIDEO_MODEL
updateNode(props.id, { model: localModel.value })
}
})

View File

@@ -3,36 +3,20 @@
* Centralized model configuration | 集中模型配置
*/
// Seedream image size options | 豆包图片尺寸选项
// SKG backend image size options | SKG 后端图片尺寸选项
export const SEEDREAM_SIZE_OPTIONS = [
{ label: '21:9', key: '3024x1296' },
{ label: '16:9', key: '2560x1440' },
{ label: '4:3', key: '2304x1728' },
{ label: '3:2', key: '2496x1664' },
{ label: '1:1', key: '2048x2048' },
{ label: '2:3', key: '1664x2496' },
{ label: '3:4', key: '1728x2304' },
{ label: '9:16', key: '1440x2560' },
{ label: '9:21', key: '1296x3024' }
{ label: '自动', key: 'auto' },
{ label: '竖图 2:3', key: '1024x1536' },
{ label: '方图 1:1', key: '1024x1024' },
{ label: '横图 3:2', key: '1536x1024' }
]
// Seedream 4K image size options | 豆包4K图片尺寸选项
export const SEEDREAM_4K_SIZE_OPTIONS = [
{ label: '21:9', key: '6198x2656' },
{ label: '16:9', key: '5404x3040' },
{ label: '4:3', key: '4694x3520' },
{ label: '3:2', key: '4992x3328' },
{ label: '1:1', key: '4096x4096' },
{ label: '2:3', key: '3328x4992' },
{ label: '3:4', key: '3520x4694' },
{ label: '9:16', key: '3040x5404' },
{ label: '9:21', key: '2656x6198' }
]
// Kept for compatibility with upstream model helpers.
export const SEEDREAM_4K_SIZE_OPTIONS = SEEDREAM_SIZE_OPTIONS
// Seedream quality options | 豆包画质选项
// SKG backend currently exposes model choice and size; quality is retained as a no-op UI field.
export const SEEDREAM_QUALITY_OPTIONS = [
{ label: '标准画质', key: 'standard' },
{ label: '4K 高清', key: '4k' }
{ label: '标准', key: 'standard' }
]
export const BANANA_SIZE_OPTIONS = [
@@ -48,51 +32,37 @@ export const BANANA_SIZE_OPTIONS = [
// Image generation models | 图片生成模型
export const IMAGE_MODELS = [
{
label: 'Nano Banana 2',
key: 'nano-banana-2',
provider: ['chatfire'], // 火宝渠道
sizes: BANANA_SIZE_OPTIONS.map(s => s.key),
// qualities: SEEDREAM_QUALITY_OPTIONS,
// getSizesByQuality: (quality) => quality === '4k' ? SEEDREAM_4K_SIZE_OPTIONS : SEEDREAM_SIZE_OPTIONS,
defaultParams: {
size: '1x1',
quality: 'standard',
style: 'vivid'
}
},
{
label: 'Nano Banana Pro',
key: 'nano-banana-pro',
provider: ['chatfire'], // 火宝渠道
sizes: BANANA_SIZE_OPTIONS.map(s => s.key),
// qualities: SEEDREAM_QUALITY_OPTIONS,
// getSizesByQuality: (quality) => quality === '4k' ? SEEDREAM_4K_SIZE_OPTIONS : SEEDREAM_SIZE_OPTIONS,
defaultParams: {
size: '1x1',
quality: 'standard',
style: 'vivid'
}
},
{
label: '豆包 Seedream 4.5',
key: 'doubao-seedream-4-5-251128',
provider: ['chatfire'], // 火宝渠道
label: '自动',
key: 'auto',
provider: ['chatfire'],
sizes: SEEDREAM_SIZE_OPTIONS.map(s => s.key),
qualities: SEEDREAM_QUALITY_OPTIONS,
getSizesByQuality: (quality) => quality === '4k' ? SEEDREAM_4K_SIZE_OPTIONS : SEEDREAM_SIZE_OPTIONS,
defaultParams: {
size: '2048x2048',
size: '1024x1536',
quality: 'standard',
style: 'vivid'
}
},
{
label: 'Nano Banana',
key: 'nano-banana',
provider: ['chatfire'], // 火宝渠道
tips: '尺寸写在提示词中: 尺寸 9:16',
sizes: [],
label: 'GPT Image 2',
key: 'gpt-image-2',
provider: ['chatfire'],
sizes: SEEDREAM_SIZE_OPTIONS.map(s => s.key),
qualities: SEEDREAM_QUALITY_OPTIONS,
defaultParams: {
size: '1024x1536',
quality: 'standard',
style: 'vivid'
}
},
{
label: 'Gemini 图片',
key: 'gemini-3-pro-image-preview',
provider: ['chatfire'],
sizes: SEEDREAM_SIZE_OPTIONS.map(s => s.key),
qualities: SEEDREAM_QUALITY_OPTIONS,
defaultParams: {
size: '1024x1536',
quality: 'standard',
style: 'vivid'
}
@@ -102,11 +72,10 @@ export const IMAGE_MODELS = [
// Video ratio options | 视频比例选项
export const VIDEO_RATIO_LIST = [
{ label: '16:9 (横版)', key: '16x9' },
{ label: '4:3', key: '4x3' },
{ label: '1:1 (方形)', key: '1x1' },
{ label: '3:4', key: '3x4' },
{ label: '9:16 (竖版)', key: '9x16' }
{ label: '竖屏 9:16', key: '720x1280' },
{ label: '横屏 16:9', key: '1280x720' },
{ label: '方形 1:1', key: '1024x1024' },
{ label: '竖屏 3:4', key: '960x1280' }
]
// Video resolution options for Seedance | Seedance 分辨率选项
@@ -118,102 +87,23 @@ export const SEEDANCE_RESOLUTION_OPTIONS = [
// Video generation models | 视频生成模型
export const VIDEO_MODELS = [
// Seedance 模型 - 1.5 Pro
{
label: 'Seedance 1.5 Pro (图文视频)',
key: 'doubao-seedance-1-5-pro-251215',
label: 'Seedance 2.0 Fast',
key: 'seedance',
provider: ['chatfire'],
type: 't2v+i2v',
ratios: ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
resolutions: ['480p', '720p', '1080p'],
defaultResolution: '1080p',
defaultParams: { ratio: '16:9', duration: 10, resolution: '1080p' }
},
// Seedance 模型 - 文生视频
{
label: 'Seedance 1.0 Lite (文生视频)',
key: 'doubao-seedance-1-0-lite-t2v-250428',
provider: ['chatfire'],
type: 't2v', // 文生视频
ratios: ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
resolutions: ['480p', '720p', '1080p'],
ratios: ['720x1280', '1280x720', '1024x1024', '960x1280'],
durs: [
{ label: '5 秒', key: 5 },
{ label: '8 秒', key: 8 },
{ label: '10 秒', key: 10 },
{ label: '12 秒', key: 12 },
{ label: '15 秒', key: 15 }
],
resolutions: ['720p'],
defaultResolution: '720p',
defaultParams: { ratio: '16:9', duration: 5, resolution: '720p' }
defaultParams: { ratio: '720x1280', duration: 10, resolution: '720p' }
},
// Seedance 模型 - 图生视频
{
label: 'Seedance 1.0 Lite (图生视频)',
key: 'doubao-seedance-1-0-lite-i2v-250428',
provider: ['chatfire'],
type: 'i2v', // 图生视频
ratios: ['16:9'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
resolutions: ['480p', '720p', '1080p'],
defaultResolution: '720p',
defaultParams: { ratio: '16:9', duration: 5, resolution: '720p' }
},
// Seedance 模型 - 图文视频 Pro
{
label: 'Seedance 1.0 Pro (图文视频)',
key: 'doubao-seedance-1-0-pro-250528',
provider: ['chatfire'],
type: 't2v+i2v', // 图文视频
ratios: ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9', '16:9'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
resolutions: ['480p', '720p', '1080p'],
defaultResolution: '1080p',
defaultParams: { ratio: '16:9', duration: 5, resolution: '1080p' }
},
// Seedance 模型 - 1.0 Pro Fast
{
label: 'Seedance 1.0 Pro Fast (图文视频)',
key: 'doubao-seedance-1-0-pro-fast-251015',
provider: ['chatfire'],
type: 't2v+i2v',
ratios: ['16:9', '4:3', '1:1', '3:4', '9:16', '21:9'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
resolutions: ['480p', '720p', '1080p'],
defaultResolution: '1080p',
defaultParams: { ratio: '16:9', duration: 5, resolution: '1080p' }
},
// 可灵 Kling
// {
// label: '可灵 Kling v2.5-turbo',
// key: 'kling-v2-1',
// provider: ['chatfire'], // 仅火宝渠道
// ratios: VIDEO_RATIO_LIST.map(s => s.key),
// durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
// defaultParams: { ratio: '9:16', duration: 10 }
// },
// {
// label: 'runway/gen4-turbo',
// key: 'runway/gen4-turbo',
// ratios: VIDEO_RATIO_LIST.map(s => s.key),
// durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
// defaultParams: { ratio: '16:9', duration: 5 }
// },
// {
// label: '可灵视频 O1',
// key: 'kling-video-o1',
// ratios: VIDEO_RATIO_LIST.map(s => s.key),
// durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
// defaultParams: { ratio: '16:9', duration: 5 }
// },
// {
// label: 'viduq2-pro_720p', key: 'viduq2-pro_720p',
// ratios: VIDEO_RATIO_LIST.map(s => s.key),
// durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
// defaultParams: { ratio: '16:9', duration: 5 }
// },
// {
// label: 'Sora 2', key: 'sora-2',
// ratios: VIDEO_RATIO_LIST.map(s => s.key),
// durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
// defaultParams: { ratio: '16:9', duration: 5 }
// }
]
// Chat/LLM models | 对话模型
@@ -228,9 +118,10 @@ export const CHAT_MODELS = [
// Image size options | 图片尺寸选项
export const IMAGE_SIZE_OPTIONS = [
{ label: '2048x2048', key: '2048x2048' },
{ label: '1792x1024 (横版)', key: '1792x1024' },
{ label: '1024x1792 (竖版)', key: '1024x1792' }
{ label: '自动', key: 'auto' },
{ label: '竖图 2:3', key: '1024x1536' },
{ label: '方图 1:1', key: '1024x1024' },
{ label: '横图 3:2', key: '1536x1024' }
]
// Image quality options | 图片质量选项
@@ -251,16 +142,19 @@ export const VIDEO_RATIO_OPTIONS = VIDEO_RATIO_LIST
// Video duration options | 视频时长选项
export const VIDEO_DURATION_OPTIONS = [
{ label: '5 秒', key: 5 },
{ label: '10 秒', key: 10 }
{ label: '8 秒', key: 8 },
{ label: '10 秒', key: 10 },
{ label: '12 秒', key: 12 },
{ label: '15 秒', key: 15 }
]
// Default values | 默认值
export const DEFAULT_IMAGE_MODEL = 'nano-banana-pro'
export const DEFAULT_VIDEO_MODEL = 'doubao-seedance-1-5-pro-251215'
export const DEFAULT_IMAGE_MODEL = 'auto'
export const DEFAULT_VIDEO_MODEL = 'seedance'
export const DEFAULT_CHAT_MODEL = 'gpt-4o-mini'
export const DEFAULT_IMAGE_SIZE = '2048x2048'
export const DEFAULT_VIDEO_RATIO = '16:9'
export const DEFAULT_VIDEO_DURATION = 5
export const DEFAULT_IMAGE_SIZE = '1024x1536'
export const DEFAULT_VIDEO_RATIO = '720x1280'
export const DEFAULT_VIDEO_DURATION = 10
// Get model by key | 根据 key 获取模型
export const getModelByName = (key) => {

View File

@@ -81,8 +81,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + nodeSpacing, y: startPosition.y + rowSpacing * 1.5 },
data: {
label: '主角色图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -145,8 +145,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: currentX, y: angleY },
data: {
label: `${angleConfig.label} (${angleConfig.english})`,
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -281,8 +281,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y },
data: {
label: '生成模特图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -294,8 +294,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y + rowSpacing },
data: {
label: '侧面展示图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -307,8 +307,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y + rowSpacing * 2 },
data: {
label: '俯瞰展示图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -320,8 +320,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y + rowSpacing * 3 },
data: {
label: '拆解图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -507,8 +507,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y },
data: {
label: '生成正面全身图',
model: 'doubao-seedream-4-5-251128',
size: '1440x2560'
model: 'auto',
size: '1024x1536'
}
})
@@ -569,8 +569,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 4 + 100, y: startPosition.y + rowSpacing },
data: {
label: '侧面半身图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -582,8 +582,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 4 + 100, y: startPosition.y + rowSpacing * 2 },
data: {
label: '表情特写图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})
@@ -595,8 +595,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 4 + 100, y: startPosition.y + rowSpacing * 3 },
data: {
label: '背面全身图',
model: 'doubao-seedream-4-5-251128',
size: '1440x2560'
model: 'auto',
size: '1024x1536'
}
})
@@ -753,8 +753,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y },
data: {
label: '生成基础场景',
model: 'doubao-seedream-4-5-251128',
size: '2560x1440'
model: 'auto',
size: '1536x1024'
}
})
@@ -815,8 +815,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 4 + 100, y: startPosition.y + rowSpacing },
data: {
label: '傍晚场景',
model: 'doubao-seedream-4-5-251128',
size: '2560x1440'
model: 'auto',
size: '1536x1024'
}
})
@@ -828,8 +828,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 4 + 100, y: startPosition.y + rowSpacing * 2 },
data: {
label: '夜晚场景',
model: 'doubao-seedream-4-5-251128',
size: '2560x1440'
model: 'auto',
size: '1536x1024'
}
})
@@ -841,8 +841,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 4 + 100, y: startPosition.y + rowSpacing * 3 },
data: {
label: '雨天场景',
model: 'doubao-seedream-4-5-251128',
size: '2560x1440'
model: 'auto',
size: '1536x1024'
}
})
@@ -996,8 +996,8 @@ export const WORKFLOW_TEMPLATES = [
// position: { x: startPosition.x + colSpacing * 2, y: startPosition.y + rowSpacing },
// data: {
// label: '分镜画面',
// model: 'doubao-seedream-4-5-251128',
// size: '2560x1440'
// model: 'auto',
// size: '1536x1024'
// }
// })
@@ -1123,8 +1123,8 @@ export const WORKFLOW_TEMPLATES = [
position: { x: startPosition.x + colSpacing * 2, y: startPosition.y - rowSpacing },
data: {
label: '角色参考图',
model: 'doubao-seedream-4-5-251128',
size: '2048x2048'
model: 'auto',
size: '1024x1536'
}
})

View File

@@ -79,6 +79,11 @@ const setStored = (key, value) => {
}
}
const getValidStoredModel = (key, defaultValue, builtInModels) => {
const stored = getStored(key, defaultValue)
return builtInModels.some(model => model.key === stored) ? stored : defaultValue
}
// Shared reactive state (singleton pattern)
const customChatModels = ref(getStoredJson(STORAGE_KEYS.CUSTOM_CHAT_MODELS, []))
const customImageModels = ref(getStoredJson(STORAGE_KEYS.CUSTOM_IMAGE_MODELS, []))
@@ -90,8 +95,8 @@ const customImageModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_IMAGE_
const customVideoModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_VIDEO_MODELS_BY_PROVIDER || 'custom-video-models-by-provider', {}))
const selectedChatModel = ref(getStored(STORAGE_KEYS.SELECTED_CHAT_MODEL, DEFAULT_CHAT_MODEL))
const selectedImageModel = ref(getStored(STORAGE_KEYS.SELECTED_IMAGE_MODEL, DEFAULT_IMAGE_MODEL))
const selectedVideoModel = ref(getStored(STORAGE_KEYS.SELECTED_VIDEO_MODEL, DEFAULT_VIDEO_MODEL))
const selectedImageModel = ref(getValidStoredModel(STORAGE_KEYS.SELECTED_IMAGE_MODEL, DEFAULT_IMAGE_MODEL, IMAGE_MODELS))
const selectedVideoModel = ref(getValidStoredModel(STORAGE_KEYS.SELECTED_VIDEO_MODEL, DEFAULT_VIDEO_MODEL, VIDEO_MODELS))
/**
* Model Configuration Hook
@@ -117,47 +122,13 @@ export const useModelConfig = () => {
}))
])
const allImageModels = computed(() => [
...IMAGE_MODELS.map(m => ({ ...m, isCustom: false })),
...customImageModels.value.map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
sizes: [],
defaultParams: { quality: 'standard', style: 'vivid' }
})),
// 添加当前渠道的自定义模型
...(customImageModelsByProvider.value[currentProvider.value] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
sizes: [],
defaultParams: { quality: 'standard', style: 'vivid' },
provider: [currentProvider.value]
}))
])
const allImageModels = computed(() =>
IMAGE_MODELS.map(m => ({ ...m, isCustom: false }))
)
const allVideoModels = computed(() => [
...VIDEO_MODELS.map(m => ({ ...m, isCustom: false })),
...customVideoModels.value.map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
ratios: ['16x9', '9:16', '1:1'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
defaultParams: { ratio: '16:9', duration: 5 }
})),
// 添加当前渠道的自定义模型
...(customVideoModelsByProvider.value[currentProvider.value] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
ratios: ['16x9', '9:16', '1:1'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
defaultParams: { ratio: '16:9', duration: 5 },
provider: [currentProvider.value]
}))
])
const allVideoModels = computed(() =>
VIDEO_MODELS.map(m => ({ ...m, isCustom: false }))
)
// Available models filtered by provider | 根据渠道过滤的可用模型
const availableChatModels = computed(() =>
@@ -188,29 +159,12 @@ export const useModelConfig = () => {
provider: [provider]
}))
]
const image = [
...IMAGE_MODELS.filter(m => isModelSupported(m, provider)).map(m => ({ ...m, isCustom: false })),
...(customImageModelsByProvider.value[provider] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
sizes: [],
defaultParams: { quality: 'standard', style: 'vivid' },
provider: [provider]
}))
]
const video = [
...VIDEO_MODELS.filter(m => isModelSupported(m, provider)).map(m => ({ ...m, isCustom: false })),
...(customVideoModelsByProvider.value[provider] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
ratios: ['16x9', '9:16', '1:1'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
defaultParams: { ratio: '16:9', duration: 5 },
provider: [provider]
}))
]
const image = IMAGE_MODELS
.filter(m => isModelSupported(m, provider))
.map(m => ({ ...m, isCustom: false }))
const video = VIDEO_MODELS
.filter(m => isModelSupported(m, provider))
.map(m => ({ ...m, isCustom: false }))
return { chat, image, video }
}

View File

@@ -401,7 +401,7 @@ export const initSampleData = () => {
// Add image config node | 添加文生图配置节点
addNode('imageConfig', { x: 450, y: 150 }, {
prompt: '',
model: 'doubao-seedream-4-5-251128',
model: 'auto',
ratio: '16:9 | 4张 | 高清',
label: '文生图'
})

View File

@@ -88,6 +88,11 @@ const setStoredJson = (key, value) => {
}
}
const getValidStoredModel = (key, defaultValue, builtInModels) => {
const stored = getStored(key, defaultValue)
return builtInModels.some(model => model.key === stored) ? stored : defaultValue
}
/**
* 检查模型是否支持指定渠道
*/
@@ -160,8 +165,8 @@ export const useModelStore = defineStore('model', () => {
// 选中的模型
const selectedChatModel = ref(getStored(STORAGE_KEYS.SELECTED_CHAT_MODEL, DEFAULT_CHAT_MODEL))
const selectedImageModel = ref(getStored(STORAGE_KEYS.SELECTED_IMAGE_MODEL, DEFAULT_IMAGE_MODEL))
const selectedVideoModel = ref(getStored(STORAGE_KEYS.SELECTED_VIDEO_MODEL, DEFAULT_VIDEO_MODEL))
const selectedImageModel = ref(getValidStoredModel(STORAGE_KEYS.SELECTED_IMAGE_MODEL, DEFAULT_IMAGE_MODEL, IMAGE_MODELS))
const selectedVideoModel = ref(getValidStoredModel(STORAGE_KEYS.SELECTED_VIDEO_MODEL, DEFAULT_VIDEO_MODEL, VIDEO_MODELS))
// 按渠道存储的 API 配置
const apiKeysByProvider = ref(getStoredJson(STORAGE_KEYS.API_KEYS_BY_PROVIDER, {}))
@@ -205,47 +210,13 @@ export const useModelStore = defineStore('model', () => {
}))
])
const allImageModels = computed(() => [
...IMAGE_MODELS.map(m => ({ ...m, isCustom: false })),
...customImageModels.value.map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
sizes: [],
defaultParams: { quality: 'standard', style: 'vivid' }
})),
// 添加当前渠道的自定义模型
...(customImageModelsByProvider.value[currentProvider.value] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
sizes: [],
defaultParams: { quality: 'standard', style: 'vivid' },
provider: [currentProvider.value]
}))
])
const allImageModels = computed(() =>
IMAGE_MODELS.map(m => ({ ...m, isCustom: false }))
)
const allVideoModels = computed(() => [
...VIDEO_MODELS.map(m => ({ ...m, isCustom: false })),
...customVideoModels.value.map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
ratios: ['16x9', '9:16', '1:1'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
defaultParams: { ratio: '16:9', duration: 5 }
})),
// 添加当前渠道的自定义模型
...(customVideoModelsByProvider.value[currentProvider.value] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
ratios: ['16x9', '9:16', '1:1'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
defaultParams: { ratio: '16:9', duration: 5 },
provider: [currentProvider.value]
}))
])
const allVideoModels = computed(() =>
VIDEO_MODELS.map(m => ({ ...m, isCustom: false }))
)
// ============ Computed: Available Models (filtered by provider) ============
@@ -412,29 +383,12 @@ export const useModelStore = defineStore('model', () => {
provider: [provider]
}))
]
const image = [
...IMAGE_MODELS.filter(m => isModelSupported(m, provider)).map(m => ({ ...m, isCustom: false })),
...(customImageModelsByProvider.value[provider] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
sizes: [],
defaultParams: { quality: 'standard', style: 'vivid' },
provider: [provider]
}))
]
const video = [
...VIDEO_MODELS.filter(m => isModelSupported(m, provider)).map(m => ({ ...m, isCustom: false })),
...(customVideoModelsByProvider.value[provider] || []).map(m => ({
label: m.label || m.key,
key: m.key,
isCustom: true,
ratios: ['16x9', '9:16', '1:1'],
durs: [{ label: '5 秒', key: 5 }, { label: '10 秒', key: 10 }],
defaultParams: { ratio: '16:9', duration: 5 },
provider: [provider]
}))
]
const image = IMAGE_MODELS
.filter(m => isModelSupported(m, provider))
.map(m => ({ ...m, isCustom: false }))
const video = VIDEO_MODELS
.filter(m => isModelSupported(m, provider))
.map(m => ({ ...m, isCustom: false }))
return { chat, image, video }
}

View File

@@ -345,8 +345,8 @@ export const initProjectsStore = () => {
position: { x: 500, y: 150 },
data: {
prompt: '',
model: 'doubao-seedream-4-5-251128',
size: '512x512',
model: 'auto',
size: '1024x1024',
label: '文生图'
}
}