diff --git a/runtime/application/gateway/inbound_service.py b/runtime/application/gateway/inbound_service.py index 1c493df2..af68933f 100644 --- a/runtime/application/gateway/inbound_service.py +++ b/runtime/application/gateway/inbound_service.py @@ -105,6 +105,7 @@ def _menu_text() -> str: def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str, session_id: str = "") -> str | None: t = (text or "").strip() + t_low = t.lower() if not t: return None if t in ("帮助", "菜单", "help", "/help"): @@ -205,14 +206,27 @@ def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str, se ok = store.scheduled_job_delete(tenant_id=tenant_id, job_id=full) return "已删除。" if ok else "未找到该定时任务。" + schedule_prefix = "" if t.startswith("记定时 ") or t.startswith("创建定时 "): - body = t.split(" ", 1)[1].strip() if " " in t else "" + schedule_prefix = t.split(" ", 1)[0] + " " + elif t_low.startswith("schedule "): + schedule_prefix = "schedule " + elif t_low.startswith("create schedule "): + schedule_prefix = "create schedule " + elif t_low.startswith("set schedule "): + schedule_prefix = "set schedule " + if schedule_prefix: + body = t[len(schedule_prefix) :].strip() parts = body.split(None, 1) if len(parts) < 2: + if schedule_prefix in {"schedule ", "create schedule ", "set schedule "}: + return "Format: schedule . Example: schedule 5min remind me to drink water" return "格式:记定时 <时间> <提醒内容>。示例:记定时 5分钟 提醒我休息" when_raw, prompt_text = parts[0].strip(), parts[1].strip() seconds = _parse_schedule_duration_seconds(when_raw) if seconds <= 0: + if schedule_prefix in {"schedule ", "create schedule ", "set schedule "}: + return "Unrecognized duration. Examples: 5min, 1hour, 300sec" return "无法识别时间。示例:5分钟、1小时、300秒" from runtime.scheduler.cron_service import build_delivery_for_session @@ -232,6 +246,8 @@ def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str, se created_by_user_id=user_id, source="chat", ) + if schedule_prefix in {"schedule ", "create schedule ", "set schedule "}: + return f"Scheduled job created: {row.id[:8]} | {prompt_text} | every {seconds} seconds" return f"已创建定时任务:{row.id[:8]} | {prompt_text} | 每 {seconds} 秒" return None diff --git a/runtime/tools/public/schedule_tools.py b/runtime/tools/public/schedule_tools.py new file mode 100644 index 00000000..c45c7d30 --- /dev/null +++ b/runtime/tools/public/schedule_tools.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +"""Scheduled job tools exposed to all specialists. + +These tools are low-risk persistence operations (create/update/pause/delete scheduled jobs) and +are useful across specialists (ops, generalist, etc.). They remain context-scoped via +`runtime.tools.context_inject.enrich_tool_arguments`, so tenant/user/session are filled from the +current chat session. +""" + +from runtime.tools.experts.productivity.schedule_tools import ( + schedule_create_tool, + schedule_delete_tool, + schedule_list_tool, + schedule_pause_tool, + schedule_resume_tool, + schedule_run_now_tool, + schedule_update_tool, +) + +__all__ = [ + "schedule_create_tool", + "schedule_delete_tool", + "schedule_list_tool", + "schedule_pause_tool", + "schedule_resume_tool", + "schedule_run_now_tool", + "schedule_update_tool", +] + diff --git a/tests/test_local_public_tools.py b/tests/test_local_public_tools.py index 3a1eb5b6..38a2c52c 100644 --- a/tests/test_local_public_tools.py +++ b/tests/test_local_public_tools.py @@ -20,6 +20,7 @@ from runtime.tools.public.set_env_tool import set_env_tool def test_local_public_read_tool_visible_by_default() -> None: clear_public_tool_cache() names = [t.name for t in default_registry(expert="network_ops+memory", specialist="ops").list()] + assert "schedule_create" in names assert "read_file" in names assert "list_directory" in names assert "search_files" in names