fix: align canvas model options with backend
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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: '生图配置'
|
||||
})
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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: '文生图'
|
||||
})
|
||||
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
@@ -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: '文生图'
|
||||
})
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
|
||||
@@ -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: '文生图'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user