From d0eba35e80422ec0c1165eb6558325ea8aa61190 Mon Sep 17 00:00:00 2001 From: cfdaily Date: Tue, 19 May 2026 23:07:37 +0800 Subject: [PATCH] auto-sync: 2026-05-19 23:07:37 --- src/daemon/ticker.py | 59 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/daemon/ticker.py b/src/daemon/ticker.py index 320764f..7d1697e 100644 --- a/src/daemon/ticker.py +++ b/src/daemon/ticker.py @@ -40,6 +40,9 @@ class Ticker: max_dispatch_per_tick: int = 3, claim_timeout_minutes: float = 5.0, default_task_timeout_minutes: float = 30.0, + health_checker: Optional[Any] = None, + inbox_watcher: Optional[Any] = None, + experience_distiller: Optional[Any] = None, ): """ Args: @@ -76,6 +79,14 @@ class Ticker: # 当前项目 ID(_dispatch_pending 需要) self._current_project_id: Optional[str] = None + # 集成模块 + self.health_checker = health_checker + self.inbox_watcher = inbox_watcher + self.experience_distiller = experience_distiller + + # InboxWatcher 即时 tick 事件 + self._inbox_event: asyncio.Event = asyncio.Event() + # ------------------------------------------------------------------ # 生命周期 # ------------------------------------------------------------------ @@ -85,12 +96,30 @@ class Ticker: if self._running: return self._running = True + + # 启动 InboxWatcher + if self.inbox_watcher: + try: + await self.inbox_watcher.start() + logger.info("InboxWatcher started") + except Exception: + logger.exception("Failed to start InboxWatcher") + self._task = asyncio.create_task(self._loop()) logger.info("Ticker started (interval=%.1fs)", self.tick_interval) async def stop(self) -> None: """停止 tick 循环""" self._running = False + + # 停止 InboxWatcher + if self.inbox_watcher: + try: + await self.inbox_watcher.stop() + logger.info("InboxWatcher stopped") + except Exception: + logger.exception("Failed to stop InboxWatcher") + if self._task: self._task.cancel() try: @@ -126,8 +155,16 @@ class Ticker: self._running = False break + # 等待下一个 tick,但 InboxWatcher 事件可提前唤醒 try: - await asyncio.sleep(self.tick_interval) + await asyncio.wait_for( + self._inbox_event.wait(), + timeout=self.tick_interval, + ) + self._inbox_event.clear() + logger.debug("Immediate tick triggered by inbox event") + except asyncio.TimeoutError: + pass # 正常超时,继续下一次 tick except asyncio.CancelledError: return @@ -241,6 +278,26 @@ class Ticker: # 8. 扫描后状态 result["summary_after"] = queries.task_summary() + # 9. HealthChecker 僵尸检测 + if self.health_checker: + try: + health_result = self.health_checker.check( + project_id, db_path, self._tick_count) + result["health"] = health_result + if not health_result.get("healthy", True): + logger.warning("Health check: %s unhealthy (zombie=%s)", + project_id, health_result.get("zombie")) + except Exception: + logger.exception("HealthChecker error for %s", project_id) + + # 10. ExperienceDistiller 经验蒸馏 + if self.experience_distiller: + try: + distilled = self._distill_completed_tasks(db_path, project_id) + result["distilled"] = distilled + except Exception: + logger.exception("ExperienceDistiller error for %s", project_id) + return result # ------------------------------------------------------------------