mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 02:32:18 +00:00
api_nodes: added prices for Ideogram V3 node (character reference) (#5241)
* api_nodes: added prices for Ideogram V3 node (character reference) * Support watching changes on connections. (#5250) * rename renderingSpeed default value from 'balanced' to 'default' * added missing type --------- Co-authored-by: AustinMroz <austin@comfy.org>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import type { LGraphNode } from '@/lib/litegraph/src/litegraph'
|
import type { INodeInputSlot, LGraphNode } from '@/lib/litegraph/src/litegraph'
|
||||||
import type { IComboWidget } from '@/lib/litegraph/src/types/widgets'
|
import type { IComboWidget } from '@/lib/litegraph/src/types/widgets'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -179,6 +179,12 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
|||||||
const numImagesWidget = node.widgets?.find(
|
const numImagesWidget = node.widgets?.find(
|
||||||
(w) => w.name === 'num_images'
|
(w) => w.name === 'num_images'
|
||||||
) as IComboWidget
|
) as IComboWidget
|
||||||
|
const characterInput = node.inputs?.find(
|
||||||
|
(i) => i.name === 'character_image'
|
||||||
|
) as INodeInputSlot
|
||||||
|
const hasCharacter =
|
||||||
|
typeof characterInput?.link !== 'undefined' &&
|
||||||
|
characterInput.link != null
|
||||||
|
|
||||||
if (!renderingSpeedWidget)
|
if (!renderingSpeedWidget)
|
||||||
return '$0.03-0.08 x num_images/Run (varies with rendering speed & num_images)'
|
return '$0.03-0.08 x num_images/Run (varies with rendering speed & num_images)'
|
||||||
@@ -188,11 +194,23 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
|||||||
|
|
||||||
const renderingSpeed = String(renderingSpeedWidget.value)
|
const renderingSpeed = String(renderingSpeedWidget.value)
|
||||||
if (renderingSpeed.toLowerCase().includes('quality')) {
|
if (renderingSpeed.toLowerCase().includes('quality')) {
|
||||||
basePrice = 0.09
|
if (hasCharacter) {
|
||||||
} else if (renderingSpeed.toLowerCase().includes('balanced')) {
|
basePrice = 0.2
|
||||||
basePrice = 0.06
|
} else {
|
||||||
|
basePrice = 0.09
|
||||||
|
}
|
||||||
|
} else if (renderingSpeed.toLowerCase().includes('default')) {
|
||||||
|
if (hasCharacter) {
|
||||||
|
basePrice = 0.15
|
||||||
|
} else {
|
||||||
|
basePrice = 0.06
|
||||||
|
}
|
||||||
} else if (renderingSpeed.toLowerCase().includes('turbo')) {
|
} else if (renderingSpeed.toLowerCase().includes('turbo')) {
|
||||||
basePrice = 0.03
|
if (hasCharacter) {
|
||||||
|
basePrice = 0.1
|
||||||
|
} else {
|
||||||
|
basePrice = 0.03
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalCost = (basePrice * numImages).toFixed(2)
|
const totalCost = (basePrice * numImages).toFixed(2)
|
||||||
@@ -1462,7 +1480,7 @@ export const useNodePricing = () => {
|
|||||||
OpenAIGPTImage1: ['quality', 'n'],
|
OpenAIGPTImage1: ['quality', 'n'],
|
||||||
IdeogramV1: ['num_images', 'turbo'],
|
IdeogramV1: ['num_images', 'turbo'],
|
||||||
IdeogramV2: ['num_images', 'turbo'],
|
IdeogramV2: ['num_images', 'turbo'],
|
||||||
IdeogramV3: ['rendering_speed', 'num_images'],
|
IdeogramV3: ['rendering_speed', 'num_images', 'character_image'],
|
||||||
FluxProKontextProNode: [],
|
FluxProKontextProNode: [],
|
||||||
FluxProKontextMaxNode: [],
|
FluxProKontextMaxNode: [],
|
||||||
VeoVideoGenerationNode: ['duration_seconds'],
|
VeoVideoGenerationNode: ['duration_seconds'],
|
||||||
|
|||||||
@@ -75,6 +75,29 @@ export const useComputedWithWidgetWatch = (
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
if (widgetNames && widgetNames.length > widgetsToObserve.length) {
|
||||||
|
//Inputs have been included
|
||||||
|
const indexesToObserve = widgetNames
|
||||||
|
.map((name) =>
|
||||||
|
widgetsToObserve.some((w) => w.name == name)
|
||||||
|
? -1
|
||||||
|
: node.inputs.findIndex((i) => i.name == name)
|
||||||
|
)
|
||||||
|
.filter((i) => i >= 0)
|
||||||
|
node.onConnectionsChange = useChainCallback(
|
||||||
|
node.onConnectionsChange,
|
||||||
|
(_type: unknown, index: number, isConnected: boolean) => {
|
||||||
|
if (!indexesToObserve.includes(index)) return
|
||||||
|
widgetValues.value = {
|
||||||
|
...widgetValues.value,
|
||||||
|
[indexesToObserve[index]]: isConnected
|
||||||
|
}
|
||||||
|
if (triggerCanvasRedraw) {
|
||||||
|
node.graph?.setDirtyCanvas(true, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a function that creates a computed that responds to widget changes.
|
// Returns a function that creates a computed that responds to widget changes.
|
||||||
|
|||||||
@@ -8,7 +8,12 @@ import type { IComboWidget } from '@/lib/litegraph/src/types/widgets'
|
|||||||
function createMockNode(
|
function createMockNode(
|
||||||
nodeTypeName: string,
|
nodeTypeName: string,
|
||||||
widgets: Array<{ name: string; value: any }> = [],
|
widgets: Array<{ name: string; value: any }> = [],
|
||||||
isApiNode = true
|
isApiNode = true,
|
||||||
|
inputs: Array<{
|
||||||
|
name: string
|
||||||
|
connected?: boolean
|
||||||
|
useLinksArray?: boolean
|
||||||
|
}> = []
|
||||||
): LGraphNode {
|
): LGraphNode {
|
||||||
const mockWidgets = widgets.map(({ name, value }) => ({
|
const mockWidgets = widgets.map(({ name, value }) => ({
|
||||||
name,
|
name,
|
||||||
@@ -16,7 +21,16 @@ function createMockNode(
|
|||||||
type: 'combo'
|
type: 'combo'
|
||||||
})) as IComboWidget[]
|
})) as IComboWidget[]
|
||||||
|
|
||||||
return {
|
const mockInputs =
|
||||||
|
inputs.length > 0
|
||||||
|
? inputs.map(({ name, connected, useLinksArray }) =>
|
||||||
|
useLinksArray
|
||||||
|
? { name, links: connected ? [1] : [] }
|
||||||
|
: { name, link: connected ? 1 : null }
|
||||||
|
)
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
const node: any = {
|
||||||
id: Math.random().toString(),
|
id: Math.random().toString(),
|
||||||
widgets: mockWidgets,
|
widgets: mockWidgets,
|
||||||
constructor: {
|
constructor: {
|
||||||
@@ -25,7 +39,24 @@ function createMockNode(
|
|||||||
api_node: isApiNode
|
api_node: isApiNode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} as unknown as LGraphNode
|
}
|
||||||
|
|
||||||
|
if (mockInputs) {
|
||||||
|
node.inputs = mockInputs
|
||||||
|
// Provide the common helpers some frontend code may call
|
||||||
|
node.findInputSlot = function (portName: string) {
|
||||||
|
return this.inputs?.findIndex((i: any) => i.name === portName) ?? -1
|
||||||
|
}
|
||||||
|
node.isInputConnected = function (idx: number) {
|
||||||
|
const port = this.inputs?.[idx]
|
||||||
|
if (!port) return false
|
||||||
|
if (typeof port.link !== 'undefined') return port.link != null
|
||||||
|
if (Array.isArray(port.links)) return port.links.length > 0
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node as LGraphNode
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('useNodePricing', () => {
|
describe('useNodePricing', () => {
|
||||||
@@ -363,34 +394,51 @@ describe('useNodePricing', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('dynamic pricing - IdeogramV3', () => {
|
describe('dynamic pricing - IdeogramV3', () => {
|
||||||
it('should return $0.09 for Quality rendering speed', () => {
|
it('should return correct prices for IdeogramV3 node', () => {
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
const { getNodeDisplayPrice } = useNodePricing()
|
||||||
const node = createMockNode('IdeogramV3', [
|
|
||||||
{ name: 'rendering_speed', value: 'Quality' }
|
|
||||||
])
|
|
||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
const testCases = [
|
||||||
expect(price).toBe('$0.09/Run')
|
{
|
||||||
})
|
rendering_speed: 'Quality',
|
||||||
|
character_image: false,
|
||||||
|
expected: '$0.09/Run'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rendering_speed: 'Quality',
|
||||||
|
character_image: true,
|
||||||
|
expected: '$0.20/Run'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rendering_speed: 'Default',
|
||||||
|
character_image: false,
|
||||||
|
expected: '$0.06/Run'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rendering_speed: 'Default',
|
||||||
|
character_image: true,
|
||||||
|
expected: '$0.15/Run'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rendering_speed: 'Turbo',
|
||||||
|
character_image: false,
|
||||||
|
expected: '$0.03/Run'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rendering_speed: 'Turbo',
|
||||||
|
character_image: true,
|
||||||
|
expected: '$0.10/Run'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
it('should return $0.06 for Balanced rendering speed', () => {
|
testCases.forEach(({ rendering_speed, character_image, expected }) => {
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
const node = createMockNode(
|
||||||
const node = createMockNode('IdeogramV3', [
|
'IdeogramV3',
|
||||||
{ name: 'rendering_speed', value: 'Balanced' }
|
[{ name: 'rendering_speed', value: rendering_speed }],
|
||||||
])
|
true,
|
||||||
|
[{ name: 'character_image', connected: character_image }]
|
||||||
const price = getNodeDisplayPrice(node)
|
)
|
||||||
expect(price).toBe('$0.06/Run')
|
expect(getNodeDisplayPrice(node)).toBe(expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return $0.03 for Turbo rendering speed', () => {
|
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
|
||||||
const node = createMockNode('IdeogramV3', [
|
|
||||||
{ name: 'rendering_speed', value: 'Turbo' }
|
|
||||||
])
|
|
||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
|
||||||
expect(price).toBe('$0.03/Run')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return range when rendering_speed widget is missing', () => {
|
it('should return range when rendering_speed widget is missing', () => {
|
||||||
@@ -935,7 +983,11 @@ describe('useNodePricing', () => {
|
|||||||
const { getRelevantWidgetNames } = useNodePricing()
|
const { getRelevantWidgetNames } = useNodePricing()
|
||||||
|
|
||||||
const widgetNames = getRelevantWidgetNames('IdeogramV3')
|
const widgetNames = getRelevantWidgetNames('IdeogramV3')
|
||||||
expect(widgetNames).toEqual(['rendering_speed', 'num_images'])
|
expect(widgetNames).toEqual([
|
||||||
|
'rendering_speed',
|
||||||
|
'num_images',
|
||||||
|
'character_image'
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user