From b963f4ad210a42841ab23dfc28a84143a0cce227 Mon Sep 17 00:00:00 2001 From: Alexander Piskun <13381981+bigcat88@users.noreply.github.com> Date: Sun, 16 Aug 2026 15:30:58 +0400 Subject: [PATCH] [Partner Nodes] feat(x-comfy-credits): remove custom price extractor logic (#15655) Signed-off-by: Alexander Piskun --- comfy_api_nodes/apis/bytedance.py | 29 ------------------------- comfy_api_nodes/nodes_bfl.py | 22 ------------------- comfy_api_nodes/nodes_bytedance.py | 34 ++---------------------------- comfy_api_nodes/nodes_grok.py | 17 --------------- comfy_api_nodes/nodes_hitpaw.py | 4 ---- comfy_api_nodes/nodes_magnific.py | 34 ------------------------------ comfy_api_nodes/nodes_topaz.py | 4 ---- comfy_api_nodes/nodes_tripo.py | 1 - comfy_api_nodes/nodes_vidu.py | 1 - 9 files changed, 2 insertions(+), 144 deletions(-) diff --git a/comfy_api_nodes/apis/bytedance.py b/comfy_api_nodes/apis/bytedance.py index 64f8cde37..ab9a4d2b6 100644 --- a/comfy_api_nodes/apis/bytedance.py +++ b/comfy_api_nodes/apis/bytedance.py @@ -187,35 +187,6 @@ class SeedanceVirtualLibraryCreateAssetRequest(BaseModel): asset_type: str | None = Field(None, description="BytePlus asset type. Defaults to Image server-side when omitted.") -# Dollars per 1K tokens, keyed by (model_id, has_video_input, resolution). -SEEDANCE2_PRICE_PER_1K_TOKENS = { - ("dreamina-seedance-2-0-260128", False, "480p"): 0.007, - ("dreamina-seedance-2-0-260128", True, "480p"): 0.0043, - ("dreamina-seedance-2-0-260128", False, "720p"): 0.007, - ("dreamina-seedance-2-0-260128", True, "720p"): 0.0043, - ("dreamina-seedance-2-0-260128", False, "1080p"): 0.0077, - ("dreamina-seedance-2-0-260128", True, "1080p"): 0.0047, - ("dreamina-seedance-2-0-260128", False, "4k"): 0.004, - ("dreamina-seedance-2-0-260128", True, "4k"): 0.0024, - ("dreamina-seedance-2-0-fast-260128", False, "480p"): 0.0056, - ("dreamina-seedance-2-0-fast-260128", True, "480p"): 0.0033, - ("dreamina-seedance-2-0-fast-260128", False, "720p"): 0.0056, - ("dreamina-seedance-2-0-fast-260128", True, "720p"): 0.0033, - ("dreamina-seedance-2-0-mini", False, "480p"): 0.0035, - ("dreamina-seedance-2-0-mini", True, "480p"): 0.0021, - ("dreamina-seedance-2-0-mini", False, "720p"): 0.0035, - ("dreamina-seedance-2-0-mini", True, "720p"): 0.0021, - ("dreamina-seedance-2-5-260628", False, "480p"): 0.0107, - ("dreamina-seedance-2-5-260628", True, "480p"): 0.0064, - ("dreamina-seedance-2-5-260628", False, "720p"): 0.0107, - ("dreamina-seedance-2-5-260628", True, "720p"): 0.0064, -} - - -def seedance2_price_per_1k_tokens(model_id: str, has_video_input: bool, resolution: str) -> float | None: - return SEEDANCE2_PRICE_PER_1K_TOKENS.get((model_id, has_video_input, resolution)) - - RECOMMENDED_PRESETS = [ ("1024x1024 (1:1)", 1024, 1024), ("864x1152 (3:4)", 864, 1152), diff --git a/comfy_api_nodes/nodes_bfl.py b/comfy_api_nodes/nodes_bfl.py index 6c961dc0c..30275bfbe 100644 --- a/comfy_api_nodes/nodes_bfl.py +++ b/comfy_api_nodes/nodes_bfl.py @@ -1,7 +1,6 @@ import math import torch -from pydantic import BaseModel from typing_extensions import override from comfy_api.latest import IO, ComfyExtension, Input @@ -588,16 +587,12 @@ class FluxEraseNode(IO.ComfyNode): ), ) - def price_extractor(_r: BaseModel) -> float | None: - return None if initial_response.cost is None else initial_response.cost / 100 - response = await poll_op( cls, ApiEndpoint(initial_response.polling_url), response_model=BFLFluxStatusResponse, status_extractor=lambda r: r.status, progress_extractor=lambda r: r.progress, - price_extractor=price_extractor, completed_statuses=[BFLStatus.ready], failed_statuses=[ BFLStatus.request_moderated, @@ -669,16 +664,12 @@ class FluxVTONode(IO.ComfyNode): ), ) - def price_extractor(_r: BaseModel) -> float | None: - return None if initial_response.cost is None else initial_response.cost / 100 - response = await poll_op( cls, ApiEndpoint(initial_response.polling_url), response_model=BFLFluxStatusResponse, status_extractor=lambda r: r.status, progress_extractor=lambda r: r.progress, - price_extractor=price_extractor, completed_statuses=[BFLStatus.ready], failed_statuses=[ BFLStatus.request_moderated, @@ -802,16 +793,12 @@ class Flux2ProImageNode(IO.ComfyNode): ), ) - def price_extractor(_r: BaseModel) -> float | None: - return None if initial_response.cost is None else initial_response.cost / 100 - response = await poll_op( cls, ApiEndpoint(initial_response.polling_url), response_model=BFLFluxStatusResponse, status_extractor=lambda r: r.status, progress_extractor=lambda r: r.progress, - price_extractor=price_extractor, completed_statuses=[BFLStatus.ready], failed_statuses=[ BFLStatus.request_moderated, @@ -994,16 +981,12 @@ class Flux2ImageNode(IO.ComfyNode): ), ) - def price_extractor(_r: BaseModel) -> float | None: - return None if initial_response.cost is None else initial_response.cost / 100 - response = await poll_op( cls, ApiEndpoint(initial_response.polling_url), response_model=BFLFluxStatusResponse, status_extractor=lambda r: r.status, progress_extractor=lambda r: r.progress, - price_extractor=price_extractor, completed_statuses=[BFLStatus.ready], failed_statuses=[ BFLStatus.request_moderated, @@ -1171,17 +1154,12 @@ async def _flux3_execute(cls: type[IO.ComfyNode], request: Flux3VideoRequest) -> response_model=BFLFluxProGenerateResponse, data=request, ) - - def price_extractor(_r: BaseModel) -> float | None: - return None if initial_response.cost is None else initial_response.cost / 100 - response = await poll_op( cls, ApiEndpoint(initial_response.polling_url), response_model=BFLFluxStatusResponse, status_extractor=lambda r: r.status, progress_extractor=lambda r: r.progress, - price_extractor=price_extractor, completed_statuses=[BFLStatus.ready], failed_statuses=[ BFLStatus.request_moderated, diff --git a/comfy_api_nodes/nodes_bytedance.py b/comfy_api_nodes/nodes_bytedance.py index 6040075f8..f69c3d8a9 100644 --- a/comfy_api_nodes/nodes_bytedance.py +++ b/comfy_api_nodes/nodes_bytedance.py @@ -49,7 +49,6 @@ from comfy_api_nodes.apis.bytedance import ( TaskVideoContentUrl, Text2ImageTaskCreationRequest, Text2VideoTaskCreationRequest, - seedance2_price_per_1k_tokens, seedance2_reference_limits, ) from comfy_api_nodes.util import ( @@ -408,20 +407,6 @@ async def _seedance_virtual_library_upload_video_asset( return f"asset://{create_resp.asset_id}" -def _seedance2_price_extractor(model_id: str, has_video_input: bool, resolution: str): - """Returns a price_extractor closure for Seedance 2.0 poll_op.""" - rate = seedance2_price_per_1k_tokens(model_id, has_video_input, resolution) - if rate is None: - return None - - def extractor(response: TaskStatusResponse) -> float | None: - if response.usage is None: - return None - return response.usage.total_tokens * 1.43 * rate / 1_000.0 - - return extractor - - def get_image_url_from_response(response: ImageTaskCreationResponse) -> str: if response.error: error_msg = f"ByteDance request failed. Code: {response.error['code']}, message: {response.error['message']}" @@ -2300,9 +2285,6 @@ _SEEDANCE_TASK_TYPE_MISMATCH_CODE = "InvalidParameter.TaskTypeMismatch" async def _seedance2_poll_video_task( cls: type[IO.ComfyNode], task_id: str, - model_id: str, - resolution: str, - has_video_input: bool, task_type: str | None = None, ) -> TaskStatusResponse: try: @@ -2311,9 +2293,6 @@ async def _seedance2_poll_video_task( ApiEndpoint(path=f"{BYTEPLUS_SEEDANCE2_TASK_STATUS_ENDPOINT}/{task_id}"), response_model=TaskStatusResponse, status_extractor=lambda r: r.status, - price_extractor=_seedance2_price_extractor( - model_id, has_video_input=has_video_input, resolution=resolution - ), poll_interval=9, ) except Exception as exc: @@ -2452,9 +2431,7 @@ class ByteDance2TextToVideoNode(IO.ComfyNode): ), response_model=TaskCreationResponse, ) - response = await _seedance2_poll_video_task( - cls, initial_response.id, model_id, model["resolution"], has_video_input=False - ) + response = await _seedance2_poll_video_task(cls, initial_response.id) return IO.NodeOutput(await download_url_to_video_output(response.content.video_url)) @@ -2645,9 +2622,7 @@ class ByteDance2FirstLastFrameNode(IO.ComfyNode): data=_seedance2_build_request(model, model_id, content, seed, watermark, ratio=request_ratio), response_model=TaskCreationResponse, ) - response = await _seedance2_poll_video_task( - cls, initial_response.id, model_id, model["resolution"], has_video_input=False - ) + response = await _seedance2_poll_video_task(cls, initial_response.id) return IO.NodeOutput(await download_url_to_video_output(response.content.video_url)) @@ -2843,8 +2818,6 @@ class ByteDance2ReferenceNodeV2(IO.ComfyNode): for key in reference_images: reference_images[key] = _prepare_seedance_image(reference_images[key]) - has_video_input = total_videos > 0 - if model.get("auto_downscale") and reference_videos: max_px = SEEDANCE2_REF_VIDEO_PIXEL_LIMITS.get(model_id, {}).get(model["resolution"], {}).get("max") if max_px: @@ -2966,9 +2939,6 @@ class ByteDance2ReferenceNodeV2(IO.ComfyNode): response = await _seedance2_poll_video_task( cls, initial_response.id, - model_id, - model["resolution"], - has_video_input=has_video_input, task_type=task_type, ) return IO.NodeOutput(await download_url_to_video_output(response.content.video_url)) diff --git a/comfy_api_nodes/nodes_grok.py b/comfy_api_nodes/nodes_grok.py index c8f0f20d5..c58fb1a29 100644 --- a/comfy_api_nodes/nodes_grok.py +++ b/comfy_api_nodes/nodes_grok.py @@ -126,19 +126,6 @@ def _normalize_grok_reference_prompt(prompt: str, total_images: int, voices: lis return prompt -def _extract_grok_price(response) -> float | None: - if response.usage and response.usage.cost_in_usd_ticks is not None: - return response.usage.cost_in_usd_ticks / 10_000_000_000 - return None - - -def _extract_grok_video_price(response) -> float | None: - price = _extract_grok_price(response) - if price is not None: - return price * 1.43 - return None - - class GrokImageNode(IO.ComfyNode): @classmethod @@ -747,7 +734,6 @@ class GrokVideoNode(IO.ComfyNode): ApiEndpoint(path=f"/proxy/xai/v1/videos/{initial_response.request_id}"), status_extractor=lambda r: r.status if r.status is not None else "complete", response_model=VideoStatusResponse, - price_extractor=_extract_grok_video_price if model == "grok-imagine-video-1.5" else _extract_grok_price, ) return IO.NodeOutput(await download_url_to_video_output(response.video.url)) @@ -825,7 +811,6 @@ class GrokVideoEditNode(IO.ComfyNode): ApiEndpoint(path=f"/proxy/xai/v1/videos/{initial_response.request_id}"), status_extractor=lambda r: r.status if r.status is not None else "complete", response_model=VideoStatusResponse, - price_extractor=_extract_grok_price, ) return IO.NodeOutput(await download_url_to_video_output(response.video.url)) @@ -1022,7 +1007,6 @@ class GrokVideoReferenceNode(IO.ComfyNode): ApiEndpoint(path=f"/proxy/xai/v1/videos/{initial_response.request_id}"), status_extractor=lambda r: r.status if r.status is not None else "complete", response_model=VideoStatusResponse, - price_extractor=_extract_grok_video_price, ) return IO.NodeOutput(await download_url_to_video_output(response.video.url)) @@ -1127,7 +1111,6 @@ class GrokVideoExtendNode(IO.ComfyNode): ApiEndpoint(path=f"/proxy/xai/v1/videos/{initial_response.request_id}"), status_extractor=lambda r: r.status if r.status is not None else "complete", response_model=VideoStatusResponse, - price_extractor=_extract_grok_video_price, ) return IO.NodeOutput(await download_url_to_video_output(response.video.url)) diff --git a/comfy_api_nodes/nodes_hitpaw.py b/comfy_api_nodes/nodes_hitpaw.py index 062d3cf1d..40da2bfc1 100644 --- a/comfy_api_nodes/nodes_hitpaw.py +++ b/comfy_api_nodes/nodes_hitpaw.py @@ -169,14 +169,12 @@ class HitPawGeneralImageEnhance(IO.ComfyNode): ) if initial_res.code != 200: raise ValueError(f"Task creation failed with code {initial_res.code}: {initial_res.message}") - request_price = initial_res.data.consume_coins / 1000 final_response = await poll_op( cls, ApiEndpoint(path="/proxy/hitpaw/api/task-status", method="POST"), data=TaskCreateDataResponse(job_id=initial_res.data.job_id), response_model=TaskStatusResponse, status_extractor=lambda x: x.data.status, - price_extractor=lambda x: request_price, poll_interval=10.0, ) return IO.NodeOutput(await download_url_to_image_tensor(final_response.data.res_url)) @@ -312,7 +310,6 @@ class HitPawVideoEnhance(IO.ComfyNode): wait_label="Creating task", final_label_on_success="Task created", ) - request_price = initial_res.data.consume_coins / 1000 if initial_res.code != 200: raise ValueError(f"Task creation failed with code {initial_res.code}: {initial_res.message}") final_response = await poll_op( @@ -321,7 +318,6 @@ class HitPawVideoEnhance(IO.ComfyNode): data=TaskStatusPollRequest(job_id=initial_res.data.job_id), response_model=TaskStatusResponse, status_extractor=lambda x: x.data.status, - price_extractor=lambda x: request_price, poll_interval=10.0, ) return IO.NodeOutput(await download_url_to_video_output(final_response.data.res_url)) diff --git a/comfy_api_nodes/nodes_magnific.py b/comfy_api_nodes/nodes_magnific.py index 4ce4735df..6e86fa795 100644 --- a/comfy_api_nodes/nodes_magnific.py +++ b/comfy_api_nodes/nodes_magnific.py @@ -30,31 +30,6 @@ from comfy_api_nodes.util import ( validate_image_dimensions, ) -_EUR_TO_USD = 1.19 - - -def _tier_price_eur(megapixels: float) -> float: - """Price in EUR for a single Magnific upscaling step based on input megapixels.""" - if megapixels <= 1.3: - return 0.143 - if megapixels <= 3.0: - return 0.286 - if megapixels <= 6.4: - return 0.429 - return 1.716 - - -def _calculate_magnific_upscale_price_usd(width: int, height: int, scale: int) -> float: - """Calculate total Magnific upscale price in USD for given input dimensions and scale factor.""" - num_steps = int(math.log2(scale)) - total_eur = 0.0 - pixels = width * height - for _ in range(num_steps): - total_eur += _tier_price_eur(pixels / 1_000_000) - pixels *= 4 - return round(total_eur * _EUR_TO_USD, 2) - - class MagnificImageUpscalerCreativeNode(IO.ComfyNode): @classmethod def define_schema(cls): @@ -203,10 +178,6 @@ class MagnificImageUpscalerCreativeNode(IO.ComfyNode): f"Use a smaller input image or lower scale factor." ) - final_height, final_width = get_image_dimensions(image) - actual_scale = int(scale_factor.rstrip("x")) - price_usd = _calculate_magnific_upscale_price_usd(final_width, final_height, actual_scale) - initial_res = await sync_op( cls, ApiEndpoint(path="/proxy/freepik/v1/ai/image-upscaler", method="POST"), @@ -228,7 +199,6 @@ class MagnificImageUpscalerCreativeNode(IO.ComfyNode): ApiEndpoint(path=f"/proxy/freepik/v1/ai/image-upscaler/{initial_res.task_id}"), response_model=TaskResponse, status_extractor=lambda x: x.status, - price_extractor=lambda _: price_usd, poll_interval=10.0, ) return IO.NodeOutput(await download_url_to_image_tensor(final_response.generated[0])) @@ -367,9 +337,6 @@ class MagnificImageUpscalerPreciseV2Node(IO.ComfyNode): f"Use a smaller input image or lower scale factor." ) - final_height, final_width = get_image_dimensions(image) - price_usd = _calculate_magnific_upscale_price_usd(final_width, final_height, requested_scale) - initial_res = await sync_op( cls, ApiEndpoint(path="/proxy/freepik/v1/ai/image-upscaler-precision-v2", method="POST"), @@ -388,7 +355,6 @@ class MagnificImageUpscalerPreciseV2Node(IO.ComfyNode): ApiEndpoint(path=f"/proxy/freepik/v1/ai/image-upscaler-precision-v2/{initial_res.task_id}"), response_model=TaskResponse, status_extractor=lambda x: x.status, - price_extractor=lambda _: price_usd, poll_interval=10.0, ) return IO.NodeOutput(await download_url_to_image_tensor(final_response.generated[0])) diff --git a/comfy_api_nodes/nodes_topaz.py b/comfy_api_nodes/nodes_topaz.py index 9a0c70b4d..d8d051224 100644 --- a/comfy_api_nodes/nodes_topaz.py +++ b/comfy_api_nodes/nodes_topaz.py @@ -217,7 +217,6 @@ class TopazImageEnhance(IO.ComfyNode): response_model=ImageStatusResponse, status_extractor=lambda x: x.status, progress_extractor=lambda x: getattr(x, "progress", 0), - price_extractor=lambda x: x.credits * 0.08, poll_interval=8.0, estimated_duration=60, ) @@ -567,7 +566,6 @@ class TopazImageEnhanceV2(IO.ComfyNode): response_model=ImageStatusResponse, status_extractor=lambda x: x.status, progress_extractor=lambda x: getattr(x, "progress", 0), - price_extractor=lambda x: x.credits * (0.08 if model_choice == "Reimagine" else 0.1144), poll_interval=8.0, estimated_duration=60, ) @@ -814,7 +812,6 @@ class TopazVideoEnhance(IO.ComfyNode): response_model=VideoStatusResponse, status_extractor=lambda x: x.status, progress_extractor=lambda x: getattr(x, "progress", 0), - price_extractor=lambda x: (x.estimates.cost[0] * 0.08 if x.estimates and x.estimates.cost[0] else None), poll_interval=10.0, ) return IO.NodeOutput(await download_url_to_video_output(final_response.download.url)) @@ -1158,7 +1155,6 @@ class TopazVideoEnhanceV2(IO.ComfyNode): response_model=VideoStatusResponse, status_extractor=lambda x: x.status, progress_extractor=lambda x: getattr(x, "progress", 0), - price_extractor=lambda x: (x.estimates.cost[0] * 0.08 if x.estimates and x.estimates.cost[0] else None), poll_interval=10.0, ) return IO.NodeOutput(await download_url_to_video_output(final_response.download.url)) diff --git a/comfy_api_nodes/nodes_tripo.py b/comfy_api_nodes/nodes_tripo.py index 228fe8a1d..10aefa189 100644 --- a/comfy_api_nodes/nodes_tripo.py +++ b/comfy_api_nodes/nodes_tripo.py @@ -66,7 +66,6 @@ async def poll_until_finished( ], status_extractor=lambda x: x.data.status, progress_extractor=lambda x: x.data.progress, - price_extractor=lambda x: x.data.consumed_credit * 0.01 if x.data.consumed_credit else None, estimated_duration=average_duration, ) if response_poll.data.status == TripoTaskStatus.SUCCESS: diff --git a/comfy_api_nodes/nodes_vidu.py b/comfy_api_nodes/nodes_vidu.py index 8c5a43f5b..702417a22 100644 --- a/comfy_api_nodes/nodes_vidu.py +++ b/comfy_api_nodes/nodes_vidu.py @@ -54,7 +54,6 @@ async def execute_task( response_model=TaskStatusResponse, status_extractor=lambda r: r.state, progress_extractor=lambda r: r.progress, - price_extractor=lambda r: r.credits * 0.005 if r.credits is not None else None, max_poll_attempts=max_poll_attempts, ) if not response.creations: