Synchronous, observational callbacks for GEPA optimization.
This behaviour mirrors the lifecycle in GEPA v0.1.1 while making the
callback contract explicit for the BEAM. A callback is a module or
{module, context}. Every hook is optional and takes (event, context):
the event map, and the context the callback was registered with, or nil
for a bare module.
Callbacks run synchronously in registration order. Their return values are ignored, so callbacks cannot replace optimizer state or decisions. A callback failure is isolated, reported as redacted telemetry and a warning, and does not prevent later callbacks from observing the event.
defmodule AuditCallback do
@behaviour Imp.Optimizer.GEPA.Callback
@impl true
def on_iteration_end(event, owner) do
send(owner, {:gepa_iteration, event.iteration, event.proposal_accepted})
end
end
Imp.Optimizer.GEPA.new(metric, callbacks: [{AuditCallback, self()}])Event maps use atom keys and expose immutable Elixir values. In particular,
:state and :final_state are snapshots by value rather than mutable handles.
Candidate identifiers, rejection reasons, trajectories, and exceptions stay
as native Elixir terms rather than being stringified. This is the deliberate
Elixir equivalent of upstream's observational state access.
Summary
Types
Callbacks
Functions
@spec events() :: [atom()]
Returns the callback lifecycle hook names in upstream order.