# `ALLM.Response`
[🔗](https://github.com/cykod/ALLM/blob/v0.5.0/lib/allm/response.ex#L1)

A single LLM response — Layer A serializable data.

Carries the assistant `:message`, optional top-level `:output_text`
convenience, `:tool_calls` the model wants invoked, `:finish_reason`
(closed union), and `:usage` / `:raw` provider extras.

`:output_text` exists so callers can read the flat text payload without
re-deriving it from `:message.content`; use `text/1` for the canonical
fallback behaviour.

## Fields

| Field | Type | Notes |
|-------|------|-------|
| `:id` | `String.t \| nil` | Provider-assigned response id. |
| `:request_id` | `String.t \| nil` | Correlation id for telemetry / logs. |
| `:model` | `String.t \| nil` | The model the provider actually served. |
| `:message` | `%Message{} \| nil` | The assistant message. |
| `:output_text` | `String.t \| nil` | Flat text convenience. |
| `:tool_calls` | `[%ToolCall{}]` | Tool calls the model wants invoked. |
| `:finish_reason` | `:stop \| :length \| :tool_calls \| :content_filter \| :error \| :other \| nil` | Canonical reason. Mid-stream errors fold in here. |
| `:raw_finish_reason` | `String.t \| nil` | Provider-native value (debugging). |
| `:usage` | `%Usage{}` | Token + cost counters. |
| `:raw` | `term` | Raw provider payload (when adapter preserves it). |
| `:metadata` | `map` | Adapter-supplied extras. Mid-stream errors land under `metadata.error`. |

See also `guides/getting_started.md`, `guides/errors_and_retries.md`.

# `finish_reason`

```elixir
@type finish_reason() ::
  :stop | :length | :tool_calls | :content_filter | :error | :other
```

Canonical finish-reason atom.

# `t`

```elixir
@type t() :: %ALLM.Response{
  finish_reason: finish_reason() | nil,
  id: String.t() | nil,
  message: ALLM.Message.t() | nil,
  metadata: map(),
  model: String.t() | nil,
  output_text: String.t() | nil,
  raw: term(),
  raw_finish_reason: String.t() | nil,
  request_id: String.t() | nil,
  tool_calls: [ALLM.ToolCall.t()],
  usage: ALLM.Usage.t()
}
```

# `new`

```elixir
@spec new(keyword()) :: t()
```

Build a `%Response{}` from keyword opts.

Every field is optional. Unknown keys raise `ArgumentError` via `struct!/2`.

## Examples

    iex> resp = ALLM.Response.new(output_text: "hi", finish_reason: :stop)
    iex> resp.output_text
    "hi"
    iex> resp.finish_reason
    :stop

# `text`

```elixir
@spec text(t()) :: String.t() | nil
```

Return the flat text of a response: `:output_text` if set, else the string
`:content` of `:message`, else `nil`.

A list-shaped `message.content` (structured parts) returns `nil` — callers
that need to flatten structured parts should do so explicitly.

## Examples

    iex> ALLM.Response.text(ALLM.Response.new(output_text: "direct"))
    "direct"

    iex> ALLM.Response.text(ALLM.Response.new(message: %ALLM.Message{role: :assistant, content: "from-msg"}))
    "from-msg"

    iex> ALLM.Response.text(ALLM.Response.new([]))
    nil

---

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