from abc import ABC, abstractmethod from collections.abc import AsyncGenerator class AIProvider(ABC): @abstractmethod async def chat(self, messages: list[dict], system_prompt: str) -> str: """Send messages and return assistant reply text.""" @abstractmethod async def stream_chat( self, messages: list[dict], system_prompt: str ) -> AsyncGenerator[str, None]: """Stream assistant reply token by token.""" @abstractmethod async def chat_with_extraction( self, messages: list[dict], system_prompt: str ) -> tuple[str, dict]: """Send messages and return (reply_text, extracted_json). The provider should request JSON output and parse it. Falls back to empty dict if parsing fails. """