mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 11:10:18 +00:00
fix: avoid duplicate live tool events
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
### Fixed
|
||||
|
||||
- Surface live tool activity when Hermes Agent reports tools through its dedicated `tool_start_callback` / `tool_complete_callback` path, so browser chat shows the existing running tool cards instead of appearing idle until the final answer.
|
||||
- Keep the composer usable if a transient restart/proxy blip prevents `commands.js` from loading while `boot.js` still attaches slash-command listeners.
|
||||
|
||||
|
||||
## [v0.51.93] — 2026-05-19 — Release BQ (stage-386 — 10-PR full sweep batch — RFC Slice 4 runner/sidecar gate + workspace tree toggle width CSS variable + settled file:// markdown link rendering + prompt-cache coverage percentage fix + terminal shell shutdown reap + configured model picker provider preservation + profile-aware assistant display names + state.db reconciliation slice 1 + queued-message cross-session drain fix + stale-stream writeback supersede)
|
||||
|
||||
@@ -3525,6 +3525,13 @@ def _run_agent_streaming(
|
||||
|
||||
args_snap = _tool_args_snapshot(args)
|
||||
|
||||
# Modern Hermes Agent builds can call both tool_progress_callback
|
||||
# and the structured tool_start/tool_complete callbacks for the
|
||||
# same tool. Prefer the structured path when it is supported so
|
||||
# the browser receives one tid-tagged tool card per real call.
|
||||
if event_type in (None, 'tool.started') and 'tool_start_callback' in _agent_params:
|
||||
return
|
||||
|
||||
if event_type in (None, 'tool.started'):
|
||||
_live_tool_calls.append({
|
||||
'name': name,
|
||||
@@ -3560,6 +3567,9 @@ def _run_agent_streaming(
|
||||
pass
|
||||
return
|
||||
|
||||
if event_type == 'tool.completed' and 'tool_complete_callback' in _agent_params:
|
||||
return
|
||||
|
||||
if event_type == 'tool.completed':
|
||||
for live_tc in reversed(_live_tool_calls):
|
||||
if live_tc.get('done'):
|
||||
|
||||
@@ -1,20 +1,3 @@
|
||||
// Slash-command helpers normally come from commands.js, which is loaded before
|
||||
// boot.js. If a restart/proxy blip makes that asset fail while boot.js loads,
|
||||
// keep the composer usable instead of throwing ReferenceError on input/keydown.
|
||||
(function(){
|
||||
function dropdown(){ return document.getElementById('cmdDropdown'); }
|
||||
if(typeof window.hideCmdDropdown!=='function'){
|
||||
window.hideCmdDropdown=function(){
|
||||
const dd=dropdown();
|
||||
if(dd){ dd.classList.remove('open'); dd.style.display='none'; }
|
||||
};
|
||||
}
|
||||
if(typeof window.showCmdDropdown!=='function') window.showCmdDropdown=function(){};
|
||||
if(typeof window.getMatchingCommands!=='function') window.getMatchingCommands=function(){ return []; };
|
||||
if(typeof window.navigateCmdDropdown!=='function') window.navigateCmdDropdown=function(){};
|
||||
if(typeof window.selectCmdDropdownItem!=='function') window.selectCmdDropdownItem=function(){};
|
||||
})();
|
||||
|
||||
async function cancelStream(){
|
||||
const streamId = S.activeStreamId;
|
||||
if(!streamId) return;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _read(relpath: str) -> str:
|
||||
return (ROOT / relpath).read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_boot_installs_safe_slash_command_fallbacks_when_commands_asset_missing():
|
||||
boot = _read("static/boot.js")
|
||||
|
||||
assert "If a restart/proxy blip makes that asset fail" in boot
|
||||
for name in (
|
||||
"hideCmdDropdown",
|
||||
"showCmdDropdown",
|
||||
"getMatchingCommands",
|
||||
"navigateCmdDropdown",
|
||||
"selectCmdDropdownItem",
|
||||
):
|
||||
assert f"typeof window.{name}!==\'function\'" in boot or f"typeof window.{name}!='function'" in boot
|
||||
|
||||
|
||||
def test_commands_asset_still_owns_real_dropdown_implementation():
|
||||
commands = _read("static/commands.js")
|
||||
|
||||
assert "function hideCmdDropdown()" in commands
|
||||
assert "function showCmdDropdown(" in commands
|
||||
assert "function getMatchingCommands(" in commands
|
||||
@@ -52,6 +52,16 @@ def test_tool_complete_callback_emits_existing_tool_complete_sse_event_with_tool
|
||||
assert "_checkpoint_activity[0] += 1" in block
|
||||
|
||||
|
||||
def test_legacy_progress_events_are_suppressed_when_structured_callbacks_are_wired():
|
||||
src = _read("api/streaming.py")
|
||||
block = _function_block(src, "on_tool")
|
||||
|
||||
assert "event_type in (None, 'tool.started') and 'tool_start_callback' in _agent_params" in block
|
||||
assert "event_type == 'tool.completed' and 'tool_complete_callback' in _agent_params" in block
|
||||
assert block.index("'tool_start_callback' in _agent_params") < block.index("put('tool'")
|
||||
assert block.index("'tool_complete_callback' in _agent_params") < block.index("put('tool_complete'")
|
||||
|
||||
|
||||
def test_tool_callback_events_keep_existing_frontend_event_contract():
|
||||
messages = _read("static/messages.js")
|
||||
ui = _read("static/ui.js")
|
||||
|
||||
Reference in New Issue
Block a user