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 (includesthinking_tokensif available)metadata- Backend-specific data
Summary
Functions
Returns true if this response is complete (stopped naturally).
Creates a new response with the given attributes.
Gets the text content from the response.
Gets the thinking/reasoning content from the response.
Gets the total token count from usage, if available.
Types
@type finish_reason() :: :stop | :max_tokens | :content_filter | :error | atom()
@type metadata() :: %{ optional(:response_id) => String.t(), optional(:model) => String.t(), optional(:provider) => String.t(), optional(:latency_ms) => non_neg_integer(), optional(atom()) => term() }
Standardized metadata keys for observability.
These align with OpenTelemetry GenAI semantic conventions.
@type t() :: %Puck.Response{ content: term(), finish_reason: finish_reason() | nil, metadata: metadata(), thinking: String.t() | nil, usage: usage() }
@type usage() :: %{ optional(:input_tokens) => non_neg_integer(), optional(:output_tokens) => non_neg_integer(), optional(:total_tokens) => non_neg_integer(), optional(:thinking_tokens) => non_neg_integer() }
Functions
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
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: %{}}
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
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
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