fix: make TPS header display optional
This commit is contained in:
1 parent
3ad8846a27
commit
89099928db
9 files changed
+93
-11
No files matched your search
@@ -3082,6 +3082,7 @@ _SETTINGS_DEFAULTS = {
|
|||||||
"onboarding_completed": False,
|
"onboarding_completed": False,
|
||||||
"send_key": "enter", # 'enter' or 'ctrl+enter'
|
"send_key": "enter", # 'enter' or 'ctrl+enter'
|
||||||
"show_token_usage": False, # show input/output token badge below assistant messages
|
"show_token_usage": False, # show input/output token badge below assistant messages
|
||||||
|
"show_tps": False, # show tokens-per-second chip in assistant message headers
|
||||||
"show_cli_sessions": False, # merge CLI sessions from state.db into the sidebar
|
"show_cli_sessions": False, # merge CLI sessions from state.db into the sidebar
|
||||||
"sync_to_insights": False, # mirror WebUI token usage to state.db for /insights
|
"sync_to_insights": False, # mirror WebUI token usage to state.db for /insights
|
||||||
"check_for_updates": True, # check if webui/agent repos are behind upstream
|
"check_for_updates": True, # check if webui/agent repos are behind upstream
|
||||||
@@ -3207,6 +3208,7 @@ _SETTINGS_ENUM_VALUES = {
|
|||||||
_SETTINGS_BOOL_KEYS = {
|
_SETTINGS_BOOL_KEYS = {
|
||||||
"onboarding_completed",
|
"onboarding_completed",
|
||||||
"show_token_usage",
|
"show_token_usage",
|
||||||
|
"show_tps",
|
||||||
"show_cli_sessions",
|
"show_cli_sessions",
|
||||||
"sync_to_insights",
|
"sync_to_insights",
|
||||||
"check_for_updates",
|
"check_for_updates",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -1238,6 +1238,7 @@ function applyBotName(){
|
|||||||
_bootSettings=s;
|
_bootSettings=s;
|
||||||
window._sendKey=s.send_key||'enter';
|
window._sendKey=s.send_key||'enter';
|
||||||
window._showTokenUsage=!!s.show_token_usage;
|
window._showTokenUsage=!!s.show_token_usage;
|
||||||
|
window._showTps=!!s.show_tps;
|
||||||
window._showCliSessions=!!s.show_cli_sessions;
|
window._showCliSessions=!!s.show_cli_sessions;
|
||||||
window._soundEnabled=!!s.sound_enabled;
|
window._soundEnabled=!!s.sound_enabled;
|
||||||
window._notificationsEnabled=!!s.notifications_enabled;
|
window._notificationsEnabled=!!s.notifications_enabled;
|
||||||
@@ -1271,6 +1272,7 @@ function applyBotName(){
|
|||||||
}catch(e){
|
}catch(e){
|
||||||
window._sendKey='enter';
|
window._sendKey='enter';
|
||||||
window._showTokenUsage=false;
|
window._showTokenUsage=false;
|
||||||
|
window._showTps=false;
|
||||||
window._showCliSessions=false;
|
window._showCliSessions=false;
|
||||||
window._soundEnabled=false;
|
window._soundEnabled=false;
|
||||||
window._notificationsEnabled=false;
|
window._notificationsEnabled=false;
|
||||||
|
|||||||
@@ -829,6 +829,13 @@
|
|||||||
</label>
|
</label>
|
||||||
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_token_usage">Displays input/output token count below each assistant reply. Also toggled with <code>/usage</code>.</div>
|
<div style="font-size:11px;color:var(--muted);margin-top:4px" data-i18n="settings_desc_token_usage">Displays input/output token count below each assistant reply. Also toggled with <code>/usage</code>.</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-field">
|
||||||
|
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
|
||||||
|
<input type="checkbox" id="settingsShowTps" style="width:15px;height:15px;accent-color:var(--accent)">
|
||||||
|
<span>Show token speed (TPS)</span>
|
||||||
|
</label>
|
||||||
|
<div style="font-size:11px;color:var(--muted);margin-top:4px">Displays tokens per second in assistant message headers while streaming and after a response completes. Off by default.</div>
|
||||||
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
|
<label style="display:flex;align-items:center;gap:8px;cursor:pointer">
|
||||||
<input type="checkbox" id="settingsSimplifiedToolCalling" style="width:15px;height:15px;accent-color:var(--accent)">
|
<input type="checkbox" id="settingsSimplifiedToolCalling" style="width:15px;height:15px;accent-color:var(--accent)">
|
||||||
|
|||||||
+15
-3
@@ -2922,6 +2922,8 @@ function _preferencesPayloadFromUi(){
|
|||||||
if(langSel) payload.language=langSel.value;
|
if(langSel) payload.language=langSel.value;
|
||||||
const showUsageCb=$('settingsShowTokenUsage');
|
const showUsageCb=$('settingsShowTokenUsage');
|
||||||
if(showUsageCb) payload.show_token_usage=showUsageCb.checked;
|
if(showUsageCb) payload.show_token_usage=showUsageCb.checked;
|
||||||
|
const showTpsCb=$('settingsShowTps');
|
||||||
|
if(showTpsCb) payload.show_tps=showTpsCb.checked;
|
||||||
const simplifiedToolCb=$('settingsSimplifiedToolCalling');
|
const simplifiedToolCb=$('settingsSimplifiedToolCalling');
|
||||||
if(simplifiedToolCb) payload.simplified_tool_calling=simplifiedToolCb.checked;
|
if(simplifiedToolCb) payload.simplified_tool_calling=simplifiedToolCb.checked;
|
||||||
const apiRedactCb=$('settingsApiRedact');
|
const apiRedactCb=$('settingsApiRedact');
|
||||||
@@ -2988,6 +2990,11 @@ async function _autosavePreferencesSettings(payload){
|
|||||||
if(typeof clearMessageRenderCache==='function') clearMessageRenderCache();
|
if(typeof clearMessageRenderCache==='function') clearMessageRenderCache();
|
||||||
if(typeof renderMessages==='function') renderMessages();
|
if(typeof renderMessages==='function') renderMessages();
|
||||||
}
|
}
|
||||||
|
if(payload&&payload.show_tps!==undefined){
|
||||||
|
window._showTps=!!(saved&&saved.show_tps);
|
||||||
|
if(typeof clearMessageRenderCache==='function') clearMessageRenderCache();
|
||||||
|
if(typeof renderMessages==='function') renderMessages();
|
||||||
|
}
|
||||||
_settingsPreferencesAutosaveRetryPayload=null;
|
_settingsPreferencesAutosaveRetryPayload=null;
|
||||||
_setPreferencesAutosaveStatus('saved');
|
_setPreferencesAutosaveStatus('saved');
|
||||||
// Only clear the global dirty flag and hide the unsaved-changes bar when
|
// Only clear the global dirty flag and hide the unsaved-changes bar when
|
||||||
@@ -3128,6 +3135,8 @@ async function loadSettingsPanel(){
|
|||||||
}
|
}
|
||||||
const showUsageCb=$('settingsShowTokenUsage');
|
const showUsageCb=$('settingsShowTokenUsage');
|
||||||
if(showUsageCb){showUsageCb.checked=!!settings.show_token_usage;showUsageCb.addEventListener('change',_schedulePreferencesAutosave,{once:false});}
|
if(showUsageCb){showUsageCb.checked=!!settings.show_token_usage;showUsageCb.addEventListener('change',_schedulePreferencesAutosave,{once:false});}
|
||||||
|
const showTpsCb=$('settingsShowTps');
|
||||||
|
if(showTpsCb){showTpsCb.checked=!!settings.show_tps;showTpsCb.addEventListener('change',_schedulePreferencesAutosave,{once:false});}
|
||||||
const simplifiedToolCb=$('settingsSimplifiedToolCalling');
|
const simplifiedToolCb=$('settingsSimplifiedToolCalling');
|
||||||
if(simplifiedToolCb){simplifiedToolCb.checked=settings.simplified_tool_calling!==false;simplifiedToolCb.addEventListener('change',_schedulePreferencesAutosave,{once:false});}
|
if(simplifiedToolCb){simplifiedToolCb.checked=settings.simplified_tool_calling!==false;simplifiedToolCb.addEventListener('change',_schedulePreferencesAutosave,{once:false});}
|
||||||
const apiRedactCb=$('settingsApiRedact');
|
const apiRedactCb=$('settingsApiRedact');
|
||||||
@@ -3562,9 +3571,10 @@ function _setSettingsAuthButtonsVisible(active){
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _applySavedSettingsUi(saved, body, opts){
|
function _applySavedSettingsUi(saved, body, opts){
|
||||||
const {sendKey,showTokenUsage,showCliSessions,theme,skin,language,sidebarDensity,fontSize}=opts;
|
const {sendKey,showTokenUsage,showTps,showCliSessions,theme,skin,language,sidebarDensity,fontSize}=opts;
|
||||||
window._sendKey=sendKey||'enter';
|
window._sendKey=sendKey||'enter';
|
||||||
window._showTokenUsage=showTokenUsage;
|
window._showTokenUsage=showTokenUsage;
|
||||||
|
window._showTps=showTps;
|
||||||
window._showCliSessions=showCliSessions;
|
window._showCliSessions=showCliSessions;
|
||||||
window._soundEnabled=body.sound_enabled;
|
window._soundEnabled=body.sound_enabled;
|
||||||
window._notificationsEnabled=body.notifications_enabled;
|
window._notificationsEnabled=body.notifications_enabled;
|
||||||
@@ -3652,6 +3662,7 @@ async function saveSettings(andClose){
|
|||||||
const modelChanged=(model||'')!==(_settingsHermesDefaultModelOnOpen||'');
|
const modelChanged=(model||'')!==(_settingsHermesDefaultModelOnOpen||'');
|
||||||
const sendKey=($('settingsSendKey')||{}).value;
|
const sendKey=($('settingsSendKey')||{}).value;
|
||||||
const showTokenUsage=!!($('settingsShowTokenUsage')||{}).checked;
|
const showTokenUsage=!!($('settingsShowTokenUsage')||{}).checked;
|
||||||
|
const showTps=!!($('settingsShowTps')||{}).checked;
|
||||||
const showCliSessions=!!($('settingsShowCliSessions')||{}).checked;
|
const showCliSessions=!!($('settingsShowCliSessions')||{}).checked;
|
||||||
const pw=($('settingsPassword')||{}).value;
|
const pw=($('settingsPassword')||{}).value;
|
||||||
const theme=($('settingsTheme')||{}).value||'dark';
|
const theme=($('settingsTheme')||{}).value||'dark';
|
||||||
@@ -3668,6 +3679,7 @@ async function saveSettings(andClose){
|
|||||||
body.font_size=fontSize;
|
body.font_size=fontSize;
|
||||||
body.language=language;
|
body.language=language;
|
||||||
body.show_token_usage=showTokenUsage;
|
body.show_token_usage=showTokenUsage;
|
||||||
|
body.show_tps=showTps;
|
||||||
body.simplified_tool_calling=!!($('settingsSimplifiedToolCalling')||{}).checked;
|
body.simplified_tool_calling=!!($('settingsSimplifiedToolCalling')||{}).checked;
|
||||||
body.api_redact_enabled=!!($('settingsApiRedact')||{}).checked;
|
body.api_redact_enabled=!!($('settingsApiRedact')||{}).checked;
|
||||||
body.show_cli_sessions=showCliSessions;
|
body.show_cli_sessions=showCliSessions;
|
||||||
@@ -3693,7 +3705,7 @@ async function saveSettings(andClose){
|
|||||||
if(typeof showToast==='function') showToast('Failed to update default model — settings saved');
|
if(typeof showToast==='function') showToast('Failed to update default model — settings saved');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_applySavedSettingsUi(saved, body, {sendKey,showTokenUsage,showCliSessions,theme,skin,language,sidebarDensity,fontSize});
|
_applySavedSettingsUi(saved, body, {sendKey,showTokenUsage,showTps,showCliSessions,theme,skin,language,sidebarDensity,fontSize});
|
||||||
showToast(t(saved.auth_just_enabled?'settings_saved_pw':'settings_saved_pw_updated'));
|
showToast(t(saved.auth_just_enabled?'settings_saved_pw':'settings_saved_pw_updated'));
|
||||||
_settingsDirty=false;
|
_settingsDirty=false;
|
||||||
_resetSettingsPanelState();
|
_resetSettingsPanelState();
|
||||||
@@ -3712,7 +3724,7 @@ async function saveSettings(andClose){
|
|||||||
if(typeof showToast==='function') showToast('Failed to update default model — settings saved');
|
if(typeof showToast==='function') showToast('Failed to update default model — settings saved');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_applySavedSettingsUi(saved, body, {sendKey,showTokenUsage,showCliSessions,theme,skin,language,sidebarDensity,fontSize});
|
_applySavedSettingsUi(saved, body, {sendKey,showTokenUsage,showTps,showCliSessions,theme,skin,language,sidebarDensity,fontSize});
|
||||||
showToast(t('settings_saved'));
|
showToast(t('settings_saved'));
|
||||||
_settingsDirty=false;
|
_settingsDirty=false;
|
||||||
_resetSettingsPanelState();
|
_resetSettingsPanelState();
|
||||||
|
|||||||
+6
-3
@@ -3185,9 +3185,12 @@ function _formatTurnTps(value){
|
|||||||
const fixed=n>=100?Math.round(n).toLocaleString():n>=10?n.toFixed(1):n.toFixed(1);
|
const fixed=n>=100?Math.round(n).toLocaleString():n>=10?n.toFixed(1):n.toFixed(1);
|
||||||
return `${fixed} t/s`;
|
return `${fixed} t/s`;
|
||||||
}
|
}
|
||||||
|
function isTpsDisplayEnabled(){
|
||||||
|
return window._showTps===true;
|
||||||
|
}
|
||||||
function _assistantRoleHtml(tsTitle='', tpsText=''){
|
function _assistantRoleHtml(tsTitle='', tpsText=''){
|
||||||
const _bn=window._botName||'Hermes';
|
const _bn=window._botName||'Hermes';
|
||||||
const tps=tpsText?`<span class="msg-tps-inline" title="Tokens per second">${esc(tpsText)}</span>`:'';
|
const tps=(isTpsDisplayEnabled()&&tpsText)?`<span class="msg-tps-inline" title="Tokens per second">${esc(tpsText)}</span>`:'';
|
||||||
return `<div class="msg-role assistant" ${tsTitle?`title="${esc(tsTitle)}"`:''}><div class="role-icon assistant">${esc(_bn.charAt(0).toUpperCase())}</div><span style="font-size:12px">${esc(_bn)}</span>${tps}</div>`;
|
return `<div class="msg-role assistant" ${tsTitle?`title="${esc(tsTitle)}"`:''}><div class="role-icon assistant">${esc(_bn.charAt(0).toUpperCase())}</div><span style="font-size:12px">${esc(_bn)}</span>${tps}</div>`;
|
||||||
}
|
}
|
||||||
function _setAssistantTurnTps(turn, tpsText=''){
|
function _setAssistantTurnTps(turn, tpsText=''){
|
||||||
@@ -3206,7 +3209,7 @@ function _setAssistantTurnTps(turn, tpsText=''){
|
|||||||
chip.textContent=text;
|
chip.textContent=text;
|
||||||
}
|
}
|
||||||
function _setLiveAssistantTps(value){
|
function _setLiveAssistantTps(value){
|
||||||
_setAssistantTurnTps($('liveAssistantTurn'), _formatTurnTps(value));
|
_setAssistantTurnTps($('liveAssistantTurn'), isTpsDisplayEnabled()?_formatTurnTps(value):'');
|
||||||
}
|
}
|
||||||
function _createAssistantTurn(tsTitle='', tpsText=''){
|
function _createAssistantTurn(tsTitle='', tpsText=''){
|
||||||
const row=document.createElement('div');
|
const row=document.createElement('div');
|
||||||
@@ -3862,7 +3865,7 @@ function renderMessages(){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!currentAssistantTurn){
|
if(!currentAssistantTurn){
|
||||||
currentAssistantTurn=_createAssistantTurn(tsTitle, _formatTurnTps(m._turnTps));
|
currentAssistantTurn=_createAssistantTurn(tsTitle, isTpsDisplayEnabled()?_formatTurnTps(m._turnTps):'');
|
||||||
inner.appendChild(currentAssistantTurn);
|
inner.appendChild(currentAssistantTurn);
|
||||||
}
|
}
|
||||||
const seg=document.createElement('div');
|
const seg=document.createElement('div');
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
Mirrors the structure of test_1003_appearance_autosave.py to verify the
|
Mirrors the structure of test_1003_appearance_autosave.py to verify the
|
||||||
preferences-panel autosave pattern is wired correctly:
|
preferences-panel autosave pattern is wired correctly:
|
||||||
|
|
||||||
- All 13 preference fields use _schedulePreferencesAutosave (not _markSettingsDirty)
|
- All 14 preference fields use _schedulePreferencesAutosave (not _markSettingsDirty)
|
||||||
- Password field MUST still call _markSettingsDirty (security: never autosave)
|
- Password field MUST still call _markSettingsDirty (security: never autosave)
|
||||||
- _preferencesPayloadFromUi covers all 13 fields
|
- _preferencesPayloadFromUi covers all 14 fields
|
||||||
- _setPreferencesAutosaveStatus uses the shared i18n keys
|
- _setPreferencesAutosaveStatus uses the shared i18n keys
|
||||||
- Status div exists in static/index.html
|
- Status div exists in static/index.html
|
||||||
- _autosavePreferencesSettings clears the dirty flag and hides the unsaved bar
|
- _autosavePreferencesSettings clears the dirty flag and hides the unsaved bar
|
||||||
@@ -38,6 +38,7 @@ PREFERENCE_FIELDS_AUTOSAVE = [
|
|||||||
("settingsSendKey", "send_key"),
|
("settingsSendKey", "send_key"),
|
||||||
("settingsLanguage", "language"),
|
("settingsLanguage", "language"),
|
||||||
("settingsShowTokenUsage", "show_token_usage"),
|
("settingsShowTokenUsage", "show_token_usage"),
|
||||||
|
("settingsShowTps", "show_tps"),
|
||||||
("settingsSimplifiedToolCalling", "simplified_tool_calling"),
|
("settingsSimplifiedToolCalling", "simplified_tool_calling"),
|
||||||
("settingsShowCliSessions", "show_cli_sessions"),
|
("settingsShowCliSessions", "show_cli_sessions"),
|
||||||
("settingsSyncInsights", "sync_to_insights"),
|
("settingsSyncInsights", "sync_to_insights"),
|
||||||
@@ -51,8 +52,8 @@ PREFERENCE_FIELDS_AUTOSAVE = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_all_13_preference_fields_have_autosave_payload_entries():
|
def test_all_14_preference_fields_have_autosave_payload_entries():
|
||||||
"""_preferencesPayloadFromUi must include all 13 preference fields."""
|
"""_preferencesPayloadFromUi must include all 14 preference fields."""
|
||||||
block = _function_block(PANELS_JS, "_preferencesPayloadFromUi")
|
block = _function_block(PANELS_JS, "_preferencesPayloadFromUi")
|
||||||
for dom_id, field in PREFERENCE_FIELDS_AUTOSAVE:
|
for dom_id, field in PREFERENCE_FIELDS_AUTOSAVE:
|
||||||
assert f"$('{dom_id}')" in block, \
|
assert f"$('{dom_id}')" in block, \
|
||||||
@@ -62,7 +63,7 @@ def test_all_13_preference_fields_have_autosave_payload_entries():
|
|||||||
|
|
||||||
|
|
||||||
def test_preference_fields_use_schedule_autosave_not_mark_dirty():
|
def test_preference_fields_use_schedule_autosave_not_mark_dirty():
|
||||||
"""All 12 listener attachments (excluding bot_name's debounce wrapper) must
|
"""All 13 listener attachments (excluding bot_name's debounce wrapper) must
|
||||||
use _schedulePreferencesAutosave. bot_name uses a wrapper but still
|
use _schedulePreferencesAutosave. bot_name uses a wrapper but still
|
||||||
eventually calls _schedulePreferencesAutosave."""
|
eventually calls _schedulePreferencesAutosave."""
|
||||||
panel = _load_settings_panel_block()
|
panel = _load_settings_panel_block()
|
||||||
|
|||||||
@@ -8,8 +8,12 @@ Product decision:
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
REPO = Path(__file__).resolve().parent.parent
|
REPO = Path(__file__).resolve().parent.parent
|
||||||
|
CONFIG_PY = (REPO / "api" / "config.py").read_text(encoding="utf-8")
|
||||||
STREAMING_PY = (REPO / "api" / "streaming.py").read_text(encoding="utf-8")
|
STREAMING_PY = (REPO / "api" / "streaming.py").read_text(encoding="utf-8")
|
||||||
|
BOOT_JS = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
|
||||||
|
INDEX_HTML = (REPO / "static" / "index.html").read_text(encoding="utf-8")
|
||||||
MESSAGES_JS = (REPO / "static" / "messages.js").read_text(encoding="utf-8")
|
MESSAGES_JS = (REPO / "static" / "messages.js").read_text(encoding="utf-8")
|
||||||
|
PANELS_JS = (REPO / "static" / "panels.js").read_text(encoding="utf-8")
|
||||||
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
|
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
|
||||||
CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
|
CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
|
||||||
|
|
||||||
@@ -57,3 +61,54 @@ def test_backend_marks_streaming_metering_availability_explicitly():
|
|||||||
assert "record_token(stream_id, len(STREAM_PARTIAL_TEXT[stream_id]))" not in STREAMING_PY, (
|
assert "record_token(stream_id, len(STREAM_PARTIAL_TEXT[stream_id]))" not in STREAMING_PY, (
|
||||||
"live TPS must not be derived from streamed character count / byte-size estimates"
|
"live TPS must not be derived from streamed character count / byte-size estimates"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tps_display_setting_is_default_off_and_persisted():
|
||||||
|
assert '"show_tps": False' in CONFIG_PY, "TPS display should be disabled by default"
|
||||||
|
assert '"show_tps"' in CONFIG_PY and "_SETTINGS_BOOL_KEYS" in CONFIG_PY, (
|
||||||
|
"TPS display should be a persisted boolean WebUI setting"
|
||||||
|
)
|
||||||
|
assert "settingsShowTps" in INDEX_HTML, "Preferences needs a user-facing TPS display toggle"
|
||||||
|
assert "payload.show_tps=showTpsCb.checked" in PANELS_JS, (
|
||||||
|
"Preferences autosave should persist the TPS display toggle through /api/settings"
|
||||||
|
)
|
||||||
|
assert "showTpsCb.checked=!!settings.show_tps" in PANELS_JS, (
|
||||||
|
"Settings panel should hydrate the TPS toggle from persisted settings"
|
||||||
|
)
|
||||||
|
assert "window._showTps=!!s.show_tps" in BOOT_JS, (
|
||||||
|
"Boot should hydrate show_tps into a runtime flag"
|
||||||
|
)
|
||||||
|
assert "window._showTps=false" in BOOT_JS, (
|
||||||
|
"Boot fallback should keep TPS hidden when settings cannot load"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tps_display_hot_applies_when_preferences_autosave():
|
||||||
|
fn_start = PANELS_JS.find("async function _autosavePreferencesSettings")
|
||||||
|
assert fn_start != -1, "preferences autosave function should exist"
|
||||||
|
fn_end = PANELS_JS.find("function _retryPreferencesAutosave", fn_start)
|
||||||
|
assert fn_end != -1, "retry function should follow preferences autosave"
|
||||||
|
fn = PANELS_JS[fn_start:fn_end]
|
||||||
|
assert "payload&&payload.show_tps!==undefined" in fn, (
|
||||||
|
"TPS preference autosave must detect the show_tps field specifically"
|
||||||
|
)
|
||||||
|
assert "window._showTps=!!(saved&&saved.show_tps)" in fn, (
|
||||||
|
"TPS preference autosave should update the runtime flag from the saved response"
|
||||||
|
)
|
||||||
|
assert "clearMessageRenderCache" in fn and "renderMessages" in fn, (
|
||||||
|
"TPS preference autosave should re-render the open transcript without refresh"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_tps_header_rendering_respects_display_setting():
|
||||||
|
assert "function isTpsDisplayEnabled()" in UI_JS, "TPS visibility should be centralized"
|
||||||
|
assert "return window._showTps===true" in UI_JS, "TPS should only render when explicitly enabled"
|
||||||
|
assert "const tps=(isTpsDisplayEnabled()&&tpsText)" in UI_JS, (
|
||||||
|
"settled assistant headers must suppress TPS when the setting is off"
|
||||||
|
)
|
||||||
|
assert "isTpsDisplayEnabled()?_formatTurnTps(value):''" in UI_JS, (
|
||||||
|
"live TPS updates must remove/suppress the chip when the setting is off"
|
||||||
|
)
|
||||||
|
assert "isTpsDisplayEnabled()?_formatTurnTps(m._turnTps):''" in UI_JS, (
|
||||||
|
"reloaded assistant messages must not render persisted TPS while disabled"
|
||||||
|
)
|
||||||
Reference in new issue
Block a user