Jedrzej KosinskiandAmp 01f71bd806 Add MODEL_3D input encoder to close the encode-side gap
Adds the symmetric client-side encoder for `File3D` values so a remote
node descriptor can declare a `MODEL_3D` input and consume a 3D mesh
from an upstream local node (e.g. `Load3D`, `TencentTextToModelNode`,
`MeshyTextToModelNode`).

Mirrors the existing AUDIO encoder (`encode_audio_input`,
`is_audio_input`) and VIDEO encoder (`encode_video_input`,
`is_video_input` — PR #19) added at
`comfy_remote_nodes/serialization.py`:

- `encode_model3d_input(file3d)` reads the primary mesh bytes via
  `File3D.get_data()` (returns a `BytesIO`, per
  `comfy_api.latest._util.geometry_types.File3D`) and emits a
  `{type: "model_3d", encoding: "glb_inline", data: b64, format: ...}`
  envelope. `encoding="glb_inline"` is the generic single-file inline
  encoding the server-side decoder (`decode_model3d_envelope`)
  already dispatches on; the decoder hands the bytes to
  `File3D(BytesIO(...), format)` regardless of whether the actual
  format is glb / obj / fbx / etc., so the encoding name is a
  logical "inline single-file 3D" tag and the `format` extra picks
  the file extension at decode time.
- `is_model3d_input(value)` duck-types on the `format` string
  attribute + `get_data` callable. Both are present on the upstream
  `File3D` class and the `BundledFile3D` subclass built lazily in
  `_bundled_file3d_class()`. Avoids importing `File3D` at module load
  (which would pull torch via the `comfy_api` tree).
- `_encode_one` in `proxy_node.py` dispatches AUDIO / VIDEO before
  MODEL_3D as defense-in-depth (the `VideoInput` shape — `save_to`
  + `get_duration` — does not expose a `format` string property, so
  VIDEO and MODEL_3D values don't collide either way).

Multi-file bundles (`BundledFile3D`-style values where every
companion file matters — e.g. an OBJ with sibling .mtl + texture
PNGs) are intentionally not supported on the encode path in this
PR: the first server consumer is the Tencent Hunyuan3D
MODEL_3D-input node family (`TencentModelTo3DUVNode` /
`Tencent3DPartNode` / `Tencent3DTextureEditNode` /
`TencentSmartTopologyNode`), and the upstream upload helper
`upload_3d_model_to_comfyapi(cls, model_3d, file_format)` only
uploads a single file (`model_3d.get_data()`). Bundle encode
(`bundle_inline`) lands when a partner node actually needs the
companion bytes.

This unblocks 4 Tencent Hunyuan3D nodes (out of 6 in the module)
that were deferred to a follow-up PR — see matrix coverage TODO #6.
First server consumers ship in a paired comfy-rnp-server PR
(TencentModelTo3DUVNode_RNP + Tencent3DPartNode_RNP).

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019e362f-db40-7338-8ca4-99f617d89812
2026-05-17 06:53:14 -07:00
2026-05-11 12:27:26 -07:00

comfy-remote-nodes

ComfyUI custom-node package that fetches node descriptors from an RNP/1 (Remote Node Protocol v1) server and exposes them as native V3 nodes inside ComfyUI.

The reference server lives at Comfy-Org/comfy-rnp-server (private). This client is intentionally provider-agnostic: any server that speaks RNP/1 can publish nodes through it.

Install

Clone into your ComfyUI custom_nodes/ directory:

cd ComfyUI/custom_nodes
git clone https://github.com/Comfy-Org/comfy-remote-nodes.git

(The directory name on disk doesn't matter — ComfyUI loads the package via spec_from_file_location, so comfy-remote-nodes/ is fine.)

Configure

Point the extension at one RNP server URL via env var before starting ComfyUI:

export RNP_SERVER_URL=http://127.0.0.1:9190
python main.py

If RNP_SERVER_URL is unset, the extension registers zero nodes and logs a single line — ComfyUI starts normally with no remote nodes.

What works today

  • Manifest + capability handshake
  • object_info fetch and dynamic V3 class generation (with ETag conditional GETs)
  • Sync execute and async execute_async + poll/cancel
  • Image / mask / audio / video envelope encode + decode
  • Presigned upload helper for >8 MiB inputs
  • Tier 3 (recent):
    • TASK_LOST detection with idempotent resubmission — survives an RNP server restart mid-task without losing the workflow
    • SERVER_BUSY / MAINTENANCE 503 envelopes treated as rate limits with Retry-After honoring and friendly per-second progress text ("Server busy, retrying in 30s...")
    • Out-of-band url_fetch for URI-only output envelopes, driven by the descriptor's remote.url_fetch policy
    • Per-poll auth refresh so long-running tasks can rotate tokens

What does not (yet)

  • validate_inputs / fingerprint_inputs / check_lazy_status — the server returns 501 NOT_IMPLEMENTED; the client falls through to ComfyUI's defaults
  • Multi-server config file (comfy_remote_nodes.yaml or similar) — today's setup is one server per ComfyUI process via env var
  • Streaming execute (server-sent events for incremental previews)
S
Description
No description provided
Readme GPL-3.0
243 KiB
Languages
Python 100%