mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
fix(api-nodes-pricing): adjust prices for Tripo3D (#6828)
This commit is contained in:
@@ -329,6 +329,123 @@ const sora2PricingCalculator: PricingFunction = (node: LGraphNode): string => {
|
||||
return formatRunPrice(perSec, duration)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pricing for Tripo 3D generation nodes (Text / Image / Multiview)
|
||||
* based on Tripo credits:
|
||||
*
|
||||
* Turbo / V3 / V2.5 / V2.0:
|
||||
* Text -> 10 (no texture) / 20 (standard texture)
|
||||
* Image -> 20 (no texture) / 30 (standard texture)
|
||||
* Multiview -> 20 (no texture) / 30 (standard texture)
|
||||
*
|
||||
* V1.4:
|
||||
* Text -> 20
|
||||
* Image -> 30
|
||||
* (Multiview treated same as Image if used)
|
||||
*
|
||||
* Advanced extras (added on top of generation credits):
|
||||
* quad -> +5 credits
|
||||
* style -> +5 credits (if style != "None")
|
||||
* HD texture -> +10 credits (texture_quality = "detailed")
|
||||
* detailed geometry -> +20 credits (geometry_quality = "detailed")
|
||||
*
|
||||
* 1 credit = $0.01
|
||||
*/
|
||||
const calculateTripo3DGenerationPrice = (
|
||||
node: LGraphNode,
|
||||
task: 'text' | 'image' | 'multiview'
|
||||
): string => {
|
||||
const getWidget = (name: string): IComboWidget | undefined =>
|
||||
node.widgets?.find((w) => w.name === name) as IComboWidget | undefined
|
||||
|
||||
const getString = (name: string, defaultValue: string): string => {
|
||||
const widget = getWidget(name)
|
||||
if (!widget || widget.value === undefined || widget.value === null) {
|
||||
return defaultValue
|
||||
}
|
||||
return String(widget.value)
|
||||
}
|
||||
|
||||
const getBool = (name: string, defaultValue: boolean): boolean => {
|
||||
const widget = getWidget(name)
|
||||
if (!widget || widget.value === undefined || widget.value === null) {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
const v = widget.value
|
||||
if (typeof v === 'number') return v !== 0
|
||||
const lower = String(v).toLowerCase()
|
||||
if (lower === 'true') return true
|
||||
if (lower === 'false') return false
|
||||
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// ---- read widget values with sensible defaults (mirroring backend) ----
|
||||
const modelVersionRaw = getString('model_version', '').toLowerCase()
|
||||
if (modelVersionRaw === '')
|
||||
return '$0.1-0.65/Run (varies with quad, style, texture & quality)'
|
||||
const styleRaw = getString('style', 'None')
|
||||
const hasStyle = styleRaw.toLowerCase() !== 'none'
|
||||
|
||||
// Backend defaults: texture=true, pbr=true, quad=false, qualities="standard"
|
||||
const hasTexture = getBool('texture', false)
|
||||
const hasPbr = getBool('pbr', false)
|
||||
const quad = getBool('quad', false)
|
||||
|
||||
const textureQualityRaw = getString(
|
||||
'texture_quality',
|
||||
'standard'
|
||||
).toLowerCase()
|
||||
const geometryQualityRaw = getString(
|
||||
'geometry_quality',
|
||||
'standard'
|
||||
).toLowerCase()
|
||||
|
||||
const isHdTexture = textureQualityRaw === 'detailed'
|
||||
const isDetailedGeometry = geometryQualityRaw === 'detailed'
|
||||
|
||||
const withTexture = hasTexture || hasPbr
|
||||
|
||||
let baseCredits: number
|
||||
|
||||
if (modelVersionRaw.includes('v1.4')) {
|
||||
// V1.4 model: Text=20, Image=30, Refine=30
|
||||
if (task === 'text') {
|
||||
baseCredits = 20
|
||||
} else {
|
||||
// treat Multiview same as Image if V1.4 is ever used there
|
||||
baseCredits = 30
|
||||
}
|
||||
} else {
|
||||
// V3.0, V2.5, V2.0 models
|
||||
if (!withTexture) {
|
||||
if (task === 'text') {
|
||||
baseCredits = 10 // Text to 3D without texture
|
||||
} else {
|
||||
baseCredits = 20 // Image/Multiview to 3D without texture
|
||||
}
|
||||
} else {
|
||||
if (task === 'text') {
|
||||
baseCredits = 20 // Text to 3D with standard texture
|
||||
} else {
|
||||
baseCredits = 30 // Image/Multiview to 3D with standard texture
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- advanced extras on top of base generation ----
|
||||
let credits = baseCredits
|
||||
|
||||
if (hasStyle) credits += 5 // Style
|
||||
if (quad) credits += 5 // Quad Topology
|
||||
if (isHdTexture) credits += 10 // HD Texture
|
||||
if (isDetailedGeometry) credits += 20 // Detailed Geometry Quality
|
||||
|
||||
const dollars = credits * 0.01
|
||||
return `$${dollars.toFixed(2)}/Run`
|
||||
}
|
||||
|
||||
/**
|
||||
* Static pricing data for API nodes, now supporting both strings and functions
|
||||
*/
|
||||
@@ -1482,119 +1599,16 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
},
|
||||
// Tripo nodes - using actual node names from ComfyUI
|
||||
TripoTextToModelNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
const quadWidget = node.widgets?.find(
|
||||
(w) => w.name === 'quad'
|
||||
) as IComboWidget
|
||||
const styleWidget = node.widgets?.find(
|
||||
(w) => w.name === 'style'
|
||||
) as IComboWidget
|
||||
const textureWidget = node.widgets?.find(
|
||||
(w) => w.name === 'texture'
|
||||
) as IComboWidget
|
||||
const textureQualityWidget = node.widgets?.find(
|
||||
(w) => w.name === 'texture_quality'
|
||||
) as IComboWidget
|
||||
|
||||
if (!quadWidget || !styleWidget || !textureWidget)
|
||||
return '$0.1-0.4/Run (varies with quad, style, texture & quality)'
|
||||
|
||||
const quad = String(quadWidget.value).toLowerCase() === 'true'
|
||||
const style = String(styleWidget.value).toLowerCase()
|
||||
const texture = String(textureWidget.value).toLowerCase() === 'true'
|
||||
const textureQuality = String(
|
||||
textureQualityWidget?.value || 'standard'
|
||||
).toLowerCase()
|
||||
|
||||
// Pricing logic based on CSV data
|
||||
if (style.includes('none')) {
|
||||
if (!quad) {
|
||||
if (!texture) return '$0.10/Run'
|
||||
else return '$0.15/Run'
|
||||
} else {
|
||||
if (textureQuality.includes('detailed')) {
|
||||
if (!texture) return '$0.30/Run'
|
||||
else return '$0.35/Run'
|
||||
} else {
|
||||
if (!texture) return '$0.20/Run'
|
||||
else return '$0.25/Run'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// any style
|
||||
if (!quad) {
|
||||
if (!texture) return '$0.15/Run'
|
||||
else return '$0.20/Run'
|
||||
} else {
|
||||
if (textureQuality.includes('detailed')) {
|
||||
if (!texture) return '$0.35/Run'
|
||||
else return '$0.40/Run'
|
||||
} else {
|
||||
if (!texture) return '$0.25/Run'
|
||||
else return '$0.30/Run'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
displayPrice: (node: LGraphNode): string =>
|
||||
calculateTripo3DGenerationPrice(node, 'text')
|
||||
},
|
||||
TripoImageToModelNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
const quadWidget = node.widgets?.find(
|
||||
(w) => w.name === 'quad'
|
||||
) as IComboWidget
|
||||
const styleWidget = node.widgets?.find(
|
||||
(w) => w.name === 'style'
|
||||
) as IComboWidget
|
||||
const textureWidget = node.widgets?.find(
|
||||
(w) => w.name === 'texture'
|
||||
) as IComboWidget
|
||||
const textureQualityWidget = node.widgets?.find(
|
||||
(w) => w.name === 'texture_quality'
|
||||
) as IComboWidget
|
||||
|
||||
if (!quadWidget || !styleWidget || !textureWidget)
|
||||
return '$0.2-0.5/Run (varies with quad, style, texture & quality)'
|
||||
|
||||
const quad = String(quadWidget.value).toLowerCase() === 'true'
|
||||
const style = String(styleWidget.value).toLowerCase()
|
||||
const texture = String(textureWidget.value).toLowerCase() === 'true'
|
||||
const textureQuality = String(
|
||||
textureQualityWidget?.value || 'standard'
|
||||
).toLowerCase()
|
||||
|
||||
// Pricing logic based on CSV data for Image to Model
|
||||
if (style.includes('none')) {
|
||||
if (!quad) {
|
||||
if (!texture) return '$0.20/Run'
|
||||
else return '$0.25/Run'
|
||||
} else {
|
||||
if (textureQuality.includes('detailed')) {
|
||||
if (!texture) return '$0.40/Run'
|
||||
else return '$0.45/Run'
|
||||
} else {
|
||||
if (!texture) return '$0.30/Run'
|
||||
else return '$0.35/Run'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// any style
|
||||
if (!quad) {
|
||||
if (!texture) return '$0.25/Run'
|
||||
else return '$0.30/Run'
|
||||
} else {
|
||||
if (textureQuality.includes('detailed')) {
|
||||
if (!texture) return '$0.45/Run'
|
||||
else return '$0.50/Run'
|
||||
} else {
|
||||
if (!texture) return '$0.35/Run'
|
||||
else return '$0.40/Run'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
displayPrice: (node: LGraphNode): string =>
|
||||
calculateTripo3DGenerationPrice(node, 'image')
|
||||
},
|
||||
TripoRefineNode: {
|
||||
displayPrice: '$0.3/Run'
|
||||
TripoMultiviewToModelNode: {
|
||||
displayPrice: (node: LGraphNode): string =>
|
||||
calculateTripo3DGenerationPrice(node, 'multiview')
|
||||
},
|
||||
TripoTextureNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
@@ -1608,68 +1622,94 @@ const apiNodeCosts: Record<string, { displayPrice: string | PricingFunction }> =
|
||||
return textureQuality.includes('detailed') ? '$0.2/Run' : '$0.1/Run'
|
||||
}
|
||||
},
|
||||
TripoConvertModelNode: {
|
||||
displayPrice: '$0.10/Run'
|
||||
TripoRigNode: {
|
||||
displayPrice: '$0.25/Run'
|
||||
},
|
||||
TripoRetargetRiggedModelNode: {
|
||||
displayPrice: '$0.10/Run'
|
||||
},
|
||||
TripoMultiviewToModelNode: {
|
||||
TripoConversionNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
const quadWidget = node.widgets?.find(
|
||||
(w) => w.name === 'quad'
|
||||
) as IComboWidget
|
||||
const styleWidget = node.widgets?.find(
|
||||
(w) => w.name === 'style'
|
||||
) as IComboWidget
|
||||
const textureWidget = node.widgets?.find(
|
||||
(w) => w.name === 'texture'
|
||||
) as IComboWidget
|
||||
const textureQualityWidget = node.widgets?.find(
|
||||
(w) => w.name === 'texture_quality'
|
||||
) as IComboWidget
|
||||
const getWidgetValue = (name: string) =>
|
||||
node.widgets?.find((w) => w.name === name)?.value
|
||||
|
||||
if (!quadWidget || !styleWidget || !textureWidget)
|
||||
return '$0.2-0.5/Run (varies with quad, style, texture & quality)'
|
||||
|
||||
const quad = String(quadWidget.value).toLowerCase() === 'true'
|
||||
const style = String(styleWidget.value).toLowerCase()
|
||||
const texture = String(textureWidget.value).toLowerCase() === 'true'
|
||||
const textureQuality = String(
|
||||
textureQualityWidget?.value || 'standard'
|
||||
).toLowerCase()
|
||||
|
||||
// Pricing logic based on CSV data for Multiview to Model (same as Image to Model)
|
||||
if (style.includes('none')) {
|
||||
if (!quad) {
|
||||
if (!texture) return '$0.20/Run'
|
||||
else return '$0.25/Run'
|
||||
} else {
|
||||
if (textureQuality.includes('detailed')) {
|
||||
if (!texture) return '$0.40/Run'
|
||||
else return '$0.45/Run'
|
||||
} else {
|
||||
if (!texture) return '$0.30/Run'
|
||||
else return '$0.35/Run'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// any style
|
||||
if (!quad) {
|
||||
if (!texture) return '$0.25/Run'
|
||||
else return '$0.30/Run'
|
||||
} else {
|
||||
if (textureQuality.includes('detailed')) {
|
||||
if (!texture) return '$0.45/Run'
|
||||
else return '$0.50/Run'
|
||||
} else {
|
||||
if (!texture) return '$0.35/Run'
|
||||
else return '$0.40/Run'
|
||||
}
|
||||
}
|
||||
const getNumber = (name: string, defaultValue: number): number => {
|
||||
const raw = getWidgetValue(name)
|
||||
if (raw === undefined || raw === null || raw === '')
|
||||
return defaultValue
|
||||
if (typeof raw === 'number')
|
||||
return Number.isFinite(raw) ? raw : defaultValue
|
||||
const n = Number(raw)
|
||||
return Number.isFinite(n) ? n : defaultValue
|
||||
}
|
||||
|
||||
const getBool = (name: string, defaultValue: boolean): boolean => {
|
||||
const v = getWidgetValue(name)
|
||||
if (v === undefined || v === null) return defaultValue
|
||||
|
||||
if (typeof v === 'number') return v !== 0
|
||||
const lower = String(v).toLowerCase()
|
||||
if (lower === 'true') return true
|
||||
if (lower === 'false') return false
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
let hasAdvancedParam = false
|
||||
|
||||
// ---- booleans that trigger advanced when true ----
|
||||
if (getBool('quad', false)) hasAdvancedParam = true
|
||||
if (getBool('force_symmetry', false)) hasAdvancedParam = true
|
||||
if (getBool('flatten_bottom', false)) hasAdvancedParam = true
|
||||
if (getBool('pivot_to_center_bottom', false)) hasAdvancedParam = true
|
||||
if (getBool('with_animation', false)) hasAdvancedParam = true
|
||||
if (getBool('pack_uv', false)) hasAdvancedParam = true
|
||||
if (getBool('bake', false)) hasAdvancedParam = true
|
||||
if (getBool('export_vertex_colors', false)) hasAdvancedParam = true
|
||||
if (getBool('animate_in_place', false)) hasAdvancedParam = true
|
||||
|
||||
// ---- numeric params with special default sentinels ----
|
||||
const faceLimit = getNumber('face_limit', -1)
|
||||
if (faceLimit !== -1) hasAdvancedParam = true
|
||||
|
||||
const textureSize = getNumber('texture_size', 4096)
|
||||
if (textureSize !== 4096) hasAdvancedParam = true
|
||||
|
||||
const flattenBottomThreshold = getNumber(
|
||||
'flatten_bottom_threshold',
|
||||
0.0
|
||||
)
|
||||
if (flattenBottomThreshold !== 0.0) hasAdvancedParam = true
|
||||
|
||||
const scaleFactor = getNumber('scale_factor', 1.0)
|
||||
if (scaleFactor !== 1.0) hasAdvancedParam = true
|
||||
|
||||
// ---- string / combo params with non-default values ----
|
||||
const textureFormatRaw = String(
|
||||
getWidgetValue('texture_format') ?? 'JPEG'
|
||||
).toUpperCase()
|
||||
if (textureFormatRaw !== 'JPEG') hasAdvancedParam = true
|
||||
|
||||
const partNamesRaw = String(getWidgetValue('part_names') ?? '')
|
||||
if (partNamesRaw.trim().length > 0) hasAdvancedParam = true
|
||||
|
||||
const fbxPresetRaw = String(
|
||||
getWidgetValue('fbx_preset') ?? 'blender'
|
||||
).toLowerCase()
|
||||
if (fbxPresetRaw !== 'blender') hasAdvancedParam = true
|
||||
|
||||
const exportOrientationRaw = String(
|
||||
getWidgetValue('export_orientation') ?? 'default'
|
||||
).toLowerCase()
|
||||
if (exportOrientationRaw !== 'default') hasAdvancedParam = true
|
||||
|
||||
const credits = hasAdvancedParam ? 10 : 5
|
||||
const dollars = credits * 0.01
|
||||
return `$${dollars.toFixed(2)}/Run`
|
||||
}
|
||||
},
|
||||
TripoRetargetNode: {
|
||||
displayPrice: '$0.10/Run'
|
||||
},
|
||||
TripoRefineNode: {
|
||||
displayPrice: '$0.30/Run'
|
||||
},
|
||||
// Google/Gemini nodes
|
||||
GeminiNode: {
|
||||
displayPrice: (node: LGraphNode): string => {
|
||||
@@ -2019,8 +2059,51 @@ export const useNodePricing = () => {
|
||||
RunwayImageToVideoNodeGen4: ['duration'],
|
||||
RunwayFirstLastFrameNode: ['duration'],
|
||||
// Tripo nodes
|
||||
TripoTextToModelNode: ['quad', 'style', 'texture', 'texture_quality'],
|
||||
TripoImageToModelNode: ['quad', 'style', 'texture', 'texture_quality'],
|
||||
TripoTextToModelNode: [
|
||||
'model_version',
|
||||
'quad',
|
||||
'style',
|
||||
'texture',
|
||||
'pbr',
|
||||
'texture_quality',
|
||||
'geometry_quality'
|
||||
],
|
||||
TripoImageToModelNode: [
|
||||
'model_version',
|
||||
'quad',
|
||||
'style',
|
||||
'texture',
|
||||
'pbr',
|
||||
'texture_quality',
|
||||
'geometry_quality'
|
||||
],
|
||||
TripoMultiviewToModelNode: [
|
||||
'model_version',
|
||||
'quad',
|
||||
'texture',
|
||||
'pbr',
|
||||
'texture_quality',
|
||||
'geometry_quality'
|
||||
],
|
||||
TripoConversionNode: [
|
||||
'quad',
|
||||
'face_limit',
|
||||
'texture_size',
|
||||
'texture_format',
|
||||
'force_symmetry',
|
||||
'flatten_bottom',
|
||||
'flatten_bottom_threshold',
|
||||
'pivot_to_center_bottom',
|
||||
'scale_factor',
|
||||
'with_animation',
|
||||
'pack_uv',
|
||||
'bake',
|
||||
'part_names',
|
||||
'fbx_preset',
|
||||
'export_vertex_colors',
|
||||
'export_orientation',
|
||||
'animate_in_place'
|
||||
],
|
||||
TripoTextureNode: ['texture_quality'],
|
||||
// Google/Gemini nodes
|
||||
GeminiNode: ['model'],
|
||||
|
||||
@@ -1414,16 +1414,22 @@ describe('useNodePricing', () => {
|
||||
'duration'
|
||||
])
|
||||
expect(getRelevantWidgetNames('TripoTextToModelNode')).toEqual([
|
||||
'model_version',
|
||||
'quad',
|
||||
'style',
|
||||
'texture',
|
||||
'texture_quality'
|
||||
'pbr',
|
||||
'texture_quality',
|
||||
'geometry_quality'
|
||||
])
|
||||
expect(getRelevantWidgetNames('TripoImageToModelNode')).toEqual([
|
||||
'model_version',
|
||||
'quad',
|
||||
'style',
|
||||
'texture',
|
||||
'texture_quality'
|
||||
'pbr',
|
||||
'texture_quality',
|
||||
'geometry_quality'
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -1507,6 +1513,7 @@ describe('useNodePricing', () => {
|
||||
it('should return v2.5 standard pricing for TripoTextToModelNode', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('TripoTextToModelNode', [
|
||||
{ name: 'model_version', value: 'v2.5' },
|
||||
{ name: 'quad', value: false },
|
||||
{ name: 'style', value: 'any style' },
|
||||
{ name: 'texture', value: false },
|
||||
@@ -1520,6 +1527,7 @@ describe('useNodePricing', () => {
|
||||
it('should return v2.5 detailed pricing for TripoTextToModelNode', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('TripoTextToModelNode', [
|
||||
{ name: 'model_version', value: 'v2.5' },
|
||||
{ name: 'quad', value: true },
|
||||
{ name: 'style', value: 'any style' },
|
||||
{ name: 'texture', value: false },
|
||||
@@ -1527,12 +1535,13 @@ describe('useNodePricing', () => {
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.35/Run') // any style, quad, no texture, detailed
|
||||
expect(price).toBe('$0.30/Run') // any style, quad, no texture, detailed
|
||||
})
|
||||
|
||||
it('should return v2.0 detailed pricing for TripoImageToModelNode', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('TripoImageToModelNode', [
|
||||
{ name: 'model_version', value: 'v2.0' },
|
||||
{ name: 'quad', value: true },
|
||||
{ name: 'style', value: 'any style' },
|
||||
{ name: 'texture', value: false },
|
||||
@@ -1540,12 +1549,13 @@ describe('useNodePricing', () => {
|
||||
])
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.45/Run') // any style, quad, no texture, detailed
|
||||
expect(price).toBe('$0.40/Run') // any style, quad, no texture, detailed
|
||||
})
|
||||
|
||||
it('should return legacy pricing for TripoTextToModelNode', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('TripoTextToModelNode', [
|
||||
{ name: 'model_version', value: 'v2.0' },
|
||||
{ name: 'quad', value: false },
|
||||
{ name: 'style', value: 'none' },
|
||||
{ name: 'texture', value: false },
|
||||
@@ -1561,7 +1571,7 @@ describe('useNodePricing', () => {
|
||||
const node = createMockNode('TripoRefineNode')
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.3/Run')
|
||||
expect(price).toBe('$0.30/Run')
|
||||
})
|
||||
|
||||
it('should return fallback for TripoTextToModelNode without model', () => {
|
||||
@@ -1570,7 +1580,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$0.1-0.4/Run (varies with quad, style, texture & quality)'
|
||||
'$0.1-0.65/Run (varies with quad, style, texture & quality)'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1592,24 +1602,39 @@ describe('useNodePricing', () => {
|
||||
|
||||
// Test different parameter combinations
|
||||
const testCases = [
|
||||
{ quad: false, style: 'none', texture: false, expected: '$0.10/Run' },
|
||||
{
|
||||
model_version: 'v3.0',
|
||||
quad: false,
|
||||
style: 'none',
|
||||
texture: false,
|
||||
expected: '$0.10/Run'
|
||||
},
|
||||
{
|
||||
model_version: 'v3.0',
|
||||
quad: false,
|
||||
style: 'any style',
|
||||
texture: false,
|
||||
expected: '$0.15/Run'
|
||||
},
|
||||
{ quad: true, style: 'none', texture: false, expected: '$0.20/Run' },
|
||||
{
|
||||
model_version: 'v3.0',
|
||||
quad: true,
|
||||
style: 'any style',
|
||||
texture: false,
|
||||
expected: '$0.25/Run'
|
||||
expected: '$0.20/Run'
|
||||
},
|
||||
{
|
||||
model_version: 'v3.0',
|
||||
quad: true,
|
||||
style: 'any style',
|
||||
texture: true,
|
||||
expected: '$0.30/Run'
|
||||
}
|
||||
]
|
||||
|
||||
testCases.forEach(({ quad, style, texture, expected }) => {
|
||||
const node = createMockNode('TripoTextToModelNode', [
|
||||
{ name: 'model_version', value: 'v2.0' },
|
||||
{ name: 'quad', value: quad },
|
||||
{ name: 'style', value: style },
|
||||
{ name: 'texture', value: texture },
|
||||
@@ -1619,17 +1644,9 @@ describe('useNodePricing', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should return static price for TripoConvertModelNode', () => {
|
||||
it('should return static price for TripoRetargetNode', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('TripoConvertModelNode')
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.10/Run')
|
||||
})
|
||||
|
||||
it('should return static price for TripoRetargetRiggedModelNode', () => {
|
||||
const { getNodeDisplayPrice } = useNodePricing()
|
||||
const node = createMockNode('TripoRetargetRiggedModelNode')
|
||||
const node = createMockNode('TripoRetargetNode')
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe('$0.10/Run')
|
||||
@@ -1640,6 +1657,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
// Test basic case - no style, no quad, no texture
|
||||
const basicNode = createMockNode('TripoMultiviewToModelNode', [
|
||||
{ name: 'model_version', value: 'v3.0' },
|
||||
{ name: 'quad', value: false },
|
||||
{ name: 'style', value: 'none' },
|
||||
{ name: 'texture', value: false },
|
||||
@@ -1649,6 +1667,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
// Test high-end case - any style, quad, texture, detailed
|
||||
const highEndNode = createMockNode('TripoMultiviewToModelNode', [
|
||||
{ name: 'model_version', value: 'v3.0' },
|
||||
{ name: 'quad', value: true },
|
||||
{ name: 'style', value: 'stylized' },
|
||||
{ name: 'texture', value: true },
|
||||
@@ -1663,7 +1682,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$0.2-0.5/Run (varies with quad, style, texture & quality)'
|
||||
'$0.1-0.65/Run (varies with quad, style, texture & quality)'
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1870,7 +1889,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
const testCases = [
|
||||
{ quad: false, style: 'none', texture: false, expected: '$0.20/Run' },
|
||||
{ quad: false, style: 'none', texture: true, expected: '$0.25/Run' },
|
||||
{ quad: false, style: 'none', texture: true, expected: '$0.30/Run' },
|
||||
{
|
||||
quad: true,
|
||||
style: 'any style',
|
||||
@@ -1879,9 +1898,9 @@ describe('useNodePricing', () => {
|
||||
expected: '$0.50/Run'
|
||||
},
|
||||
{
|
||||
quad: true,
|
||||
quad: false,
|
||||
style: 'any style',
|
||||
texture: false,
|
||||
texture: true,
|
||||
textureQuality: 'standard',
|
||||
expected: '$0.35/Run'
|
||||
}
|
||||
@@ -1890,6 +1909,7 @@ describe('useNodePricing', () => {
|
||||
testCases.forEach(
|
||||
({ quad, style, texture, textureQuality, expected }) => {
|
||||
const widgets = [
|
||||
{ name: 'model_version', value: 'v3.0' },
|
||||
{ name: 'quad', value: quad },
|
||||
{ name: 'style', value: style },
|
||||
{ name: 'texture', value: texture }
|
||||
@@ -1909,7 +1929,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$0.2-0.5/Run (varies with quad, style, texture & quality)'
|
||||
'$0.1-0.65/Run (varies with quad, style, texture & quality)'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1919,7 +1939,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$0.1-0.4/Run (varies with quad, style, texture & quality)'
|
||||
'$0.1-0.65/Run (varies with quad, style, texture & quality)'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1931,7 +1951,7 @@ describe('useNodePricing', () => {
|
||||
|
||||
const price = getNodeDisplayPrice(node)
|
||||
expect(price).toBe(
|
||||
'$0.1-0.4/Run (varies with quad, style, texture & quality)'
|
||||
'$0.1-0.65/Run (varies with quad, style, texture & quality)'
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user