Fix Agent task relative-time validation (#6146)

This commit is contained in:
jxxghp
2026-07-19 21:07:27 +08:00
committed by GitHub
parent 31544629b4
commit b0ff3ae3c7
3 changed files with 50 additions and 26 deletions

View File

@@ -60,13 +60,12 @@ class CreateAgentTaskInput(BaseModel):
if not self.name or not self.content:
raise ValueError("name 和 content 不能只包含空白字符")
if self.trigger_type == "date":
if (self.trigger is None) == (self.delay_minutes is None):
raise ValueError("date 任务必须且只能提供 trigger 或 delay_minutes 之一")
if self.delay_minutes is not None:
timezone = pytz.timezone(settings.TZ)
self.trigger = (
datetime.now(timezone) + timedelta(minutes=self.delay_minutes)
).isoformat(timespec="seconds")
# LangChain 会在 run() 前后各校验一次,延迟时间在持久化前统一计算。
self.trigger = None
return self
if self.trigger is None:
raise ValueError("date 任务必须提供 trigger 或 delay_minutes")
elif self.trigger is None or self.delay_minutes is not None:
raise ValueError("cron 任务必须提供 trigger且不能提供 delay_minutes")
self.trigger_type, self.trigger = TimerUtils.normalize_schedule_trigger(
@@ -102,6 +101,18 @@ class CreateAgentTaskTool(MoviePilotTool):
"""持久化任务并立即注册到运行时调度器。"""
from app.scheduler import Scheduler
trigger_value = payload.trigger
if payload.trigger_type == "date" and payload.delay_minutes is not None:
timezone = pytz.timezone(settings.TZ)
trigger_value = (
datetime.now(timezone) + timedelta(minutes=payload.delay_minutes)
).isoformat(timespec="seconds")
_, trigger_value = TimerUtils.normalize_schedule_trigger(
trigger_type=payload.trigger_type,
trigger_value=trigger_value,
timezone_name=settings.TZ,
require_future=True,
)
chat = AgentChatOper().get(
session_id=self._session_id,
user_id=self._user_id,
@@ -110,8 +121,8 @@ class CreateAgentTaskTool(MoviePilotTool):
name=payload.name.strip(),
content=payload.content.strip(),
trigger_type=payload.trigger_type,
cron_expression=payload.trigger if payload.trigger_type == "cron" else None,
run_at=payload.trigger if payload.trigger_type == "date" else None,
cron_expression=trigger_value if payload.trigger_type == "cron" else None,
run_at=trigger_value if payload.trigger_type == "date" else None,
user_id=str(self._user_id),
username=self._username or (chat.username if chat else None),
session_id=str(self._session_id),

View File

@@ -61,13 +61,11 @@ class UpdateAgentTaskInput(BaseModel):
if self.trigger_type is None:
raise ValueError("修改触发配置时必须提供 trigger_type")
if self.trigger_type == "date":
if (self.trigger is None) == (self.delay_minutes is None):
raise ValueError("date 任务必须且只能提供 trigger 或 delay_minutes 之一")
if self.delay_minutes is not None:
timezone = pytz.timezone(settings.TZ)
self.trigger = (
datetime.now(timezone) + timedelta(minutes=self.delay_minutes)
).isoformat(timespec="seconds")
# 保持校验幂等,具体绝对时间在更新调度前只计算一次。
self.trigger = None
elif self.trigger is None:
raise ValueError("date 任务必须提供 trigger 或 delay_minutes")
elif self.trigger is None or self.delay_minutes is not None:
raise ValueError("cron 任务必须提供 trigger且不能提供 delay_minutes")
if all(
@@ -112,9 +110,16 @@ class UpdateAgentTaskTool(MoviePilotTool):
return {"error": f"Agent 定时任务 {payload.task_id} 正在执行,请稍后再修改"}
trigger_type = payload.trigger_type or task.trigger_type
trigger_value = payload.trigger or (
task.cron_expression if trigger_type == "cron" else task.run_at
)
trigger_value = payload.trigger
if trigger_type == "date" and payload.delay_minutes is not None:
timezone = pytz.timezone(settings.TZ)
trigger_value = (
datetime.now(timezone) + timedelta(minutes=payload.delay_minutes)
).isoformat(timespec="seconds")
if trigger_value is None:
trigger_value = (
task.cron_expression if trigger_type == "cron" else task.run_at
)
enabled = task.enabled if payload.enabled is None else payload.enabled
normalized_type, normalized_trigger = TimerUtils.normalize_schedule_trigger(
trigger_type=trigger_type,
@@ -128,7 +133,7 @@ class UpdateAgentTaskTool(MoviePilotTool):
update_payload["name"] = payload.name.strip()
if payload.content is not None:
update_payload["content"] = payload.content.strip()
if payload.trigger is not None:
if payload.trigger_type is not None:
update_payload.update(
{
"trigger_type": normalized_type,

View File

@@ -195,14 +195,12 @@ async def test_agent_task_tools_manage_persistent_schedule(monkeypatch) -> None:
monkeypatch.setattr("app.scheduler.Scheduler", lambda: fake_scheduler)
create_tool = _build_tool(CreateAgentTaskTool, user_id)
created = json.loads(
await create_tool.run(
name="十分钟后检查",
content="检查示例电影是否有资源,不要自动下载",
trigger_type="date",
delay_minutes=10,
)
)
created = json.loads(await create_tool.ainvoke({
"name": "十分钟后检查",
"content": "检查示例电影是否有资源,不要自动下载",
"trigger_type": "date",
"delay_minutes": 10,
}))
task_id = created["id"]
assert created["enabled"] is True
assert datetime.fromisoformat(created["run_at"]) > datetime.now(
@@ -217,6 +215,16 @@ async def test_agent_task_tools_manage_persistent_schedule(monkeypatch) -> None:
assert queried["tasks"][0]["content"].startswith("检查示例电影")
update_tool = _build_tool(UpdateAgentTaskTool, user_id)
delayed_update = json.loads(await update_tool.ainvoke({
"task_id": task_id,
"trigger_type": "date",
"delay_minutes": 20,
}))
assert delayed_update["trigger_type"] == "date"
assert datetime.fromisoformat(delayed_update["run_at"]) > datetime.now(
pytz.timezone(settings.TZ)
)
updated = json.loads(
await update_tool.run(
task_id=task_id,