diff --git a/.env.example b/.env.example index 9e200d7..9a23c65 100644 --- a/.env.example +++ b/.env.example @@ -42,3 +42,7 @@ QQ_ACCESS_TOKEN= # Leave blank to allow everyone. # Example: QQ_ALLOWED_IDS=group_123456,private_987654 QQ_ALLOWED_IDS= + +# Set to true to suppress routine progress messages in QQ chats. +# Final answers, errors, files, images, and human-assistance prompts are still sent. +QQ_QUIET_MODE=false diff --git a/_agent_runner.py b/_agent_runner.py index bd35b41..5bdcbc1 100644 --- a/_agent_runner.py +++ b/_agent_runner.py @@ -17,11 +17,63 @@ from pathlib import Path from typing import Callable +from config import QQ_QUIET_MODE from message import UnifiedMessage from session import SessionStore logger = logging.getLogger(__name__) +_QUIET_QQ_MESSAGE_KEYS = { + "common.connected_ws", + "common.context_compressing", + "common.manual_compressing", + "common.agent_resumed", + "common.sent_resume", + "common.message_queued", + "common.agent_starting", + "common.agent_thinking", + "common.executing_action", + "common.takeover_browser_opened", + "common.takeover_ended_message", + "common.agent_paused_for_takeover", +} + +_QUIET_QQ_TEXT_PREFIXES = ( + "Connected to NeoFish Agent WebSocket", + "Agent is thinking", + "Agent starting task:", + "Executing action:", + "[Takeover]", + "[Takeover Ended]", +) + +_QUIET_QQ_TEXT_SNIPPETS = ( + "Context threshold reached", + "Manual compression triggered", + "已发送继续执行", + "Agent paused for manual takeover", +) + + +def _should_suppress_message_for_platform(msg, platform: str) -> bool: + """Return True for routine progress messages hidden in quiet QQ mode.""" + if platform != "qq" or not QQ_QUIET_MODE: + return False + + if isinstance(msg, dict): + if msg.get("message_key") in _QUIET_QQ_MESSAGE_KEYS: + return True + text = str(msg.get("message", "")) + else: + text = str(msg) + + if not text: + return False + + return any(text.startswith(prefix) for prefix in _QUIET_QQ_TEXT_PREFIXES) or any( + snippet in text for snippet in _QUIET_QQ_TEXT_SNIPPETS + ) + def make_message_handler(adapter, pm, session_store: SessionStore, workdir: Path = None, scheduler_service=None) -> Callable: """ @@ -124,6 +176,8 @@ async def on_message(unified_msg: UnifiedMessage) -> None: try: async def _send(msg) -> None: + if _should_suppress_message_for_platform(msg, unified_msg.platform): + return text = msg.get("message", "") if isinstance(msg, dict) else str(msg) await adapter.send_message(session_id, text) @@ -161,4 +215,4 @@ async def _send_file(file_path: str, description: str) -> None: finally: session_store.set_running(session_id, False) - return on_message \ No newline at end of file + return on_message diff --git a/config.py b/config.py index 377d9d3..1af0657 100644 --- a/config.py +++ b/config.py @@ -49,6 +49,15 @@ else [] ) +# Suppress routine agent progress messages in QQ chats while keeping final +# answers, errors, files, images, and human-assistance prompts. +QQ_QUIET_MODE: bool = os.getenv("QQ_QUIET_MODE", "").lower() in { + "1", + "true", + "yes", + "on", +} + # ── Telegram platform ───────────────────────────────────────────────────────── # Obtain a token from @BotFather on Telegram. diff --git a/test_agent_runner_quiet_mode.py b/test_agent_runner_quiet_mode.py new file mode 100644 index 0000000..7293aff --- /dev/null +++ b/test_agent_runner_quiet_mode.py @@ -0,0 +1,67 @@ +""" +Tests for platform-specific quiet message filtering. +Run: python test_agent_runner_quiet_mode.py +""" + +import _agent_runner as runner + + +def test_qq_quiet_mode_suppresses_progress_messages(): + old_value = runner.QQ_QUIET_MODE + runner.QQ_QUIET_MODE = True + try: + assert runner._should_suppress_message_for_platform( + {"message_key": "common.executing_action", "message": "Executing action: `click`"}, + "qq", + ) + assert runner._should_suppress_message_for_platform( + {"message": "Agent is thinking..."}, + "qq", + ) + finally: + runner.QQ_QUIET_MODE = old_value + + print("[PASS] test_qq_quiet_mode_suppresses_progress_messages") + + +def test_qq_quiet_mode_keeps_final_messages(): + old_value = runner.QQ_QUIET_MODE + runner.QQ_QUIET_MODE = True + try: + assert not runner._should_suppress_message_for_platform( + { + "message_key": "common.task_completed", + "message": "Task completed", + "params": {"report": "done"}, + }, + "qq", + ) + assert not runner._should_suppress_message_for_platform( + {"message_key": "common.error", "message": "Something failed"}, + "qq", + ) + finally: + runner.QQ_QUIET_MODE = old_value + + print("[PASS] test_qq_quiet_mode_keeps_final_messages") + + +def test_quiet_mode_does_not_affect_other_platforms(): + old_value = runner.QQ_QUIET_MODE + runner.QQ_QUIET_MODE = True + try: + assert not runner._should_suppress_message_for_platform( + {"message_key": "common.executing_action", "message": "Executing action: `click`"}, + "telegram", + ) + finally: + runner.QQ_QUIET_MODE = old_value + + print("[PASS] test_quiet_mode_does_not_affect_other_platforms") + + +if __name__ == "__main__": + test_qq_quiet_mode_suppresses_progress_messages() + test_qq_quiet_mode_keeps_final_messages() + test_quiet_mode_does_not_affect_other_platforms() + print("[PASS] all quiet mode tests")