"""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)