mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-16 01:40:15 +08:00
fix: return an explicit erro from the cron tool when scheduling a task fails instead of processing silently(#7513)
* fix: 定时任务创建失败时返回错误信息而非静默处理 * fix: test and format --------- Co-authored-by: Soulter <905617992@qq.com>
This commit is contained in:
@@ -22,6 +22,12 @@ if TYPE_CHECKING:
|
||||
from astrbot.core.star.context import Context
|
||||
|
||||
|
||||
class CronJobSchedulingError(Exception):
|
||||
"""Raised when a cron job fails to be scheduled."""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class CronJobManager:
|
||||
"""Central scheduler for BasicCronJob and ActiveAgentCronJob."""
|
||||
|
||||
@@ -59,7 +65,10 @@ class CronJobManager:
|
||||
job.job_id,
|
||||
)
|
||||
continue
|
||||
self._schedule_job(job)
|
||||
try:
|
||||
self._schedule_job(job)
|
||||
except CronJobSchedulingError:
|
||||
continue # Error already logged in _schedule_job
|
||||
|
||||
async def add_basic_job(
|
||||
self,
|
||||
@@ -181,8 +190,9 @@ class CronJobManager:
|
||||
job.job_id, next_run_time=self._get_next_run_time(job.job_id)
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to schedule cron job {job.job_id}: {e!s}")
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.exception("Failed to schedule cron job %s", job.job_id)
|
||||
raise CronJobSchedulingError(str(e)) from e
|
||||
|
||||
def _get_next_run_time(self, job_id: str):
|
||||
aps_job = self.scheduler.get_job(job_id)
|
||||
|
||||
@@ -7,6 +7,7 @@ from pydantic.dataclasses import dataclass
|
||||
from astrbot.core.agent.run_context import ContextWrapper
|
||||
from astrbot.core.agent.tool import FunctionTool, ToolExecResult
|
||||
from astrbot.core.astr_agent_context import AstrAgentContext
|
||||
from astrbot.core.cron.manager import CronJobSchedulingError
|
||||
from astrbot.core.tools.registry import builtin_tool
|
||||
|
||||
_CRON_TOOL_CONFIG = {
|
||||
@@ -112,14 +113,17 @@ class FutureTaskTool(FunctionTool[AstrAgentContext]):
|
||||
"origin": "tool",
|
||||
}
|
||||
|
||||
job = await cron_mgr.add_active_job(
|
||||
name=name,
|
||||
cron_expression=str(cron_expression) if cron_expression else None,
|
||||
payload=payload,
|
||||
description=note,
|
||||
run_once=run_once,
|
||||
run_at=run_at_dt,
|
||||
)
|
||||
try:
|
||||
job = await cron_mgr.add_active_job(
|
||||
name=name,
|
||||
cron_expression=str(cron_expression) if cron_expression else None,
|
||||
payload=payload,
|
||||
description=note,
|
||||
run_once=run_once,
|
||||
run_at=run_at_dt,
|
||||
)
|
||||
except CronJobSchedulingError:
|
||||
return "error: failed to schedule task due to invalid configuration."
|
||||
next_run = job.next_run_time or run_at_dt
|
||||
suffix = (
|
||||
f"one-time at {next_run}"
|
||||
@@ -195,7 +199,10 @@ class FutureTaskTool(FunctionTool[AstrAgentContext]):
|
||||
updates["cron_expression"] = cron_expression
|
||||
updates["payload"] = payload
|
||||
|
||||
job = await cron_mgr.update_job(str(job_id), **updates)
|
||||
try:
|
||||
job = await cron_mgr.update_job(str(job_id), **updates)
|
||||
except CronJobSchedulingError:
|
||||
return "error: failed to update task due to invalid configuration."
|
||||
if not job:
|
||||
return f"error: cron job {job_id} not found."
|
||||
return f"Updated future task {job.job_id} ({job.name})."
|
||||
|
||||
@@ -5,7 +5,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from astrbot.core.cron.manager import CronJobManager
|
||||
from astrbot.core.cron.manager import CronJobManager, CronJobSchedulingError
|
||||
from astrbot.core.db.po import CronJob
|
||||
|
||||
|
||||
@@ -190,24 +190,25 @@ class TestAddActiveJob:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_active_job_run_once(self, cron_manager, mock_db, sample_cron_job):
|
||||
"""Test adding a run-once active job."""
|
||||
"""Test adding a run-once active job with an invalid returned job."""
|
||||
sample_cron_job.job_type = "active_agent"
|
||||
sample_cron_job.run_once = True
|
||||
mock_db.create_cron_job.return_value = sample_cron_job
|
||||
|
||||
run_at = datetime.now(timezone.utc) + timedelta(days=30)
|
||||
|
||||
result = await cron_manager.add_active_job(
|
||||
name="Test Run Once Job",
|
||||
cron_expression=None,
|
||||
payload={"session": "test:group:123"},
|
||||
run_once=True,
|
||||
run_at=run_at,
|
||||
)
|
||||
with pytest.raises(CronJobSchedulingError, match="Invalid isoformat string"):
|
||||
await cron_manager.add_active_job(
|
||||
name="Test Run Once Job",
|
||||
cron_expression=None,
|
||||
payload={"session": "test:group:123"},
|
||||
run_once=True,
|
||||
run_at=run_at,
|
||||
)
|
||||
|
||||
assert result == sample_cron_job
|
||||
call_kwargs = mock_db.create_cron_job.call_args.kwargs
|
||||
assert call_kwargs["run_once"] is True
|
||||
assert call_kwargs["payload"]["run_at"] == run_at.isoformat()
|
||||
|
||||
|
||||
class TestUpdateJob:
|
||||
|
||||
Reference in New Issue
Block a user