fix: wake thinking scheduler immediately on user message
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:
@@ -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:
|
||||
|
||||
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user