mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-30 03:01:54 +00:00
[API Node] Add cost indicators on API nodes (#3924)
Co-authored-by: github-actions <github-actions@github.com> Co-authored-by: Chenlei Hu <huchenlei@proton.me>
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
|||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
import { computed, onMounted, watch } from 'vue'
|
import { computed, onMounted, watch } from 'vue'
|
||||||
|
|
||||||
|
import { useNodePricing } from '@/composables/node/useNodePricing'
|
||||||
import { app } from '@/scripts/app'
|
import { app } from '@/scripts/app'
|
||||||
import { useExtensionStore } from '@/stores/extensionStore'
|
import { useExtensionStore } from '@/stores/extensionStore'
|
||||||
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
|
import { ComfyNodeDefImpl, useNodeDefStore } from '@/stores/nodeDefStore'
|
||||||
@@ -42,9 +43,21 @@ export const useNodeBadge = () => {
|
|||||||
) as NodeBadgeMode
|
) as NodeBadgeMode
|
||||||
)
|
)
|
||||||
|
|
||||||
watch([nodeSourceBadgeMode, nodeIdBadgeMode, nodeLifeCycleBadgeMode], () => {
|
const showApiPricingBadge = computed(() =>
|
||||||
app.graph?.setDirtyCanvas(true, true)
|
settingStore.get('Comfy.NodeBadge.ShowApiPricing')
|
||||||
})
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
[
|
||||||
|
nodeSourceBadgeMode,
|
||||||
|
nodeIdBadgeMode,
|
||||||
|
nodeLifeCycleBadgeMode,
|
||||||
|
showApiPricingBadge
|
||||||
|
],
|
||||||
|
() => {
|
||||||
|
app.graph?.setDirtyCanvas(true, true)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const nodeDefStore = useNodeDefStore()
|
const nodeDefStore = useNodeDefStore()
|
||||||
function badgeTextVisible(
|
function badgeTextVisible(
|
||||||
@@ -58,6 +71,8 @@ export const useNodeBadge = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
const nodePricing = useNodePricing()
|
||||||
|
|
||||||
extensionStore.registerExtension({
|
extensionStore.registerExtension({
|
||||||
name: 'Comfy.NodeBadge',
|
name: 'Comfy.NodeBadge',
|
||||||
nodeCreated(node: LGraphNode) {
|
nodeCreated(node: LGraphNode) {
|
||||||
@@ -95,13 +110,15 @@ export const useNodeBadge = () => {
|
|||||||
|
|
||||||
node.badges.push(() => badge.value)
|
node.badges.push(() => badge.value)
|
||||||
|
|
||||||
if (node.constructor.nodeData?.api_node) {
|
if (node.constructor.nodeData?.api_node && showApiPricingBadge.value) {
|
||||||
|
const price = nodePricing.getNodeDisplayPrice(node)
|
||||||
|
// Always add the badge for API nodes, with or without price text
|
||||||
const creditsBadge = computed(() => {
|
const creditsBadge = computed(() => {
|
||||||
// Use dynamic background color based on the theme
|
// Use dynamic background color based on the theme
|
||||||
const isLightTheme =
|
const isLightTheme =
|
||||||
colorPaletteStore.completedActivePalette.light_theme
|
colorPaletteStore.completedActivePalette.light_theme
|
||||||
return new LGraphBadge({
|
return new LGraphBadge({
|
||||||
text: '',
|
text: price,
|
||||||
iconOptions: {
|
iconOptions: {
|
||||||
unicode: '\ue96b',
|
unicode: '\ue96b',
|
||||||
fontFamily: 'PrimeIcons',
|
fontFamily: 'PrimeIcons',
|
||||||
|
|||||||
426
src/composables/node/useNodePricing.ts
Normal file
426
src/composables/node/useNodePricing.ts
Normal file
@@ -0,0 +1,426 @@
|
|||||||
|
import type { LGraphNode } from '@comfyorg/litegraph'
|
||||||
|
|
||||||
|
import { ApiNodeCostRecord } from '@/types/apiNodeTypes'
|
||||||
|
|
||||||
|
const apiNodeCosts: ApiNodeCostRecord = {
|
||||||
|
FluxProCannyNode: {
|
||||||
|
vendor: 'BFL',
|
||||||
|
nodeName: 'Flux 1: Canny Control Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.05',
|
||||||
|
displayPrice: '$0.05/Run'
|
||||||
|
},
|
||||||
|
FluxProDepthNode: {
|
||||||
|
vendor: 'BFL',
|
||||||
|
nodeName: 'Flux 1: Depth Control Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.05',
|
||||||
|
displayPrice: '$0.05/Run'
|
||||||
|
},
|
||||||
|
FluxProExpandNode: {
|
||||||
|
vendor: 'BFL',
|
||||||
|
nodeName: 'Flux 1: Expand Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.05',
|
||||||
|
rateDocumentationUrl: 'https://docs.bfl.ml/pricing/',
|
||||||
|
displayPrice: '$0.05/Run'
|
||||||
|
},
|
||||||
|
FluxProFillNode: {
|
||||||
|
vendor: 'BFL',
|
||||||
|
nodeName: 'Flux 1: Fill Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.05',
|
||||||
|
displayPrice: '$0.05/Run'
|
||||||
|
},
|
||||||
|
FluxProUltraImageNode: {
|
||||||
|
vendor: 'BFL',
|
||||||
|
nodeName: 'Flux 1.1: [pro] Ultra Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.06',
|
||||||
|
displayPrice: '$0.06/Run'
|
||||||
|
},
|
||||||
|
IdeogramV1: {
|
||||||
|
vendor: 'Ideogram',
|
||||||
|
nodeName: 'Ideogram V1',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.06',
|
||||||
|
rateDocumentationUrl: 'https://about.ideogram.ai/api-pricing',
|
||||||
|
displayPrice: '$0.06/Run'
|
||||||
|
},
|
||||||
|
IdeogramV2: {
|
||||||
|
vendor: 'Ideogram',
|
||||||
|
nodeName: 'Ideogram V2',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.08',
|
||||||
|
displayPrice: '$0.08/Run'
|
||||||
|
},
|
||||||
|
IdeogramV3: {
|
||||||
|
vendor: 'Ideogram',
|
||||||
|
nodeName: 'Ideogram V3',
|
||||||
|
pricingParams: 'rendering_speed',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (low to medium)'
|
||||||
|
},
|
||||||
|
KlingCameraControlI2VNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Image to Video (Camera Control)',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.49',
|
||||||
|
displayPrice: '$0.49/Run'
|
||||||
|
},
|
||||||
|
KlingCameraControlT2VNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Text to Video (Camera Control)',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.14',
|
||||||
|
displayPrice: '$0.14/Run'
|
||||||
|
},
|
||||||
|
KlingDualCharacterVideoEffectNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Dual Character Video Effects',
|
||||||
|
pricingParams: 'Priced the same as t2v based on mode, model, and duration.',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
KlingImage2VideoNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Image to Video',
|
||||||
|
pricingParams: 'Same as Text to Video',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
KlingImageGenerationNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Image Generation',
|
||||||
|
pricingParams: 'modality | model',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (low)'
|
||||||
|
},
|
||||||
|
KlingLipSyncAudioToVideoNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Lip Sync Video with Audio',
|
||||||
|
pricingParams: 'duration of input video',
|
||||||
|
pricePerRunRange: '$0.07',
|
||||||
|
displayPrice: '$0.07/Run'
|
||||||
|
},
|
||||||
|
KlingLipSyncTextToVideoNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Lip Sync Video with Text',
|
||||||
|
pricingParams: 'duration of input video',
|
||||||
|
pricePerRunRange: '$0.07',
|
||||||
|
displayPrice: '$0.07/Run'
|
||||||
|
},
|
||||||
|
KlingSingleImageVideoEffectNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Video Effects',
|
||||||
|
pricingParams: 'effect_scene',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
KlingStartEndFrameNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Start-End Frame to Video',
|
||||||
|
pricingParams: 'Same as text to video',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
KlingTextToVideoNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Text to Video',
|
||||||
|
pricingParams: 'model | duration | mode',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium to high)'
|
||||||
|
},
|
||||||
|
KlingVideoExtendNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Video Extend',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.28',
|
||||||
|
displayPrice: '$0.28/Run'
|
||||||
|
},
|
||||||
|
KlingVirtualTryOnNode: {
|
||||||
|
vendor: 'Kling',
|
||||||
|
nodeName: 'Kling Virtual Try On',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.07',
|
||||||
|
displayPrice: '$0.07/Run'
|
||||||
|
},
|
||||||
|
LumaImageToVideoNode: {
|
||||||
|
vendor: 'Luma',
|
||||||
|
nodeName: 'Luma Image to Video',
|
||||||
|
pricingParams: 'Same as Text to Video',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://lumalabs.ai/api/pricing',
|
||||||
|
displayPrice: 'Variable pricing (medium to high)'
|
||||||
|
},
|
||||||
|
LumaVideoNode: {
|
||||||
|
vendor: 'Luma',
|
||||||
|
nodeName: 'Luma Text to Video',
|
||||||
|
pricingParams: 'model | resolution | duration',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://lumalabs.ai/api/pricing',
|
||||||
|
displayPrice: 'Variable pricing (medium to high)'
|
||||||
|
},
|
||||||
|
MinimaxImageToVideoNode: {
|
||||||
|
vendor: 'Minimax',
|
||||||
|
nodeName: 'MiniMax Image to Video',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.43',
|
||||||
|
rateDocumentationUrl: 'https://www.minimax.io/price',
|
||||||
|
displayPrice: '$0.43/Run'
|
||||||
|
},
|
||||||
|
MinimaxTextToVideoNode: {
|
||||||
|
vendor: 'Minimax',
|
||||||
|
nodeName: 'MiniMax Text to Video',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.43',
|
||||||
|
rateDocumentationUrl: 'https://www.minimax.io/price',
|
||||||
|
displayPrice: '$0.43/Run'
|
||||||
|
},
|
||||||
|
OpenAIDalle2: {
|
||||||
|
vendor: 'OpenAI',
|
||||||
|
nodeName: 'dall-e-2',
|
||||||
|
pricingParams: 'size',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://platform.openai.com/docs/pricing',
|
||||||
|
displayPrice: 'Variable pricing (low)'
|
||||||
|
},
|
||||||
|
OpenAIDalle3: {
|
||||||
|
vendor: 'OpenAI',
|
||||||
|
nodeName: 'dall-e-3',
|
||||||
|
pricingParams: 'size | quality',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://platform.openai.com/docs/pricing',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
OpenAIGPTImage1: {
|
||||||
|
vendor: 'OpenAI',
|
||||||
|
nodeName: 'gpt-image-1',
|
||||||
|
pricingParams: 'quality',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://platform.openai.com/docs/pricing',
|
||||||
|
displayPrice: 'Variable pricing (low to high)'
|
||||||
|
},
|
||||||
|
PikaImageToVideoNode2_2: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pika Image to Video',
|
||||||
|
pricingParams: 'duration | resolution',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
PikaScenesV2_2: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pika Scenes (Video Image Composition)',
|
||||||
|
pricingParams: 'duration | resolution',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
PikaStartEndFrameNode2_2: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pika Start and End Frame to Video',
|
||||||
|
pricingParams: 'duration | resolution',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
PikaTextToVideoNode2_2: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pika Text to Video',
|
||||||
|
pricingParams: 'duration | resolution',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium)'
|
||||||
|
},
|
||||||
|
Pikadditions: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pikadditions (Video Object Insertion)',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.3',
|
||||||
|
displayPrice: '$0.3/Run'
|
||||||
|
},
|
||||||
|
Pikaffects: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pikaffects (Video Effects)',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.45',
|
||||||
|
displayPrice: '$0.45/Run'
|
||||||
|
},
|
||||||
|
Pikaswaps: {
|
||||||
|
vendor: 'Pika',
|
||||||
|
nodeName: 'Pika Swaps (Video Object Replacement)',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.3',
|
||||||
|
displayPrice: '$0.3/Run'
|
||||||
|
},
|
||||||
|
PixverseImageToVideoNode: {
|
||||||
|
vendor: 'Pixverse',
|
||||||
|
nodeName: 'PixVerse Image to Video',
|
||||||
|
pricingParams: 'same as text to video',
|
||||||
|
pricePerRunRange: '$0.9',
|
||||||
|
displayPrice: '$0.9/Run'
|
||||||
|
},
|
||||||
|
PixverseTextToVideoNode: {
|
||||||
|
vendor: 'Pixverse',
|
||||||
|
nodeName: 'PixVerse Text to Video',
|
||||||
|
pricingParams: 'duration | quality | motion_mode',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (medium to high)'
|
||||||
|
},
|
||||||
|
PixverseTransitionVideoNode: {
|
||||||
|
vendor: 'Pixverse',
|
||||||
|
nodeName: 'PixVerse Transition Video',
|
||||||
|
pricingParams: 'same as text to video',
|
||||||
|
pricePerRunRange: '$0.9',
|
||||||
|
displayPrice: '$0.9/Run'
|
||||||
|
},
|
||||||
|
RecraftCreativeUpscaleNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Creative Upscale Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.25',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.25/Run'
|
||||||
|
},
|
||||||
|
RecraftCrispUpscaleNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Crisp Upscale Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.004',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.004/Run'
|
||||||
|
},
|
||||||
|
RecraftImageInpaintingNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Image Inpainting',
|
||||||
|
pricingParams: 'n',
|
||||||
|
pricePerRunRange: '$$0.04 x n',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.04 x n/Run'
|
||||||
|
},
|
||||||
|
RecraftImageToImageNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Image to Image',
|
||||||
|
pricingParams: 'n',
|
||||||
|
pricePerRunRange: '$0.04 x n',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.04 x n/Run'
|
||||||
|
},
|
||||||
|
RecraftRemoveBackgroundNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Remove Background',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.01',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.01/Run'
|
||||||
|
},
|
||||||
|
RecraftReplaceBackgroundNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Replace Background',
|
||||||
|
pricingParams: 'n',
|
||||||
|
pricePerRunRange: '$0.04',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.04/Run'
|
||||||
|
},
|
||||||
|
RecraftTextToImageNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Text to Image',
|
||||||
|
pricingParams: 'model | n',
|
||||||
|
pricePerRunRange: '$0.04 x n',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.04 x n/Run'
|
||||||
|
},
|
||||||
|
RecraftTextToVectorNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Text to Vector',
|
||||||
|
pricingParams: 'model | n',
|
||||||
|
pricePerRunRange: '$0.08 x n',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.08 x n/Run'
|
||||||
|
},
|
||||||
|
RecraftVectorizeImageNode: {
|
||||||
|
vendor: 'Recraft',
|
||||||
|
nodeName: 'Recraft Vectorize Image',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.01',
|
||||||
|
rateDocumentationUrl: 'https://www.recraft.ai/docs#pricing',
|
||||||
|
displayPrice: '$0.01/Run'
|
||||||
|
},
|
||||||
|
StabilityStableImageSD_3_5Node: {
|
||||||
|
vendor: 'Stability',
|
||||||
|
nodeName: 'Stability AI Stable Diffusion 3.5 Image',
|
||||||
|
pricingParams: 'model',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
displayPrice: 'Variable pricing (low)'
|
||||||
|
},
|
||||||
|
StabilityStableImageUltraNode: {
|
||||||
|
vendor: 'Stability',
|
||||||
|
nodeName: 'Stability AI Stable Image Ultra',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.08',
|
||||||
|
displayPrice: '$0.08/Run'
|
||||||
|
},
|
||||||
|
StabilityUpscaleConservativeNode: {
|
||||||
|
vendor: 'Stability',
|
||||||
|
nodeName: 'Stability AI Upscale Conservative',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.25',
|
||||||
|
displayPrice: '$0.25/Run'
|
||||||
|
},
|
||||||
|
StabilityUpscaleCreativeNode: {
|
||||||
|
vendor: 'Stability',
|
||||||
|
nodeName: 'Stability AI Upscale Creative',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.25',
|
||||||
|
displayPrice: '$0.25/Run'
|
||||||
|
},
|
||||||
|
StabilityUpscaleFastNode: {
|
||||||
|
vendor: 'Stability',
|
||||||
|
nodeName: 'Stability AI Upscale Fast',
|
||||||
|
pricingParams: '-',
|
||||||
|
pricePerRunRange: '$0.01',
|
||||||
|
displayPrice: '$0.01/Run'
|
||||||
|
},
|
||||||
|
VeoVideoGenerationNode: {
|
||||||
|
vendor: 'Veo',
|
||||||
|
nodeName: 'Google Veo2 Video Generation',
|
||||||
|
pricingParams: 'duration_seconds',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl:
|
||||||
|
'https://cloud.google.com/vertex-ai/generative-ai/pricing',
|
||||||
|
displayPrice: 'Variable pricing (high)'
|
||||||
|
},
|
||||||
|
LumaTextToImageNode: {
|
||||||
|
vendor: 'Luma',
|
||||||
|
nodeName: 'Luma Text to Image',
|
||||||
|
pricingParams: 'model | aspect_ratio',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://lumalabs.ai/api/pricing',
|
||||||
|
displayPrice: 'Variable pricing (low to medium)'
|
||||||
|
},
|
||||||
|
LumaImageToImageNode: {
|
||||||
|
vendor: 'Luma',
|
||||||
|
nodeName: 'Luma Image to Image',
|
||||||
|
pricingParams: 'Same as Text to Image',
|
||||||
|
pricePerRunRange: 'dynamic',
|
||||||
|
rateDocumentationUrl: 'https://lumalabs.ai/api/pricing',
|
||||||
|
displayPrice: 'Variable pricing (low to medium)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composable to get node pricing information for API nodes
|
||||||
|
*/
|
||||||
|
export const useNodePricing = () => {
|
||||||
|
const getNodePrice = (nodeName: string): string =>
|
||||||
|
apiNodeCosts[nodeName]?.displayPrice || ''
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the price display for a node
|
||||||
|
*/
|
||||||
|
const getNodeDisplayPrice = (node: LGraphNode): string => {
|
||||||
|
if (!node.constructor.nodeData?.api_node) return ''
|
||||||
|
return getNodePrice(node.constructor.nodeData.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getNodeDisplayPrice
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -322,6 +322,14 @@ export const CORE_SETTINGS: SettingParams[] = [
|
|||||||
options: [NodeBadgeMode.None, NodeBadgeMode.ShowAll],
|
options: [NodeBadgeMode.None, NodeBadgeMode.ShowAll],
|
||||||
defaultValue: NodeBadgeMode.ShowAll
|
defaultValue: NodeBadgeMode.ShowAll
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'Comfy.NodeBadge.ShowApiPricing',
|
||||||
|
category: ['Comfy', 'API Nodes'],
|
||||||
|
name: 'Show API node pricing badge',
|
||||||
|
type: 'boolean',
|
||||||
|
defaultValue: true,
|
||||||
|
versionAdded: '1.20.3'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'Comfy.ConfirmClear',
|
id: 'Comfy.ConfirmClear',
|
||||||
category: ['Comfy', 'Workflow', 'ConfirmClear'],
|
category: ['Comfy', 'Workflow', 'ConfirmClear'],
|
||||||
|
|||||||
@@ -886,7 +886,8 @@
|
|||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
"Light": "Light",
|
"Light": "Light",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"Credits": "Credits"
|
"Credits": "Credits",
|
||||||
|
"API Nodes": "API Nodes"
|
||||||
},
|
},
|
||||||
"serverConfigItems": {
|
"serverConfigItems": {
|
||||||
"listen": {
|
"listen": {
|
||||||
|
|||||||
@@ -225,6 +225,9 @@
|
|||||||
"Hide built-in": "Hide built-in"
|
"Hide built-in": "Hide built-in"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "Show API node pricing badge"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "Node search box implementation",
|
"name": "Node search box implementation",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -1001,6 +1001,7 @@
|
|||||||
},
|
},
|
||||||
"settingsCategories": {
|
"settingsCategories": {
|
||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
|
"API Nodes": "Nodos API",
|
||||||
"About": "Acerca de",
|
"About": "Acerca de",
|
||||||
"Appearance": "Apariencia",
|
"Appearance": "Apariencia",
|
||||||
"BrushAdjustment": "Ajuste de Pincel",
|
"BrushAdjustment": "Ajuste de Pincel",
|
||||||
|
|||||||
@@ -193,6 +193,9 @@
|
|||||||
"Show all": "Mostrar todo"
|
"Show all": "Mostrar todo"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "Mostrar insignia de precios de nodo API"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "Implementación de la caja de búsqueda de nodos",
|
"name": "Implementación de la caja de búsqueda de nodos",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -1001,6 +1001,7 @@
|
|||||||
},
|
},
|
||||||
"settingsCategories": {
|
"settingsCategories": {
|
||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
|
"API Nodes": "Nœuds API",
|
||||||
"About": "À Propos",
|
"About": "À Propos",
|
||||||
"Appearance": "Apparence",
|
"Appearance": "Apparence",
|
||||||
"BrushAdjustment": "Ajustement de Brosse",
|
"BrushAdjustment": "Ajustement de Brosse",
|
||||||
|
|||||||
@@ -193,6 +193,9 @@
|
|||||||
"Show all": "Afficher tout"
|
"Show all": "Afficher tout"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "Afficher l’insigne de tarification API du nœud"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "Implémentation de la boîte de recherche de nœud",
|
"name": "Implémentation de la boîte de recherche de nœud",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -1001,6 +1001,7 @@
|
|||||||
},
|
},
|
||||||
"settingsCategories": {
|
"settingsCategories": {
|
||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
|
"API Nodes": "APIノード",
|
||||||
"About": "情報",
|
"About": "情報",
|
||||||
"Appearance": "外観",
|
"Appearance": "外観",
|
||||||
"BrushAdjustment": "ブラシ調整",
|
"BrushAdjustment": "ブラシ調整",
|
||||||
|
|||||||
@@ -193,6 +193,9 @@
|
|||||||
"Show all": "すべて表示"
|
"Show all": "すべて表示"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "APIノードの料金バッジを表示"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "ノード検索ボックスの実装",
|
"name": "ノード検索ボックスの実装",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -1001,6 +1001,7 @@
|
|||||||
},
|
},
|
||||||
"settingsCategories": {
|
"settingsCategories": {
|
||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
|
"API Nodes": "API 노드",
|
||||||
"About": "정보",
|
"About": "정보",
|
||||||
"Appearance": "모양",
|
"Appearance": "모양",
|
||||||
"BrushAdjustment": "브러시 조정",
|
"BrushAdjustment": "브러시 조정",
|
||||||
|
|||||||
@@ -193,6 +193,9 @@
|
|||||||
"Show all": "모두 표시"
|
"Show all": "모두 표시"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "API 노드 가격 배지 표시"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "노드 검색 상자 구현",
|
"name": "노드 검색 상자 구현",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -1001,6 +1001,7 @@
|
|||||||
},
|
},
|
||||||
"settingsCategories": {
|
"settingsCategories": {
|
||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
|
"API Nodes": "API-узлы",
|
||||||
"About": "О программе",
|
"About": "О программе",
|
||||||
"Appearance": "Внешний вид",
|
"Appearance": "Внешний вид",
|
||||||
"BrushAdjustment": "Настройка кисти",
|
"BrushAdjustment": "Настройка кисти",
|
||||||
|
|||||||
@@ -193,6 +193,9 @@
|
|||||||
"Show all": "Показать все"
|
"Show all": "Показать все"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "Показать значок стоимости узла API"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "Реализация поискового поля нод",
|
"name": "Реализация поискового поля нод",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -1001,6 +1001,7 @@
|
|||||||
},
|
},
|
||||||
"settingsCategories": {
|
"settingsCategories": {
|
||||||
"3D": "3D",
|
"3D": "3D",
|
||||||
|
"API Nodes": "API 节点",
|
||||||
"About": "关于",
|
"About": "关于",
|
||||||
"Appearance": "外观",
|
"Appearance": "外观",
|
||||||
"BrushAdjustment": "画笔调整",
|
"BrushAdjustment": "画笔调整",
|
||||||
|
|||||||
@@ -193,6 +193,9 @@
|
|||||||
"Show all": "显示全部"
|
"Show all": "显示全部"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Comfy_NodeBadge_ShowApiPricing": {
|
||||||
|
"name": "显示 API 节点定价徽章"
|
||||||
|
},
|
||||||
"Comfy_NodeSearchBoxImpl": {
|
"Comfy_NodeSearchBoxImpl": {
|
||||||
"name": "节点搜索框",
|
"name": "节点搜索框",
|
||||||
"options": {
|
"options": {
|
||||||
|
|||||||
@@ -421,6 +421,7 @@ const zSettings = z.object({
|
|||||||
'Comfy.NodeBadge.NodeSourceBadgeMode': zNodeBadgeMode,
|
'Comfy.NodeBadge.NodeSourceBadgeMode': zNodeBadgeMode,
|
||||||
'Comfy.NodeBadge.NodeIdBadgeMode': zNodeBadgeMode,
|
'Comfy.NodeBadge.NodeIdBadgeMode': zNodeBadgeMode,
|
||||||
'Comfy.NodeBadge.NodeLifeCycleBadgeMode': zNodeBadgeMode,
|
'Comfy.NodeBadge.NodeLifeCycleBadgeMode': zNodeBadgeMode,
|
||||||
|
'Comfy.NodeBadge.ShowApiPricing': z.boolean(),
|
||||||
'Comfy.QueueButton.BatchCountLimit': z.number(),
|
'Comfy.QueueButton.BatchCountLimit': z.number(),
|
||||||
'Comfy.Queue.MaxHistoryItems': z.number(),
|
'Comfy.Queue.MaxHistoryItems': z.number(),
|
||||||
'Comfy.Keybinding.UnsetBindings': z.array(zKeybinding),
|
'Comfy.Keybinding.UnsetBindings': z.array(zKeybinding),
|
||||||
|
|||||||
@@ -2,3 +2,23 @@ export interface ApiNodeCost {
|
|||||||
name: string
|
name: string
|
||||||
cost: number
|
cost: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about an API node's cost and pricing details
|
||||||
|
*/
|
||||||
|
export interface ApiNodeCostData {
|
||||||
|
/** The vendor/company providing the API service (e.g., 'OpenAI', 'Stability') */
|
||||||
|
vendor: string
|
||||||
|
/** The human-readable name of the node as displayed in the UI */
|
||||||
|
nodeName: string
|
||||||
|
/** Parameters that affect pricing (e.g., 'size | quality', 'duration', '-' if none) */
|
||||||
|
pricingParams: string
|
||||||
|
/** The price range per run (e.g., '$0.05', '$0.04 x n', 'dynamic') */
|
||||||
|
pricePerRunRange: string
|
||||||
|
/** Formatted price string for display in the UI */
|
||||||
|
displayPrice: string
|
||||||
|
/** URL to the vendor's pricing documentation page */
|
||||||
|
rateDocumentationUrl?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiNodeCostRecord = Record<string, ApiNodeCostData>
|
||||||
|
|||||||
Reference in New Issue
Block a user