mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-07-21 07:00:23 +00:00
Includes a showToast arg-order fix (4000ms,'warning') caught at gate. Co-authored-by: rodboev <rodboev@users.noreply.github.com> Closes #4456
This commit is contained in:
@@ -3,6 +3,12 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.51.509] — 2026-06-19 — Release RT (notice when the connected gateway can't do approvals)
|
||||
|
||||
### Added
|
||||
|
||||
- **The chat now shows a one-time notice when the connected Hermes gateway is too old to support command approvals (#4300).** On the legacy gateway path, if the gateway lacks approval capability, a single per-session warning ("Approvals require a newer gateway — upgrade the connected Hermes gateway to enable this") is surfaced instead of approvals silently doing nothing. Localized in all bundled locales. Thanks @rodboev.
|
||||
|
||||
## [v0.51.508] — 2026-06-19 — Release RS (the /model command works under a reverse-proxy subpath)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -584,6 +584,18 @@ def _run_gateway_chat_streaming(
|
||||
if final_text is None:
|
||||
return
|
||||
else:
|
||||
# Legacy gateway path: emit unsupported approval notice once per session,
|
||||
# but only when the gateway genuinely lacks approval capability.
|
||||
if not gateway_supports_approval(base_url, api_key):
|
||||
if not hasattr(s, "_approval_notice_emitted"):
|
||||
s._approval_notice_emitted = False
|
||||
if not s._approval_notice_emitted:
|
||||
put_gateway_event("warning", {
|
||||
"type": "approval_gateway_unsupported",
|
||||
"message": "Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.",
|
||||
})
|
||||
s._approval_notice_emitted = True
|
||||
|
||||
url = f"{base_url}/v1/chat/completions"
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
|
||||
@@ -156,6 +156,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Deny — do not run this command',
|
||||
approval_responding: 'Responding\u2026',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Approvals not supported',
|
||||
clarify_heading: 'Clarification needed',
|
||||
clarify_hint: 'Pick a choice, or type your own answer below.',
|
||||
clarify_other: 'Other',
|
||||
@@ -1668,6 +1669,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Nega — non eseguire questo comando',
|
||||
approval_responding: 'Rispondendo\u2026',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Approvazioni non supportate',
|
||||
clarify_heading: 'Chiarimento necessario',
|
||||
clarify_hint: "Scegli un'opzione o scrivi la tua risposta qui sotto.",
|
||||
clarify_other: 'Altro',
|
||||
@@ -3171,6 +3173,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: '拒否 — このコマンドを実行しない',
|
||||
approval_responding: '応答中…',
|
||||
approval_gateway_unsupported: '承認を実行するには新しいゲートウェイが必要です。有効にするには接続されている Hermes ゲートウェイをアップグレードしてください。',
|
||||
approval_gateway_unsupported_label: '承認はサポートされていません',
|
||||
clarify_heading: '確認が必要',
|
||||
clarify_hint: '選択肢を選ぶか、下に独自の回答を入力してください。',
|
||||
clarify_other: 'その他',
|
||||
@@ -4662,6 +4665,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Запретить — не выполнять эту команду',
|
||||
approval_responding: 'Отвечаю…',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Одобрение не поддерживается',
|
||||
untitled: 'Без названия',
|
||||
n_messages: (n) => `${n} сообщений`,
|
||||
load_older_messages: '↑ Прокрутите вверх или нажмите, чтобы загрузить ранние сообщения',
|
||||
@@ -6098,6 +6102,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Denegar — no ejecutar este comando',
|
||||
approval_responding: 'Respondiendo…',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Las aprobaciones no son compatibles',
|
||||
clarify_heading: 'Se necesita aclaración',
|
||||
clarify_hint: 'Elige una opción o escribe tu propia respuesta abajo.',
|
||||
clarify_other: 'Otra',
|
||||
@@ -7537,6 +7542,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Ablehnen \u2014 diesen Befehl nicht ausführen',
|
||||
approval_responding: 'Antwortet\u2026',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Genehmigungen nicht unterstützt',
|
||||
clarify_heading: 'Klärung erforderlich',
|
||||
clarify_hint: 'Wähle eine Option oder schreibe deine eigene Antwort unten.',
|
||||
clarify_other: 'Andere',
|
||||
@@ -8980,6 +8986,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: '拒绝 — 不执行此命令',
|
||||
approval_responding: '处理中…',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: '不支持批准',
|
||||
clarify_heading: '需要澄清',
|
||||
clarify_hint: '请选择一个选项,或在下方输入你自己的回答。',
|
||||
clarify_other: '其他',
|
||||
@@ -10444,6 +10451,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: '拒絕,不要執行這道命令',
|
||||
approval_responding: '正在回應……',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: '不支援核准',
|
||||
clarify_heading: '需要釐清',
|
||||
clarify_hint: '請選擇一個選項,或在下方輸入回答。',
|
||||
clarify_other: '其他',
|
||||
@@ -11857,6 +11865,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Negar — não executar este comando',
|
||||
approval_responding: 'Respondendo…',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Aprovações não suportadas',
|
||||
clarify_heading: 'Esclarecimento necessário',
|
||||
clarify_hint: 'Escolha uma opção ou digite sua resposta abaixo.',
|
||||
clarify_other: 'Outro',
|
||||
@@ -13246,6 +13255,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: '거부 — 이 명령을 실행하지 않음',
|
||||
approval_responding: '응답 중\u2026',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: '승인이 지원되지 않음',
|
||||
clarify_heading: '확인 필요',
|
||||
clarify_hint: '선택지를 고르거나 아래에 직접 입력하세요.',
|
||||
clarify_other: '기타',
|
||||
@@ -14757,6 +14767,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Refuser - n\'exécutez pas cette commande',
|
||||
approval_responding: 'Répondre\u2026',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Les approbations ne sont pas prises en charge',
|
||||
clarify_heading: 'Des éclaircissements sont nécessaires',
|
||||
clarify_hint: 'Choisissez un choix ou saisissez votre propre réponse ci-dessous.',
|
||||
clarify_other: 'Autre',
|
||||
@@ -16196,6 +16207,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Reddet — bu komutu çalıştırma',
|
||||
approval_responding: 'Yanıt veriliyor\u2026',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Onaylar desteklenmiyor',
|
||||
clarify_heading: 'Açıklama gerekli',
|
||||
clarify_hint: 'Bir seçim yapın veya kendi cevabınızı aşağıya yazın.',
|
||||
clarify_other: 'Diğer',
|
||||
@@ -17701,6 +17713,7 @@ const LOCALES = {
|
||||
approval_btn_deny_title: 'Odmów — nie uruchamiaj tego polecenia',
|
||||
approval_responding: 'Odpowiadanie…',
|
||||
approval_gateway_unsupported: 'Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.',
|
||||
approval_gateway_unsupported_label: 'Zatwierdzenia nie są obsługiwane',
|
||||
clarify_heading: 'Wymagane wyjaśnienie',
|
||||
clarify_hint: 'Wybierz opcję lub wpisz własną odpowiedź poniżej.',
|
||||
clarify_other: 'Inne',
|
||||
|
||||
@@ -3759,6 +3759,10 @@ function attachLiveStream(activeSid, streamId, uploaded=[], options={}){
|
||||
if(!S.session||S.session.session_id!==activeSid) return;
|
||||
try{
|
||||
const d=JSON.parse(e.data);
|
||||
if(d.type==='approval_gateway_unsupported'){
|
||||
if(typeof showToast==='function') showToast(typeof t==='function'?t('approval_gateway_unsupported_label'):'Approvals not supported',4000,'warning');
|
||||
return;
|
||||
}
|
||||
// Show as a small inline notice, not a full error
|
||||
setComposerStatus(`${d.message||'Warning'}`);
|
||||
// If it's a fallback notice, show it briefly then clear
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
"""Regression coverage for #4300 legacy gateway approval unsupported notice."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[1]
|
||||
GATEWAY_CHAT = (REPO / "api" / "gateway_chat.py").read_text(encoding="utf-8")
|
||||
MESSAGES_JS = (REPO / "static" / "messages.js").read_text(encoding="utf-8")
|
||||
I18N_JS = (REPO / "static" / "i18n.js").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def test_gateway_chat_has_approval_notice_emitted_attribute_check():
|
||||
"""Verify _approval_notice_emitted attribute is checked before emitting."""
|
||||
assert "if not hasattr(s, \"_approval_notice_emitted\"):" in GATEWAY_CHAT
|
||||
assert "s._approval_notice_emitted = False" in GATEWAY_CHAT
|
||||
|
||||
|
||||
def test_gateway_chat_emits_approval_gateway_unsupported_event():
|
||||
"""Verify put_gateway_event is called with approval_gateway_unsupported type on non-terminal channel."""
|
||||
assert "put_gateway_event(\"warning\"" in GATEWAY_CHAT
|
||||
assert "\"type\": \"approval_gateway_unsupported\"" in GATEWAY_CHAT
|
||||
|
||||
|
||||
def test_gateway_chat_once_per_session_guard_pattern():
|
||||
"""Verify the once-per-session guard: capability check + hasattr + flag check + flag set."""
|
||||
assert "if not gateway_supports_approval(base_url, api_key):" in GATEWAY_CHAT
|
||||
assert "if not hasattr(s, \"_approval_notice_emitted\"):" in GATEWAY_CHAT
|
||||
assert "if not s._approval_notice_emitted:" in GATEWAY_CHAT
|
||||
assert "s._approval_notice_emitted = True" in GATEWAY_CHAT
|
||||
# Verify order: capability gate before session guard before flag set
|
||||
cap_pos = GATEWAY_CHAT.find("if not gateway_supports_approval(base_url, api_key):")
|
||||
hasattr_pos = GATEWAY_CHAT.find("if not hasattr(s, \"_approval_notice_emitted\"):")
|
||||
flag_check_pos = GATEWAY_CHAT.find("if not s._approval_notice_emitted:")
|
||||
flag_set_pos = GATEWAY_CHAT.find("s._approval_notice_emitted = True")
|
||||
assert cap_pos < hasattr_pos < flag_check_pos < flag_set_pos
|
||||
|
||||
|
||||
def test_gateway_chat_event_payload_contains_type_and_message():
|
||||
"""Verify the event payload has type and message fields."""
|
||||
assert "\"type\": \"approval_gateway_unsupported\"" in GATEWAY_CHAT
|
||||
assert "\"message\": \"Approvals require a newer gateway. Upgrade the connected Hermes gateway to enable this.\"" in GATEWAY_CHAT
|
||||
|
||||
|
||||
def test_messages_js_handles_approval_gateway_unsupported_event():
|
||||
"""Verify client-side warning handler recognizes the event type."""
|
||||
assert "d.type==='approval_gateway_unsupported'" in MESSAGES_JS
|
||||
|
||||
|
||||
def test_messages_js_references_i18n_key_for_approval_gateway_unsupported():
|
||||
"""Verify the i18n key is referenced in messages.js."""
|
||||
# The key should be used in the message handling logic
|
||||
assert "approval_gateway_unsupported" in MESSAGES_JS
|
||||
|
||||
|
||||
def test_i18n_js_has_approval_gateway_unsupported_key():
|
||||
"""Verify the i18n key exists in at least the English locale (first occurrence)."""
|
||||
assert "approval_gateway_unsupported: 'Approvals require a newer gateway" in I18N_JS
|
||||
lines = I18N_JS.split("\n")
|
||||
en_end = next(i for i, l in enumerate(lines) if l.strip().startswith("zh:"))
|
||||
found_in_english = any(
|
||||
"approval_gateway_unsupported:" in l
|
||||
for l in lines[:en_end]
|
||||
)
|
||||
assert found_in_english, "approval_gateway_unsupported key not found in English i18n locale"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import pytest
|
||||
pytest.main([__file__, "-v"])
|
||||
Reference in New Issue
Block a user