fix: gate video models by runtime availability

This commit is contained in:
2026-06-03 17:48:49 +08:00
parent d038f1b2f4
commit ff0bfaa8b2
8 changed files with 109 additions and 32 deletions

View File

@@ -202,6 +202,7 @@ const localModel = ref(props.data?.model || DEFAULT_VIDEO_MODEL)
const localRatio = ref(props.data?.ratio || '16:9')
const localDuration = ref(props.data?.dur || 5)
const localResolution = ref(props.data?.resolution || currentModelDefaultResolution(props.data?.model || DEFAULT_VIDEO_MODEL))
const availableVideoModels = computed(() => Array.isArray(modelStore.availableVideoModels) ? modelStore.availableVideoModels : [])
// Label editing state | Label 编辑状态
const isEditingLabel = ref(false)
@@ -245,10 +246,11 @@ const imagesByRole = computed(() => {
// Get current model config | 获取当前模型配置
const currentModelConfig = computed(() => getModelConfig(localModel.value))
const isConfigured = computed(() => !!modelStore.currentApiKey)
const canGenerate = computed(() => isConfigured.value && currentModelConfig.value?.available !== false)
const currentModelAvailable = computed(() => availableVideoModels.value.some(model => model.key === localModel.value))
const canGenerate = computed(() => isConfigured.value && currentModelAvailable.value && currentModelConfig.value?.available !== false)
// Model options from Pinia store (filtered by provider) | 从 Pinia store 获取模型选项(根据渠道过滤)
const modelOptions = computed(() => modelStore.allVideoModelOptions)
const modelOptions = computed(() => modelStore.videoModelOptions)
// Display model name | 显示模型名称
const displayModelName = computed(() => {
@@ -277,7 +279,8 @@ const resolutionOptions = computed(() => {
})
// Handle model selection | 处理模型选择
const handleModelSelect = (key) => {
const applyModelSelection = (key) => {
if (!key) return
localModel.value = key
// Update ratio and duration to model's default | 更新为模型默认比例和时长
const config = getModelConfig(key)
@@ -296,6 +299,28 @@ const handleModelSelect = (key) => {
updateNode(props.id, updates)
}
const syncModelWithAvailableOptions = () => {
const availableModels = availableVideoModels.value
if (!availableModels.length) return
const isModelAvailable = availableModels.some(model => model.key === localModel.value)
if (!localModel.value || !isModelAvailable) {
const selected = availableModels.find(model => model.key === modelStore.selectedVideoModel)?.key
applyModelSelection(selected || availableModels[0]?.key || DEFAULT_VIDEO_MODEL)
return
}
const nextResolution = normalizeResolutionForModel(localModel.value, localResolution.value)
if (nextResolution !== localResolution.value || !props.data?.resolution) {
localResolution.value = nextResolution
updateNode(props.id, { resolution: nextResolution })
}
}
const handleModelSelect = (key) => {
applyModelSelection(key)
}
// Handle duplicate | 处理复制
const handleDuplicate = () => {
const newNodeId = duplicateNode(props.id)
@@ -530,23 +555,7 @@ const handleDelete = () => {
// Initialize on mount | 挂载时初始化
onMounted(() => {
// 检查当前模型是否在可用模型列表中
const availableModels = modelStore.availableVideoModels
const isModelAvailable = availableModels.some(m => m.key === localModel.value)
if (!localModel.value || !isModelAvailable) {
// 使用 store 中的默认模型或第一个可用模型
const selected = availableModels.find(m => m.key === modelStore.selectedVideoModel)?.key
localModel.value = selected || availableModels[0]?.key || DEFAULT_VIDEO_MODEL
localResolution.value = normalizeResolutionForModel(localModel.value, localResolution.value)
updateNode(props.id, { model: localModel.value, resolution: localResolution.value })
} else {
const nextResolution = normalizeResolutionForModel(localModel.value, localResolution.value)
if (nextResolution !== localResolution.value || !props.data?.resolution) {
localResolution.value = nextResolution
updateNode(props.id, { resolution: nextResolution })
}
}
syncModelWithAvailableOptions()
})
// Watch for model changes from props | 监听 props 中模型变化
@@ -554,9 +563,16 @@ watch(() => props.data?.model, (newModel) => {
if (newModel && newModel !== localModel.value) {
localModel.value = newModel
localResolution.value = normalizeResolutionForModel(newModel, props.data?.resolution || localResolution.value)
syncModelWithAvailableOptions()
}
})
watch(
() => availableVideoModels.value.map(model => model.key).join('|'),
() => syncModelWithAvailableOptions(),
{ immediate: true }
)
watch(() => props.data?.resolution, (newResolution) => {
if (newResolution && newResolution !== localResolution.value) {
localResolution.value = normalizeResolutionForModel(localModel.value, newResolution)

View File

@@ -264,6 +264,7 @@ export const useModelStore = defineStore('model', () => {
const customVideoModelsByProvider = ref(getStoredJson(STORAGE_KEYS.CUSTOM_VIDEO_MODELS_BY_PROVIDER, {}))
const runtimeImageModels = ref([])
const runtimeVideoModels = ref([])
const runtimeVideoModelsLoaded = ref(false)
// 选中的模型
const selectedChatModel = ref(getStored(STORAGE_KEYS.SELECTED_CHAT_MODEL, DEFAULT_CHAT_MODEL))
@@ -317,7 +318,9 @@ export const useModelStore = defineStore('model', () => {
)
const allVideoModels = computed(() =>
mergeModels(VIDEO_MODELS, runtimeVideoModels.value)
runtimeVideoModelsLoaded.value
? runtimeVideoModels.value
: mergeModels(VIDEO_MODELS, runtimeVideoModels.value)
)
// ============ Computed: Available Models (filtered by provider) ============
@@ -463,6 +466,10 @@ export const useModelStore = defineStore('model', () => {
.filter(item => item?.id && item.available !== false)
.map(normalizeRuntimeVideoModel)
.filter(Boolean)
runtimeVideoModelsLoaded.value = true
if (!availableVideoModels.value.some(model => model.key === selectedVideoModel.value)) {
selectedVideoModel.value = availableVideoModels.value[0]?.key || DEFAULT_VIDEO_MODEL
}
return true
} catch (err) {
console.warn('[model store] runtime model load failed', err)
@@ -513,7 +520,7 @@ export const useModelStore = defineStore('model', () => {
const image = allImageModels.value
.filter(m => isModelSupported(m, provider))
.map(m => ({ ...m, isCustom: false }))
const video = VIDEO_MODELS
const video = allVideoModels.value
.filter(m => isModelSupported(m, provider))
.map(m => ({ ...m, isCustom: false }))
return { chat, image, video }
@@ -629,6 +636,7 @@ export const useModelStore = defineStore('model', () => {
allVideoModels,
runtimeImageModels,
runtimeVideoModels,
runtimeVideoModelsLoaded,
// Available models filtered by provider
availableChatModels,