Mirrors the AUDIO sweep: adds encode_video_input / is_video_input
helpers in serialization.py, wires them into proxy_node._encode_one,
and advertises VIDEO_MP4_BASE64 in CLIENT_CAPABILITIES so servers can
require it for VIDEO inputs.
encode_video_input re-encodes through video.save_to(buf, MP4, H264) to
normalize the container — Bria's video endpoints (the first vendor with
VIDEO inputs) only accept mp4_h264. Populates an optional duration_s
extra from get_duration() so servers can pre-validate against duration
bounds without demuxing.
Amp-Thread-ID: https://ampcode.com/threads/T-019e210b-f792-7389-b8db-8e02c81e4295
Co-authored-by: Amp <amp@ampcode.com>
Wire-format rework — pairs with comfy-rnp-server branch
feat/async-polling-helper which dropped descriptor.remote.execution.
max_poll_attempts. The client now derives its poll-loop cap as
ceil((hard_timeout_s + DEFAULT_POLL_GRACE_S) / poll_interval_s)
inside _extract_execution_policy.
Why: coupling a contract bound to a cadence value silently breaks
every time you tune the cadence. A descriptor saying '150 attempts'
shrinks 4x if poll_interval_s drops 2.0 -> 0.5; expressing the
bound as a duration (hard_timeout_s) keeps it invariant. Industry
precedent: OAuth2 device flow §3.5, Kubernetes --timeout, gRPC
deadlines all express long-poll bounds as durations rather than
counts.
Changes:
- protocol.py: drop DEFAULT_MAX_POLL_ATTEMPTS, add
DEFAULT_SOFT_TIMEOUT_S / DEFAULT_HARD_TIMEOUT_S /
DEFAULT_POLL_GRACE_S.
- proxy_node.py: import math, derive max_poll_attempts inline in
_extract_execution_policy from hard_timeout_s + grace /
poll_interval_s. Falls back to soft_timeout_s when
hard_timeout_s is absent, then to DEFAULT_HARD_TIMEOUT_S.
No compat shim — RNP/1 is prototype-stage with zero backward-
compat obligation (see comfy-rnp-server/AGENTS.md). Server and
client roll in lockstep.
Verified end-to-end with notes/run_url_fetch_executor.py,
notes/run_recraft_custom_io_executor.py, and the paid
notes/run_recraft_text_to_image_smoke.py (~\.04) after
restarting both processes with the new code on both sides.
Amp-Thread-ID: https://ampcode.com/threads/T-019e1f96-a6f7-729b-9526-82fcebee2e17
Co-authored-by: Amp <amp@ampcode.com>
* proxy_node.py: gate `_encode_one` dict-recursion on
`_dict_contains_tensor` (new helper) + `not is_envelope` so scalar
custom-IO chain dicts (RECRAFT_V3_STYLE / RECRAFT_CONTROLS) pass
through untouched and already-encoded envelopes don't get re-traversed
* proxy_node.py: AUTOGROW parser validates `template_spec[0]` is a
string and emits `log.warning` on every parse-failure path so a
malformed descriptor surfaces visibly instead of producing a missing
input
* serialization.py: `decode_svg_envelope` now splits on
`</svg>\n` (with bare `</svg>` fallback) so an SVG document
containing `</svg>` in CDATA / `<foreignObject>` / a comment
isn't shredded mid-frame; matches the `\n` join the server uses
in `_emit_svg_from_urls`
Amp-Thread-ID: https://ampcode.com/threads/T-019e19fc-748d-76fb-b8fa-00efd7c2d697
Co-authored-by: Amp <amp@ampcode.com>
Three additive parser changes that unblock bulk porting of partner
nodes from comfy_api_nodes/nodes_*.py without breaking existing
descriptors. Spec: notes/rnp-phaseA-spec.md.
A1 — Custom IO pass-through: any unrecognised string io_type now
builds an opaque IO.Custom socket instead of being rejected. Lets
partner-defined types like RECRAFT_STYLE_V3, RECRAFT_COLOR,
Tripo3DModel, Hunyuan3DMesh round-trip between RNP nodes verbatim.
Applies to both inputs (_parse_input_spec) and outputs
(_parse_outputs).
A2 — control_after_generate round-trip: forwarded into IO.Int.Input
and IO.Combo.Input from the input options dict so seed widgets keep
their randomize/increment/fixed dropdown.
A4 — remote.hidden_forwarded opt-in: non-auth hidden markers
(prompt, extra_pnginfo, dynprompt) now only forward into the request
context when explicitly listed in descriptor.remote.hidden_forwarded.
Default = drop. Auth tokens (auth_token_comfy_org, api_key_comfy_org)
and unique_id are always forwarded when declared. Stops large
EXTRA_PNGINFO blobs from shipping unconditionally.
Amp-Thread-ID: https://ampcode.com/threads/T-019e1889-d8bd-732f-8170-b85fd94da503
Co-authored-by: Amp <amp@ampcode.com>
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.
Includes:
- Manifest + capability handshake, ETag-conditional object_info fetch
- Sync execute_remote and async _run_async_polling flows via
comfy_api_nodes.util.client.poll_op_raw
- Image / mask / audio / video envelope encode + decode
- Presigned upload helper for >8 MiB inputs
- Tier 3:
* TASK_LOST detection + idempotent resume — survives an RNP server
restart mid-task
* SERVER_BUSY / MAINTENANCE 503 envelopes treated as rate limits
with Retry-After honoring and friendly per-second progress text
* Out-of-band url_fetch for URI-only output envelopes per the
descriptor's remote.url_fetch policy
* Per-poll auth refresh so long-running tasks pick up rotated tokens
Pairs with Comfy-Org/comfy-rnp-server (private) as the reference
server implementation.