mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-17 11:07:11 +08:00
Merge branch 'agent/scheduler-silent-mode'
This commit is contained in:
@@ -255,6 +255,12 @@ def _execute_agent_task(task: dict, agent_bridge) -> bool:
|
|||||||
logger.error(f"[Scheduler] Task {task['id']}: No result from agent execution")
|
logger.error(f"[Scheduler] Task {task['id']}: No result from agent execution")
|
||||||
return True # agent ran but produced nothing; don't loop
|
return True # agent ran but produced nothing; don't loop
|
||||||
|
|
||||||
|
if action.get("silent", False):
|
||||||
|
logger.info(
|
||||||
|
f"[Scheduler] Task {task['id']} executed successfully in silent mode"
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
from channel.channel_factory import create_channel
|
from channel.channel_factory import create_channel
|
||||||
channel = create_channel(channel_type)
|
channel = create_channel(channel_type)
|
||||||
if not channel:
|
if not channel:
|
||||||
|
|||||||
@@ -64,6 +64,11 @@ class SchedulerTool(BaseTool):
|
|||||||
"schedule_value": {
|
"schedule_value": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "调度值: cron表达式/间隔秒数/时间(+5s,+10m,+1h或ISO格式)"
|
"description": "调度值: cron表达式/间隔秒数/时间(+5s,+10m,+1h或ISO格式)"
|
||||||
|
},
|
||||||
|
"silent": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": False,
|
||||||
|
"description": "Silent mode (default false): when true, the task runs normally but its result is not pushed. Set true only when the user explicitly says they don't need the result; reminder, notification and broadcast tasks must keep it false"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["action"]
|
"required": ["action"]
|
||||||
@@ -184,6 +189,9 @@ class SchedulerTool(BaseTool):
|
|||||||
"channel_type": self.config.get("channel_type", "unknown"),
|
"channel_type": self.config.get("channel_type", "unknown"),
|
||||||
"notify_session_id": notify_session_id,
|
"notify_session_id": notify_session_id,
|
||||||
}
|
}
|
||||||
|
# silent only applies to ai_task; fixed messages always deliver
|
||||||
|
if kwargs.get("silent", False):
|
||||||
|
action["silent"] = True
|
||||||
|
|
||||||
# 针对钉钉单聊,额外存储 sender_staff_id
|
# 针对钉钉单聊,额外存储 sender_staff_id
|
||||||
msg = context.kwargs.get("msg")
|
msg = context.kwargs.get("msg")
|
||||||
@@ -216,14 +224,17 @@ class SchedulerTool(BaseTool):
|
|||||||
content_desc = f"💬 固定消息: {message}"
|
content_desc = f"💬 固定消息: {message}"
|
||||||
else:
|
else:
|
||||||
content_desc = f"🤖 AI任务: {ai_task}"
|
content_desc = f"🤖 AI任务: {ai_task}"
|
||||||
|
|
||||||
|
# Warn the user at creation time so a mistaken silent flag is easy to spot
|
||||||
|
silent_desc = "\n🔇 静默模式: 执行后不会推送结果" if action.get("silent") else ""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f"✅ 定时任务创建成功\n\n"
|
f"✅ 定时任务创建成功\n\n"
|
||||||
f"📋 任务ID: {task_id}\n"
|
f"📋 任务ID: {task_id}\n"
|
||||||
f"📝 名称: {name}\n"
|
f"📝 名称: {name}\n"
|
||||||
f"⏰ 调度: {schedule_desc}\n"
|
f"⏰ 调度: {schedule_desc}\n"
|
||||||
f"👤 接收者: {receiver_desc}\n"
|
f"👤 接收者: {receiver_desc}\n"
|
||||||
f"{content_desc}\n"
|
f"{content_desc}{silent_desc}\n"
|
||||||
f"🕐 下次执行: {next_run.strftime('%Y-%m-%d %H:%M:%S') if next_run else '未知'}"
|
f"🕐 下次执行: {next_run.strftime('%Y-%m-%d %H:%M:%S') if next_run else '未知'}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
92
tests/test_scheduler_silent.py
Normal file
92
tests/test_scheduler_silent.py
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
"""Regression tests for silent scheduled agent tasks."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
|
||||||
|
from agent.tools.scheduler.integration import _execute_agent_task
|
||||||
|
from agent.tools.scheduler.scheduler_tool import SchedulerTool
|
||||||
|
|
||||||
|
|
||||||
|
class _Context(dict):
|
||||||
|
kwargs = {}
|
||||||
|
|
||||||
|
|
||||||
|
class _TaskStore:
|
||||||
|
def __init__(self):
|
||||||
|
self.added = []
|
||||||
|
|
||||||
|
def add_task(self, task):
|
||||||
|
self.added.append(task)
|
||||||
|
|
||||||
|
|
||||||
|
class _AgentBridge:
|
||||||
|
def __init__(self, content="maintenance complete"):
|
||||||
|
self.content = content
|
||||||
|
self.calls = []
|
||||||
|
|
||||||
|
def agent_reply(self, task_description, **kwargs):
|
||||||
|
self.calls.append((task_description, kwargs))
|
||||||
|
return SimpleNamespace(content=self.content)
|
||||||
|
|
||||||
|
|
||||||
|
class TestSchedulerSilentMode(unittest.TestCase):
|
||||||
|
def test_schema_exposes_silent_for_agent_tasks(self):
|
||||||
|
silent = SchedulerTool.params["properties"]["silent"]
|
||||||
|
|
||||||
|
self.assertEqual(silent["type"], "boolean")
|
||||||
|
self.assertFalse(silent["default"])
|
||||||
|
|
||||||
|
def test_create_persists_silent_on_agent_task(self):
|
||||||
|
tool = SchedulerTool({"channel_type": "web"})
|
||||||
|
store = _TaskStore()
|
||||||
|
tool.task_store = store
|
||||||
|
tool.current_context = _Context(
|
||||||
|
receiver="user-1",
|
||||||
|
session_id="session-1",
|
||||||
|
isgroup=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = tool.execute(
|
||||||
|
{
|
||||||
|
"action": "create",
|
||||||
|
"name": "refresh token",
|
||||||
|
"ai_task": "refresh the token",
|
||||||
|
"schedule_type": "interval",
|
||||||
|
"schedule_value": "3000",
|
||||||
|
"silent": True,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(result.status, "success")
|
||||||
|
self.assertEqual(len(store.added), 1)
|
||||||
|
self.assertIs(store.added[0]["action"]["silent"], True)
|
||||||
|
|
||||||
|
def test_silent_agent_task_executes_without_delivery(self):
|
||||||
|
bridge = _AgentBridge()
|
||||||
|
task = {
|
||||||
|
"id": "task-1",
|
||||||
|
"action": {
|
||||||
|
"type": "agent_task",
|
||||||
|
"task_description": "rotate logs",
|
||||||
|
"receiver": "user-1",
|
||||||
|
"is_group": False,
|
||||||
|
"channel_type": "web",
|
||||||
|
"silent": True,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch("channel.channel_factory.create_channel") as create_channel:
|
||||||
|
result = _execute_agent_task(task, bridge)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
self.assertEqual(len(bridge.calls), 1)
|
||||||
|
create_channel.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user