fix: wake thinking scheduler immediately on user message
All checks were successful
Tests / lint (pull_request) Successful in 2s
Tests / test (pull_request) Successful in 1m11s

The thinking scheduler slept for a fixed 5-minute interval between
cycles, meaning user messages could wait up to ~11 minutes before
being "registered" as a thought. Replace the fixed asyncio.sleep()
with an interruptible wait using asyncio.Event that wakes immediately
when record_user_input() is called.

Fixes #582

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
kimi
2026-03-20 11:42:01 -04:00
parent 1dd0f3ffe1
commit 53d85491bd
2 changed files with 24 additions and 1 deletions

View File

@@ -169,7 +169,7 @@ async def _thinking_scheduler() -> None:
except Exception as exc:
logger.error("Thinking scheduler error: %s", exc)
await asyncio.sleep(settings.thinking_interval_seconds)
await thinking_engine.wait_for_interval(settings.thinking_interval_seconds)
async def _loop_qa_scheduler() -> None:

View File

@@ -17,6 +17,7 @@ Usage::
chain = thinking_engine.get_thought_chain(thought_id)
"""
import asyncio
import logging
import random
import re
@@ -211,6 +212,7 @@ class ThinkingEngine:
self._db_path = db_path
self._last_thought_id: str | None = None
self._last_input_time: datetime = datetime.now(UTC)
self._wake_event: asyncio.Event | None = None
# Load the most recent thought for chain continuity
try:
@@ -221,9 +223,30 @@ class ThinkingEngine:
logger.debug("Failed to load recent thought: %s", exc)
pass # Fresh start if DB doesn't exist yet
def _get_wake_event(self) -> asyncio.Event:
"""Lazily create the wake event bound to the current event loop."""
if self._wake_event is None:
self._wake_event = asyncio.Event()
return self._wake_event
def record_user_input(self) -> None:
"""Record that a user interaction occurred, resetting the idle timer."""
self._last_input_time = datetime.now(UTC)
try:
event = self._get_wake_event()
event.set()
except RuntimeError:
pass # No event loop — called from sync context
async def wait_for_interval(self, seconds: float) -> None:
"""Sleep up to *seconds*, waking early if a user message arrives."""
event = self._get_wake_event()
event.clear()
try:
await asyncio.wait_for(event.wait(), timeout=seconds)
logger.debug("Thinking scheduler woke early — user input received")
except TimeoutError:
pass # Normal expiry — full interval elapsed
def _is_idle(self) -> bool:
"""Return True if no user input has occurred within the idle timeout."""