Jedrzej KosinskiandAmp 35b8a3b17f feat(serialization): tighten batch dispatch + accept response-side batch URIs
Three follow-ups identified in the PR-2 / PR-3 review pass:

1. ``serialization.maybe_externalize`` now keys batch dispatch off
   the ``encoding`` string (``png_base64_batch``) instead of the
   presence of a ``frames`` field. The old ``isinstance(frames, list)
   and frames`` heuristic would silently take the batch branch on a
   malformed envelope that mixed both fields and externalise garbage
   under a non-batch encoding.

2. An empty / missing ``frames`` list on a ``png_base64_batch``
   envelope now raises ``RnpProtocolError(INTERNAL)`` rather than
   silently no-op-ing through to the wire (where the server's
   ``resolve_image_envelope_pngs`` rejects it after the round trip
   anyway). Mirrors the server-side ``make_image_batch_envelope``
   contract: an empty batch is a hard wire bug.

3. ``proxy_node._deserialize_output`` now accepts the externalised
   ``png_base64_batch_uri`` shape on the response path. No server
   emits this shape today (future-proofing only — the symmetric
   counterpart of the request-side externalisation), but the decoder
   owns the symmetry: any envelope shape the client may externalise
   on the way out, the client must also accept on the way in.
   Per-frame URIs are fetched in parallel via ``asyncio.gather`` so
   a 9-frame batch import is one round-trip rather than N. Frame
   ordering is preserved (gather returns positional results) — the
   IMAGE tensor's batch axis carries semantic order. Fetched bytes
   are re-stamped as inline ``frames`` so the existing
   ``_decode_image_batch_envelope`` path handles them uniformly.

notes/run_batch_uri_followups.py — new standalone smoke test that
exercises all three changes (encoding-keyed dispatch, empty-frames
hard-error, response-side batch URI parallel fetch + ordering).

Amp-Thread-ID: https://ampcode.com/threads/T-019e2ce6-9403-720c-ae5b-1597b8068be7
Co-authored-by: Amp <amp@ampcode.com>
2026-05-15 12:05:58 -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%