"""Request model pattern — used by public client methods.

Every non-trivial RPC provides:

1. A frozen Pydantic model with :data:`FlexTimeRange` / :data:`FlexSignalSpec`
   fields so raw Python types are coerced automatically.
2. An ``@overload``-ed client method that accepts either the model
   *or* individual kwargs, giving callers a choice between structured
   and ad-hoc invocations.

Example (for ``get_signals``, to be wired when that RPC is restored)::

    class GetSignalsRequest(BaseModel):
        model_config = ConfigDict(frozen=True)

        time_range: FlexTimeRange | None = None
        signal_spec: FlexSignalSpec | None = None
        dataset: str | DatasetMetadata | None = None
        hub_name: str = DEFAULT_HUB_ID

    # On AllegoClient:
    @overload
    def get_signals(self, request: GetSignalsRequest) -> SignalArrays: ...

    @overload
    def get_signals(
        self,
        *,
        time_range: TimeRangeSpec | list[float] | None = ...,
        signal_spec: SignalSpec | list[int] | dict[str, Any] | None = ...,
        dataset: str | DatasetMetadata | None = ...,
        hub_name: str = ...,
    ) -> SignalArrays: ...

    def get_signals(
        self,
        request: GetSignalsRequest | None = None,
        **kwargs: Any,
    ) -> SignalArrays:
        if request is None:
            request = GetSignalsRequest(**kwargs)
        # Pydantic has already normalized all inputs via BeforeValidator
        ...

This module is a *template reference* — it doesn't export anything used
at runtime. Concrete request models are defined next to the client method
that uses them (e.g., in ``allego_client.py``).
"""
