Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/composables/node/useNodePricing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,32 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
return 'Token-based'
}
},
ByteDanceSeedreamNode: {
displayPrice: (node: LGraphNode): string => {
const sequentialGenerationWidget = node.widgets?.find(
(w) => w.name === 'sequential_image_generation'
) as IComboWidget
const maxImagesWidget = node.widgets?.find(
(w) => w.name === 'max_images'
) as IComboWidget

if (!sequentialGenerationWidget || !maxImagesWidget)
return '$0.03/Run ($0.03 for one output image)'

if (
String(sequentialGenerationWidget.value).toLowerCase() === 'disabled'
) {
return '$0.03/Run'
}

const maxImages = Number(maxImagesWidget.value)
if (maxImages === 1) {
return '$0.03/Run'
}
const cost = (0.03 * maxImages).toFixed(2)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Float math. A dangerous game.

Completely optional, but how would you feel about changing it to an int of cents when multiplying then dividing by 100 at the end for formatting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am fine with changing that, but I am already afk for today. :(

I took this code somewhere from this file

return `$${cost}/Run ($0.03 for one output image)`
}
},
ByteDanceTextToVideoNode: {
displayPrice: byteDanceVideoPricingCalculator
},
Expand Down Expand Up @@ -1613,6 +1639,11 @@ export const useNodePricing = () => {
// ByteDance
ByteDanceImageNode: ['model'],
ByteDanceImageEditNode: ['model'],
ByteDanceSeedreamNode: [
'model',
'sequential_image_generation',
'max_images'
],
ByteDanceTextToVideoNode: ['model', 'duration', 'resolution'],
ByteDanceImageToVideoNode: ['model', 'duration', 'resolution'],
ByteDanceFirstLastFrameNode: ['model', 'duration', 'resolution'],
Expand Down
32 changes: 32 additions & 0 deletions tests-ui/tests/composables/node/useNodePricing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,38 @@ describe('useNodePricing', () => {
})
})

describe('dynamic pricing - ByteDanceSeedreamNode', () => {
it('should return fallback when widgets are missing', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('ByteDanceSeedreamNode', [])

const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.03/Run ($0.03 for one output image)')
})

it('should return $0.03/Run when sequential generation is disabled', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('ByteDanceSeedreamNode', [
{ name: 'sequential_image_generation', value: 'disabled' },
{ name: 'max_images', value: 5 }
])

const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.03/Run')
})

it('should multiply by max_images when sequential generation is enabled', () => {
const { getNodeDisplayPrice } = useNodePricing()
const node = createMockNode('ByteDanceSeedreamNode', [
{ name: 'sequential_image_generation', value: 'enabled' },
{ name: 'max_images', value: 4 }
])

const price = getNodeDisplayPrice(node)
expect(price).toBe('$0.12/Run ($0.03 for one output image)')
})
})

describe('dynamic pricing - ByteDance Seedance video nodes', () => {
it('should return base 10s range for PRO 1080p on ByteDanceTextToVideoNode', () => {
const { getNodeDisplayPrice } = useNodePricing()
Expand Down
Loading