mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-03-08 10:47:32 +08:00
feat(api-nodes): add TencentSmartTopology node (#12741)
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
Python Linting / Run Pylint (push) Waiting to run
Build package / Build Test (3.10) (push) Waiting to run
Build package / Build Test (3.11) (push) Waiting to run
Build package / Build Test (3.12) (push) Waiting to run
Build package / Build Test (3.13) (push) Waiting to run
Build package / Build Test (3.14) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Waiting to run
Execution Tests / test (macos-latest) (push) Waiting to run
Execution Tests / test (ubuntu-latest) (push) Waiting to run
Execution Tests / test (windows-latest) (push) Waiting to run
Test server launches without errors / test (push) Waiting to run
Unit Tests / test (macos-latest) (push) Waiting to run
Unit Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (windows-2022) (push) Waiting to run
Some checks are pending
Python Linting / Run Ruff (push) Waiting to run
Python Linting / Run Pylint (push) Waiting to run
Build package / Build Test (3.10) (push) Waiting to run
Build package / Build Test (3.11) (push) Waiting to run
Build package / Build Test (3.12) (push) Waiting to run
Build package / Build Test (3.13) (push) Waiting to run
Build package / Build Test (3.14) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.10, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.11, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-stable (12.1, , linux, 3.12, [self-hosted Linux], stable) (push) Waiting to run
Full Comfy CI Workflow Runs / test-unix-nightly (12.1, , linux, 3.11, [self-hosted Linux], nightly) (push) Waiting to run
Execution Tests / test (macos-latest) (push) Waiting to run
Execution Tests / test (ubuntu-latest) (push) Waiting to run
Execution Tests / test (windows-latest) (push) Waiting to run
Test server launches without errors / test (push) Waiting to run
Unit Tests / test (macos-latest) (push) Waiting to run
Unit Tests / test (ubuntu-latest) (push) Waiting to run
Unit Tests / test (windows-2022) (push) Waiting to run
* feat(api-nodes): add TencentSmartTopology node * feat(api-nodes): enable TencentModelTo3DUV node * chore(Tencent endpoints): add "wait" to queued statuses
This commit is contained in:
parent
e544c65db9
commit
3b93d5d571
@ -66,13 +66,17 @@ class To3DProTaskQueryRequest(BaseModel):
|
|||||||
JobId: str = Field(...)
|
JobId: str = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class To3DUVFileInput(BaseModel):
|
class TaskFile3DInput(BaseModel):
|
||||||
Type: str = Field(..., description="File type: GLB, OBJ, or FBX")
|
Type: str = Field(..., description="File type: GLB, OBJ, or FBX")
|
||||||
Url: str = Field(...)
|
Url: str = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class To3DUVTaskRequest(BaseModel):
|
class To3DUVTaskRequest(BaseModel):
|
||||||
File: To3DUVFileInput = Field(...)
|
File: TaskFile3DInput = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class To3DPartTaskRequest(BaseModel):
|
||||||
|
File: TaskFile3DInput = Field(...)
|
||||||
|
|
||||||
|
|
||||||
class TextureEditImageInfo(BaseModel):
|
class TextureEditImageInfo(BaseModel):
|
||||||
@ -80,7 +84,13 @@ class TextureEditImageInfo(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class TextureEditTaskRequest(BaseModel):
|
class TextureEditTaskRequest(BaseModel):
|
||||||
File3D: To3DUVFileInput = Field(...)
|
File3D: TaskFile3DInput = Field(...)
|
||||||
Image: TextureEditImageInfo | None = Field(None)
|
Image: TextureEditImageInfo | None = Field(None)
|
||||||
Prompt: str | None = Field(None)
|
Prompt: str | None = Field(None)
|
||||||
EnablePBR: bool | None = Field(None)
|
EnablePBR: bool | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class SmartTopologyRequest(BaseModel):
|
||||||
|
File3D: TaskFile3DInput = Field(...)
|
||||||
|
PolygonType: str | None = Field(...)
|
||||||
|
FaceLevel: str | None = Field(...)
|
||||||
|
|||||||
@ -5,18 +5,19 @@ from comfy_api_nodes.apis.hunyuan3d import (
|
|||||||
Hunyuan3DViewImage,
|
Hunyuan3DViewImage,
|
||||||
InputGenerateType,
|
InputGenerateType,
|
||||||
ResultFile3D,
|
ResultFile3D,
|
||||||
|
SmartTopologyRequest,
|
||||||
|
TaskFile3DInput,
|
||||||
TextureEditTaskRequest,
|
TextureEditTaskRequest,
|
||||||
|
To3DPartTaskRequest,
|
||||||
To3DProTaskCreateResponse,
|
To3DProTaskCreateResponse,
|
||||||
To3DProTaskQueryRequest,
|
To3DProTaskQueryRequest,
|
||||||
To3DProTaskRequest,
|
To3DProTaskRequest,
|
||||||
To3DProTaskResultResponse,
|
To3DProTaskResultResponse,
|
||||||
To3DUVFileInput,
|
|
||||||
To3DUVTaskRequest,
|
To3DUVTaskRequest,
|
||||||
)
|
)
|
||||||
from comfy_api_nodes.util import (
|
from comfy_api_nodes.util import (
|
||||||
ApiEndpoint,
|
ApiEndpoint,
|
||||||
download_url_to_file_3d,
|
download_url_to_file_3d,
|
||||||
download_url_to_image_tensor,
|
|
||||||
downscale_image_tensor_by_max_side,
|
downscale_image_tensor_by_max_side,
|
||||||
poll_op,
|
poll_op,
|
||||||
sync_op,
|
sync_op,
|
||||||
@ -344,7 +345,6 @@ class TencentModelTo3DUVNode(IO.ComfyNode):
|
|||||||
outputs=[
|
outputs=[
|
||||||
IO.File3DOBJ.Output(display_name="OBJ"),
|
IO.File3DOBJ.Output(display_name="OBJ"),
|
||||||
IO.File3DFBX.Output(display_name="FBX"),
|
IO.File3DFBX.Output(display_name="FBX"),
|
||||||
IO.Image.Output(),
|
|
||||||
],
|
],
|
||||||
hidden=[
|
hidden=[
|
||||||
IO.Hidden.auth_token_comfy_org,
|
IO.Hidden.auth_token_comfy_org,
|
||||||
@ -375,7 +375,7 @@ class TencentModelTo3DUVNode(IO.ComfyNode):
|
|||||||
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-uv", method="POST"),
|
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-uv", method="POST"),
|
||||||
response_model=To3DProTaskCreateResponse,
|
response_model=To3DProTaskCreateResponse,
|
||||||
data=To3DUVTaskRequest(
|
data=To3DUVTaskRequest(
|
||||||
File=To3DUVFileInput(
|
File=TaskFile3DInput(
|
||||||
Type=file_format.upper(),
|
Type=file_format.upper(),
|
||||||
Url=await upload_3d_model_to_comfyapi(cls, model_3d, file_format),
|
Url=await upload_3d_model_to_comfyapi(cls, model_3d, file_format),
|
||||||
)
|
)
|
||||||
@ -394,7 +394,6 @@ class TencentModelTo3DUVNode(IO.ComfyNode):
|
|||||||
return IO.NodeOutput(
|
return IO.NodeOutput(
|
||||||
await download_url_to_file_3d(get_file_from_response(result.ResultFile3Ds, "obj").Url, "obj"),
|
await download_url_to_file_3d(get_file_from_response(result.ResultFile3Ds, "obj").Url, "obj"),
|
||||||
await download_url_to_file_3d(get_file_from_response(result.ResultFile3Ds, "fbx").Url, "fbx"),
|
await download_url_to_file_3d(get_file_from_response(result.ResultFile3Ds, "fbx").Url, "fbx"),
|
||||||
await download_url_to_image_tensor(get_file_from_response(result.ResultFile3Ds, "image").Url),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -463,7 +462,7 @@ class Tencent3DTextureEditNode(IO.ComfyNode):
|
|||||||
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-texture-edit", method="POST"),
|
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-texture-edit", method="POST"),
|
||||||
response_model=To3DProTaskCreateResponse,
|
response_model=To3DProTaskCreateResponse,
|
||||||
data=TextureEditTaskRequest(
|
data=TextureEditTaskRequest(
|
||||||
File3D=To3DUVFileInput(Type=file_format.upper(), Url=model_url),
|
File3D=TaskFile3DInput(Type=file_format.upper(), Url=model_url),
|
||||||
Prompt=prompt,
|
Prompt=prompt,
|
||||||
EnablePBR=True,
|
EnablePBR=True,
|
||||||
),
|
),
|
||||||
@ -538,8 +537,8 @@ class Tencent3DPartNode(IO.ComfyNode):
|
|||||||
cls,
|
cls,
|
||||||
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-part", method="POST"),
|
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-part", method="POST"),
|
||||||
response_model=To3DProTaskCreateResponse,
|
response_model=To3DProTaskCreateResponse,
|
||||||
data=To3DUVTaskRequest(
|
data=To3DPartTaskRequest(
|
||||||
File=To3DUVFileInput(Type=file_format.upper(), Url=model_url),
|
File=TaskFile3DInput(Type=file_format.upper(), Url=model_url),
|
||||||
),
|
),
|
||||||
is_rate_limited=_is_tencent_rate_limited,
|
is_rate_limited=_is_tencent_rate_limited,
|
||||||
)
|
)
|
||||||
@ -557,15 +556,107 @@ class Tencent3DPartNode(IO.ComfyNode):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TencentSmartTopologyNode(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="TencentSmartTopologyNode",
|
||||||
|
display_name="Hunyuan3D: Smart Topology",
|
||||||
|
category="api node/3d/Tencent",
|
||||||
|
description="Perform smart retopology on a 3D model. "
|
||||||
|
"Supports GLB/OBJ formats; max 200MB; recommended for high-poly models.",
|
||||||
|
inputs=[
|
||||||
|
IO.MultiType.Input(
|
||||||
|
"model_3d",
|
||||||
|
types=[IO.File3DGLB, IO.File3DOBJ, IO.File3DAny],
|
||||||
|
tooltip="Input 3D model (GLB or OBJ)",
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"polygon_type",
|
||||||
|
options=["triangle", "quadrilateral"],
|
||||||
|
tooltip="Surface composition type.",
|
||||||
|
),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"face_level",
|
||||||
|
options=["medium", "high", "low"],
|
||||||
|
tooltip="Polygon reduction level.",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed controls whether the node should re-run; "
|
||||||
|
"results are non-deterministic regardless of seed.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IO.File3DOBJ.Output(display_name="OBJ"),
|
||||||
|
],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(expr='{"type":"usd","usd":1.0}'),
|
||||||
|
)
|
||||||
|
|
||||||
|
SUPPORTED_FORMATS = {"glb", "obj"}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
model_3d: Types.File3D,
|
||||||
|
polygon_type: str,
|
||||||
|
face_level: str,
|
||||||
|
seed: int,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
_ = seed
|
||||||
|
file_format = model_3d.format.lower()
|
||||||
|
if file_format not in cls.SUPPORTED_FORMATS:
|
||||||
|
raise ValueError(
|
||||||
|
f"Unsupported file format: '{file_format}'. " f"Supported: {', '.join(sorted(cls.SUPPORTED_FORMATS))}."
|
||||||
|
)
|
||||||
|
model_url = await upload_3d_model_to_comfyapi(cls, model_3d, file_format)
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-smart-topology", method="POST"),
|
||||||
|
response_model=To3DProTaskCreateResponse,
|
||||||
|
data=SmartTopologyRequest(
|
||||||
|
File3D=TaskFile3DInput(Type=file_format.upper(), Url=model_url),
|
||||||
|
PolygonType=polygon_type,
|
||||||
|
FaceLevel=face_level,
|
||||||
|
),
|
||||||
|
is_rate_limited=_is_tencent_rate_limited,
|
||||||
|
)
|
||||||
|
if response.Error:
|
||||||
|
raise ValueError(f"Task creation failed: [{response.Error.Code}] {response.Error.Message}")
|
||||||
|
result = await poll_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path="/proxy/tencent/hunyuan/3d-smart-topology/query", method="POST"),
|
||||||
|
data=To3DProTaskQueryRequest(JobId=response.JobId),
|
||||||
|
response_model=To3DProTaskResultResponse,
|
||||||
|
status_extractor=lambda r: r.Status,
|
||||||
|
)
|
||||||
|
return IO.NodeOutput(
|
||||||
|
await download_url_to_file_3d(get_file_from_response(result.ResultFile3Ds, "obj").Url, "obj"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TencentHunyuan3DExtension(ComfyExtension):
|
class TencentHunyuan3DExtension(ComfyExtension):
|
||||||
@override
|
@override
|
||||||
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
|
||||||
return [
|
return [
|
||||||
TencentTextToModelNode,
|
TencentTextToModelNode,
|
||||||
TencentImageToModelNode,
|
TencentImageToModelNode,
|
||||||
# TencentModelTo3DUVNode,
|
TencentModelTo3DUVNode,
|
||||||
# Tencent3DTextureEditNode,
|
# Tencent3DTextureEditNode,
|
||||||
Tencent3DPartNode,
|
Tencent3DPartNode,
|
||||||
|
TencentSmartTopologyNode,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -83,7 +83,7 @@ class _PollUIState:
|
|||||||
_RETRY_STATUS = {408, 500, 502, 503, 504} # status 429 is handled separately
|
_RETRY_STATUS = {408, 500, 502, 503, 504} # status 429 is handled separately
|
||||||
COMPLETED_STATUSES = ["succeeded", "succeed", "success", "completed", "finished", "done", "complete"]
|
COMPLETED_STATUSES = ["succeeded", "succeed", "success", "completed", "finished", "done", "complete"]
|
||||||
FAILED_STATUSES = ["cancelled", "canceled", "canceling", "fail", "failed", "error"]
|
FAILED_STATUSES = ["cancelled", "canceled", "canceling", "fail", "failed", "error"]
|
||||||
QUEUED_STATUSES = ["created", "queued", "queueing", "submitted", "initializing"]
|
QUEUED_STATUSES = ["created", "queued", "queueing", "submitted", "initializing", "wait"]
|
||||||
|
|
||||||
|
|
||||||
async def sync_op(
|
async def sync_op(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user