mirror of
https://github.com/open-webui/open-webui.git
synced 2026-09-16 23:43:03 +00:00
fix(stream): resume before mark-done, safe env parsing
- loadChat now calls requestResumeForAllInProgress BEFORE the interrupted-generation mark-done heuristic. That heuristic flips done=true when getTaskIdsByChatId returns empty/null, which includes API failures; previously that path would prevent resume from being attempted even when the stream was still alive server-side. Spurious resume requests cost one round trip and clear the fence via empty reply, so calling it unconditionally first is strictly safer. - Env-var float parsing wrapped in a _float_env helper that falls back to default with a warning on invalid input instead of raising at import time. Previous code would ValueError out of the module and prevent socket service startup on operator typo. Pushed back: bot flagged asyncio.Lock as unable to be stored in a WeakValueDictionary. Verified directly on CPython — both weakref.ref and WeakValueDictionary insertion work fine for asyncio.Lock. No change needed.
This commit is contained in:
parent
cffe1537ec
commit
630f452e93
2 changed files with 22 additions and 13 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue