"""Provider-neutral boundary for structured LLM calls. The harness owns prompt selection, privacy minimisation, and validation. A provider adapter only has to execute one structured request and return either the requested Pydantic model or a mapping that can be validated as that model. """ from __future__ import annotations from collections.abc import Mapping from typing import Any, Protocol, TypeVar, runtime_checkable from pydantic import BaseModel StructuredModel = TypeVar("StructuredModel", bound=BaseModel) @runtime_checkable class LLMBackend(Protocol): """Minimal synchronous interface implemented by model-provider adapters.""" def complete_json( self, *, stage: str, system_prompt: str, task_prompt: str, user_payload: Mapping[str, Any], output_model: type[StructuredModel], ) -> StructuredModel | Mapping[str, Any]: """Return structured data for ``output_model``. Adapters may return a validated model or a plain mapping. The pipeline deliberately validates the value again at its trust boundary. """ __all__ = ["LLMBackend", "StructuredModel"]