Stop known-failing rig / retarget combinations before any request

Every option was re-run on OV and CN production (2026-10-15). Rig v1.0 rigs
bipeds only and fails with unity / unreal / vrm; v2.5 rejects humanoid
presets; v3.0 rejects quadruped / hexapod / octopod presets. The Rig and
Retarget nodes now stop with an explanation for those combinations (Rig
passes its effective version downstream in MODEL_INFO), and new Rig nodes
default to v3.0-20260909, which rigs every type and runs every humanoid preset.

Remove the Generate option v2.0-20240919: API v3 rejects it.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
PeakLee
2026-10-15 04:48:38 +08:00
co-authored by Cursor
parent 8ef06e08da
commit 02a91ef2f0
3 changed files with 72 additions and 19 deletions
+9 -5
View File
@@ -8,8 +8,9 @@ The Tripo v2 API is shut down on 2026-10-31. This release runs every node on Tri
### Compatibility
- Requires `tripo3d>=1.0.0,<2.0.0` (pinned in both `requirements.txt` and `pyproject.toml`).
- Saved workflows keep loading: no input name, input order or existing option was changed or removed;
new inputs were added at the end with defaults that keep the old behaviour. No node was removed.
- Saved workflows keep loading: no node, input name or input order was changed or removed; new inputs
were added at the end with defaults that keep the old behaviour. The only removed option is Generate
model `v2.0-20240919`, which API v3 rejects (a saved workflow using it is refused before any request).
- If an incompatible `tripo3d` is installed, ComfyUI still starts; the log and every Tripo node report
the `pip install "tripo3d>=1.0.0,<2.0.0"` command to run.
- 1.3.x of this extension also keeps working on `tripo3d` 1.x until you update it.
@@ -20,7 +21,8 @@ The Tripo v2 API is shut down on 2026-10-31. This release runs every node on Tri
- Generate model: `P1-20260311`, `P2-20260801`; `export_orientation`.
- Texture model: `v3.5-20260815`, `texture_quality=fast`, `delight` (auto / on / off).
- Rig model: `v2.5-20260210`, `v3.0-20260909`; specs `actorcore`, `unity`, `unreal`, `vrm`;
optional `rig_type` (auto runs the rig check first, as before).
optional `rig_type` (auto runs the rig check first, as before). `unity` / `unreal` / `vrm` need
`v2.5-20260210` or `v3.0-20260909`.
- Smart low poly: `P-v3.0-20260909` and `smart_uv`; `face_limit <= 0` uses the server default.
- Complete mesh: `completion_mode` (`ai_completion`, `quick_cap`).
- Convert model: `fbx_preset=bake_scale`.
@@ -28,8 +30,10 @@ The Tripo v2 API is shut down on 2026-10-31. This release runs every node on Tri
### Changed
- Import model sends `model_url` to the API instead of downloading it inside ComfyUI first.
- Rig model `v2.0-20250506` runs as `v2.5-20260210` (same as API v2). v2.5 rigs only accept animal
presets; new Rig nodes default to `v1.0-20240301`, which accepts the humanoid presets.
- Rig model `v2.0-20250506` runs as `v2.5-20260210` (same as API v2). New Rig nodes default to
`v3.0-20260909`. Combinations that fail upstream (v1.0 with a non-biped model; v2.5 with humanoid
presets; v3.0 with quadruped / hexapod / octopod presets; unity / unreal / vrm on v1.0) now stop with
an explanation before any request. Rig passes its version downstream in `MODEL_INFO`.
- `bake_animation` on Retarget is kept for saved workflows but ignored by API v3.
### Deprecated
+10 -3
View File
@@ -40,9 +40,16 @@ generated model is usually much larger, so connect it after **Tripo: Smart low p
`Generate model → Smart low poly → Smart UV → Preview 3D / Preview Image`.
### Rigging notes
`v2.0-20250506` runs as `v2.5-20260210`, exactly as on API v2. v2.5 rigs only accept the animal presets
(`preset:quadruped:walk`, `preset:hexapod:walk`, `preset:octopod:walk`, `preset:serpentine:march`,
`preset:aquatic:march`); use `v1.0-20240301` (the default for new nodes) for humanoid presets.
Measured on the Tripo API (2026-10-15). New Rig nodes default to `v3.0-20260909`.
| Rig version | Skeleton types it can rig | Humanoid presets | serpentine / aquatic presets | quadruped / hexapod / octopod presets |
| --- | --- | --- | --- | --- |
| `v3.0-20260909` | all | ✓ | ✓ | ✗ |
| `v2.5-20260210` (and `v2.0-20250506`) | all | ✗ | ✓ | ✓ |
| `v1.0-20240301` | biped only | ✓ | — | — |
`unity`, `unreal` and `vrm` specs need `v2.5-20260210` or `v3.0-20260909`. The Rig and Retarget nodes stop
with an explanation, before sending anything, when a combination in the table is known to fail.
## Installation
### [method1] From Source
+53 -11
View File
@@ -153,7 +153,7 @@ class TripoAPIDraft:
"image_left": ("IMAGE",),
"image_back": ("IMAGE",),
"image_right": ("IMAGE",),
"model_version": (["v1.4-20240625", "v2.0-20240919", "v2.5-20250123", "v3.0-20250812", "v3.1-20260211", "P1-20260311", "P2-20260801"], {"default": "v3.1-20260211", "tooltip": "v2.0-20240919 is no longer available on Tripo API v3."}),
"model_version": (["v1.4-20240625", "v2.5-20250123", "v3.0-20250812", "v3.1-20260211", "P1-20260311", "P2-20260801"], {"default": "v3.1-20260211"}),
"texture": ("BOOLEAN", {"default": True}),
"pbr": ("BOOLEAN", {"default": True}),
"image_seed": ("INT", {"default": 42}),
@@ -394,15 +394,40 @@ class TripoRefineModel:
async def generate_mesh(self, model_info):
raise RuntimeError(RETIRED_NODE_MESSAGE.format(name="Tripo: Refine Draft model"))
# Rig / retarget combinations measured on the live API (OV + CN, 2026-10-15). Combinations listed
# here fail upstream, so the nodes stop before sending anything; everything else is passed through.
SPECS_NEEDING_NEWER_RIG = {"unity", "unreal", "vrm"} # fail on rig v1.0-20240301
RIG_VERSIONS_FOR_NEWER_SPECS = {"v2.0-20250506", "v2.5-20260210", "v3.0-20260909"}
RIG_V1_BIPED_ONLY = "v1.0-20240301" # v1.0 rigs every non-biped type with 1004
EFFECTIVE_RIG_VERSION = {"v2.0-20250506": "v2.5-20260210"} # what the SDK actually sends
RETARGET_PRESETS_FAILING_ON_RIG = {
"v2.5-20260210": "humanoid", # every preset without a skeleton prefix
"v3.0-20260909": {"preset:quadruped:walk", "preset:hexapod:walk", "preset:octopod:walk"},
}
def _is_humanoid_preset(animation):
return animation.startswith("preset:") and animation.count(":") == 1
def retarget_blocked_reason(rig_version, animation):
rule = RETARGET_PRESETS_FAILING_ON_RIG.get(rig_version)
if rule == "humanoid" and _is_humanoid_preset(animation):
return f"humanoid preset '{animation}' does not work on a {rig_version} rig; rig with v3.0-20260909 or v1.0-20240301"
if isinstance(rule, set) and animation in rule:
return f"preset '{animation}' does not work on a {rig_version} rig; rig this model with v2.5-20260210"
return None
class TripoAnimateRigNode:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model_info": ("MODEL_INFO",),
"model_version": (["v1.0-20240301", "v2.0-20250506", "v2.5-20260210", "v3.0-20260909"], {"default": "v1.0-20240301", "tooltip": "v2.0-20250506 runs as v2.5-20260210. v2.5 rigs only retarget animal presets (quadruped/hexapod/octopod/serpentine/aquatic); use v1.0-20240301 for humanoid presets."}),
"model_version": (["v1.0-20240301", "v2.0-20250506", "v2.5-20260210", "v3.0-20260909"], {"default": "v3.0-20260909", "tooltip": "v3.0: every skeleton type and every preset except quadruped/hexapod/octopod walk. v2.5: every skeleton type, animal presets only. v1.0: biped only. v2.0-20250506 runs as v2.5-20260210."}),
"out_format": (["glb", "fbx"], {"default": "glb"}),
"spec": (["mixamo", "tripo", "actorcore", "unity", "unreal", "vrm"], {"default": "tripo"}),
"spec": (["mixamo", "tripo", "actorcore", "unity", "unreal", "vrm"], {"default": "tripo", "tooltip": "unity / unreal / vrm need model_version v2.5-20260210 or v3.0-20260909."}),
},
"optional": {
"rig_type": (["auto", "biped", "quadruped", "hexapod", "octopod", "avian", "serpentine", "aquatic", "others"], {"default": "auto", "tooltip": "auto detects the skeleton with a rig check first."}),
@@ -415,6 +440,12 @@ class TripoAnimateRigNode:
CATEGORY = "TripoAPI"
async def generate_mesh(self, model_info, model_version, out_format, spec, rig_type="auto"):
if spec in SPECS_NEEDING_NEWER_RIG and model_version not in RIG_VERSIONS_FOR_NEWER_SPECS:
raise RuntimeError(f"spec '{spec}' needs model_version v2.5-20260210 or v3.0-20260909 "
f"(got {model_version}); no request was sent.")
if model_version == RIG_V1_BIPED_ONLY and rig_type not in (None, "", "auto", "biped"):
raise RuntimeError(f"rig {RIG_V1_BIPED_ONLY} only supports biped models (rig_type '{rig_type}'); "
"use v3.0-20260909 or v2.5-20260210. No request was sent.")
client, key = await GetTripoAPI(model_info["apikey"])
if not rig_type or rig_type == "auto":
@@ -430,6 +461,10 @@ class TripoAnimateRigNode:
if not rig_type:
raise RuntimeError("No suitable rig type found for the model")
if model_version == RIG_V1_BIPED_ONLY and rig_type != "biped":
raise RuntimeError(f"rig {RIG_V1_BIPED_ONLY} only supports biped models (this one is '{rig_type}'); "
"use v3.0-20260909 or v2.5-20260210. No rig task was sent.")
task_id = await client.rig_model(
original_model_task_id=model_info["task_id"],
out_format=out_format,
@@ -447,7 +482,8 @@ class TripoAnimateRigNode:
"task_id": task_id,
"apikey": key,
"file_prefix": model_info["file_prefix"],
"output_directory": model_info["output_directory"]
"output_directory": model_info["output_directory"],
"rig_model_version": EFFECTIVE_RIG_VERSION.get(model_version, model_version),
}
else:
raise RuntimeError(f"Failed to generate mesh: {task.error_code} {task.error_msg if hasattr(task, 'error_msg') else ''}")
@@ -490,6 +526,10 @@ class TripoAnimateRetargetNode:
CATEGORY = "TripoAPI"
async def generate_mesh(self, model_info, animation, out_format, bake_animation=True, export_with_geometry=False):
rig_version = model_info.get("rig_model_version")
blocked = retarget_blocked_reason(rig_version, animation) if rig_version else None
if blocked:
raise RuntimeError(blocked + ". No request was sent.")
client, key = await GetTripoAPI(model_info["apikey"])
task_id = await client.retarget_animation(
@@ -504,13 +544,15 @@ class TripoAnimateRetargetNode:
downloaded = await client.download_task_models(task, get_output_directory())
model_file = next(iter(downloaded.values()))
print(f"model_file: {model_file}")
return rename_model(model_file, model_info["file_prefix"], model_info["output_directory"]), \
{
"task_id": task_id,
"apikey": key,
"file_prefix": model_info["file_prefix"],
"output_directory": model_info["output_directory"]
}
info = {
"task_id": task_id,
"apikey": key,
"file_prefix": model_info["file_prefix"],
"output_directory": model_info["output_directory"],
}
if rig_version:
info["rig_model_version"] = rig_version
return rename_model(model_file, model_info["file_prefix"], model_info["output_directory"]), info
else:
raise RuntimeError(f"Failed to generate mesh: {task.error_code} {task.error_msg if hasattr(task, 'error_msg') else ''}")