Skip to content

Commit 9f13a2e

Browse files
committed
feat(video-generator): refresh native provider catalogs and request protocols
1 parent e350f50 commit 9f13a2e

18 files changed

Lines changed: 1829 additions & 728 deletions

‎apps/sim/blocks/blocks/video_generator.ts‎

Lines changed: 240 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { VideoIcon } from '@/components/icons'
2+
import {
3+
directVideoDefaults,
4+
directVideoModels,
5+
getDirectVideoModel,
6+
} from '@/lib/media/direct-video-catalog'
27
import { falVideoModels, getFalVideoModel } from '@/lib/media/fal-video-catalog'
38
import { AuthMode, type BlockConfig, IntegrationType, type SubBlockConfig } from '@/blocks/types'
49
import { normalizeFileInput, parseOptionalBooleanInput } from '@/blocks/utils'
@@ -57,6 +62,19 @@ function tuningField(
5762
},
5863
dependsOn: ['model'],
5964
options: (params) => {
65+
if (!params) {
66+
const values = endpointModels.flatMap((model) => {
67+
const choices = model[key]
68+
return Array.isArray(choices)
69+
? choices.map((value) =>
70+
key === 'durations' ? String(Number.parseFloat(String(value))) : String(value)
71+
)
72+
: choices
73+
? ['true', 'false']
74+
: []
75+
})
76+
return [FAL_MODEL_DEFAULT, ...[...new Set(values)].map((id) => ({ id, label: id }))]
77+
}
6078
const model = getFalVideoModel(params?.values.model)
6179
if (!model?.[key]) return []
6280
if (key === 'audioField' || key === 'promptOptimizer') {
@@ -127,6 +145,171 @@ function withFalVideoCatalog(subBlocks: SubBlockConfig[]): SubBlockConfig[] {
127145
})
128146
}
129147

