commit
4a8b29db9e
3 files changed
+33
-4
No files matched your search
@@ -8,6 +8,7 @@
|
||||
- When the session index is missing, WebUI now starts a background rebuild while preserving the first sidebar full-scan result, so the index is primed for later requests without temporarily hiding existing sessions.
|
||||
- Live token-usage hints now cap the cumulative in-flight tool-result prompt estimate per assistant turn, preventing many large tool callbacks from temporarily inflating the context ring before exact provider accounting arrives.
|
||||
- Streaming checkpoint saves now run under the session's profile environment, so periodic background checkpoints no longer risk falling back to the process-global profile when a WebUI tab is using a non-default profile.
|
||||
- Chat streaming now keeps a single live EventSource registered per session/stream, preventing reconnect or context-compaction paths from stacking subscribers and rendering one assistant token stream multiple times.
|
||||
|
||||
## [v0.51.135] — 2026-05-25 — Release DG (stage-batch17 — 9-PR small-fix batch)
|
||||
|
||||
|
||||
+12
-4
@@ -609,10 +609,11 @@ async function send(){
|
||||
|
||||
const LIVE_STREAMS={};
|
||||
|
||||
function closeLiveStream(sessionId, streamId){
|
||||
function closeLiveStream(sessionId, streamId, source){
|
||||
const live=LIVE_STREAMS[sessionId];
|
||||
if(!live) return;
|
||||
if(streamId&&live.streamId!==streamId) return;
|
||||
if(source&&live.source!==source) return;
|
||||
try{live.source.close();}catch(_){ }
|
||||
delete LIVE_STREAMS[sessionId];
|
||||
}
|
||||
@@ -747,8 +748,8 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
if(_persistTimer) return;
|
||||
_persistTimer=setTimeout(()=>{_persistTimer=null;persistInflightState();},2000);
|
||||
}
|
||||
function _closeSource(){
|
||||
closeLiveStream(activeSid, streamId);
|
||||
function _closeSource(source){
|
||||
closeLiveStream(activeSid, streamId, source);
|
||||
}
|
||||
function _stripLiveVisibleAssistantEchoFromThinking(text, snippets){
|
||||
let out=String(text||'');
|
||||
@@ -1455,6 +1456,12 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
}
|
||||
|
||||
function _wireSSE(source){
|
||||
const existingLive=LIVE_STREAMS[activeSid];
|
||||
if(existingLive&&existingLive.source&&existingLive.source!==source){
|
||||
try{existingLive.source.close();}catch(_){ }
|
||||
}
|
||||
LIVE_STREAMS[activeSid]={streamId,source};
|
||||
|
||||
// Note on #631 Bug B: the original PR description stated the server
|
||||
// "replays buffered token events" on reconnect, and proposed resetting
|
||||
// the accumulators here so the re-sent tokens wouldn't double the prefix.
|
||||
@@ -2081,13 +2088,14 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
|
||||
source.addEventListener('error',async e=>{
|
||||
if(_terminalStateReached || _streamFinalized){
|
||||
_closeSource();
|
||||
_closeSource(source);
|
||||
return;
|
||||
}
|
||||
if(typeof recordClientSSEError==='function') recordClientSSEError('chat-response',{ready_state:source?source.readyState:null,session_id:activeSid,stream_id:streamId,reason:'chat EventSource.onerror'});
|
||||
source.close();
|
||||
if(_deferStreamErrorIfOffline()) return;
|
||||
if(_deferStreamErrorIfPageHidden()) return;
|
||||
_closeSource(source);
|
||||
// Attempt one reconnect if the stream is still active server-side
|
||||
if(!_reconnectAttempted && streamId){
|
||||
_reconnectAttempted=true;
|
||||
|
||||
@@ -121,3 +121,23 @@ def test_done_handler_is_idempotent_for_replay_or_duplicate_done_events():
|
||||
assert guard_idx != -1 and guard_idx < sound_idx, (
|
||||
"completion sound must be behind the duplicate-done finalization guard"
|
||||
)
|
||||
|
||||
|
||||
def test_attach_live_stream_registers_one_source_per_session_stream():
|
||||
"""Reconnect/compaction paths must not stack same-stream EventSources.
|
||||
|
||||
The stream channel broadcasts each token to every subscriber. If the browser
|
||||
opens four live EventSources for the same run, one assistant paragraph is
|
||||
appended four times even though the run journal contains it once.
|
||||
"""
|
||||
close_body = _function_body("closeLiveStream")
|
||||
attach_body = _function_body("attachLiveStream")
|
||||
wire_body = _function_body("_wireSSE")
|
||||
error_body = _event_body("error")
|
||||
|
||||
assert "const LIVE_STREAMS={};" in MESSAGES_JS
|
||||
assert "LIVE_STREAMS[activeSid]={streamId,source};" in wire_body
|
||||
assert "existingLive.source.close();" in wire_body
|
||||
assert "if(source&&live.source!==source) return;" in close_body
|
||||
assert "existingLive&&existingLive.streamId===streamId" in attach_body
|
||||
assert "_closeSource(source);" in error_body
|
||||
Reference in new issue
Block a user