fix(chat): align reasoning fallback identity key

This commit is contained in:
Frank Song 2026-07-15 07:10:02 +08:00
parent e5c5fd63d4
commit e13ebd96c1
2 changed files with 50 additions and 34 deletions

View file

@ -12084,6 +12084,7 @@ function _anchorSceneNodeForRow(row, opts){
if(!node) return null;
node.setAttribute('data-anchor-scene-row','1');
node.setAttribute('data-anchor-row-id',String(row.row_id||row.local_id||''));
if(row.local_id) node.setAttribute('data-anchor-local-id',String(row.local_id));
node.setAttribute('data-anchor-row-role',String(row.role||'activity'));
node.setAttribute('data-anchor-source-event-type',String(row.source_event_type||''));
return node;
@ -12153,6 +12154,7 @@ function _anchorSceneTransparentNodeForRow(row, opts){
if(settled) node.setAttribute('data-anchor-settled-scene-row','1');
if(live) node.setAttribute('data-anchor-live-scene-row','1');
node.setAttribute('data-anchor-row-id',String(row.row_id||row.local_id||''));
if(row.local_id) node.setAttribute('data-anchor-local-id',String(row.local_id));
node.setAttribute('data-anchor-row-role',String(row.role||'activity'));
node.setAttribute('data-anchor-source-event-type',String(row.source_event_type||''));
if(opts&&opts.streamId) node.setAttribute('data-anchor-stream-id',String(opts.streamId));
@ -12366,18 +12368,14 @@ function _liveAnchorReasoningRowForFallback(turn, opts){
if(!turn||!blocks||!blocks.querySelectorAll) return null;
const streamId=String(opts.streamId||S.activeStreamId||'');
const sessionId=String(opts.sessionId||(S.session&&S.session.session_id)||'');
const segmentSeq=opts.segmentSeq!==undefined&&opts.segmentSeq!==null
? String(opts.segmentSeq).trim()
: '';
const localId=String(opts.anchorReasoningLocalId||opts.localId||'').trim();
const rowKey=localId||(streamId&&segmentSeq?`live-reasoning:${streamId}:${segmentSeq}`:'');
if(!rowKey) return null;
if(!localId) return null;
const rows=blocks.querySelectorAll(
'[data-anchor-scene-row="1"][data-anchor-row-id]'
'[data-anchor-scene-row="1"][data-anchor-local-id]'
);
for(const row of Array.from(rows)){
const anchorRowId=String(row.getAttribute&&row.getAttribute('data-anchor-row-id')||'');
if(anchorRowId!==rowKey&&!anchorRowId.startsWith(`${rowKey}:`)) continue;
const anchorLocalId=String(row.getAttribute&&row.getAttribute('data-anchor-local-id')||'');
if(anchorLocalId!==localId) continue;
const rowRole=String(row.getAttribute&&row.getAttribute('data-anchor-row-role')||'');
const rowSource=String(row.getAttribute&&row.getAttribute('data-anchor-source-event-type')||'');
if(rowRole!=='thinking'&&rowSource!=='reasoning') continue;

View file

@ -175,6 +175,9 @@ def test_later_deferred_anchor_paint_updates_exact_reasoning_owner(activity_mode
assert exact["second_before"] == "Second draft"
assert exact["second_after"] == "Second updated"
assert exact["legacy_after"] == "Legacy unkeyed"
assert exact["anchor_run_id"] == "run-5720"
assert exact["second_row_id"] == "run-5720:2"
assert exact["second_local_id"] == "live-reasoning:stream-1:2"
assert exact["live_reasoning_rows"] == 2
assert exact["fallback_rows"] == 0
@ -548,31 +551,40 @@ function reasoningText(row){
const pre=row.querySelector&&row.querySelector('pre');
return pre?pre.textContent:row.textContent;
}
function appendSyntheticAnchorReasoningRow(rowId, text){
const row=_thinkingActivityNode(text, false, rowId);
row.setAttribute('data-anchor-scene-row','1');
row.setAttribute('data-anchor-row-id',rowId);
row.setAttribute('data-anchor-row-role','thinking');
row.setAttribute('data-anchor-source-event-type','reasoning');
row.setAttribute('data-anchor-stream-id','stream-1');
row.setAttribute('data-session-id','sid-1');
if(isTransparentStream()){
_decorateTransparentEventRow(row,{
type:'thinking',
text,
preview:text,
live:true,
segmentSeq:'2',
burstId:'0',
});
row.setAttribute('data-anchor-live-scene-row','1');
row.setAttribute('data-live-stream-owned','1');
turn.appendChild(row);
}else{
const group=ensureActivityGroup(turn,{activityKey:'live:stream-1'});
const list=_toolWorklogListEl(group);
(list||group||turn).appendChild(row);
}
function applyProductionAnchorEvent(sourceEventType, data){
const registry=window._liveAnchorRegistries&&window._liveAnchorRegistries.get('stream-1');
if(!registry) throw new Error('missing live anchor registry');
window.HermesAssistantTurnAnchors.applyAssistantTurnAnchorSourceEvent(
registry,
{
source_event_type:sourceEventType,
run_id:'run-5720',
stream_id:'stream-1',
...data,
},
{
session_id:'sid-1',
stream_id:'stream-1',
run_id:'run-5720',
}
);
return registry;
}
function appendProductionAnchorReasoningRow(localId, text, seq){
applyProductionAnchorEvent('reasoning',{
local_id:localId,
seq,
text,
activitySegmentSeq:seq,
activityBurstId:0,
});
window._renderLiveAnchorActivitySceneForStream('stream-1','sid-1',{
mode:process.env.ISSUE5720_ACTIVITY_MODE||'transparent_stream',
});
const row=anchorReasoningRows().find(candidate=>
candidate.getAttribute('data-anchor-row-id')===`run-5720:${seq}`
);
if(!row) throw new Error('production anchor reasoning row not rendered');
return row;
}
function appendUnkeyedLegacyReasonRow(text){
@ -610,7 +622,11 @@ if(process.env.ISSUE5720_MULTI_SEGMENT_FALLBACK==='1'){
const firstReasoningRow=anchorReasoningRows()[0]||null;
const firstBefore=reasoningText(firstReasoningRow);
const secondId='live-reasoning:stream-1:2';
const secondReasoningRow=appendSyntheticAnchorReasoningRow(`${secondId}:reasoning:1`,'Second draft');
const registry=applyProductionAnchorEvent('interim_assistant',{
seq:1,
text:'',
});
const secondReasoningRow=appendProductionAnchorReasoningRow(secondId,'Second draft',2);
const secondBefore=reasoningText(secondReasoningRow);
const legacyRow=appendUnkeyedLegacyReasonRow('Legacy unkeyed');
appendThinking('Second updated',{
@ -622,10 +638,12 @@ if(process.env.ISSUE5720_MULTI_SEGMENT_FALLBACK==='1'){
burstId:0,
});
multiSegmentExactFallback={
anchor_run_id:registry&&registry.anchor&&registry.anchor.identity&&registry.anchor.identity.run_id,
first_row_id:firstReasoningRow&&firstReasoningRow.getAttribute('data-anchor-row-id'),
first_before:firstBefore,
first_after:reasoningText(firstReasoningRow),
second_row_id:secondReasoningRow.getAttribute('data-anchor-row-id'),
second_local_id:secondReasoningRow.getAttribute('data-anchor-local-id'),
second_before:secondBefore,
second_after:reasoningText(secondReasoningRow),
legacy_after:reasoningText(legacyRow),