148+
function selectedDirectModel(values?: Record<string, unknown>) {
149+
const withImage = [
150+
values?.visualReference,
151+
values?.visualReferenceInput,
152+
values?.visualReferenceUpload,
153+
].some((reference) => (Array.isArray(reference) ? reference.length > 0 : Boolean(reference)))
154+
return getDirectVideoModel(values?.provider, values?.model, withImage)
155+
}
156+
157+
const directVideoFields = (
158+
[
159+
['directDuration', 'Duration (seconds)', 'durations'],
160+
['directAspectRatio', 'Aspect Ratio', 'aspectRatios'],
161+
['directResolution', 'Resolution', 'resolutions'],
162+
['directGenerateAudio', 'Generate Audio', 'audio'],
163+
['directPromptOptimizer', 'Prompt Optimizer', 'promptOptimizer'],
164+
] as const
165+
).map(
166+
([id, title, key]): SubBlockConfig => ({
167+
id,
168+
title,
169+
type: 'dropdown',
170+
value: () => FAL_MODEL_DEFAULT.id,
171+
dependsOn: ['provider', 'model', 'visualReferenceInput', 'visualReferenceUpload'],
172+
condition: (values) => ({
173+
field: 'model',
174+
value: selectedDirectModel(values)?.settings?.[key] ? [String(values?.model)] : [],
175+
}),
176+
options: (params) => {
177+
if (!params) {
178+
const values = directVideoModels.flatMap((model) =>
179+
[model.text, model.image].flatMap((settings) => {
180+
const choices = settings?.[key]
181+
return Array.isArray(choices) ? choices.map(String) : choices ? ['true', 'false'] : []
182+
})
183+
)
184+
return [FAL_MODEL_DEFAULT, ...[...new Set(values)].map((id) => ({ id, label: id }))]
185+
}
186+
const model = selectedDirectModel(params?.values)
187+
const choices = model?.settings?.[key]
188+
if (!choices) return []
189+
if (typeof choices === 'boolean')
190+
return [FAL_MODEL_DEFAULT, { id: 'true', label: 'On' }, { id: 'false', label: 'Off' }]
191+
return [
192+
FAL_MODEL_DEFAULT,
193+
...choices
194+
.filter((value) => {
195+
if (model?.protocol !== 'minimax-v1') return true
196+
if (key === 'durations' && params?.values.directResolution === '1080P')
197+
return value === 6
198+
if (key === 'resolutions' && Number(params?.values.directDuration) === 10)
199+
return value !== '1080P'
200+
return true
201+
})
202+
.map((value) => ({ id: String(value), label: String(value) })),
203+
]
204+
},
205+
})
206+
)
207+
208+
function withDirectVideoCatalog(subBlocks: SubBlockConfig[]): SubBlockConfig[] {
209+
return subBlocks.flatMap((field): SubBlockConfig[] => {
210+
if (
211+
field.id === 'model' &&
212+
typeof field.condition === 'object' &&
213+
field.condition.value === 'falai'
214+
) {
215+
const options = field.options
216+
return [
217+
{
218+
...field,
219+
options: (params) => {
220+
const falOptions = typeof options === 'function' ? options(params) : (options ?? [])
221+
return params
222+
? falOptions
223+
: [
224+
...falOptions,
225+
...directVideoModels.flatMap((model) => [
226+
{ id: model.id, label: model.label },
227+
...(model.aliases ?? []).map((id) => ({
228+
id,
229+
label: model.label,
230+
hidden: true,
231+
})),
232+
]),
233+
]
234+
},
235+
},
236+
]
237+
}
238+
if (field.id === 'provider')
239+
return [
240+
{
241+
...field,
242+
options: [
243+
{ id: 'runway', label: 'Runway' },
244+
{ id: 'veo', label: 'Google Gemini' },
245+
{ id: 'luma', label: 'Luma' },
246+
{ id: 'minimax', label: 'MiniMax' },
247+
{ id: 'falai', label: 'Fal.ai (Multi-Model)' },
248+
],
249+
},
250+
...(['runway', 'veo', 'luma', 'minimax'] as const).map(
251+
(provider): SubBlockConfig => ({
252+
id: 'model',
253+
title: 'Model',
254+
type: 'dropdown',
255+
searchable: true,
256+
condition: { field: 'provider', value: provider },
257+
options: directVideoModels
258+
.filter((model) => model.provider === provider)
259+
.flatMap((model) => [
260+
{ id: model.id, label: model.label },
261+
...(model.aliases ?? []).map((id) => ({ id, label: model.label, hidden: true })),
262+
]),
263+
value: () => directVideoDefaults[provider],
264+
dependsOn: ['provider'],
265+
required: true,
266+
})
267+
),
268+
]
269+
if (field.id === 'prompt') return [field, ...directVideoFields]
270+
const condition = field.condition
271+
const isDirect =
272+
typeof condition === 'object' &&
273+
condition.field === 'provider' &&
274+
typeof condition.value === 'string' &&
275+
condition.value !== 'falai'
276+
if (
277+
isDirect &&
278+
['model', 'duration', 'aspectRatio', 'resolution', 'promptOptimizer'].includes(field.id)
279+
)
280+
return []
281+
if (field.id === 'endpoint')
282+
return [
283+
{
284+
...field,
285+
condition: {
286+
field: 'provider',
287+
value: 'minimax',
288+
and: { field: 'model', value: ['hailuo-2.3', 'hailuo-02'] },
289+
},
290+
dependsOn: ['provider', 'model'],
291+
},
292+
]
293+
if (field.id === 'cameraControl')
294+
return [
295+
{
296+
...field,
297+
condition: {
298+
field: 'provider',
299+
value: 'luma',
300+
and: { field: 'model', value: ['ray-2', 'ray-flash-2'] },
301+
},
302+
dependsOn: ['provider', 'model'],
303+
},
304+
]
305+
if (field.id === 'visualReferenceUpload' || field.id === 'visualReferenceInput')
306+
return [
307+
{ ...field, required: { field: 'model', value: ['gen4_turbo', 'gen-4', 'gen-4-turbo'] } },
308+
]
309+
return [field]
310+
})
311+
}
312+
130313
export const VideoGeneratorBlock: BlockConfig = {
131314
type: 'video_generator',
132315
name: 'Video Generator (Legacy)',
@@ -1733,16 +1916,71 @@ export const VideoGeneratorV3Block: BlockConfig = {
17331916
name: 'Video Generator',
17341917
description: 'Generate videos from text using AI',
17351918
longDescription:
1736-
'Generate high-quality videos from text prompts using leading AI providers. Supports Runway, Google Veo, Luma, MiniMax, and Fal.ai multi-model generation with provider-specific durations, aspect ratios, resolutions, prompt optimization, and native audio controls.',
1919+
'Generate high-quality videos from text prompts using leading AI providers. Supports Runway, Google Gemini, Luma, MiniMax, and Fal.ai multi-model generation with provider-specific durations, aspect ratios, resolutions, prompt optimization, and native audio controls.',
17371920
docsLink: 'https://docs.sim.ai/integrations/video_generator',
17381921
category: 'blocks',
17391922
integrationType: IntegrationType.AI,
17401923
bgColor: '#181C1E',
17411924
icon: VideoIcon,
17421925
hideFromToolbar: false,
1743-
subBlocks: withFalVideoCatalog(VideoGeneratorV2Block.subBlocks),
1926+
subBlocks: withDirectVideoCatalog(withFalVideoCatalog(VideoGeneratorV2Block.subBlocks)),
1927+
tools: {
1928+
...VideoGeneratorV2Block.tools,
1929+
config: {
1930+
tool: (params) => VideoGeneratorV2Block.tools.config?.tool(params) ?? 'video_runway',
1931+
params: (params) => {
1932+
if (params.provider === 'falai')
1933+
return VideoGeneratorV2Block.tools.config?.params?.(params) ?? params
1934+
const visualReference =
1935+
params.provider === 'runway'
1936+
? normalizeFileInput(params.visualReference, { single: true })
1937+
: undefined
1938+
const model = getDirectVideoModel(params.provider, params.model, Boolean(visualReference))
1939+
const setting = (legacy: string, current: string, supported: unknown) => {
1940+
if (!supported) return undefined
1941+
const value = params[current] ?? params[legacy]
1942+
return value === FAL_MODEL_DEFAULT.id ? undefined : value
1943+
}
1944+
const duration = setting('duration', 'directDuration', model?.settings?.durations)
1945+
return {
1946+
provider: params.provider,
1947+
model: params.model,
1948+
apiKey: params.apiKey,
1949+
prompt: params.prompt,
1950+
visualReference,
1951+
duration: duration ? Number(duration) : undefined,
1952+
aspectRatio: setting('aspectRatio', 'directAspectRatio', model?.settings?.aspectRatios),
1953+
resolution: setting('resolution', 'directResolution', model?.settings?.resolutions),
1954+
generateAudio: parseOptionalBooleanInput(
1955+
setting('generateAudio', 'directGenerateAudio', model?.settings?.audio)
1956+
),
1957+
promptOptimizer: parseOptionalBooleanInput(
1958+
setting('promptOptimizer', 'directPromptOptimizer', model?.settings?.promptOptimizer)
1959+
),
1960+
endpoint: model?.protocol === 'minimax-v1' ? params.endpoint : undefined,
1961+
cameraControl:
1962+
model?.protocol === 'luma-legacy' && params.cameraControl
1963+
? typeof params.cameraControl === 'string'
1964+
? JSON.parse(params.cameraControl)
1965+
: params.cameraControl
1966+
: undefined,
1967+
}
1968+
},
1969+
},
1970+
},
17441971
inputs: {
17451972
...VideoGeneratorV2Block.inputs,
1973+
directDuration: { type: 'number', description: 'Duration for the selected provider model' },
1974+
directAspectRatio: {
1975+
type: 'string',
1976+
description: 'Frame size or aspect ratio for the selected provider model',
1977+
},
1978+
directResolution: { type: 'string', description: 'Resolution for the selected provider model' },
1979+
directGenerateAudio: { type: 'boolean', description: 'Generate audio when supported' },
1980+
directPromptOptimizer: {
1981+
type: 'boolean',
1982+
description: 'Enable prompt optimization when supported',
1983+
},
17461984
falDuration: {
17471985
type: 'number',
17481986
description: 'Duration in seconds for the selected fal endpoint',

0 commit comments

Comments
 (0)