"""Per-provider history state for the AI client layer. Promotes 14 module globals in src/ai_client.py: - 7x `__history: list[Metadata]` (anthropic/deepseek/minimax/qwen/grok/llama) - 7x `__history_lock: threading.Lock` To a single `_PROVIDER_HISTORIES: dict[str, ProviderHistory]` keyed by provider name. Each `ProviderHistory` owns its own lock and message list; the cross-provider pattern is encapsulated behind a 4-method interface. SDK client holders (`_gemini_chat`, `_deepseek_client`, etc.) stay as module-level `Any` variables per Pattern 3 (heterogeneous SDK types, lazy-initialized). Only the homogeneous history aspect is unified. CONVENTION: 1-space indentation. NO COMMENTS. """ from __future__ import annotations import threading from dataclasses import dataclass, field from src.type_aliases import HistoryMessage, Metadata @dataclass class ProviderHistory: messages: list[HistoryMessage] = field(default_factory=list) lock: threading.Lock = field(default_factory=threading.Lock) def append(self, message: HistoryMessage) -> None: with self.lock: self.messages.append(message) def get_all(self) -> list[HistoryMessage]: with self.lock: return list(self.messages) def replace_all(self, messages: list[HistoryMessage]) -> None: with self.lock: self.messages = list(messages) def clear(self) -> None: with self.lock: self.messages = [] _PROVIDER_HISTORIES: dict[str, ProviderHistory] = { "anthropic": ProviderHistory(), "deepseek": ProviderHistory(), "minimax": ProviderHistory(), "qwen": ProviderHistory(), "grok": ProviderHistory(), "llama": ProviderHistory(), } def get_history(provider: str) -> ProviderHistory: if provider not in _PROVIDER_HISTORIES: raise KeyError(f"Unknown provider: {provider!r}") return _PROVIDER_HISTORIES[provider] def clear_all() -> None: for h in _PROVIDER_HISTORIES.values(): h.clear() def providers() -> tuple[str, ...]: return tuple(_PROVIDER_HISTORIES.keys())