# `Puck.Response`
[🔗](https://github.com/bradleygolden/puck/blob/v0.2.25/lib/puck/response.ex#L1)

Normalized response struct for LLM outputs.

## Fields

- `content` - Response content (text or structured data)
- `thinking` - Thinking/reasoning content from extended thinking models (nil if not available)
- `finish_reason` - Why the model stopped (`:stop`, `:max_tokens`, etc.)
- `usage` - Token usage information (includes `thinking_tokens` if available)
- `metadata` - Backend-specific data

# `finish_reason`

```elixir
@type finish_reason() :: :stop | :max_tokens | :content_filter | :error | atom()
```

# `metadata`

```elixir
@type metadata() :: %{
  optional(:response_id) =&gt; String.t(),
  optional(:model) =&gt; String.t(),
  optional(:provider) =&gt; String.t(),
  optional(:latency_ms) =&gt; non_neg_integer(),
  optional(atom()) =&gt; term()
}
```

Standardized metadata keys for observability.

These align with OpenTelemetry GenAI semantic conventions.

# `t`

```elixir
@type t() :: %Puck.Response{
  content: term(),
  finish_reason: finish_reason() | nil,
  metadata: metadata(),
  thinking: String.t() | nil,
  usage: usage()
}
```

# `usage`

```elixir
@type usage() :: %{
  optional(:input_tokens) =&gt; non_neg_integer(),
  optional(:output_tokens) =&gt; non_neg_integer(),
  optional(:total_tokens) =&gt; non_neg_integer(),
  optional(:thinking_tokens) =&gt; non_neg_integer()
}
```

# `complete?`

Returns true if this response is complete (stopped naturally).

## Examples

    iex> response = Puck.Response.new(finish_reason: :stop)
    iex> Puck.Response.complete?(response)
    true

    iex> response = Puck.Response.new(finish_reason: :max_tokens)
    iex> Puck.Response.complete?(response)
    false

# `new`

Creates a new response with the given attributes.

## Examples

    iex> Puck.Response.new(content: "Hello!")
    %Puck.Response{content: "Hello!", thinking: nil, finish_reason: nil, usage: %{}, metadata: %{}}

# `text`

Gets the text content from the response.

Returns the content if it's a string, or nil if content is nil or non-string.
This is a convenience for the common case of extracting text responses.

## Examples

    iex> response = Puck.Response.new(content: "Hello, world!")
    iex> Puck.Response.text(response)
    "Hello, world!"

    iex> response = Puck.Response.new(content: nil)
    iex> Puck.Response.text(response)
    nil

# `thinking`

Gets the thinking/reasoning content from the response.

Returns the thinking content if available, or nil if the model doesn't support
extended thinking or thinking was not enabled.

## Examples

    iex> response = Puck.Response.new(thinking: "Let me think about this...")
    iex> Puck.Response.thinking(response)
    "Let me think about this..."

    iex> response = Puck.Response.new(content: "Hello!")
    iex> Puck.Response.thinking(response)
    nil

# `total_tokens`

Gets the total token count from usage, if available.

## Examples

    iex> response = Puck.Response.new(usage: %{input_tokens: 10, output_tokens: 20})
    iex> Puck.Response.total_tokens(response)
    30

    iex> response = Puck.Response.new(usage: %{total_tokens: 50})
    iex> Puck.Response.total_tokens(response)
    50

    iex> response = Puck.Response.new()
    iex> Puck.Response.total_tokens(response)
    nil

---

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