mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-15 12:10:19 +00:00
Improve latest assistant response landmark
This commit is contained in:
committed by
nesquena-hermes
parent
4ef9d7c5cb
commit
944d57eedc
@@ -453,6 +453,9 @@ const _recycleResetAttrs=[
|
||||
'data-transparent-turn-toggle-bound',
|
||||
'data-anchor-scene-live-owner',
|
||||
'data-anchor-stream-id',
|
||||
'data-latest-assistant-response',
|
||||
'role',
|
||||
'aria-label',
|
||||
// Defensive reset for legacy/restored shells that may still carry the fallback live-turn marker.
|
||||
'data-live-assistant-turn',
|
||||
];
|
||||
@@ -8634,6 +8637,24 @@ function _createAssistantTurn(tsTitle='', tpsText=''){
|
||||
row.innerHTML=`${_assistantRoleHtml(tsTitle, tpsText)}<div class="assistant-turn-blocks"></div>`;
|
||||
return row;
|
||||
}
|
||||
function _setLatestAssistantTurnLandmark(turn, isLatest){
|
||||
if(!turn) return;
|
||||
const label='Latest Hermes response';
|
||||
if(isLatest){
|
||||
if(typeof document!=='undefined'){
|
||||
document.querySelectorAll('.assistant-turn[data-latest-assistant-response="true"]').forEach(el=>{
|
||||
if(el!==turn) _setLatestAssistantTurnLandmark(el, false);
|
||||
});
|
||||
}
|
||||
turn.setAttribute('role','region');
|
||||
turn.setAttribute('aria-label',label);
|
||||
turn.dataset.latestAssistantResponse='true';
|
||||
return;
|
||||
}
|
||||
if(turn.getAttribute('role')==='region') turn.removeAttribute('role');
|
||||
if(turn.getAttribute('aria-label')===label) turn.removeAttribute('aria-label');
|
||||
delete turn.dataset.latestAssistantResponse;
|
||||
}
|
||||
function _assistantTurnBlocks(turn){
|
||||
return turn?turn.querySelector('.assistant-turn-blocks'):null;
|
||||
}
|
||||
@@ -12342,6 +12363,13 @@ function renderMessages(options){
|
||||
}catch(e){}
|
||||
}
|
||||
const transparentToolResultsByTid=_transparentModeActive?_collectToolResultSnippetsByTid(S.messages):{};
|
||||
const latestRenderedAssistantRawIdx=(()=>{
|
||||
for(let i=renderVisWithIdx.length-1;i>=0;i--){
|
||||
const entry=renderVisWithIdx[i];
|
||||
if(entry&&entry.m&&entry.m.role==='assistant'&&!entry.m._live) return entry.rawIdx;
|
||||
}
|
||||
return -1;
|
||||
})();
|
||||
// Windowed render loop replaces the legacy full loop:
|
||||
// for(let vi=0;vi<visWithIdx.length;vi++)
|
||||
for(let vi=0;vi<renderVisWithIdx.length;vi++){
|
||||
@@ -12529,6 +12557,7 @@ function renderMessages(options){
|
||||
currentAssistantTurn.dataset.recycleKey=rawIdx;
|
||||
inner.appendChild(currentAssistantTurn);
|
||||
}
|
||||
_setLatestAssistantTurnLandmark(currentAssistantTurn, !m._live&&rawIdx===latestRenderedAssistantRawIdx);
|
||||
const seg=document.createElement('div');
|
||||
if(Array.isArray(orderedTransparentParts)&&orderedTransparentParts.length){
|
||||
const blocks=_assistantTurnBlocks(currentAssistantTurn);
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
INDEX_HTML = (ROOT / "static" / "index.html").read_text(encoding="utf-8")
|
||||
UI_JS = (ROOT / "static" / "ui.js").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _function_body(source: str, name: str) -> str:
|
||||
start = source.find(f"function {name}(")
|
||||
assert start != -1, f"{name}() not found"
|
||||
end = source.find("\nfunction ", start + 1)
|
||||
if end == -1:
|
||||
end = len(source)
|
||||
return source[start:end]
|
||||
|
||||
|
||||
def test_conversation_transcript_is_not_a_named_region():
|
||||
match = re.search(r'<div class="messages" id="messages"[^>]*>', INDEX_HTML)
|
||||
assert match, "#messages container not found"
|
||||
tag = match.group(0)
|
||||
assert 'role="region"' not in tag
|
||||
assert 'Conversation transcript' not in tag
|
||||
assert "tabindex" not in tag.lower()
|
||||
|
||||
|
||||
def test_latest_assistant_landmark_helper_is_named_but_not_focusable():
|
||||
helper = _function_body(UI_JS, "_setLatestAssistantTurnLandmark")
|
||||
assert "Latest Hermes response" in helper
|
||||
assert "setAttribute('role','region')" in helper
|
||||
assert "setAttribute('aria-label',label)" in helper
|
||||
assert "data-latest-assistant-response" in helper
|
||||
assert "tabindex" not in helper.lower()
|
||||
assert "<h" not in helper.lower()
|
||||
|
||||
|
||||
def test_settled_render_marks_only_the_latest_assistant_turn():
|
||||
render_messages = _function_body(UI_JS, "renderMessages")
|
||||
assert "const latestRenderedAssistantRawIdx=(()=>{" in render_messages
|
||||
assert "if(entry&&entry.m&&entry.m.role==='assistant'&&!entry.m._live) return entry.rawIdx;" in render_messages
|
||||
assert "_setLatestAssistantTurnLandmark(currentAssistantTurn, !m._live&&rawIdx===latestRenderedAssistantRawIdx);" in render_messages
|
||||
assert "seg.setAttribute('role','region')" not in render_messages
|
||||
assert "orderedSeg.setAttribute('role','region')" not in render_messages
|
||||
|
||||
|
||||
def test_live_assistant_turn_paths_do_not_own_final_response_landmark():
|
||||
restore_live_turn = _function_body(UI_JS, "restoreLiveTurnHtmlForSession")
|
||||
render_live_scene = _function_body(UI_JS, "renderLiveAnchorActivityScene")
|
||||
append_thinking = _function_body(UI_JS, "appendThinking")
|
||||
|
||||
assert "_setLatestAssistantTurnLandmark(restored, true);" not in restore_live_turn
|
||||
assert "_setLatestAssistantTurnLandmark(turn, true);" not in render_live_scene
|
||||
assert "_setLatestAssistantTurnLandmark(turn, true);" not in append_thinking
|
||||
@@ -604,6 +604,7 @@ def test_render_messages_keeps_anchor_owned_turn_out_of_legacy_activity_rebuilds
|
||||
return turn;
|
||||
}}
|
||||
function _assistantTurnBlocks(turn) {{ return turn ? turn.querySelector('.assistant-turn-blocks') : null; }}
|
||||
function _setLatestAssistantTurnLandmark() {{}}
|
||||
function _assistantRoleHtml() {{ return ''; }}
|
||||
function _userMessageDomId(rawIdx) {{ return `user-${{rawIdx}}`; }}
|
||||
function _messageSessionIndexForRawIdx(rawIdx) {{ return rawIdx; }}
|
||||
|
||||
@@ -89,6 +89,8 @@ const S = { session: { session_id: 'sess-1' } };
|
||||
const _assistantRoleHtml = (title, tps) => `role:${title}:${tps}`;
|
||||
const isTpsDisplayEnabled = () => true;
|
||||
const _formatTurnTps = value => `tps:${value}`;
|
||||
const latestRenderedAssistantRawIdx = rawIdx;
|
||||
function _setLatestAssistantTurnLandmark() {}
|
||||
eval(recycleChunk);
|
||||
console.log(JSON.stringify({
|
||||
removedAttrs,
|
||||
@@ -110,6 +112,9 @@ console.log(JSON.stringify({
|
||||
"data-transparent-turn-toggle-bound",
|
||||
"data-anchor-scene-live-owner",
|
||||
"data-anchor-stream-id",
|
||||
"data-latest-assistant-response",
|
||||
"role",
|
||||
"aria-label",
|
||||
"data-live-assistant-turn",
|
||||
}
|
||||
assert set(result["resetAttrs"]) == expected_attrs
|
||||
@@ -155,6 +160,8 @@ const S = { session: { session_id: 'sess-1' } };
|
||||
const _assistantRoleHtml = (title, tps) => `role:${title}:${tps}`;
|
||||
const isTpsDisplayEnabled = () => true;
|
||||
const _formatTurnTps = value => `tps:${value}`;
|
||||
const latestRenderedAssistantRawIdx = rawIdx;
|
||||
function _setLatestAssistantTurnLandmark() {}
|
||||
eval(recycleChunk);
|
||||
console.log(JSON.stringify({
|
||||
blocksInnerHTML: blocks.innerHTML,
|
||||
|
||||
Reference in New Issue
Block a user