class CancelScope: """Unified cancellation signal for the speech-to-speech pipeline. Uses a generation counter so pipeline threads (LLM, TTS) can detect cancellation without brief-pulse timing games, and an internal ``discarding`` flag so the async send loop can drop stale output. Thread safety: one writer (the asyncio router thread) and multiple readers (pipeline handler threads). Python's GIL makes int/bool reads and writes atomic at the bytecode level, so no lock is needed. """ def __init__(self) -> None: self._gen: int = 0 self._discarding: bool = False self._discarded_generation: int | None = None @property def generation(self) -> int: """Current generation number. Pipeline threads capture this at the start of each response and compare with ``is_stale``.""" return self._gen def cancel(self) -> None: """Cancel the current response. Increments the generation (so pipeline threads see their captured generation as stale) and enables the send-loop discard guard. """ # prevent overflow... after 4 billion generations, we'll wrap around xD... self._discarded_generation = self._gen self._gen = (self._gen + 1) & 0xFFFFFFFF self._discarding = True def response_done(self, generation: int | None = None) -> None: """Pipeline acknowledged completion. Clears the discard guard.""" if ( generation is not None and self._discarded_generation is not None and generation not in {self._discarded_generation, self._gen} ): return self._discarding = False self._discarded_generation = None def new_response(self) -> None: """An explicit ``response.create`` starts a new response. Clears the discard guard.""" self._discarding = False self._discarded_generation = None def is_stale(self, gen: int) -> bool: """Return True if *gen* has been superseded by a ``cancel`` call.""" return gen != self._gen @property def discarding(self) -> bool: """Whether the send loop should silently drop stale output.""" return self._discarding def reset(self) -> None: """Clear discard state (e.g. on new session connect).""" self._discarding = False self._discarded_generation = None