Files
chatgpt-on-wechat/agent/protocol/steer.py
2026-07-19 18:22:05 +08:00

128 lines
3.9 KiB
Python

"""Thread-safe active-run steering primitives.
Steering is deliberately separate from the normal per-session message queue.
An instruction is accepted only while exactly one run for the scoped session
is active; idle sessions never start a new run as a side effect.
"""
from __future__ import annotations
import threading
from collections import deque
from dataclasses import dataclass
from enum import Enum
from typing import Deque, Dict, List, Optional, Set
class SteerStatus(str, Enum):
ACCEPTED = "accepted"
INACTIVE = "inactive"
AMBIGUOUS = "ambiguous"
INVALID = "invalid"
FULL = "full"
CLOSING = "closing"
@dataclass(frozen=True)
class SteerResult:
status: SteerStatus
@property
def accepted(self) -> bool:
return self.status == SteerStatus.ACCEPTED
class SteerInbox:
"""Bounded inbox owned by one active agent run."""
def __init__(self, max_pending: int = 16, max_chars: int = 8000):
self.max_pending = max(1, int(max_pending))
self.max_chars = max(1, int(max_chars))
self._lock = threading.Lock()
self._pending: Deque[str] = deque()
self._accepting = True
def submit(self, instruction: str) -> SteerResult:
text = (instruction or "").strip()
if not text or len(text) > self.max_chars:
return SteerResult(SteerStatus.INVALID)
with self._lock:
if not self._accepting:
return SteerResult(SteerStatus.CLOSING)
if len(self._pending) >= self.max_pending:
return SteerResult(SteerStatus.FULL)
self._pending.append(text)
return SteerResult(SteerStatus.ACCEPTED)
def drain(self) -> List[str]:
with self._lock:
items = list(self._pending)
self._pending.clear()
return items
def close_if_empty(self) -> bool:
"""Atomically stop accepting when no instruction is pending.
This closes the race between a final empty drain and an agent run
returning: after this method succeeds, submitters receive CLOSING.
"""
with self._lock:
if self._pending:
return False
self._accepting = False
return True
def close(self) -> None:
with self._lock:
self._accepting = False
class SteerRegistry:
"""Map a scoped agent/session key to its active run inboxes."""
def __init__(self):
self._lock = threading.Lock()
self._by_session: Dict[str, Set[SteerInbox]] = {}
def register(self, session_id: str, inbox: Optional[SteerInbox] = None) -> SteerInbox:
inbox = inbox or SteerInbox()
if not session_id:
return inbox
with self._lock:
self._by_session.setdefault(session_id, set()).add(inbox)
return inbox
def unregister(self, session_id: str, inbox: Optional[SteerInbox]) -> None:
if not session_id or inbox is None:
return
inbox.close()
with self._lock:
bucket = self._by_session.get(session_id)
if bucket is None:
return
bucket.discard(inbox)
if not bucket:
self._by_session.pop(session_id, None)
def submit(self, session_id: str, instruction: str) -> SteerResult:
if not (instruction or "").strip():
return SteerResult(SteerStatus.INVALID)
with self._lock:
inboxes = list(self._by_session.get(session_id, ()))
if not inboxes:
return SteerResult(SteerStatus.INACTIVE)
if len(inboxes) != 1:
return SteerResult(SteerStatus.AMBIGUOUS)
return inboxes[0].submit(instruction)
def active_count(self, session_id: str) -> int:
with self._lock:
return len(self._by_session.get(session_id, ()))
_registry = SteerRegistry()
def get_steer_registry() -> SteerRegistry:
return _registry