Pydantic contracts that cross module or process seams. Import-linter enforces that nothing outside this package matters here — src.models depends on nothing in src/ (forbidden contract in pyproject.toml). The point: schema bugs surface at construction with clear ValidationErrors, not as AttributeErrors deep in the request path.
_base.StrictModel—BaseModelsubclass withmodel_config = ConfigDict(extra="forbid"). Inherit this for any contract that crosses a boundary; opt into stricter type coercion withclass Foo(StrictModel, strict=True)when JSON coercion (UUID, int) is undesirable.health.HealthResponse—status: Literal["ok"]+version: str. Returned byGET /api/v1/health.session.SessionCreate/session.SessionInfo— the create-empty / public-info shapes used bysrc.api.sessions.SessionStore.config.Settings—pydantic_settings.BaseSettingsreading the fourLLM_*env vars (LLM_PROVIDER,LLM_API_KEY,LLM_BASE_URL,LLM_MODEL). Provider-pluggable seam — wire OpenAI, Anthropic, Azure, or vLLM by swapping the values without touching this code.config.get_settings()— freshSettingsconstructor (deliberately unmemoised so tests can re-construct aftermonkeypatch.setenv). Real callers should hold a single instance at startup.
- One module per logical contract group —
health,session,config. Add a new file rather than appending to an existing one. - Per-class
strict=Trueis the call site for any model that should reject"3.14"for a float; HTTP-boundary models often skip it because JSON requires the coercion. pyproject.toml[tool.ruff.lint.per-file-ignores]adds"src/models/**" = ["TCH003"]because Pydantic needs runtime imports for type annotations.