[PR #168] fix(models,transport): read the Idempotent-Replayed header and lift the run timeout above Router's deadline #168

Open
opened 2026-10-31 22:01:58 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/Comfy-Org/comfy-python-sdk/pull/168
Author: @mattmillerai
Created: 9/18/2026
Status: 🔄 Open

Base: main ← Head: matt/be-15641-replayed-header-and-run-timeout


📝 Commits (10+)

  • fff2165 fix(models): read the Idempotent-Replayed header the Router contract declares
  • 5ec2ad1 fix(transport): raise the model-run timeout above Router's deadline
  • f6865ac fix(models,transport): case-insensitive run-result headers, spec-pinned names, sane write bound
  • 3e3cb7a merge main: reconcile Idempotent-Replayed/timeout lift with credits_used header
  • ef02cd3 docs(transport): stop the pool/write timeout prose contradicting itself
  • fe40327 Merge remote-tracking branch 'origin/main' into matt/be-15641-replayed-header-and-run-timeout
  • 501bbfd docs(changelog): record the run timeout lift and case-insensitive run headers
  • 449c171 Merge remote-tracking branch 'origin/main' into matt/be-15641-replayed-header-and-run-timeout
  • 40add95 Merge remote-tracking branch 'origin/main' into matt/be-15641-replayed-header-and-run-timeout
  • 0a962b8 fix(retry): reserve the run's pool wait outside the collect budget

📊 Changes

8 files changed (+355 additions, -44 deletions)

View changed files

📝 CHANGELOG.md (+17 -0)
📝 README.md (+13 -9)
📝 src/comfy_low/transport.py (+70 -2)
📝 src/comfy_sdk/models.py (+48 -8)
📝 src/comfy_sdk/retry.py (+35 -13)
📝 tests/test_models_run.py (+51 -1)
📝 tests/test_models_run_retry.py (+64 -9)
📝 tests/test_router_spec_contract.py (+57 -2)

📄 Description

ELI5

When a model run takes as long as the server allows, the server sends back a proper "ran out of time" answer that says which request it was and when to try again. The SDK used to give up waiting at exactly the same moment, so callers could get a bare timeout and lose that information about a generation that may already be billed. The SDK now waits one minute longer than the server's limit, it also reads the server's reply details reliably however their names are capitalised, and it keeps its automatic "collect the finished result" retry available even for a run that first had to wait for a free connection.

What changed

Update after merging main: main has since landed the same Idempotent-Replayed spelling fix independently (#166), and the credits-header work (X-Comfy-Credits-Used) has landed too. Against current main, this PR's net change is: the run-timeout lift (item 2), the named header constants plus httpx.Headers normalization in _run_result, and the source-table contract pins in tests/test_router_spec_contract.py, which now sit alongside main's field-level lift pins rather than replacing them.

1. replayed reads the header the contract declares. RouterRunResult._run_result now reads Idempotent-Replayed instead of X-Comfy-Idempotent-Replayed. The vendored contract confirms the name: spec/router-openapi.yaml defines the RouterIdempotentReplayedHeader component and references it as Idempotent-Replayed on every replayable response (the 200, and the recorded 4xx/5xx shapes). The field's own docstring carried the same wrong prefix and is corrected too. Headers arrive as httpx.Headers (case-insensitive), so the defect was purely the wrong name, not casing.

  • Tests: the existing unit test is re-pointed at the contract name (Idempotent-Replayed) rather than a literal copied from the implementation — a literal repeated from the code is what let the bug pass review in the first place. A new end-to-end test (test_run_detailed_reports_a_replay_end_to_end_against_the_server) drives a real replay through the fake server, which replies with the actual Idempotent-Replayed: true header, and asserts run_detailed(...).replayed is True. Reverting only the code line makes both of these fail (confirmed locally), so they genuinely guard the fix rather than restating it.

2. Client run timeout now sits above Router's deadline. MODEL_RUN_TIMEOUT is no longer a bare 600.0. A named ROUTER_DEADLINE = 600.0 constant mirrors Router's server-side DefaultDeadline, and the client read timeout is derived as ROUTER_DEADLINE + 60s of headroom — enough to receive the 504 and its headers instead of racing it. The coupling is documented at the constant ("if you raise Router's deadline, raise this too"). connect stays 10s.

  • The retry test test_the_collect_budget_outlasts_a_server_deadline_window previously used MODEL_RUN_TIMEOUT.read as its stand-in for "the server's deadline window" — that only worked because the two values were mistakenly equal, which is defect 2 itself. It now sources the window from ROUTER_DEADLINE and additionally asserts the new invariant MODEL_RUN_TIMEOUT.read > ROUTER_DEADLINE. The other timeout tests (>= 120s read, <= 30s connect; the literal 600.0 server-deadline advances in the fake-clock tests) remain correct because Router's deadline itself is unchanged.

Scope swept

Both defects are fully addressed in this repo. Sweep of the portion not being changed: grep across src/ found exactly three occurrences of the replayed-header spelling — the code read (fixed), the field docstring (fixed), and one prose reference at models.py:436 that was already correct — and no other reader of the header anywhere in src/. All MODEL_RUN_TIMEOUT consumers were checked; none assume the literal 600 except the one retry test corrected here.

Residual

  • Credits-header work has landed on main. X-Comfy-Credits-Used is now one of the five constants in _RUN_RESULT_HEADERS, and its name is pinned against spec/router-openapi.yaml by both this PR's source-table test and main's field-level lift test.
  • Cross-repo artifacts not exercised (no access from this environment). The originating report cites two files that live in the Router service repo, not here: router-reference.mdx (the header docs) and routerdeadline/routerdeadline.go (DefaultDeadline = 10 * time.Minute). I could not open either. I verified the header name against this repo's vendored spec/router-openapi.yaml (synced from that service) instead, and encoded the 10-minute deadline as ROUTER_DEADLINE = 600.0 with a comment pointing at the Go constant; if Router's deadline is ever raised, this default must be raised to match.
  • Deliberately did not read COMFY_ROUTER_DEADLINE in the client. The report floated deriving the timeout from that env var. I did not, because it configures the Router deployment's environment, not the caller's — a same-named client-side variable would be a misleading second source of truth that silently disagrees with the server it is meant to track. The client-side mitigation is the named-and-documented ROUTER_DEADLINE constant plus headroom, which is the "at minimum, comment the relationship" path.

Review round — cursor-review panel

Five threads, all resolved. Three fixes, one partial, one answered without a code change.

Fixed — _run_result reads headers case-insensitively. It is annotated Mapping[str, str] and all the names it reads are mixed-case, so it worked only because post_model_run happens to hand it an httpx.Headers. Any plain mapping — dict(resp.headers), which httpx lowercases — would have missed all of them and yielded replayed=False and request_id=None without raising, which is the same silent shape as the wrong header name this PR fixes. It now normalizes through httpx.Headers(headers) first.

Fixed — the header names are pinned to the contract. The names (now five, including X-Comfy-Credits-Used) are constants (_RUN_RESULT_HEADERS) and tests/test_router_spec_contract.py::test_the_headers_a_run_result_reads_are_the_spec_s pins each against spec/router-openapi.yaml's runRouterModel 200, parametrized per name, reached by searching for the operationId rather than looking up the path. The assertion is one-way (everything read is declared) because the 200 also carries headers RouterRunResult does not surface. Plus test_the_replayed_header_carries_no_x_comfy_prefix as a named regression case. This is the check whose absence let the prefixed name through.

Fixed — docs that asserted the old equality. Decoupling the client timeout from Router's deadline falsified prose saying the 504 arrives at "the same ten minutes as MODEL_RUN_TIMEOUT". retry.py's module docstring and collect_max_elapsed doc, models.py's run docstring, and README.md now point at ROUTER_DEADLINE — the constant that is actually ten minutes and the one collect_max_elapsed's "two windows" derivation depends on.

Partial — the positional httpx.Timeout argument. Correct that one positional value set write and pool as well as read. write is fixed: pushing a request body has no business inheriting an eleven-minute generation wait, so it is now 120s (still generous for a multi-megabyte inline base64 image, which httpx hands to the transport as a single write). pool is deliberately kept generation-scale, because on this route the pool is full of generations: a run is awaited server-side, so all 100 of httpx's default connections are held for up to ROUTER_DEADLINE, and the 101st caller is queued behind a generation rather than a handshake. A short pool bound would convert a legitimate fan-out from a queue that drains into a wall of PoolTimeout — 150 concurrent 2-minute runs succeed today and would fail inside ~70s under a 10s bound plus the 60s max_elapsed. The accepted cost is written on the constant: in the pathological case where the pool never frees, the caller waits eleven minutes and PoolTimeout surfaces without a retry, its fast-class budget long spent. All four bounds are now named explicitly so none is inherited again, and test_every_bound_of_the_run_timeout_is_chosen_rather_than_inherited asserts the trade.

Answered, no code change — a client-side COMFY_ROUTER_DEADLINE. Already covered under Residual below; it configures Router's environment, not the caller's, and a same-named client variable would be a second source of truth that can silently disagree with the server it tracks. Additionally, this repo's env idiom is per-construction (_resolve_router_base_url() at client.py:250/:414), not import-time, so doing it properly means threading a resolved value through every timeout=MODEL_RUN_TIMEOUT default — a design change, not an override. The supported lever for a longer-deadline deployment is the public run(..., timeout=...).

Deferred to a tracked follow-up. Comfy/AsyncComfy expose no limits=, so a caller cannot raise max_connections — the real lever for a fan-out that should not queue at all. New public API on both client surfaces, out of scope for a defect fix; proposed as a follow-up rather than dropped.

Review round — collect budget after a pool wait

Fixed — the collect budget now reserves the pool wait. The collect budget's clock starts before a run waits for a pooled connection, but Router's deadline only starts once the request is sent. A run queued for most of the 660s pool bound and then held to the full 600s deadline received its 504 deadline_exceeded after the 1200s budget had closed, so it was raised instead of collected. The default RetryPolicy.collect_max_elapsed is now derived as the pool bound plus two ROUTER_DEADLINE windows (1860s); an explicitly set value keeps its meaning. test_a_deadline_504_after_a_long_pool_wait_still_gets_a_collect_attempt pins the sequence on a fake clock and fails against the old default. README, CHANGELOG and docstrings now say thirty-one minutes.

Provenance

  • Authored by: agent-work loop
  • Verified:
    • Full suite at the current head: 1286 passed, 9 skipped.
    • ruff check ., ruff format --check ., mypy src and scripts/check_public_repo_hygiene.py: clean.
    • The new pool-wait collect test fails with the default put back to 1200s and passes with the derived 1860s.
    • Earlier rounds checked each guard by reverting its fix: dropping the httpx.Headers normalization, re-adding the X-Comfy- prefix, or putting read back at 600 each makes its test fail.
  • Deviations:
    • MODEL_RUN_TIMEOUT.pool stays generation-scale rather than shortened, so fan-out past 100 concurrent runs still works; the longer default collect budget covers that wait instead.
    • COMFY_ROUTER_DEADLINE is not read client-side (see Residual), and the limits= passthrough on the clients was deferred to a follow-up.
    • Merge-conflict resolution kept both main's lift pins and this PR's source-table checks, and kept both CHANGELOG entries.

Summary by CodeRabbit

  • Bug Fixes

    • Model runs now allow Router’s ten-minute deadline to expire before the client read timeout, so Router timeout responses can surface.
    • Response metadata is parsed consistently regardless of HTTP header capitalization.
    • Replay information is preserved across failed runs and retries without starting duplicate generations.
    • Replay metadata uses the unprefixed Idempotent-Replayed response header.
  • Documentation

    • Clarified model-run and retry timeout behavior: read and pool timeouts include one minute beyond Router’s deadline, write timeout is 120 seconds, and connect timeout is 10 seconds.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/Comfy-Org/comfy-python-sdk/pull/168 **Author:** [@mattmillerai](https://github.com/mattmillerai) **Created:** 9/18/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `matt/be-15641-replayed-header-and-run-timeout` --- ### 📝 Commits (10+) - [`fff2165`](https://github.com/Comfy-Org/comfy-python-sdk/commit/fff216542d7729bd6be1bf5e774dc2b02a304517) fix(models): read the Idempotent-Replayed header the Router contract declares - [`5ec2ad1`](https://github.com/Comfy-Org/comfy-python-sdk/commit/5ec2ad10b41b32869893cab6ad5c51aaf1316608) fix(transport): raise the model-run timeout above Router's deadline - [`f6865ac`](https://github.com/Comfy-Org/comfy-python-sdk/commit/f6865acedfd8edadba322979274b40ed710abb92) fix(models,transport): case-insensitive run-result headers, spec-pinned names, sane write bound - [`3e3cb7a`](https://github.com/Comfy-Org/comfy-python-sdk/commit/3e3cb7a4ca2ab9f361b3526ee1fef3e738fdfbb3) merge main: reconcile Idempotent-Replayed/timeout lift with credits_used header - [`ef02cd3`](https://github.com/Comfy-Org/comfy-python-sdk/commit/ef02cd337a47ff727c505bb67090c5936bb42a25) docs(transport): stop the pool/write timeout prose contradicting itself - [`fe40327`](https://github.com/Comfy-Org/comfy-python-sdk/commit/fe403273583ba6609ed5f8bd694ac5b725bc687d) Merge remote-tracking branch 'origin/main' into matt/be-15641-replayed-header-and-run-timeout - [`501bbfd`](https://github.com/Comfy-Org/comfy-python-sdk/commit/501bbfd50853472ec674febb00e6fd1e30784217) docs(changelog): record the run timeout lift and case-insensitive run headers - [`449c171`](https://github.com/Comfy-Org/comfy-python-sdk/commit/449c17187e9a8f935545db8b3a02fcfdf17a9a66) Merge remote-tracking branch 'origin/main' into matt/be-15641-replayed-header-and-run-timeout - [`40add95`](https://github.com/Comfy-Org/comfy-python-sdk/commit/40add95ee5ceffd0538e0202803810c79f51dd7a) Merge remote-tracking branch 'origin/main' into matt/be-15641-replayed-header-and-run-timeout - [`0a962b8`](https://github.com/Comfy-Org/comfy-python-sdk/commit/0a962b854aa2ccfadea070a0f3c7f94bb483625f) fix(retry): reserve the run's pool wait outside the collect budget ### 📊 Changes **8 files changed** (+355 additions, -44 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+17 -0) 📝 `README.md` (+13 -9) 📝 `src/comfy_low/transport.py` (+70 -2) 📝 `src/comfy_sdk/models.py` (+48 -8) 📝 `src/comfy_sdk/retry.py` (+35 -13) 📝 `tests/test_models_run.py` (+51 -1) 📝 `tests/test_models_run_retry.py` (+64 -9) 📝 `tests/test_router_spec_contract.py` (+57 -2) </details> ### 📄 Description ## ELI5 When a model run takes as long as the server allows, the server sends back a proper "ran out of time" answer that says which request it was and when to try again. The SDK used to give up waiting at exactly the same moment, so callers could get a bare timeout and lose that information about a generation that may already be billed. The SDK now waits one minute longer than the server's limit, it also reads the server's reply details reliably however their names are capitalised, and it keeps its automatic "collect the finished result" retry available even for a run that first had to wait for a free connection. ## What changed > **Update after merging `main`:** `main` has since landed the same `Idempotent-Replayed` spelling fix independently (#166), and the credits-header work (`X-Comfy-Credits-Used`) has landed too. Against current `main`, this PR's net change is: the run-timeout lift (item 2), the named header constants plus `httpx.Headers` normalization in `_run_result`, and the source-table contract pins in `tests/test_router_spec_contract.py`, which now sit alongside `main`'s field-level lift pins rather than replacing them. **1. `replayed` reads the header the contract declares.** `RouterRunResult._run_result` now reads `Idempotent-Replayed` instead of `X-Comfy-Idempotent-Replayed`. The vendored contract confirms the name: `spec/router-openapi.yaml` defines the `RouterIdempotentReplayedHeader` component and references it as `Idempotent-Replayed` on every replayable response (the `200`, and the recorded `4xx`/`5xx` shapes). The field's own docstring carried the same wrong prefix and is corrected too. Headers arrive as `httpx.Headers` (case-insensitive), so the defect was purely the wrong name, not casing. - Tests: the existing unit test is re-pointed at the contract name (`Idempotent-Replayed`) rather than a literal copied from the implementation — a literal repeated from the code is what let the bug pass review in the first place. A new end-to-end test (`test_run_detailed_reports_a_replay_end_to_end_against_the_server`) drives a real replay through the fake server, which replies with the actual `Idempotent-Replayed: true` header, and asserts `run_detailed(...).replayed is True`. Reverting only the code line makes **both** of these fail (confirmed locally), so they genuinely guard the fix rather than restating it. **2. Client run timeout now sits above Router's deadline.** `MODEL_RUN_TIMEOUT` is no longer a bare `600.0`. A named `ROUTER_DEADLINE = 600.0` constant mirrors Router's server-side `DefaultDeadline`, and the client read timeout is derived as `ROUTER_DEADLINE + 60s` of headroom — enough to receive the `504` and its headers instead of racing it. The coupling is documented at the constant ("if you raise Router's deadline, raise this too"). `connect` stays 10s. - The retry test `test_the_collect_budget_outlasts_a_server_deadline_window` previously used `MODEL_RUN_TIMEOUT.read` as its stand-in for "the server's deadline window" — that only worked because the two values were mistakenly equal, which is defect 2 itself. It now sources the window from `ROUTER_DEADLINE` and additionally asserts the new invariant `MODEL_RUN_TIMEOUT.read > ROUTER_DEADLINE`. The other timeout tests (`>= 120s` read, `<= 30s` connect; the literal `600.0` server-deadline advances in the fake-clock tests) remain correct because Router's deadline itself is unchanged. ## Scope swept Both defects are fully addressed in this repo. Sweep of the portion not being changed: `grep` across `src/` found exactly three occurrences of the replayed-header spelling — the code read (fixed), the field docstring (fixed), and one prose reference at `models.py:436` that was already correct — and no other reader of the header anywhere in `src/`. All `MODEL_RUN_TIMEOUT` consumers were checked; none assume the literal `600` except the one retry test corrected here. ## Residual - **Credits-header work has landed on `main`.** `X-Comfy-Credits-Used` is now one of the five constants in `_RUN_RESULT_HEADERS`, and its name is pinned against `spec/router-openapi.yaml` by both this PR's source-table test and `main`'s field-level lift test. - **Cross-repo artifacts not exercised (no access from this environment).** The originating report cites two files that live in the Router service repo, not here: `router-reference.mdx` (the header docs) and `routerdeadline/routerdeadline.go` (`DefaultDeadline = 10 * time.Minute`). I could not open either. I verified the header name against this repo's vendored `spec/router-openapi.yaml` (synced from that service) instead, and encoded the 10-minute deadline as `ROUTER_DEADLINE = 600.0` with a comment pointing at the Go constant; if Router's deadline is ever raised, this default must be raised to match. - **Deliberately did not read `COMFY_ROUTER_DEADLINE` in the client.** The report floated deriving the timeout from that env var. I did not, because it configures the *Router deployment's* environment, not the caller's — a same-named client-side variable would be a misleading second source of truth that silently disagrees with the server it is meant to track. The client-side mitigation is the named-and-documented `ROUTER_DEADLINE` constant plus headroom, which is the "at minimum, comment the relationship" path. ## Review round — cursor-review panel Five threads, all resolved. Three fixes, one partial, one answered without a code change. **Fixed — `_run_result` reads headers case-insensitively.** It is annotated `Mapping[str, str]` and all the names it reads are mixed-case, so it worked only because `post_model_run` happens to hand it an `httpx.Headers`. Any plain mapping — `dict(resp.headers)`, which httpx lowercases — would have missed all of them and yielded `replayed=False` and `request_id=None` without raising, which is the same silent shape as the wrong header name this PR fixes. It now normalizes through `httpx.Headers(headers)` first. **Fixed — the header names are pinned to the contract.** The names (now five, including `X-Comfy-Credits-Used`) are constants (`_RUN_RESULT_HEADERS`) and `tests/test_router_spec_contract.py::test_the_headers_a_run_result_reads_are_the_spec_s` pins each against `spec/router-openapi.yaml`'s `runRouterModel` 200, parametrized per name, reached by searching for the `operationId` rather than looking up the path. The assertion is one-way (everything read is declared) because the 200 also carries headers `RouterRunResult` does not surface. Plus `test_the_replayed_header_carries_no_x_comfy_prefix` as a named regression case. This is the check whose absence let the prefixed name through. **Fixed — docs that asserted the old equality.** Decoupling the client timeout from Router's deadline falsified prose saying the `504` arrives at "the same ten minutes as `MODEL_RUN_TIMEOUT`". `retry.py`'s module docstring and `collect_max_elapsed` doc, `models.py`'s `run` docstring, and `README.md` now point at `ROUTER_DEADLINE` — the constant that is actually ten minutes and the one `collect_max_elapsed`'s "two windows" derivation depends on. **Partial — the positional `httpx.Timeout` argument.** Correct that one positional value set `write` and `pool` as well as `read`. `write` is fixed: pushing a request body has no business inheriting an eleven-minute generation wait, so it is now 120s (still generous for a multi-megabyte inline base64 image, which httpx hands to the transport as a single write). `pool` is deliberately kept generation-scale, because on this route the pool is *full of generations*: a run is awaited server-side, so all 100 of httpx's default connections are held for up to `ROUTER_DEADLINE`, and the 101st caller is queued behind a generation rather than a handshake. A short pool bound would convert a legitimate fan-out from a queue that drains into a wall of `PoolTimeout` — 150 concurrent 2-minute runs succeed today and would fail inside ~70s under a 10s bound plus the 60s `max_elapsed`. The accepted cost is written on the constant: in the pathological case where the pool never frees, the caller waits eleven minutes and `PoolTimeout` surfaces without a retry, its fast-class budget long spent. All four bounds are now named explicitly so none is inherited again, and `test_every_bound_of_the_run_timeout_is_chosen_rather_than_inherited` asserts the trade. **Answered, no code change — a client-side `COMFY_ROUTER_DEADLINE`.** Already covered under Residual below; it configures Router's environment, not the caller's, and a same-named client variable would be a second source of truth that can silently disagree with the server it tracks. Additionally, this repo's env idiom is per-construction (`_resolve_router_base_url()` at `client.py:250`/`:414`), not import-time, so doing it properly means threading a resolved value through every `timeout=MODEL_RUN_TIMEOUT` default — a design change, not an override. The supported lever for a longer-deadline deployment is the public `run(..., timeout=...)`. **Deferred to a tracked follow-up.** `Comfy`/`AsyncComfy` expose no `limits=`, so a caller cannot raise `max_connections` — the real lever for a fan-out that should not queue at all. New public API on both client surfaces, out of scope for a defect fix; proposed as a follow-up rather than dropped. ## Review round — collect budget after a pool wait **Fixed — the collect budget now reserves the pool wait.** The collect budget's clock starts before a run waits for a pooled connection, but Router's deadline only starts once the request is sent. A run queued for most of the 660s pool bound and then held to the full 600s deadline received its `504 deadline_exceeded` after the 1200s budget had closed, so it was raised instead of collected. The default `RetryPolicy.collect_max_elapsed` is now derived as the pool bound plus two `ROUTER_DEADLINE` windows (1860s); an explicitly set value keeps its meaning. `test_a_deadline_504_after_a_long_pool_wait_still_gets_a_collect_attempt` pins the sequence on a fake clock and fails against the old default. README, CHANGELOG and docstrings now say thirty-one minutes. ## Provenance - **Authored by:** agent-work loop - **Verified:** - Full suite at the current head: 1286 passed, 9 skipped. - `ruff check .`, `ruff format --check .`, `mypy src` and `scripts/check_public_repo_hygiene.py`: clean. - The new pool-wait collect test fails with the default put back to 1200s and passes with the derived 1860s. - Earlier rounds checked each guard by reverting its fix: dropping the `httpx.Headers` normalization, re-adding the `X-Comfy-` prefix, or putting `read` back at 600 each makes its test fail. - **Deviations:** - `MODEL_RUN_TIMEOUT.pool` stays generation-scale rather than shortened, so fan-out past 100 concurrent runs still works; the longer default collect budget covers that wait instead. - `COMFY_ROUTER_DEADLINE` is not read client-side (see Residual), and the `limits=` passthrough on the clients was deferred to a follow-up. - Merge-conflict resolution kept both `main`'s lift pins and this PR's source-table checks, and kept both CHANGELOG entries. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Bug Fixes** * Model runs now allow Router’s ten-minute deadline to expire before the client read timeout, so Router timeout responses can surface. * Response metadata is parsed consistently regardless of HTTP header capitalization. * Replay information is preserved across failed runs and retries without starting duplicate generations. * Replay metadata uses the unprefixed `Idempotent-Replayed` response header. * **Documentation** * Clarified model-run and retry timeout behavior: read and pool timeouts include one minute beyond Router’s deadline, write timeout is 120 seconds, and connect timeout is 10 seconds. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Comfy-Org/comfy-python-sdk#168
No description provided.