Replies: 3 comments
|
Confirmed the root cause and verified the fix isn't in The root cause is right, and it's a known one. undici's The tension I'd push back on:
Better shape — compose through the existing owner rather than beside it. Rather than llm-pi-ai installing its own dispatcher and detecting proxy activity, thread the timeout through This is fundamentally the same class as the |
|
I hit a matching five-minute failure on my locally patched DSH 0.1.5-rc.2 installation with LM Studio/Qwen, and tried the per-request fetch approach mentioned above. My profile already had My experimental adapter patch supplies a custom fetch through pi-ai's existing options. It delegates to the existing dispatcher and overrides After applying it, local compactions completed in approximately 537, 544 and 631 seconds, followed by continuation. A separate custom tool-budget guard later stopped the task; that is independent of the transport timeout. Implementation and three transport tests: version-pinned patch directory. Tests cover scaled HTTP timeout/completion and cancellation behavior, with origin-scoping checks; they are not a corporate-proxy end-to-end test. The backup contains a patched compiled adapter artifact, not an upstream-ready source PR, and I have not established applicability to current master. Sharing this as additional local evidence for the per-request seam, rather than proposing another process-global transport owner. The existing workaround in #5124 is also relevant for people comparing approaches. |
|
Verified on master (c291e79): the egress-timeout fix you linked has not been merged. There is no Current state on master:
Workarounds today: what xoykor described (a fresh |
Uh oh!
There was an error while loading. Please reload this page.
TL;DR
Some model requests that should only take a minute silently fail after exactly ~302 seconds with a bare
terminatedtransport error, and retries don't help. This happens whenever a provider needs more than five minutes of silent prefill before its first token — notably self-hosted vLLM/EXL3 clusters serving very large context windows. This PR removes the hidden five-minute kill switch that caused it.Root cause
Node's global
fetchis its built-in undici, which arms two defaults no slow LLM request can satisfy:headersTimeoutandbodyTimeout, both 300,000 ms. A streaming endpoint emits response headers immediately and then sends no body bytes until prefill completes, so any request whose provider prefills longer than five minutes is aborted mid-flight by the client's own HTTP stack — surfacing as a bareTypeError: terminated(causeUND_ERR_BODY_TIMEOUT), flattened by pi-ai's error handling into a retryableTRANSPORTerror, then retried five identically doomed times.This is not a server-side or harness-watchdog issue: the deployment already raised its engine timeout (
VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=1800) and its adapterfirstEventTimeoutMsto 900s+, but undici's client-side body default wins the race against both and kills the request first. A production session deadlocked this way: context grew to ~494k of a 500k window while all compaction attempts (which must send the whole conversation to be summarized) died the same way.Change
dsh-llm-pi-ainow installs the process-wide fetch dispatcher itself (src/egress.ts), configured by two new top-level settings fields:httpBodyTimeoutMs— max ms between response-body chunks before the dispatcher aborts; default0(disabled).httpHeadersTimeoutMs— max ms to wait for headers; default0(disabled).Both default to disabled, which is deliberate: it returns timeout ownership to the adapter's own
firstEventTimeoutMs/streamIdleTimeoutMswatchdogs (the design those fields already encode) instead of introducing a second, hidden timeout owner. A finite value re-installs a dispatcher-level floor below them. The guards install at mount and re-install through the settings seam on change; the install is idempotent.The scope is process-wide by necessity: pi-ai constructs its OpenAI clients as
new OpenAI({ apiKey, baseURL })with no fetch seam, so there is no per-request place to attach a dispatcher. The npmundicisetGlobalDispatchergoverns Node's built-in fetch through the shared global-dispatcher symbol. The guards yield to@deepseek-ai/dsh-http-proxywhen one of its policies is active, so they never displace a proxy-configured dispatcher (added as a runtime dependency).Tests and verification
UND_ERR_BODY_TIMEOUTat a finite bound; missing headers abort withUND_ERR_HEADERS_TIMEOUT; the install is idempotent.srcfiles,tscclean, oxlint clean, and the doc gates (translation pairing, agent note format, README known-limitations) all pass.Known limitation
When a proxy policy is active,
dsh-http-proxyowns undici's global dispatcher and the guards install nothing, so the proxy dispatcher keeps undici's default timeouts. Eliminating that gap would require threading both concerns into a single dispatcher; recorded in the package README under Known Limitations.All reactions