Puck.Hooks behaviour (Puck v0.2.11)

Copy Markdown View Source

Behaviour for lifecycle hooks.

Hooks observe and transform data at each stage of client execution. All callbacks are optional.

Return Types

  • {:cont, value} - Continue with value
  • {:halt, response} - Short-circuit with response
  • {:error, reason} - Abort with error

Callbacks

  • on_call_start/3 - Before LLM call
  • on_call_end/3 - After successful call
  • on_call_error/3 - On call failure
  • on_stream_start/3, on_stream_chunk/3, on_stream_end/2 - Stream lifecycle
  • on_backend_request/2, on_backend_response/2 - Backend lifecycle
  • on_compaction_start/3, on_compaction_end/2 - Compaction lifecycle

Example

defmodule MyApp.LoggingHooks do
  @behaviour Puck.Hooks
  require Logger

  @impl true
  def on_call_start(_client, content, _context) do
    Logger.info("Call started")
    {:cont, content}
  end

  @impl true
  def on_call_end(_client, response, _context) do
    Logger.info("Call completed")
    {:cont, response}
  end
end

Usage

client = Puck.Client.new({Puck.Backends.ReqLLM, "anthropic:claude-sonnet-4-5"},
  hooks: MyApp.LoggingHooks
)

# Multiple hooks execute in order
Puck.call(client, "Hello", context,
  hooks: [MyApp.LoggingHooks, MyApp.MetricsHooks]
)

Summary

Functions

Invokes an observational hook callback (return value is ignored).

Invokes a transforming hook callback on the given hook module(s).

Merges client-level hooks with per-call hooks.

Types

chunk()

@type chunk() :: map()

client()

@type client() :: Puck.Client.t()

config()

@type config() :: map()

context()

@type context() :: Puck.Context.t()

messages()

@type messages() :: [map()]

response()

@type response() :: Puck.Response.t()

Callbacks

on_backend_request(config, messages)

(optional)
@callback on_backend_request(config(), messages()) ::
  {:cont, messages()} | {:halt, response()} | {:error, term()}

on_backend_response(config, response)

(optional)
@callback on_backend_response(config(), response()) ::
  {:cont, response()} | {:error, term()}

on_call_end(client, response, context)

(optional)
@callback on_call_end(client(), response(), context()) ::
  {:cont, response()} | {:error, term()}

on_call_error(client, error, context)

(optional)
@callback on_call_error(client(), error :: term(), context()) :: term()

on_call_start(client, content, context)

(optional)
@callback on_call_start(client(), content :: term(), context()) ::
  {:cont, term()} | {:halt, response()} | {:error, term()}

on_compaction_end(context, strategy)

(optional)
@callback on_compaction_end(context(), strategy :: module()) ::
  {:cont, context()} | {:error, term()}

on_compaction_start(context, strategy, config)

(optional)
@callback on_compaction_start(context(), strategy :: module(), config :: map()) ::
  {:cont, context()} | {:halt, context()} | {:error, term()}

on_stream_chunk(client, chunk, context)

(optional)
@callback on_stream_chunk(client(), chunk(), context()) :: term()

on_stream_end(client, context)

(optional)
@callback on_stream_end(client(), context()) :: term()

on_stream_start(client, content, context)

(optional)
@callback on_stream_start(client(), content :: term(), context()) ::
  {:cont, term()} | {:error, term()}

Functions

invoke(hooks, callback, args)

Invokes an observational hook callback (return value is ignored).

Used for callbacks like on_call_error, on_stream_chunk, on_stream_end where the return value doesn't affect the pipeline.

invoke(hooks, callback, args, initial_value)

Invokes a transforming hook callback on the given hook module(s).

Returns the transformed value, a halt response, or an error. If a callback is not implemented, the initial value is passed through.

Returns

  • {:cont, value} - Continue with (possibly transformed) value
  • {:halt, response} - Short-circuit with a response
  • {:error, reason} - Abort with error

merge(client_hooks, call_hooks)

Merges client-level hooks with per-call hooks.

Per-call hooks come after client-level hooks (client hooks run first).