fix(ui): use absolute keep_count for edit and regenerate truncate

Refs nesquena/hermes-webui#5096

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
This commit is contained in:
b3nwandb3nw committed 2026-06-28 18:01:54 +00:00
1 parent e24e6c54a6
commit bf1f8339b8
2 files changed
+60 -12

No files matched your search

+18 -12
View File
@@ -14086,16 +14086,19 @@ function autoResizeTextarea(ta) {
async function submitEdit(msgIdx, newText) {
if(!S.session || S.busy) return;
// Truncate session at msgIdx (keep messages before the edited one)
// then re-send the edited text
const initialSid = S.session.session_id;
const absoluteKeepCount = _oldestIdx + msgIdx;
if(typeof _ensureAllMessagesLoaded==='function'){
await _ensureAllMessagesLoaded();
}
if(!S.session || S.session.session_id !== initialSid) return;
try {
await api('/api/session/truncate', {method:'POST', body:JSON.stringify({
session_id: S.session.session_id,
keep_count: msgIdx // keep messages[0..msgIdx-1], discard from msgIdx onward
session_id: initialSid,
keep_count: absoluteKeepCount
})});
S.messages = S.messages.slice(0, msgIdx);
S.messages = S.messages.slice(0, absoluteKeepCount);
renderMessages();
// Now send the edited message as a new chat
$('msg').value = newText;
await send();
} catch(e) { setStatus(t('edit_failed') + e.message); }
@@ -14103,24 +14106,27 @@ async function submitEdit(msgIdx, newText) {
async function regenerateResponse(btn) {
if(!S.session || S.busy) return;
// Find the last user message and re-run it
// Remove the last assistant message first (truncate to before it)
const row = btn.closest('[data-msg-idx]');
if(!row) return;
const assistantIdx = parseInt(row.dataset.msgIdx, 10);
// Find the last user message text (one before this assistant message)
const absoluteKeepCount = _oldestIdx + assistantIdx;
const initialSid = S.session.session_id;
let lastUserText = '';
for(let i = assistantIdx - 1; i >= 0; i--) {
const m = S.messages[i];
if(m && m.role === 'user') { lastUserText = msgContent(m); break; }
}
if(!lastUserText) return;
if(typeof _ensureAllMessagesLoaded==='function'){
await _ensureAllMessagesLoaded();
}
if(!S.session || S.session.session_id !== initialSid) return;
try {
await api('/api/session/truncate', {method:'POST', body:JSON.stringify({
session_id: S.session.session_id,
keep_count: assistantIdx // remove the assistant message
session_id: initialSid,
keep_count: absoluteKeepCount
})});
S.messages = S.messages.slice(0, assistantIdx);
S.messages = S.messages.slice(0, absoluteKeepCount);
renderMessages();
$('msg').value = lastUserText;
await send();
@@ -0,0 +1,42 @@
"""Regression: edit/regenerate use absolute keep_count (#2184 pattern)."""
import re
from pathlib import Path
REPO = Path(__file__).resolve().parents[1]
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
def _function_body(src: str, name: str) -> str:
needle_async = f"async function {name}"
start = src.index(needle_async)
brace = src.index("{", start)
depth = 0
for i in range(brace, len(src)):
if src[i] == "{":
depth += 1
elif src[i] == "}":
depth -= 1
if depth == 0:
return src[start : i + 1]
raise AssertionError(f"function {name!r} body not found")
def test_submit_edit_uses_absolute_keep_count():
body = _function_body(UI_JS, "submitEdit")
assert re.search(r"absoluteKeepCount\s*=\s*_oldestIdx\s*\+\s*msgIdx", body)
assert "keep_count: absoluteKeepCount" in body
def test_regenerate_uses_absolute_keep_count():
body = _function_body(UI_JS, "regenerateResponse")
assert re.search(r"absoluteKeepCount\s*=\s*_oldestIdx\s*\+\s*assistantIdx", body)
assert "keep_count: absoluteKeepCount" in body
def test_submit_edit_captures_absolute_before_await():
body = _function_body(UI_JS, "submitEdit")
cap = re.search(r"absoluteKeepCount\s*=\s*_oldestIdx\s*\+\s*msgIdx", body)
assert cap
first_await = re.search(r"\bawait\b", body)
assert first_await and cap.start() < first_await.start()