ClaudeWrapper.StreamEvent (ClaudeWrapper v0.8.0)

Copy Markdown View Source

A single event from the Claude CLI's NDJSON streaming output.

When using --output-format stream-json, the CLI emits one JSON object per line. Each event has a type and associated data.

Event types

Common event types include:

  • "system" -- system initialization
  • "assistant" -- assistant message content
  • "tool_use" -- tool invocation
  • "tool_result" -- tool execution result
  • "result" -- final result with cost/session info
  • "error" -- error during execution

Summary

Types

The incremental payload carried by a :block_delta partial message.

The kind of content block reported by a :block_start partial message.

A decoded partial-message event from a streaming claude call.

t()

Functions

Extract cost in USD, if present.

Parse a single NDJSON line into a stream event.

Decode a partial-message event into a typed, tagged view.

Whether this is the final result event.

Extract the result text, if this is a result event.

Extract the session ID, if present.

Types

block_delta()

@type block_delta() ::
  {:text, String.t()}
  | {:thinking, String.t()}
  | {:input_json, String.t()}
  | :other

The incremental payload carried by a :block_delta partial message.

Mirrors the delta.type field from the Anthropic streaming API. Less common delta kinds (signature, citations, ...) collapse to :other; callers that need them can fall back to the struct's :data field.

block_type()

@type block_type() ::
  :text
  | :thinking
  | {:tool_use, id :: String.t(), name :: String.t()}
  | {:other, String.t()}

The kind of content block reported by a :block_start partial message.

Mirrors the content_block.type field from the Anthropic streaming API. Block kinds not modelled here surface as {:other, raw_type} -- callers can still recover the type name from the carried string.

partial_message()

@type partial_message() ::
  {:block_start, index :: non_neg_integer(), block_type()}
  | {:block_delta, index :: non_neg_integer(), block_delta()}
  | {:block_stop, index :: non_neg_integer()}

A decoded partial-message event from a streaming claude call.

Returned by partial_message/1. The three variants correspond to the Anthropic streaming content-block lifecycle: a block starts, gets one or more deltas, then stops.

t()

@type t() :: %ClaudeWrapper.StreamEvent{
  data: map(),
  raw: String.t() | nil,
  type: String.t() | nil
}

Functions

cost_usd(stream_event)

@spec cost_usd(t()) :: float() | nil

Extract cost in USD, if present.

parse(line)

@spec parse(String.t()) :: {:ok, t()} | {:error, ClaudeWrapper.Error.t()}

Parse a single NDJSON line into a stream event.

partial_message(stream_event)

@spec partial_message(t()) :: partial_message() | nil

Decode a partial-message event into a typed, tagged view.

Returns the tagged tuple when the event is one of the content-block lifecycle events surfaced with --include-partial-messages -- start, delta, or stop. Returns nil for any other event (system, assistant, result, message-level stream events, etc.).

The CLI wraps each raw streaming event as %{"type" => "stream_event", "event" => %{...}}; this accessor unwraps that envelope. A raw (unwrapped) content-block event is also accepted. Unknown block types and unknown delta types fall through to {:other, type} / :other rather than returning nil, so future content-block kinds remain accessible (just untyped).

Examples

iex> {:ok, event} =
...>   ClaudeWrapper.StreamEvent.parse(
...>     ~s({"type":"stream_event","event":{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hi"}}})
...>   )
iex> ClaudeWrapper.StreamEvent.partial_message(event)
{:block_delta, 0, {:text, "Hi"}}

iex> {:ok, event} = ClaudeWrapper.StreamEvent.parse(~s({"type":"result","result":"done"}))
iex> ClaudeWrapper.StreamEvent.partial_message(event)
nil

result?(stream_event)

@spec result?(t()) :: boolean()

Whether this is the final result event.

result_text(stream_event)

@spec result_text(t()) :: String.t() | nil

Extract the result text, if this is a result event.

session_id(stream_event)

@spec session_id(t()) :: String.t() | nil

Extract the session ID, if present.