53 lines
1.7 KiB
Python
53 lines
1.7 KiB
Python
"""Gateway response filtering helpers.
|
|
|
|
These helpers operate at the gateway boundary: they decide whether a completed
|
|
agent turn should be delivered to the chat, not what should be persisted in the
|
|
conversation history.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
# Canonical model-emitted control token for intentional silence.
|
|
SILENT_REPLY_TOKEN = "NO_REPLY"
|
|
|
|
# Exact whole-response markers that mean "the agent intentionally chose not to
|
|
# reply". Keep this list small and explicit; arbitrary empty output remains an
|
|
# error/empty-response path, not silence.
|
|
LIVE_GATEWAY_SILENT_MARKERS = frozenset({
|
|
"[SILENT]",
|
|
"SILENT",
|
|
"NO_REPLY",
|
|
"NO REPLY",
|
|
})
|
|
|
|
|
|
def _canonical_silence_candidate(text: str) -> str:
|
|
return " ".join(text.strip().upper().split())
|
|
|
|
|
|
def is_intentional_silence_response(response: Any) -> bool:
|
|
"""Return True only when ``response`` is exactly a silence marker.
|
|
|
|
Substantive prose that merely mentions ``NO_REPLY`` or ``[SILENT]`` must be
|
|
delivered normally. A blank response is also not silence; blank output is
|
|
handled by the empty-response failure path.
|
|
"""
|
|
if not isinstance(response, str):
|
|
return False
|
|
stripped = response.strip()
|
|
if not stripped:
|
|
return False
|
|
if len(stripped) > 64:
|
|
return False
|
|
return _canonical_silence_candidate(stripped) in LIVE_GATEWAY_SILENT_MARKERS
|
|
|
|
|
|
def is_intentional_silence_agent_result(agent_result: dict | None, response: Any) -> bool:
|
|
"""Silence markers suppress delivery only for successful agent turns."""
|
|
if not isinstance(agent_result, dict):
|
|
return False
|
|
if agent_result.get("failed"):
|
|
return False
|
|
return is_intentional_silence_response(response)
|