# `Dsxir.Predictor.Predict`

Atomic predictor: format with the configured adapter, dispatch through the
LM, parse, validate, wrap in a `%Dsxir.Prediction{}`.

Telemetry spans:

  * `[:dsxir, :predictor, :start]` — meta `%{predictor, signature, adapter, **metadata}`.
  * `[:dsxir, :predictor, :stop]`  — meas `%{duration, tokens_in, tokens_out, cost}`, meta `%{predictor, signature, adapter, prediction, error_class: nil, **metadata}`.
  * `[:dsxir, :predictor, :exception]` — meas `%{duration}`, meta `%{kind, reason, stacktrace, error_class, **metadata}`.

Token measurements are always present on the stop event; their values are
`nil` when the upstream LM did not report usage.

## Adapter fallback

When the configured adapter is `Dsxir.Adapter.Chat` and the primary call
fails with an adapter-class error (`ParseError`, `ZoiValidation`) or an
`Dsxir.Errors.LM.ContextWindow`, the predictor retries exactly once via
`Dsxir.Adapter.Json`. Other LM-class errors (`Authentication`,
`RateLimited`, `RequestFailed`) propagate without retry. If the configured
adapter is not `Dsxir.Adapter.Chat`, no fallback is attempted.

Each retry emits `[:dsxir, :adapter, :fallback]` *before* dispatching the
second call so subscribers see the intent even if the retry also fails. A
second class-matched failure raises
`Dsxir.Errors.Adapter.FallbackExhausted`.

`FallbackExhausted` can surface from two sources: the Chat→Json fallback
(handled here, distinguished by `from: Dsxir.Adapter.Chat, to:
Dsxir.Adapter.Json`) and the Json→Json schema retry (handled internally by
`Dsxir.Adapter.Json`, distinguished by `from: Dsxir.Adapter.Json, to:
Dsxir.Adapter.Json`). The rescue list catches both.

## Recognised opts

  * `:adapter` — override the adapter module for this call. Defaults to
    `Settings.resolve(:adapter)` or `Dsxir.Adapter.Chat`.
  * `:path` — list of path segments stamped onto raised adapter errors for
    nested predictor composition.
  * `:stream` — 1-arity callback `(chunk -> :ok)` forwarded to the LM impl.
    The callback fires with `%Sycophant.StreamChunk{}` values during
    generation; the final `%Dsxir.Prediction{}` is still returned. Chat
    adapter only — the Json adapter raises
    `Dsxir.Errors.Invalid.Configuration` when `:stream` is set.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
