mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 11:11:53 +00:00
[backport cloud/1.35] fix(api-nodes-pricing): adjust prices for Tripo3D (#7715)
Backport of #6828 to `cloud/1.35` Automatically created by backport workflow. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7715-backport-cloud-1-35-fix-api-nodes-pricing-adjust-prices-for-Tripo3D-2d16d73d365081949ce8cf3029d2d98d) by [Unito](https://www.unito.io) Co-authored-by: Alexander Piskun <13381981+bigcat88@users.noreply.github.com>
This commit is contained in:
@@ -329,6 +329,123 @@ const sora2PricingCalculator: PricingFunction = (node: LGraphNode): string => {
|
|||||||
return formatRunPrice(perSec, duration)
|
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
|
* 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
|
// Tripo nodes - using actual node names from ComfyUI
|
||||||
TripoTextToModelNode: {
|
TripoTextToModelNode: {
|
||||||
displayPrice: (node: LGraphNode): string => {
|
displayPrice: (node: LGraphNode): string =>
|
||||||
const quadWidget = node.widgets?.find(
|
calculateTripo3DGenerationPrice(node, 'text')
|
||||||
(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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
TripoImageToModelNode: {
|
TripoImageToModelNode: {
|
||||||
displayPrice: (node: LGraphNode): string => {
|
displayPrice: (node: LGraphNode): string =>
|
||||||
const quadWidget = node.widgets?.find(
|
calculateTripo3DGenerationPrice(node, 'image')
|
||||||
(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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
TripoRefineNode: {
|
TripoMultiviewToModelNode: {
|
||||||
displayPrice: '$0.3/Run'
|
displayPrice: (node: LGraphNode): string =>
|
||||||
|
calculateTripo3DGenerationPrice(node, 'multiview')
|
||||||
},
|
},
|
||||||
TripoTextureNode: {
|
TripoTextureNode: {
|
||||||
displayPrice: (node: LGraphNode): string => {
|
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'
|
return textureQuality.includes('detailed') ? '$0.2/Run' : '$0.1/Run'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TripoConvertModelNode: {
|
TripoRigNode: {
|
||||||
displayPrice: '$0.10/Run'
|
displayPrice: '$0.25/Run'
|
||||||
},
|
},
|
||||||
TripoRetargetRiggedModelNode: {
|
TripoConversionNode: {
|
||||||
displayPrice: '$0.10/Run'
|
|
||||||
},
|
|
||||||
TripoMultiviewToModelNode: {
|
|
||||||
displayPrice: (node: LGraphNode): string => {
|
displayPrice: (node: LGraphNode): string => {
|
||||||
const quadWidget = node.widgets?.find(
|
const getWidgetValue = (name: string) =>
|
||||||
(w) => w.name === 'quad'
|
node.widgets?.find((w) => w.name === name)?.value
|
||||||
) 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)
|
const getNumber = (name: string, defaultValue: number): number => {
|
||||||
return '$0.2-0.5/Run (varies with quad, style, texture & quality)'
|
const raw = getWidgetValue(name)
|
||||||
|
if (raw === undefined || raw === null || raw === '')
|
||||||
const quad = String(quadWidget.value).toLowerCase() === 'true'
|
return defaultValue
|
||||||
const style = String(styleWidget.value).toLowerCase()
|
if (typeof raw === 'number')
|
||||||
const texture = String(textureWidget.value).toLowerCase() === 'true'
|
return Number.isFinite(raw) ? raw : defaultValue
|
||||||
const textureQuality = String(
|
const n = Number(raw)
|
||||||
textureQualityWidget?.value || 'standard'
|
return Number.isFinite(n) ? n : defaultValue
|
||||||
).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 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
|
// Google/Gemini nodes
|
||||||
GeminiNode: {
|
GeminiNode: {
|
||||||
displayPrice: (node: LGraphNode): string => {
|
displayPrice: (node: LGraphNode): string => {
|
||||||
@@ -2019,8 +2059,51 @@ export const useNodePricing = () => {
|
|||||||
RunwayImageToVideoNodeGen4: ['duration'],
|
RunwayImageToVideoNodeGen4: ['duration'],
|
||||||
RunwayFirstLastFrameNode: ['duration'],
|
RunwayFirstLastFrameNode: ['duration'],
|
||||||
// Tripo nodes
|
// Tripo nodes
|
||||||
TripoTextToModelNode: ['quad', 'style', 'texture', 'texture_quality'],
|
TripoTextToModelNode: [
|
||||||
TripoImageToModelNode: ['quad', 'style', 'texture', 'texture_quality'],
|
'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'],
|
TripoTextureNode: ['texture_quality'],
|
||||||
// Google/Gemini nodes
|
// Google/Gemini nodes
|
||||||
GeminiNode: ['model'],
|
GeminiNode: ['model'],
|
||||||
|
|||||||
@@ -1414,16 +1414,22 @@ describe('useNodePricing', () => {
|
|||||||
'duration'
|
'duration'
|
||||||
])
|
])
|
||||||
expect(getRelevantWidgetNames('TripoTextToModelNode')).toEqual([
|
expect(getRelevantWidgetNames('TripoTextToModelNode')).toEqual([
|
||||||
|
'model_version',
|
||||||
'quad',
|
'quad',
|
||||||
'style',
|
'style',
|
||||||
'texture',
|
'texture',
|
||||||
'texture_quality'
|
'pbr',
|
||||||
|
'texture_quality',
|
||||||
|
'geometry_quality'
|
||||||
])
|
])
|
||||||
expect(getRelevantWidgetNames('TripoImageToModelNode')).toEqual([
|
expect(getRelevantWidgetNames('TripoImageToModelNode')).toEqual([
|
||||||
|
'model_version',
|
||||||
'quad',
|
'quad',
|
||||||
'style',
|
'style',
|
||||||
'texture',
|
'texture',
|
||||||
'texture_quality'
|
'pbr',
|
||||||
|
'texture_quality',
|
||||||
|
'geometry_quality'
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -1507,6 +1513,7 @@ describe('useNodePricing', () => {
|
|||||||
it('should return v2.5 standard pricing for TripoTextToModelNode', () => {
|
it('should return v2.5 standard pricing for TripoTextToModelNode', () => {
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
const { getNodeDisplayPrice } = useNodePricing()
|
||||||
const node = createMockNode('TripoTextToModelNode', [
|
const node = createMockNode('TripoTextToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v2.5' },
|
||||||
{ name: 'quad', value: false },
|
{ name: 'quad', value: false },
|
||||||
{ name: 'style', value: 'any style' },
|
{ name: 'style', value: 'any style' },
|
||||||
{ name: 'texture', value: false },
|
{ name: 'texture', value: false },
|
||||||
@@ -1520,6 +1527,7 @@ describe('useNodePricing', () => {
|
|||||||
it('should return v2.5 detailed pricing for TripoTextToModelNode', () => {
|
it('should return v2.5 detailed pricing for TripoTextToModelNode', () => {
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
const { getNodeDisplayPrice } = useNodePricing()
|
||||||
const node = createMockNode('TripoTextToModelNode', [
|
const node = createMockNode('TripoTextToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v2.5' },
|
||||||
{ name: 'quad', value: true },
|
{ name: 'quad', value: true },
|
||||||
{ name: 'style', value: 'any style' },
|
{ name: 'style', value: 'any style' },
|
||||||
{ name: 'texture', value: false },
|
{ name: 'texture', value: false },
|
||||||
@@ -1527,12 +1535,13 @@ describe('useNodePricing', () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
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', () => {
|
it('should return v2.0 detailed pricing for TripoImageToModelNode', () => {
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
const { getNodeDisplayPrice } = useNodePricing()
|
||||||
const node = createMockNode('TripoImageToModelNode', [
|
const node = createMockNode('TripoImageToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v2.0' },
|
||||||
{ name: 'quad', value: true },
|
{ name: 'quad', value: true },
|
||||||
{ name: 'style', value: 'any style' },
|
{ name: 'style', value: 'any style' },
|
||||||
{ name: 'texture', value: false },
|
{ name: 'texture', value: false },
|
||||||
@@ -1540,12 +1549,13 @@ describe('useNodePricing', () => {
|
|||||||
])
|
])
|
||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
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', () => {
|
it('should return legacy pricing for TripoTextToModelNode', () => {
|
||||||
const { getNodeDisplayPrice } = useNodePricing()
|
const { getNodeDisplayPrice } = useNodePricing()
|
||||||
const node = createMockNode('TripoTextToModelNode', [
|
const node = createMockNode('TripoTextToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v2.0' },
|
||||||
{ name: 'quad', value: false },
|
{ name: 'quad', value: false },
|
||||||
{ name: 'style', value: 'none' },
|
{ name: 'style', value: 'none' },
|
||||||
{ name: 'texture', value: false },
|
{ name: 'texture', value: false },
|
||||||
@@ -1561,7 +1571,7 @@ describe('useNodePricing', () => {
|
|||||||
const node = createMockNode('TripoRefineNode')
|
const node = createMockNode('TripoRefineNode')
|
||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe('$0.3/Run')
|
expect(price).toBe('$0.30/Run')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return fallback for TripoTextToModelNode without model', () => {
|
it('should return fallback for TripoTextToModelNode without model', () => {
|
||||||
@@ -1570,7 +1580,7 @@ describe('useNodePricing', () => {
|
|||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe(
|
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
|
// Test different parameter combinations
|
||||||
const testCases = [
|
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,
|
quad: false,
|
||||||
style: 'any style',
|
style: 'any style',
|
||||||
texture: false,
|
texture: false,
|
||||||
expected: '$0.15/Run'
|
expected: '$0.15/Run'
|
||||||
},
|
},
|
||||||
{ quad: true, style: 'none', texture: false, expected: '$0.20/Run' },
|
|
||||||
{
|
{
|
||||||
|
model_version: 'v3.0',
|
||||||
quad: true,
|
quad: true,
|
||||||
style: 'any style',
|
style: 'any style',
|
||||||
texture: false,
|
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 }) => {
|
testCases.forEach(({ quad, style, texture, expected }) => {
|
||||||
const node = createMockNode('TripoTextToModelNode', [
|
const node = createMockNode('TripoTextToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v2.0' },
|
||||||
{ name: 'quad', value: quad },
|
{ name: 'quad', value: quad },
|
||||||
{ name: 'style', value: style },
|
{ name: 'style', value: style },
|
||||||
{ name: 'texture', value: texture },
|
{ 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 { getNodeDisplayPrice } = useNodePricing()
|
||||||
const node = createMockNode('TripoConvertModelNode')
|
const node = createMockNode('TripoRetargetNode')
|
||||||
|
|
||||||
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 price = getNodeDisplayPrice(node)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe('$0.10/Run')
|
expect(price).toBe('$0.10/Run')
|
||||||
@@ -1640,6 +1657,7 @@ describe('useNodePricing', () => {
|
|||||||
|
|
||||||
// Test basic case - no style, no quad, no texture
|
// Test basic case - no style, no quad, no texture
|
||||||
const basicNode = createMockNode('TripoMultiviewToModelNode', [
|
const basicNode = createMockNode('TripoMultiviewToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v3.0' },
|
||||||
{ name: 'quad', value: false },
|
{ name: 'quad', value: false },
|
||||||
{ name: 'style', value: 'none' },
|
{ name: 'style', value: 'none' },
|
||||||
{ name: 'texture', value: false },
|
{ name: 'texture', value: false },
|
||||||
@@ -1649,6 +1667,7 @@ describe('useNodePricing', () => {
|
|||||||
|
|
||||||
// Test high-end case - any style, quad, texture, detailed
|
// Test high-end case - any style, quad, texture, detailed
|
||||||
const highEndNode = createMockNode('TripoMultiviewToModelNode', [
|
const highEndNode = createMockNode('TripoMultiviewToModelNode', [
|
||||||
|
{ name: 'model_version', value: 'v3.0' },
|
||||||
{ name: 'quad', value: true },
|
{ name: 'quad', value: true },
|
||||||
{ name: 'style', value: 'stylized' },
|
{ name: 'style', value: 'stylized' },
|
||||||
{ name: 'texture', value: true },
|
{ name: 'texture', value: true },
|
||||||
@@ -1663,7 +1682,7 @@ describe('useNodePricing', () => {
|
|||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe(
|
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 = [
|
const testCases = [
|
||||||
{ quad: false, style: 'none', texture: false, expected: '$0.20/Run' },
|
{ 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,
|
quad: true,
|
||||||
style: 'any style',
|
style: 'any style',
|
||||||
@@ -1879,9 +1898,9 @@ describe('useNodePricing', () => {
|
|||||||
expected: '$0.50/Run'
|
expected: '$0.50/Run'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
quad: true,
|
quad: false,
|
||||||
style: 'any style',
|
style: 'any style',
|
||||||
texture: false,
|
texture: true,
|
||||||
textureQuality: 'standard',
|
textureQuality: 'standard',
|
||||||
expected: '$0.35/Run'
|
expected: '$0.35/Run'
|
||||||
}
|
}
|
||||||
@@ -1890,6 +1909,7 @@ describe('useNodePricing', () => {
|
|||||||
testCases.forEach(
|
testCases.forEach(
|
||||||
({ quad, style, texture, textureQuality, expected }) => {
|
({ quad, style, texture, textureQuality, expected }) => {
|
||||||
const widgets = [
|
const widgets = [
|
||||||
|
{ name: 'model_version', value: 'v3.0' },
|
||||||
{ name: 'quad', value: quad },
|
{ name: 'quad', value: quad },
|
||||||
{ name: 'style', value: style },
|
{ name: 'style', value: style },
|
||||||
{ name: 'texture', value: texture }
|
{ name: 'texture', value: texture }
|
||||||
@@ -1909,7 +1929,7 @@ describe('useNodePricing', () => {
|
|||||||
|
|
||||||
const price = getNodeDisplayPrice(node)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe(
|
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)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe(
|
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)
|
const price = getNodeDisplayPrice(node)
|
||||||
expect(price).toBe(
|
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