"""LLMProvider protocol — the port all agents depend on (D-017). Implementations: openai_compat.OpenAICompatProvider (ollama-cloud + local), mock.MockProvider (deterministic, tests/CI). Providers are dumb pipes: no envelope logic here — the API layer owns meta/done/error events (D-016). """ from collections.abc import AsyncIterator from typing import Protocol from .types import Message class LLMProvider(Protocol): async def stream_chat( self, messages: list[Message], *, model: str, temperature: float = 0.7, response_format: dict | None = None, ) -> AsyncIterator[str]: """Yield incremental content deltas (plain text chunks).""" ... async def chat( self, messages: list[Message], *, model: str, temperature: float = 0.7, response_format: dict | None = None, ) -> str: """Non-streaming completion — returns the full reply text.""" ...