Modular async pipeline: FrameSource->Vision->context and STT/text->Brain->TTS. All stages are Protocols; mock backends run end-to-end with no deps/keys. Real backends included: mss screen capture, Claude vision+brain (guarded imports).
35 lines
1.1 KiB
Python
35 lines
1.1 KiB
Python
"""Shared, thread/async-safe screen context.
|
|
|
|
The perception loop keeps writing the latest ScreenObservation here; the
|
|
conversation loop reads it when the user says something. We only keep the most
|
|
recent observation plus a short ring buffer of recent ones so the Brain can
|
|
notice "the screen changed" without us re-sending every frame.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from collections import deque
|
|
|
|
from .interfaces import ScreenObservation
|
|
|
|
|
|
class SharedScreenContext:
|
|
def __init__(self, history: int = 8) -> None:
|
|
self._latest: ScreenObservation | None = None
|
|
self._recent: deque[ScreenObservation] = deque(maxlen=history)
|
|
self._lock = asyncio.Lock()
|
|
|
|
async def update(self, obs: ScreenObservation) -> None:
|
|
async with self._lock:
|
|
self._latest = obs
|
|
self._recent.append(obs)
|
|
|
|
async def latest(self) -> ScreenObservation | None:
|
|
async with self._lock:
|
|
return self._latest
|
|
|
|
async def recent(self) -> list[ScreenObservation]:
|
|
async with self._lock:
|
|
return list(self._recent)
|