Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -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
56 changes: 55 additions & 1 deletion _agent_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
return on_message
9 changes: 9 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
67 changes: 67 additions & 0 deletions test_agent_runner_quiet_mode.py
Original file line number Diff line number Diff line change
@@ -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")