diff --git a/api/gateway_chat.py b/api/gateway_chat.py index f641b15d..35c7dd03 100644 --- a/api/gateway_chat.py +++ b/api/gateway_chat.py @@ -142,6 +142,18 @@ def _gateway_stream_usage(payload: dict) -> dict: } +def _gateway_reasoning_delta(payload: dict) -> str: + if not isinstance(payload, dict): + return "" + return str( + payload.get("text") + or payload.get("preview") + or payload.get("delta") + or payload.get("content") + or "" + ).strip() + + def _gateway_tool_progress_event(payload: dict) -> tuple[str, dict] | None: """Translate Hermes Gateway tool-progress SSE payloads to WebUI events.""" if not isinstance(payload, dict): @@ -150,7 +162,7 @@ def _gateway_tool_progress_event(payload: dict) -> tuple[str, dict] | None: if not name: return None if name == "_thinking": - reason_delta = str(payload.get("preview") or payload.get("delta") or "").strip() + reason_delta = _gateway_reasoning_delta(payload) if not reason_delta: return None return "reasoning", {"text": reason_delta} @@ -388,14 +400,7 @@ def _run_gateway_chat_streaming( sse_event = "message" continue if sse_event == "reasoning.available": - reason_delta = ( - payload.get("text") - or payload.get("preview") - or payload.get("delta") - or payload.get("content") - or "" - ) - reason_delta = str(reason_delta).strip() + reason_delta = _gateway_reasoning_delta(payload) if reason_delta: if stream_id in STREAM_REASONING_TEXT: STREAM_REASONING_TEXT[stream_id] += reason_delta diff --git a/tests/test_webui_gateway_chat_backend.py b/tests/test_webui_gateway_chat_backend.py index fb54dbfb..7a3e574c 100644 --- a/tests/test_webui_gateway_chat_backend.py +++ b/tests/test_webui_gateway_chat_backend.py @@ -138,6 +138,14 @@ def test_gateway_tool_progress_event_translates_gateway_lifecycle_payloads(): "text": "Thinking...", }, ) + assert _gateway_tool_progress_event( + {"tool": "_thinking", "status": "running", "text": "Thinking from text..."} + ) == ( + "reasoning", + { + "text": "Thinking from text...", + }, + ) assert _gateway_tool_progress_event({"tool": "_thinking", "status": "running"}) is None @@ -242,6 +250,8 @@ def test_gateway_chat_worker_translates_sse_and_persists_session(tmp_path, monke yield b'event: hermes.tool.progress\n' yield b'data: {"tool":"terminal","label":"terminal: pytest","toolCallId":"call-1","status":"running"}\n\n' yield b'data: {"choices":[{"delta":{"content":"hel"}}]}\n\n' + yield b'event: hermes.tool.progress\n' + yield b'data: {"tool":"_thinking","text":"Thinking from tool progress"}\n\n' yield b'event: reasoning.available\n' yield b'data: {"text":"Reasoning preview", "preview":"Reasoning preview"}\n\n' yield b'event: hermes.tool.progress\n' @@ -334,6 +344,7 @@ def test_gateway_chat_worker_translates_sse_and_persists_session(tmp_path, monke "is_error": False, "tid": "call-1", }) in event_pairs + assert ("reasoning", {"text": "Thinking from tool progress"}) in event_pairs assert ("reasoning", {"text": "Reasoning preview"}) in event_pairs assert ("tool_complete", { "event_type": "tool.completed",