Backwards LLM invocation timeouts

September 11, 2026 ยท View on GitHub

Agent strategy plugins can invoke an LLM through the daemon's reverse connection to Dify's inner API. That connection uses framed responses even when the model request has stream: false.

LLM and structured-output LLM reverse calls have three transport budgets. All values below are milliseconds. Zero means inherit, not unlimited.

VariableDefaultMeaning
DIFY_BACKWARDS_INVOCATION_LLM_TOTAL_TIMEOUT0Entire reverse HTTP request, including headers and response body. Inherits DIFY_BACKWARDS_INVOCATION_READ_TIMEOUT (normally 240000).
DIFY_BACKWARDS_INVOCATION_LLM_FIRST_RESPONSE_TIMEOUT0Time until the first decoded response frame. Inherits the effective LLM total budget.
DIFY_BACKWARDS_INVOCATION_LLM_IDLE_TIMEOUT0Maximum read inactivity after the first decoded frame. Incoming body bytes or decoded frames reset it. Inherits DIFY_BACKWARDS_INVOCATION_READ_TIMEOUT.

These budgets do not change the timeout settings for tool, embedding, storage, or other reverse calls. They also do not change the forward model invocation protocol. The first-response budget is not a first-token SLA: an empty model delta can already be a valid response frame. A blocking model invocation waits under its first-response/total budgets, not the post-response idle budget.

For example, to allow a reverse LLM call to run for up to ten minutes without changing the existing four-minute budget for other reverse calls:

DIFY_BACKWARDS_INVOCATION_READ_TIMEOUT=240000
DIFY_BACKWARDS_INVOCATION_LLM_TOTAL_TIMEOUT=600000
DIFY_BACKWARDS_INVOCATION_LLM_FIRST_RESPONSE_TIMEOUT=600000
DIFY_BACKWARDS_INVOCATION_LLM_IDLE_TIMEOUT=240000

Keep the enclosing plugin/Agent execution budget longer than the single LLM call budget. Check PLUGIN_MAX_EXECUTION_TIMEOUT, the SDK's invocation timeout, serverless transaction limits, and API/worker HTTP read timeouts separately; their units and semantics differ. A caller cancellation or earlier parent deadline always wins. Multiple LLM/tool calls share the enclosing execution budget; increasing the per-call limit does not grant unlimited Agent execution.

Timeout messages include phase=first_response, phase=read_idle, or phase=total, and timeout_ms. They use the existing reverse-call error envelope so older SDKs can display the reason without a protocol upgrade. The daemon cancels the reverse HTTP request and releases its timers/body when it completes or the consumer closes it. Cancellation of the provider's actual computation still depends on the downstream API/runtime/SDK honoring the disconnect.

No automatic retries are added. Retrying an entire Agent execution can repeat tool side effects and model charges.

Long-stream regression

The optional integration test below uses a local framed HTTP producer, not a model provider. It runs a legacy control and the LLM policy concurrently, proving that the deployment budgets permit an active stream past the old four-minute cutoff. Export the daemon timeout settings from the example above first.

DIFY_TEST_BACKWARDS_LLM_STREAM_SECONDS=260 go test \
  ./internal/core/dify_invocation/calldify \
  -run '^TestBackwardsLLMLongStream$' -v -count=1 -timeout=8m