[fix] Add dynamic pricing for Ideogram nodes based on num_images parameter (#4351)

This commit is contained in:
Christian Byrne
2025-07-04 16:13:33 -07:00
committed by GitHub
parent ee5088551e
commit 3b435e337e
2 changed files with 131 additions and 9 deletions

View File

@@ -111,30 +111,55 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
displayPrice: '$0.06/Run'
},
IdeogramV1: {
displayPrice: '$0.06/Run'
displayPrice: (node: LGraphNode): string => {
const numImagesWidget = node.widgets?.find(
(w) => w.name === 'num_images'
) as IComboWidget
if (!numImagesWidget) return '$0.06 x num_images/Run'
const numImages = Number(numImagesWidget.value) || 1
const cost = (0.06 * numImages).toFixed(2)
return `$${cost}/Run`
}
},
IdeogramV2: {
displayPrice: '$0.08/Run'
displayPrice: (node: LGraphNode): string => {
const numImagesWidget = node.widgets?.find(
(w) => w.name === 'num_images'
) as IComboWidget
if (!numImagesWidget) return '$0.08 x num_images/Run'
const numImages = Number(numImagesWidget.value) || 1
const cost = (0.08 * numImages).toFixed(2)
return `$${cost}/Run`
}
},
IdeogramV3: {
displayPrice: (node: LGraphNode): string => {
const renderingSpeedWidget = node.widgets?.find(
(w) => w.name === 'rendering_speed'
) as IComboWidget
const numImagesWidget = node.widgets?.find(
(w) => w.name === 'num_images'
) as IComboWidget
if (!renderingSpeedWidget)
return '$0.03-0.08/Run (varies with rendering speed)'
return '$0.03-0.08 x num_images/Run (varies with rendering speed & num_images)'
const numImages = Number(numImagesWidget?.value) || 1
let basePrice = 0.06 // default balanced price
const renderingSpeed = String(renderingSpeedWidget.value)
if (renderingSpeed.toLowerCase().includes('quality')) {
return '$0.08/Run'
basePrice = 0.08
} else if (renderingSpeed.toLowerCase().includes('balanced')) {
return '$0.06/Run'
basePrice = 0.06
} else if (renderingSpeed.toLowerCase().includes('turbo')) {
return '$0.03/Run'
basePrice = 0.03
}
return '$0.06/Run'
const totalCost = (basePrice * numImages).toFixed(2)
return `$${totalCost}/Run`
}
},
KlingCameraControlI2VNode: {
@@ -897,7 +922,9 @@ export const useNodePricing = () => {
OpenAIDalle3: ['size', 'quality'],
OpenAIDalle2: ['size'],
OpenAIGPTImage1: ['quality'],
IdeogramV3: ['rendering_speed'],
IdeogramV1: ['num_images'],
IdeogramV2: ['num_images'],
IdeogramV3: ['rendering_speed', 'num_images'],
VeoVideoGenerationNode: ['duration_seconds'],
LumaVideoNode: ['model', 'resolution', 'duration'],
LumaImageToVideoNode: ['model', 'resolution', 'duration'],

View File

@@ -335,7 +335,31 @@ describe('useNodePricing', () => {
const node = createMockNode('IdeogramV3', [])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.03-0.08/Run (varies with rendering speed)')
expect(price).toBe(
'$0.03-0.08 x num_images/Run (varies with rendering speed & num_images)'
)
})
it('should multiply price by num_images for Quality rendering speed', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV3', [
{ name: 'rendering_speed', value: 'Quality' },
{ name: 'num_images', value: 3 }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.24/Run') // 0.08 * 3
})
it('should multiply price by num_images for Turbo rendering speed', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV3', [
{ name: 'rendering_speed', value: 'Turbo' },
{ name: 'num_images', value: 5 }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.15/Run') // 0.03 * 5
})
})
@@ -742,6 +766,29 @@ describe('useNodePricing', () => {
expect(widgetNames).toEqual([])
})
describe('Ideogram nodes with num_images parameter', () => {
it('should return correct widget names for IdeogramV1', () => {
const { getRelevantWidgetNames } = useNodePricing()
const widgetNames = getRelevantWidgetNames('IdeogramV1')
expect(widgetNames).toEqual(['num_images'])
})
it('should return correct widget names for IdeogramV2', () => {
const { getRelevantWidgetNames } = useNodePricing()
const widgetNames = getRelevantWidgetNames('IdeogramV2')
expect(widgetNames).toEqual(['num_images'])
})
it('should return correct widget names for IdeogramV3', () => {
const { getRelevantWidgetNames } = useNodePricing()
const widgetNames = getRelevantWidgetNames('IdeogramV3')
expect(widgetNames).toEqual(['rendering_speed', 'num_images'])
})
})
describe('Recraft nodes with n parameter', () => {
it('should return correct widget names for RecraftTextToImageNode', () => {
const { getRelevantWidgetNames } = useNodePricing()
@@ -759,6 +806,54 @@ describe('useNodePricing', () => {
})
})
describe('Ideogram nodes dynamic pricing', () => {
it('should calculate dynamic pricing for IdeogramV1 based on num_images value', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV1', [
{ name: 'num_images', value: 3 }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.18/Run') // 0.06 * 3
})
it('should calculate dynamic pricing for IdeogramV2 based on num_images value', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV2', [
{ name: 'num_images', value: 4 }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.32/Run') // 0.08 * 4
})
it('should fall back to static display when num_images widget is missing for IdeogramV1', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV1', [])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.06 x num_images/Run')
})
it('should fall back to static display when num_images widget is missing for IdeogramV2', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV2', [])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.08 x num_images/Run')
})
it('should handle edge case when num_images value is 1 for IdeogramV1', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('IdeogramV1', [
{ name: 'num_images', value: 1 }
])
const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.06/Run') // 0.06 * 1
})
})
describe('Recraft nodes dynamic pricing', () => {
it('should calculate dynamic pricing for RecraftTextToImageNode based on n value', () => {
const { getNodeDisplayPrice } = useNodePricing()