mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
feat(api-nodes): add pricing for new LTXV-2 models (#6307)
## Summary For the upcoming LTXV API nodes. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6307-feat-api-nodes-add-pricing-for-new-LTXV-2-models-2986d73d365081db9994deffc219c6c4) by [Unito](https://www.unito.io)
This commit is contained in:
@@ -169,6 +169,46 @@ const byteDanceVideoPricingCalculator = (node: LGraphNode): string => {
|
||||
: `$${minCost.toFixed(2)}-$${maxCost.toFixed(2)}/Run`
|
||||
}
|
||||
|
||||
const ltxvPricingCalculator = (node: LGraphNode): string => {
|
||||
const modelWidget = node.widgets?.find(
|
||||
(w) => w.name === 'model'
|
||||
) as IComboWidget
|
||||
const durationWidget = node.widgets?.find(
|
||||
(w) => w.name === 'duration'
|
||||
) as IComboWidget
|
||||
const resolutionWidget = node.widgets?.find(
|
||||
(w) => w.name === 'resolution'
|
||||
) as IComboWidget
|
||||
|
||||
const fallback = '$0.04-0.24/second'
|
||||
if (!modelWidget || !durationWidget || !resolutionWidget) return fallback
|
||||
|
||||
const model = String(modelWidget.value).toLowerCase()
|
||||
const resolution = String(resolutionWidget.value).toLowerCase()
|
||||
const seconds = parseFloat(String(durationWidget.value))
|
||||
const priceByModel: Record<string, Record<string, number>> = {
|
||||
'ltx-2 (pro)': {
|
||||
'1920x1080': 0.06,
|
||||
'2560x1440': 0.12,
|
||||
'3840x2160': 0.24
|
||||
},
|
||||
'ltx-2 (fast)': {
|
||||
'1920x1080': 0.04,
|
||||
'2560x1440': 0.08,
|
||||
'3840x2160': 0.16
|
||||
}
|
||||
}
|
||||
|
||||
const modelTable = priceByModel[model]
|
||||
if (!modelTable) return fallback
|
||||
|
||||
const pps = modelTable[resolution]
|
||||
if (!pps) return fallback
|
||||
|
||||
const cost = (pps * seconds).toFixed(2)
|
||||
return `$${cost}/Run`
|
||||
}
|
||||
|
||||
// ---- constants ----
|
||||
const SORA_SIZES = {
|
||||
BASIC: new Set(['720x1280', '1280x720']),
|
||||
@@ -1694,6 +1734,12 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
},
|
||||
WanImageToImageApi: {
|
||||
displayPrice: '$0.03/Run'
|
||||
},
|
||||
LtxvApiTextToVideo: {
|
||||
displayPrice: ltxvPricingCalculator
|
||||
},
|
||||
LtxvApiImageToVideo: {
|
||||
displayPrice: ltxvPricingCalculator
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1796,7 +1842,9 @@ export const useNodePricing = () => {
|
||||
ByteDanceFirstLastFrameNode: ['model', 'duration', 'resolution'],
|
||||
ByteDanceImageReferenceNode: ['model', 'duration', 'resolution'],
|
||||
WanTextToVideoApi: ['duration', 'size'],
|
||||
WanImageToVideoApi: ['duration', 'resolution']
|
||||
WanImageToVideoApi: ['duration', 'resolution'],
|
||||
LtxvApiTextToVideo: ['model', 'duration', 'resolution'],
|
||||
LtxvApiImageToVideo: ['model', 'duration', 'resolution']
|
||||
}
|
||||
return widgetMap[nodeType] || []
|
||||
}
|
||||
|
||||
@@ -2189,4 +2189,54 @@ describe('useNodePricing', () => {
|
||||
expect(price).toBe('$0.05-0.15/second')
|
||||
})
|
||||
})
|
||||
|
||||
describe('dynamic pricing - LtxvApiTextToVideo', () => {
|
||||
it('should return $0.30 for Pro 1080p 5s', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('LtxvApiTextToVideo', [
|
||||
{ name: 'model', value: 'LTX-2 (Pro)' },
|
||||
{ name: 'duration', value: '5' },
|
||||
{ name: 'resolution', value: '1920x1080' }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.30/Run') // 0.06 * 5
|
||||
})
|
||||
|
||||
it('should parse "10s" duration strings', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('LtxvApiTextToVideo', [
|
||||
{ name: 'model', value: 'LTX-2 (Fast)' },
|
||||
{ name: 'duration', value: '10' },
|
||||
{ name: 'resolution', value: '3840x2160' }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$1.60/Run') // 0.16 * 10
|
||||
})
|
||||
|
||||
it('should fall back when a required widget is missing (no resolution)', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('LtxvApiTextToVideo', [
|
||||
{ name: 'model', value: 'LTX-2 (Pro)' },
|
||||
{ name: 'duration', value: '5' }
|
||||
// missing resolution
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.04-0.24/second')
|
||||
})
|
||||
|
||||
it('should fall back for unknown model', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('LtxvApiTextToVideo', [
|
||||
{ name: 'model', value: 'LTX-3 (Pro)' },
|
||||
{ name: 'duration', value: 5 },
|
||||
{ name: 'resolution', value: '1920x1080' }
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.04-0.24/second')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user