mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-06 16:10:09 +00:00
[feat] Add dynamic price badge for Veo3VideoGenerationNode (#4682)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -919,6 +919,33 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
return `$${price.toFixed(2)}/Run`
|
||||
}
|
||||
},
|
||||
Veo3VideoGenerationNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
const modelWidget = node.widgets?.find(
|
||||
(w) => w.name === 'model'
|
||||
) as IComboWidget
|
||||
const generateAudioWidget = node.widgets?.find(
|
||||
(w) => w.name === 'generate_audio'
|
||||
) as IComboWidget
|
||||
|
||||
if (!modelWidget || !generateAudioWidget) {
|
||||
return '$2.00-6.00/Run (varies with model & audio generation)'
|
||||
}
|
||||
|
||||
const model = String(modelWidget.value)
|
||||
const generateAudio =
|
||||
String(generateAudioWidget.value).toLowerCase() === 'true'
|
||||
|
||||
if (model.includes('veo-3.0-fast-generate-001')) {
|
||||
return generateAudio ? '$3.20/Run' : '$2.00/Run'
|
||||
} else if (model.includes('veo-3.0-generate-001')) {
|
||||
return generateAudio ? '$6.00/Run' : '$4.00/Run'
|
||||
}
|
||||
|
||||
// Default fallback
|
||||
return '$2.00-6.00/Run'
|
||||
}
|
||||
},
|
||||
LumaImageNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
const modelWidget = node.widgets?.find(
|
||||
@@ -1340,6 +1367,7 @@ export const useNodePricing = () => {
|
||||
FluxProKontextProNode: [],
|
||||
FluxProKontextMaxNode: [],
|
||||
VeoVideoGenerationNode: ['duration_seconds'],
|
||||
Veo3VideoGenerationNode: ['model', 'generate_audio'],
|
||||
LumaVideoNode: ['model', 'resolution', 'duration'],
|
||||
LumaImageToVideoNode: ['model', 'resolution', 'duration'],
|
||||
LumaImageNode: ['model', 'aspect_ratio'],
|
||||
|
||||
@@ -393,6 +393,86 @@ describe('useNodePricing', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('dynamic pricing - Veo3VideoGenerationNode', () => {
|
||||
it('should return $2.00 for veo-3.0-fast-generate-001 without audio', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [
|
||||
{ name: 'model', value: 'veo-3.0-fast-generate-001' },
|
||||
{ name: 'generate_audio', value: false }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$2.00/Run')
|
||||
})
|
||||
|
||||
it('should return $3.20 for veo-3.0-fast-generate-001 with audio', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [
|
||||
{ name: 'model', value: 'veo-3.0-fast-generate-001' },
|
||||
{ name: 'generate_audio', value: true }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$3.20/Run')
|
||||
})
|
||||
|
||||
it('should return $4.00 for veo-3.0-generate-001 without audio', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [
|
||||
{ name: 'model', value: 'veo-3.0-generate-001' },
|
||||
{ name: 'generate_audio', value: false }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$4.00/Run')
|
||||
})
|
||||
|
||||
it('should return $6.00 for veo-3.0-generate-001 with audio', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [
|
||||
{ name: 'model', value: 'veo-3.0-generate-001' },
|
||||
{ name: 'generate_audio', value: true }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$6.00/Run')
|
||||
})
|
||||
|
||||
it('should return range when widgets are missing', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$2.00-6.00/Run (varies with model & audio generation)'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return range when only model widget is present', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [
|
||||
{ name: 'model', value: 'veo-3.0-generate-001' }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$2.00-6.00/Run (varies with model & audio generation)'
|
||||
)
|
||||
})
|
||||
|
||||
it('should return range when only generate_audio widget is present', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('Veo3VideoGenerationNode', [
|
||||
{ name: 'generate_audio', value: true }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$2.00-6.00/Run (varies with model & audio generation)'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('dynamic pricing - LumaVideoNode', () => {
|
||||
it('should return $2.19 for ray-flash-2 4K 5s', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
@@ -736,6 +816,13 @@ describe('useNodePricing', () => {
|
||||
expect(widgetNames).toEqual(['duration_seconds'])
|
||||
})
|
||||
|
||||
it('should return correct widget names for Veo3VideoGenerationNode', () => {
|
||||
const { getRelevantWidgetNames } = useNodePricing()
|
||||
|
||||
const widgetNames = getRelevantWidgetNames('Veo3VideoGenerationNode')
|
||||
expect(widgetNames).toEqual(['model', 'generate_audio'])
|
||||
})
|
||||
|
||||
it('should return correct widget names for LumaVideoNode', () => {
|
||||
const { getRelevantWidgetNames } = useNodePricing()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user