fix(gateway): keep 600s read-timeout default (gate finding — 120s would kill legit long silent tool calls)
Co-authored-by: ai-ag2026 <ai-ag2026@users.noreply.github.com>
This commit is contained in:
parent
fcb135a133
commit
dfd9b57183
2 changed files with 17 additions and 13 deletions
|
|
@ -55,16 +55,20 @@ _GATEWAY_CHAT_BACKENDS = {"gateway", "api_server", "api-server"}
|
|||
# read timeout ends the turn (surfacing Stop if pressed). It replaces the old flat
|
||||
# 600s timeout, under which a half-open gateway (TCP open, zero bytes) pinned the
|
||||
# worker for the full 10 minutes and ignored Stop (cancel is only re-checked
|
||||
# between SSE lines). 120s is generous for a slow first token / short silent
|
||||
# server-side tool call while capping the dead-gateway pin far below 600s; no
|
||||
# protocol heartbeat exists, so this is purely timeout-based. Deployments with
|
||||
# long fully-silent tool calls can raise ``HERMES_WEBUI_GATEWAY_READ_TIMEOUT``.
|
||||
# between SSE lines). We KEEP the 600s default budget: there is no Gateway
|
||||
# protocol heartbeat guaranteeing sub-600s progress bytes, so a legitimately
|
||||
# long/fully-silent server-side tool call must not be terminated early — reducing
|
||||
# the default below 600s would kill currently-working turns (gate finding, #5789).
|
||||
# The win here is that a read timeout is now TERMINAL and Stop-honoring (the old
|
||||
# flat timeout ignored Stop on a half-open gateway); the budget itself stays 600s
|
||||
# for backward compatibility. Deployments that want a tighter dead-gateway cap can
|
||||
# lower ``HERMES_WEBUI_GATEWAY_READ_TIMEOUT``.
|
||||
_GATEWAY_READ_TIMEOUT_ENV = "HERMES_WEBUI_GATEWAY_READ_TIMEOUT"
|
||||
_GATEWAY_READ_TIMEOUT_DEFAULT = 120.0
|
||||
_GATEWAY_READ_TIMEOUT_DEFAULT = 600.0
|
||||
|
||||
|
||||
def _gateway_read_timeout_secs() -> float:
|
||||
"""Total byte-silence budget for gateway SSE reads (default 120s, env-tunable)."""
|
||||
"""Total byte-silence budget for gateway SSE reads (default 600s, env-tunable)."""
|
||||
raw = os.environ.get(_GATEWAY_READ_TIMEOUT_ENV)
|
||||
if raw:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ bytes) blocked each read for the full 600s, so the worker thread was pinned for
|
|||
10 minutes and a user's Stop was ignored until then.
|
||||
|
||||
The socket now carries a bounded byte-silence budget (``_gateway_read_timeout_secs``,
|
||||
default 120s, env-tunable) and iteration goes through ``_iter_sse_lines_cancellable``.
|
||||
default 600s, env-tunable) and iteration goes through ``_iter_sse_lines_cancellable``.
|
||||
A read timeout is TERMINAL: CPython's ``socket.makefile`` latches
|
||||
``_timeout_occurred`` on the first ``socket.timeout``, so every subsequent read
|
||||
raises a bare ``OSError`` and the connection cannot be resumed. The generator
|
||||
|
|
@ -172,10 +172,10 @@ def test_eof_ends_iteration_cleanly():
|
|||
|
||||
class TestReadTimeoutConfig:
|
||||
|
||||
def test_default_is_120s(self, monkeypatch):
|
||||
def test_default_is_600s(self, monkeypatch):
|
||||
monkeypatch.delenv("HERMES_WEBUI_GATEWAY_READ_TIMEOUT", raising=False)
|
||||
assert _gateway_read_timeout_secs() == 120.0
|
||||
assert _GATEWAY_READ_TIMEOUT_DEFAULT == 120.0
|
||||
assert _gateway_read_timeout_secs() == 600.0
|
||||
assert _GATEWAY_READ_TIMEOUT_DEFAULT == 600.0
|
||||
|
||||
def test_env_override(self, monkeypatch):
|
||||
monkeypatch.setenv("HERMES_WEBUI_GATEWAY_READ_TIMEOUT", "300")
|
||||
|
|
@ -183,12 +183,12 @@ class TestReadTimeoutConfig:
|
|||
|
||||
def test_invalid_env_falls_back_to_default(self, monkeypatch):
|
||||
monkeypatch.setenv("HERMES_WEBUI_GATEWAY_READ_TIMEOUT", "not-a-number")
|
||||
assert _gateway_read_timeout_secs() == 120.0
|
||||
assert _gateway_read_timeout_secs() == 600.0
|
||||
|
||||
def test_non_positive_env_falls_back_to_default(self, monkeypatch):
|
||||
monkeypatch.setenv("HERMES_WEBUI_GATEWAY_READ_TIMEOUT", "0")
|
||||
assert _gateway_read_timeout_secs() == 120.0
|
||||
assert _gateway_read_timeout_secs() == 600.0
|
||||
|
||||
def test_negative_env_falls_back_to_default(self, monkeypatch):
|
||||
monkeypatch.setenv("HERMES_WEBUI_GATEWAY_READ_TIMEOUT", "-5")
|
||||
assert _gateway_read_timeout_secs() == 120.0
|
||||
assert _gateway_read_timeout_secs() == 600.0
|
||||
|
|
|
|||
Loading…
Reference in a new issue