Current section
Files
Jump to
Current section
Files
src/pig@hooks.erl
-module(pig@hooks).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pig/hooks.gleam").
-export([new/1, on_session_start/2, on_session_shutdown/2, on_before_inference/2, on_after_inference/2, on_tool_call/2, on_tool_result/2, on_error/2, on_complete/2, allow_tool/0, block_tool/1, keep_result/0, replace_result/2, keep_messages/0, replace_messages/1, decide_tool_call/2, decide_tool_result/2, decide_messages/2, notify_after_inference/2, notify_error/2, notify_complete/2, notify_session_start/2, notify_session_shutdown/2]).
-export_type([before_inference_event/0, before_inference_action/0, after_inference_event/0, tool_call_event/0, tool_call_action/0, tool_result_event/0, tool_result_action/0, error_event/0, complete_event/0, session_start_event/0, session_shutdown_event/0, hooks/0, tool_call_decision/0, tool_result_decision/0, messages_decision/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Hooks system for pig agent lifecycle mediation.\n"
"\n"
" Hooks allow library users to intercept agent lifecycle events:\n"
" - Blocking tool calls\n"
" - Transforming results\n"
" - Reacting to events\n"
"\n"
" Hooks are composed as a List(Hooks) — no wrapper type needed.\n"
" Composition functions run handlers in order with per-event semantics.\n"
).
-type before_inference_event() :: {before_inference_event,
binary(),
list(pig_protocol@message:message())}.
-type before_inference_action() :: keep_messages |
{replace_messages, list(pig_protocol@message:message())}.
-type after_inference_event() :: {after_inference_event,
binary(),
pig_protocol@message:message(),
integer()}.
-type tool_call_event() :: {tool_call_event, binary(), binary(), binary()}.
-type tool_call_action() :: allow_tool | {block_tool, binary()}.
-type tool_result_event() :: {tool_result_event,
binary(),
binary(),
binary(),
boolean(),
integer()}.
-type tool_result_action() :: keep_result |
{replace_result, binary(), boolean()}.
-type error_event() :: {error_event, binary(), pig_protocol@error:ai_error()}.
-type complete_event() :: {complete_event,
binary(),
pig_protocol@message:message(),
integer()}.
-type session_start_event() :: {session_start_event,
list(pig_protocol@message:message())}.
-type session_shutdown_event() :: {session_shutdown_event,
list(pig_protocol@message:message()),
integer()}.
-type hooks() :: {hooks,
binary(),
fun((session_start_event()) -> nil),
fun((session_shutdown_event()) -> nil),
fun((before_inference_event()) -> before_inference_action()),
fun((after_inference_event()) -> nil),
fun((tool_call_event()) -> tool_call_action()),
fun((tool_result_event()) -> tool_result_action()),
fun((error_event()) -> nil),
fun((complete_event()) -> nil)}.
-type tool_call_decision() :: tool_allowed | {tool_blocked, binary(), binary()}.
-type tool_result_decision() :: {result_unchanged, tool_result_event()} |
{result_transformed, tool_result_event(), list(binary())}.
-type messages_decision() :: {messages_unchanged,
list(pig_protocol@message:message())} |
{messages_replaced, list(pig_protocol@message:message()), list(binary())}.
-file("src/pig/hooks.gleam", 103).
?DOC(" Creates hooks with all no-op handlers.\n").
-spec new(binary()) -> hooks().
new(Name) ->
{hooks,
Name,
fun(_) -> nil end,
fun(_) -> nil end,
fun(_) -> keep_messages end,
fun(_) -> nil end,
fun(_) -> allow_tool end,
fun(_) -> keep_result end,
fun(_) -> nil end,
fun(_) -> nil end}.
-file("src/pig/hooks.gleam", 118).
?DOC(" Sets the session_start handler.\n").
-spec on_session_start(hooks(), fun((session_start_event()) -> nil)) -> hooks().
on_session_start(H, Handler) ->
{hooks,
erlang:element(2, H),
Handler,
erlang:element(4, H),
erlang:element(5, H),
erlang:element(6, H),
erlang:element(7, H),
erlang:element(8, H),
erlang:element(9, H),
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 126).
?DOC(" Sets the session_shutdown handler.\n").
-spec on_session_shutdown(hooks(), fun((session_shutdown_event()) -> nil)) -> hooks().
on_session_shutdown(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
Handler,
erlang:element(5, H),
erlang:element(6, H),
erlang:element(7, H),
erlang:element(8, H),
erlang:element(9, H),
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 134).
?DOC(" Sets the before_inference handler.\n").
-spec on_before_inference(
hooks(),
fun((before_inference_event()) -> before_inference_action())
) -> hooks().
on_before_inference(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
erlang:element(4, H),
Handler,
erlang:element(6, H),
erlang:element(7, H),
erlang:element(8, H),
erlang:element(9, H),
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 142).
?DOC(" Sets the after_inference handler.\n").
-spec on_after_inference(hooks(), fun((after_inference_event()) -> nil)) -> hooks().
on_after_inference(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
erlang:element(4, H),
erlang:element(5, H),
Handler,
erlang:element(7, H),
erlang:element(8, H),
erlang:element(9, H),
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 150).
?DOC(" Sets the tool_call handler.\n").
-spec on_tool_call(hooks(), fun((tool_call_event()) -> tool_call_action())) -> hooks().
on_tool_call(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
erlang:element(4, H),
erlang:element(5, H),
erlang:element(6, H),
Handler,
erlang:element(8, H),
erlang:element(9, H),
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 158).
?DOC(" Sets the tool_result handler.\n").
-spec on_tool_result(
hooks(),
fun((tool_result_event()) -> tool_result_action())
) -> hooks().
on_tool_result(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
erlang:element(4, H),
erlang:element(5, H),
erlang:element(6, H),
erlang:element(7, H),
Handler,
erlang:element(9, H),
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 166).
?DOC(" Sets the error handler.\n").
-spec on_error(hooks(), fun((error_event()) -> nil)) -> hooks().
on_error(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
erlang:element(4, H),
erlang:element(5, H),
erlang:element(6, H),
erlang:element(7, H),
erlang:element(8, H),
Handler,
erlang:element(10, H)}.
-file("src/pig/hooks.gleam", 171).
?DOC(" Sets the complete handler.\n").
-spec on_complete(hooks(), fun((complete_event()) -> nil)) -> hooks().
on_complete(H, Handler) ->
{hooks,
erlang:element(2, H),
erlang:element(3, H),
erlang:element(4, H),
erlang:element(5, H),
erlang:element(6, H),
erlang:element(7, H),
erlang:element(8, H),
erlang:element(9, H),
Handler}.
-file("src/pig/hooks.gleam", 178).
?DOC(" Returns an action that allows a tool call to proceed.\n").
-spec allow_tool() -> tool_call_action().
allow_tool() ->
allow_tool.
-file("src/pig/hooks.gleam", 183).
?DOC(" Returns an action that blocks a tool call with the given reason.\n").
-spec block_tool(binary()) -> tool_call_action().
block_tool(Reason) ->
{block_tool, Reason}.
-file("src/pig/hooks.gleam", 188).
?DOC(" Returns an action that keeps the original tool result.\n").
-spec keep_result() -> tool_result_action().
keep_result() ->
keep_result.
-file("src/pig/hooks.gleam", 193).
?DOC(" Returns an action that replaces the tool result.\n").
-spec replace_result(binary(), boolean()) -> tool_result_action().
replace_result(Content, Is_error) ->
{replace_result, Content, Is_error}.
-file("src/pig/hooks.gleam", 198).
?DOC(" Returns an action that keeps the original messages.\n").
-spec keep_messages() -> before_inference_action().
keep_messages() ->
keep_messages.
-file("src/pig/hooks.gleam", 203).
?DOC(" Returns an action that replaces the messages.\n").
-spec replace_messages(list(pig_protocol@message:message())) -> before_inference_action().
replace_messages(Messages) ->
{replace_messages, Messages}.
-file("src/pig/hooks.gleam", 239).
-spec find_blocking_hook(list(hooks()), tool_call_event()) -> tool_call_decision().
find_blocking_hook(Hooks_list, Event) ->
case Hooks_list of
[] ->
tool_allowed;
[H | Rest] ->
case (erlang:element(7, H))(Event) of
{block_tool, Reason} ->
{tool_blocked, erlang:element(2, H), Reason};
allow_tool ->
find_blocking_hook(Rest, Event)
end
end.
-file("src/pig/hooks.gleam", 232).
?DOC(
" Decide whether a tool call is allowed. First BlockTool wins.\n"
" Returns ToolBlocked with the blocker's name and reason for attribution.\n"
).
-spec decide_tool_call(list(hooks()), tool_call_event()) -> tool_call_decision().
decide_tool_call(Hooks_list, Event) ->
find_blocking_hook(Hooks_list, Event).
-file("src/pig/hooks.gleam", 256).
?DOC(
" Decide the final tool result. Chains ReplaceResult transformations.\n"
" Returns ResultTransformed with all transformer names for observability.\n"
).
-spec decide_tool_result(list(hooks()), tool_result_event()) -> tool_result_decision().
decide_tool_result(Hooks_list, Event) ->
{Final_event, Transformers} = gleam@list:fold(
Hooks_list,
{Event, []},
fun(Acc, H) ->
{Ev, Names} = Acc,
Action = (erlang:element(8, H))(Ev),
case Action of
keep_result ->
{Ev, Names};
{replace_result, Content, Is_error} ->
{{tool_result_event,
erlang:element(2, Ev),
erlang:element(3, Ev),
Content,
Is_error,
erlang:element(6, Ev)},
lists:append(Names, [erlang:element(2, H)])}
end
end
),
case Transformers of
[] ->
{result_unchanged, Event};
_ ->
{result_transformed, Final_event, Transformers}
end.
-file("src/pig/hooks.gleam", 286).
?DOC(
" Decide the final messages for inference. Chains ReplaceMessages.\n"
" Returns MessagesReplaced with all transformer names for observability.\n"
).
-spec decide_messages(list(hooks()), before_inference_event()) -> messages_decision().
decide_messages(Hooks_list, Event) ->
{Final_event, Transformers} = gleam@list:fold(
Hooks_list,
{Event, []},
fun(Acc, H) ->
{Ev, Names} = Acc,
Action = (erlang:element(5, H))(Ev),
case Action of
keep_messages ->
{Ev, Names};
{replace_messages, Messages} ->
{{before_inference_event, erlang:element(2, Ev), Messages},
lists:append(Names, [erlang:element(2, H)])}
end
end
),
case Transformers of
[] ->
{messages_unchanged, erlang:element(3, Event)};
_ ->
{messages_replaced, erlang:element(3, Final_event), Transformers}
end.
-file("src/pig/hooks.gleam", 311).
?DOC(" Run all after_inference handlers (fire-and-forget).\n").
-spec notify_after_inference(list(hooks()), after_inference_event()) -> nil.
notify_after_inference(Hooks_list, Event) ->
_ = gleam@list:map(Hooks_list, fun(H) -> (erlang:element(6, H))(Event) end),
nil.
-file("src/pig/hooks.gleam", 320).
?DOC(" Run all error handlers (fire-and-forget).\n").
-spec notify_error(list(hooks()), error_event()) -> nil.
notify_error(Hooks_list, Event) ->
_ = gleam@list:map(Hooks_list, fun(H) -> (erlang:element(9, H))(Event) end),
nil.
-file("src/pig/hooks.gleam", 326).
?DOC(" Run all complete handlers (fire-and-forget).\n").
-spec notify_complete(list(hooks()), complete_event()) -> nil.
notify_complete(Hooks_list, Event) ->
_ = gleam@list:map(Hooks_list, fun(H) -> (erlang:element(10, H))(Event) end),
nil.
-file("src/pig/hooks.gleam", 332).
?DOC(" Run all session_start handlers (fire-and-forget).\n").
-spec notify_session_start(list(hooks()), session_start_event()) -> nil.
notify_session_start(Hooks_list, Event) ->
_ = gleam@list:map(Hooks_list, fun(H) -> (erlang:element(3, H))(Event) end),
nil.
-file("src/pig/hooks.gleam", 341).
?DOC(" Run all session_shutdown handlers (fire-and-forget).\n").
-spec notify_session_shutdown(list(hooks()), session_shutdown_event()) -> nil.
notify_session_shutdown(Hooks_list, Event) ->
_ = gleam@list:map(Hooks_list, fun(H) -> (erlang:element(4, H))(Event) end),
nil.