diff --git a/backend/open_webui/socket/main.py b/backend/open_webui/socket/main.py index 7a585830a9..7229601f53 100644 --- a/backend/open_webui/socket/main.py +++ b/backend/open_webui/socket/main.py @@ -185,12 +185,19 @@ RESUME_STREAM_TTL_REFRESH_EVERY = 64 # Replay read timeout: looser since a resume is user-blocking anyway # and silent timeout here is worse than a brief extra wait. Both # configurable for infra where Redis isn't colocated. -RESUME_STREAM_REDIS_TIMEOUT_SEC = float( - os.environ.get('RESUME_STREAM_REDIS_TIMEOUT_SEC', '0.1') -) -RESUME_STREAM_READ_TIMEOUT_SEC = float( - os.environ.get('RESUME_STREAM_READ_TIMEOUT_SEC', '1.0') -) +def _float_env(name: str, default: float) -> float: + val = os.environ.get(name) + if val is None or val == '': + return default + try: + return float(val) + except (TypeError, ValueError): + log.warning(f'Invalid {name}={val!r}; using default {default}') + return default + + +RESUME_STREAM_REDIS_TIMEOUT_SEC = _float_env('RESUME_STREAM_REDIS_TIMEOUT_SEC', 0.1) +RESUME_STREAM_READ_TIMEOUT_SEC = _float_env('RESUME_STREAM_READ_TIMEOUT_SEC', 1.0) # Module-level circuit breaker for the streaming hot path. After N # consecutive Redis failures/timeouts, short-circuit seq/log calls for diff --git a/src/lib/components/chat/Chat.svelte b/src/lib/components/chat/Chat.svelte index 0e9a331646..e2ef682f50 100644 --- a/src/lib/components/chat/Chat.svelte +++ b/src/lib/components/chat/Chat.svelte @@ -1544,6 +1544,15 @@ taskIds = taskRes.task_ids; } + // Request resume BEFORE marking messages done. The + // interrupted-generation heuristic below flips `done=true` + // when getTaskIdsByChatId returns empty/null, which can + // happen on API failure too; asking for resume first lets + // us recover when the stream is actually still alive. + // Server replies empty if no log exists, so a spurious + // request costs one round trip. + requestResumeForAllInProgress(); + // If no active tasks and current message is incomplete, generation was interrupted const currentMessage = history.currentId ? history.messages[history.currentId] : null; if ( @@ -1555,13 +1564,6 @@ currentMessage.done = true; } - // Resume any in-flight streams. Not gated on taskIds — - // that call can fail or race and is not authoritative; - // requestResumeForAllInProgress already filters to - // unfinished assistants and the server no-ops when no - // log exists. - requestResumeForAllInProgress(); - await tick(); return true;