Packages

A Gleam library for building and orchestrating agents on the BEAM.

Current section

Files

Jump to
pig src pig@agent@runtime.erl
Raw

src/pig@agent@runtime.erl

-module(pig@agent@runtime).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/pig/agent/runtime.gleam").
-export([start_with_state/2, start/1, run/3, run_continue/2, stop/1, history/2, try_run/3, try_run_continue/2, supervised/3]).
-export_type([runtime_config/0, runtime_msg/0, runtime_state/0, blocked_tool/0, tool_result/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(
" Sans-IO runtime interpreter for the pig agent.\n"
"\n"
" The runtime is an OTP actor that:\n"
" 1. Receives prompts (Run) or control messages (Stop)\n"
" 2. Calls `update.update(state, msg)` — pure state machine\n"
" 3. For each effect, applies hooks then executes\n"
" 4. Produces SessionEvent values and sends to dispatcher\n"
" 5. Feeds effect results back as new AgentMsg values\n"
"\n"
" The core logic (update.gleam) is pure. This module is all IO.\n"
).
-type runtime_config() :: {runtime_config,
fun((list(pig_protocol@message:message()), list(pig_protocol@tool_definition:tool_definition())) -> {ok,
pig_protocol@inference:inference_result()} |
{error, pig_protocol@error:ai_error()}),
pig@tool:tool_registry(),
list(pig@hooks:hooks()),
gleam@erlang@process:subject(pig@obs@dispatcher:dispatcher_message()),
binary(),
integer()}.
-type runtime_msg() :: {run,
binary(),
gleam@erlang@process:subject({ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()})} |
{continue,
gleam@erlang@process:subject({ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()})} |
{get_history,
gleam@erlang@process:subject(list(pig_protocol@message:message()))} |
stop.
-type runtime_state() :: {runtime_state,
pig@agent@state:agent_state(),
runtime_config()}.
-type blocked_tool() :: {blocked_tool,
pig_protocol@message:tool_call(),
binary(),
binary()}.
-type tool_result() :: {tool_result,
binary(),
{ok, gleam@json:json()} | {error, pig@tool:tool_error()},
integer()}.
-file("src/pig/agent/runtime.gleam", 682).
-spec find_result(list(tool_result()), binary()) -> {ok,
{{ok, gleam@json:json()} | {error, pig@tool:tool_error()}, integer()}} |
{error, nil}.
find_result(Results, Call_id) ->
case gleam@list:find(
Results,
fun(R) -> erlang:element(2, R) =:= Call_id end
) of
{ok, R@1} ->
{ok, {erlang:element(3, R@1), erlang:element(4, R@1)}};
{error, nil} ->
{error, nil}
end.
-file("src/pig/agent/runtime.gleam", 675).
-spec find_blocked(list(blocked_tool()), binary()) -> {ok, blocked_tool()} |
{error, nil}.
find_blocked(Blocked, Call_id) ->
gleam@list:find(
Blocked,
fun(B) -> erlang:element(2, erlang:element(2, B)) =:= Call_id end
).
-file("src/pig/agent/runtime.gleam", 699).
-spec emit_hook_acted_list(
gleam@erlang@process:subject(pig@obs@dispatcher:dispatcher_message()),
list(binary()),
pig@obs@events:hook_point(),
binary(),
binary()
) -> nil.
emit_hook_acted_list(Disp, Transformer_names, Hook, Action_type, Description) ->
gleam@list:each(
Transformer_names,
fun(Name) ->
pig@obs@emit:to_dispatcher(
Disp,
{hook_acted,
Name,
Hook,
{hook_action_detail, Action_type, Description}}
)
end
).
-file("src/pig/agent/runtime.gleam", 692).
-spec is_error({ok, any()} | {error, any()}) -> boolean().
is_error(Result) ->
case Result of
{ok, _} ->
false;
{error, _} ->
true
end.
-file("src/pig/agent/runtime.gleam", 636).
-spec spawn_and_collect(
runtime_config(),
list(pig_protocol@message:tool_call())
) -> list(tool_result()).
spawn_and_collect(Config, Calls) ->
Disp = erlang:element(5, Config),
Pairs = gleam@list:map(
Calls,
fun(Call) ->
Reply_subject = gleam@erlang@process:new_subject(),
Pid = proc_lib:spawn_link(
fun() ->
pig@obs@emit:to_dispatcher(Disp, {tool_started, Call}),
Start_time = pig@obs@events:system_time(),
Result = pig@tool@execution:execute_tool(
erlang:element(3, Config),
Call
),
Duration = pig@obs@events:system_time() - Start_time,
gleam@erlang@process:send(
Reply_subject,
{tool_result, erlang:element(2, Call), Result, Duration}
)
end
),
{Pid, Reply_subject, erlang:element(2, Call)}
end
),
Timeout_ms = 5000,
gleam@list:map(
Pairs,
fun(Pair) ->
{Pid@1, Subject, Call_id} = Pair,
case gleam@erlang@process:'receive'(Subject, Timeout_ms) of
{ok, Result@1} ->
Result@1;
{error, nil} ->
gleam@erlang@process:kill(Pid@1),
logging:log(
error,
<<<<"Tool execution timed out after "/utf8,
(erlang:integer_to_binary(Timeout_ms))/binary>>/binary,
"ms"/utf8>>
),
{tool_result,
Call_id,
{error,
{tool_error, <<"Tool execution timed out"/utf8>>}},
Timeout_ms}
end
end
).
-file("src/pig/agent/runtime.gleam", 612).
-spec partition_by_hook_decision(
list(pig@hooks:hooks()),
list(pig_protocol@message:tool_call())
) -> {list(blocked_tool()), list(pig_protocol@message:tool_call())}.
partition_by_hook_decision(Hooks_list, Calls) ->
gleam@list:fold(
Calls,
{[], []},
fun(Acc, Call) ->
{Blocked_acc, Allowed_acc} = Acc,
Hook_event = {tool_call_event,
erlang:element(3, Call),
erlang:element(2, Call),
erlang:element(4, Call)},
case pig@hooks:decide_tool_call(Hooks_list, Hook_event) of
tool_allowed ->
{Blocked_acc, lists:append(Allowed_acc, [Call])};
{tool_blocked, Hook_name, Reason} ->
{lists:append(
Blocked_acc,
[{blocked_tool, Call, Hook_name, Reason}]
),
Allowed_acc}
end
end
).
-file("src/pig/agent/runtime.gleam", 496).
?DOC(
" Execute ExecuteTools: apply tool call hooks, execute allowed tools\n"
" in parallel, apply result hooks, emit events.\n"
).
-spec execute_tools_effect(
runtime_config(),
pig@agent@state:agent_state(),
list(pig_protocol@message:tool_call()),
fun((list({pig_protocol@message:tool_call(),
{ok, gleam@json:json()} | {error, pig@tool:tool_error()}})) -> pig@agent@msg:agent_msg())
) -> {pig@agent@state:agent_state(), pig@agent@msg:agent_msg()}.
execute_tools_effect(Config, Agent_st, Calls, On_results) ->
Disp = erlang:element(5, Config),
{Blocked, Allowed} = partition_by_hook_decision(
erlang:element(4, Config),
Calls
),
gleam@list:each(
Blocked,
fun(B) ->
pig@obs@emit:to_dispatcher(
Disp,
{tool_blocked,
erlang:element(2, B),
erlang:element(3, B),
erlang:element(4, B)}
),
pig@obs@emit:to_dispatcher(
Disp,
{hook_acted,
erlang:element(3, B),
before_tool_call,
{hook_action_detail,
<<"block"/utf8>>,
<<"Blocked tool: "/utf8, (erlang:element(4, B))/binary>>}}
)
end
),
Results = spawn_and_collect(Config, Allowed),
_ = gleam@list:map(
Allowed,
fun(Call) ->
{Result@1, Duration@1} = case find_result(
Results,
erlang:element(2, Call)
) of
{ok, {Result, Duration}} -> {Result, Duration};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pig/agent/runtime"/utf8>>,
function => <<"execute_tools_effect"/utf8>>,
line => 538,
value => _assert_fail,
start => 18021,
'end' => 18087,
pattern_start => 18032,
pattern_end => 18055})
end,
Raw_content = case Result@1 of
{ok, Json_result} ->
gleam@json:to_string(Json_result);
{error, Tool_err} ->
<<"Tool error: "/utf8,
(erlang:element(2, Tool_err))/binary>>
end,
Result_event = {tool_result_event,
erlang:element(3, Call),
erlang:element(2, Call),
Raw_content,
is_error(Result@1),
Duration@1},
Final_content = case pig@hooks:decide_tool_result(
erlang:element(4, Config),
Result_event
) of
{result_unchanged, _} ->
Raw_content;
{result_transformed, Final_event, Transformers} ->
emit_hook_acted_list(
Disp,
Transformers,
after_tool_call,
<<"transform"/utf8>>,
<<"Transformed result"/utf8>>
),
erlang:element(4, Final_event)
end,
pig@obs@emit:to_dispatcher(
Disp,
{tool_executed, Call, Final_content, Duration@1}
)
end
),
All_results = gleam@list:map(
Calls,
fun(Call@1) -> case find_blocked(Blocked, erlang:element(2, Call@1)) of
{ok, B@1} ->
{Call@1,
{error,
{tool_error,
<<<<<<"Tool blocked by '"/utf8,
(erlang:element(3, B@1))/binary>>/binary,
"': "/utf8>>/binary,
(erlang:element(4, B@1))/binary>>}}};
{error, nil} ->
Res@1 = case find_result(Results, erlang:element(2, Call@1)) of
{ok, {Res, _}} -> Res;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pig/agent/runtime"/utf8>>,
function => <<"execute_tools_effect"/utf8>>,
line => 589,
value => _assert_fail@1,
start => 19523,
'end' => 19582,
pattern_start => 19534,
pattern_end => 19550})
end,
{Call@1, Res@1}
end end
),
{Agent_st, On_results(All_results)}.
-file("src/pig/agent/runtime.gleam", 414).
?DOC(
" Execute CallProvider: apply before_inference hooks, call provider,\n"
" emit events, fire notification hooks.\n"
).
-spec execute_call_provider(
runtime_config(),
pig@agent@state:agent_state(),
list(pig_protocol@message:message()),
list(pig_protocol@tool_definition:tool_definition()),
fun(({ok, pig_protocol@inference:inference_result()} |
{error, pig_protocol@error:ai_error()}) -> pig@agent@msg:agent_msg())
) -> {pig@agent@state:agent_state(), pig@agent@msg:agent_msg()}.
execute_call_provider(Config, Agent_st, Messages, Tools, On_response) ->
Disp = erlang:element(5, Config),
Model = erlang:element(6, Config),
Before_event = {before_inference_event, Model, Messages},
Final_msgs = case pig@hooks:decide_messages(
erlang:element(4, Config),
Before_event
) of
{messages_unchanged, _} ->
Messages;
{messages_replaced, Final_messages, Transformers} ->
emit_hook_acted_list(
Disp,
Transformers,
before_inference,
<<"transform"/utf8>>,
<<"Transformed messages before inference"/utf8>>
),
Final_messages
end,
Msg_count = erlang:length(Final_msgs),
pig@obs@emit:to_dispatcher(Disp, {inference_started, Model, Msg_count}),
Start_time = pig@obs@events:system_time(),
Result = case (erlang:element(2, Config))(Final_msgs, Tools) of
{ok, Inference_result} ->
Msg = erlang:element(2, Inference_result),
Meta = erlang:element(3, Inference_result),
Duration = pig@obs@events:system_time() - Start_time,
Response_model = case erlang:element(3, Meta) of
{some, _} = M ->
M;
none ->
{some, Model}
end,
pig@obs@emit:to_dispatcher(
Disp,
{inference_completed,
Msg,
erlang:element(2, Meta),
Response_model,
erlang:element(4, Meta),
erlang:element(5, Meta),
erlang:element(6, Meta),
Duration,
erlang:element(3, Agent_st)}
),
pig@hooks:notify_after_inference(
erlang:element(4, Config),
{after_inference_event, Model, Msg, Duration}
),
{ok, Inference_result};
{error, E} ->
Duration@1 = pig@obs@events:system_time() - Start_time,
pig@obs@emit:to_dispatcher(
Disp,
{inference_failed, E, Duration@1, erlang:element(3, Agent_st)}
),
pig@hooks:notify_error(
erlang:element(4, Config),
{error_event, Model, E}
),
{error, E}
end,
{Agent_st, On_response(Result)}.
-file("src/pig/agent/runtime.gleam", 399).
?DOC(" Execute a single effect: apply hooks, execute, emit events.\n").
-spec execute_effect(
runtime_config(),
pig@agent@state:agent_state(),
pig@agent@effect:effect(pig@agent@msg:agent_msg())
) -> {pig@agent@state:agent_state(), pig@agent@msg:agent_msg()}.
execute_effect(Config, Agent_st, Eff) ->
case Eff of
{call_provider, Messages, Tools, On_response} ->
execute_call_provider(
Config,
Agent_st,
Messages,
Tools,
On_response
);
{execute_tools, Calls, On_results} ->
execute_tools_effect(Config, Agent_st, Calls, On_results)
end.
-file("src/pig/agent/runtime.gleam", 261).
-spec do_loop(
runtime_config(),
pig@agent@state:agent_state(),
pig@agent@msg:agent_msg()
) -> {pig@agent@state:agent_state(),
{ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}}.
do_loop(Config, Agent_st, M) ->
Result = pig@agent@update:update(Agent_st, M),
case Result of
{done, Final_st, Msg} ->
{Final_st, {ok, Msg}};
{failed, Final_st@1, E} ->
{Final_st@1, {error, E}};
{continue, New_st, Effs} ->
{Updated_st, Response_msgs} = gleam@list:fold(
Effs,
{New_st, []},
fun(Acc, Eff) ->
{St_acc, Msgs_acc} = Acc,
{New_st_acc, Response_msg} = execute_effect(
Config,
St_acc,
Eff
),
{New_st_acc, lists:append(Msgs_acc, [Response_msg])}
end
),
case Response_msgs of
[First_msg | _] ->
do_loop(Config, Updated_st, First_msg);
[] ->
{Updated_st,
{error,
{api_error, <<"no response from effects"/utf8>>}}}
end
end.
-file("src/pig/agent/runtime.gleam", 339).
?DOC(
" Decide how to resume based on the last assistant message's stop_reason\n"
" and tool_calls.\n"
).
-spec resume_from_assistant(
runtime_config(),
pig@agent@state:agent_state(),
list(pig_protocol@message:tool_call()),
gleam@option:option(pig_protocol@stop_reason:stop_reason())
) -> {pig@agent@state:agent_state(),
{ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}}.
resume_from_assistant(Config, St, Tool_calls, Sr) ->
case Sr of
{some, tool_use} ->
{_, Agent_msg} = execute_tools_effect(
Config,
St,
Tool_calls,
fun(Results) -> {tool_results, Results} end
),
do_loop(Config, St, Agent_msg);
{some, stop} ->
Msg@1 = case gleam@list:last(erlang:element(3, St)) of
{ok, Msg} -> Msg;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pig/agent/runtime"/utf8>>,
function => <<"resume_from_assistant"/utf8>>,
line => 357,
value => _assert_fail,
start => 12351,
'end' => 12393,
pattern_start => 12362,
pattern_end => 12369})
end,
{St, {ok, Msg@1}};
{some, length} ->
{St_after, Provider_msg} = execute_call_provider(
Config,
St,
pig@agent@state:messages_for_provider(St),
pig@agent@state:tool_definitions(St),
fun(R) ->
{provider_responded,
gleam@result:map(
R,
fun(Ir) -> erlang:element(2, Ir) end
)}
end
),
do_loop(Config, St_after, Provider_msg);
{some, error} ->
{St_after, Provider_msg} = execute_call_provider(
Config,
St,
pig@agent@state:messages_for_provider(St),
pig@agent@state:tool_definitions(St),
fun(R) ->
{provider_responded,
gleam@result:map(
R,
fun(Ir) -> erlang:element(2, Ir) end
)}
end
),
do_loop(Config, St_after, Provider_msg);
{some, {unknown, _}} ->
{St_after, Provider_msg} = execute_call_provider(
Config,
St,
pig@agent@state:messages_for_provider(St),
pig@agent@state:tool_definitions(St),
fun(R) ->
{provider_responded,
gleam@result:map(
R,
fun(Ir) -> erlang:element(2, Ir) end
)}
end
),
do_loop(Config, St_after, Provider_msg);
none ->
case Tool_calls of
[] ->
Msg@3 = case gleam@list:last(erlang:element(3, St)) of
{ok, Msg@2} -> Msg@2;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"pig/agent/runtime"/utf8>>,
function => <<"resume_from_assistant"/utf8>>,
line => 381,
value => _assert_fail@1,
start => 13150,
'end' => 13192,
pattern_start => 13161,
pattern_end => 13168})
end,
{St, {ok, Msg@3}};
Calls ->
{_, Agent_msg@1} = execute_tools_effect(
Config,
St,
Calls,
fun(Results@1) -> {tool_results, Results@1} end
),
do_loop(Config, St, Agent_msg@1)
end
end.
-file("src/pig/agent/runtime.gleam", 294).
?DOC(
" Resume the agent loop from its current history.\n"
"\n"
" Determines the entry point by inspecting the last message in history.\n"
" This enables the durability pattern: an external system checkpoints\n"
" messages, and on retry, rebuilds history from those checkpoints.\n"
).
-spec resume_from_history(runtime_config(), pig@agent@state:agent_state()) -> {pig@agent@state:agent_state(),
{ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}}.
resume_from_history(Config, St) ->
case gleam@list:last(erlang:element(3, St)) of
{error, _} ->
{St, {error, {api_error, <<"no history to continue"/utf8>>}}};
{ok, Last_msg} ->
case Last_msg of
{assistant, _, Tool_calls, _, Sr} ->
resume_from_assistant(Config, St, Tool_calls, Sr);
{user, _} ->
{St_after, Provider_msg} = execute_call_provider(
Config,
St,
pig@agent@state:messages_for_provider(St),
pig@agent@state:tool_definitions(St),
fun(R) ->
{provider_responded,
gleam@result:map(
R,
fun(Ir) -> erlang:element(2, Ir) end
)}
end
),
do_loop(Config, St_after, Provider_msg);
{tool, _, _} ->
{St_after, Provider_msg} = execute_call_provider(
Config,
St,
pig@agent@state:messages_for_provider(St),
pig@agent@state:tool_definitions(St),
fun(R) ->
{provider_responded,
gleam@result:map(
R,
fun(Ir) -> erlang:element(2, Ir) end
)}
end
),
do_loop(Config, St_after, Provider_msg);
{system, _} ->
{St,
{error,
{api_error,
<<"unexpected system message at end of history"/utf8>>}}}
end
end.
-file("src/pig/agent/runtime.gleam", 253).
?DOC(
" Execute the sans-IO loop: call update, interpret effects, feed back.\n"
" Returns the final agent state and the result.\n"
).
-spec execute_loop(
runtime_config(),
pig@agent@state:agent_state(),
pig@agent@msg:agent_msg()
) -> {pig@agent@state:agent_state(),
{ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}}.
execute_loop(Config, Agent_st, Initial_msg) ->
do_loop(Config, Agent_st, Initial_msg).
-file("src/pig/agent/runtime.gleam", 212).
-spec handle_message(runtime_state(), runtime_msg()) -> gleam@otp@actor:next(runtime_state(), runtime_msg()).
handle_message(St, M) ->
case M of
{run, Prompt, Reply_to} ->
Agent_st = {agent_state,
erlang:element(2, erlang:element(2, St)),
erlang:element(3, erlang:element(2, St)),
0},
Result = execute_loop(
erlang:element(3, St),
Agent_st,
{user_prompt, Prompt}
),
{Final_state, Outcome} = Result,
gleam@erlang@process:send(Reply_to, Outcome),
gleam@otp@actor:continue(
{runtime_state, Final_state, erlang:element(3, St)}
);
{continue, Reply_to@1} ->
Agent_st@1 = {agent_state,
erlang:element(2, erlang:element(2, St)),
erlang:element(3, erlang:element(2, St)),
0},
{Final_state@1, Outcome@1} = resume_from_history(
erlang:element(3, St),
Agent_st@1
),
gleam@erlang@process:send(Reply_to@1, Outcome@1),
gleam@otp@actor:continue(
{runtime_state, Final_state@1, erlang:element(3, St)}
);
{get_history, Reply_to@2} ->
gleam@erlang@process:send(
Reply_to@2,
erlang:element(3, erlang:element(2, St))
),
gleam@otp@actor:continue(St);
stop ->
gleam@otp@actor:stop()
end.
-file("src/pig/agent/runtime.gleam", 103).
?DOC(
" Start the runtime actor with a pre-built state.\n"
" Used by `pig.gleam` when session replay needs to happen before start.\n"
).
-spec start_with_state(runtime_config(), runtime_state()) -> {ok,
gleam@erlang@process:subject(runtime_msg())} |
{error, gleam@otp@actor:start_error()}.
start_with_state(_, Initial_state) ->
Builder = begin
_pipe = gleam@otp@actor:new(Initial_state),
gleam@otp@actor:on_message(_pipe, fun handle_message/2)
end,
case gleam@otp@actor:start(Builder) of
{ok, Started} ->
{ok, erlang:element(3, Started)};
{error, E} ->
{error, E}
end.
-file("src/pig/agent/runtime.gleam", 78).
?DOC(" Start the runtime actor with the given configuration.\n").
-spec start(runtime_config()) -> {ok,
gleam@erlang@process:subject(runtime_msg())} |
{error, gleam@otp@actor:start_error()}.
start(Config) ->
Agent_config = {agent_config,
erlang:element(2, Config),
erlang:element(3, Config),
none,
erlang:element(7, Config),
erlang:element(6, Config),
none,
none,
none,
none,
none,
none},
Initial_state = {runtime_state, pig@agent@state:new(Agent_config), Config},
start_with_state(Config, Initial_state).
-file("src/pig/agent/runtime.gleam", 117).
?DOC(" Send a prompt to the runtime and wait for a response.\n").
-spec run(gleam@erlang@process:subject(runtime_msg()), binary(), integer()) -> {ok,
pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}.
run(Subject, Prompt, Timeout) ->
gleam@otp@actor:call(
Subject,
Timeout,
fun(Reply_to) -> {run, Prompt, Reply_to} end
).
-file("src/pig/agent/runtime.gleam", 132).
?DOC(
" Resume the agent loop from its current history.\n"
"\n"
" Looks at the last message in history to determine the entry point:\n"
" - User/Tool message → call the provider\n"
" - Assistant with stop_reason=ToolUse → execute pending tool calls\n"
" - Assistant with stop_reason=Stop → return immediately\n"
" - Assistant with stop_reason=Length/Error → re-call provider\n"
).
-spec run_continue(gleam@erlang@process:subject(runtime_msg()), integer()) -> {ok,
pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}.
run_continue(Subject, Timeout) ->
gleam@otp@actor:call(
Subject,
Timeout,
fun(Reply_to) -> {continue, Reply_to} end
).
-file("src/pig/agent/runtime.gleam", 140).
?DOC(" Send a stop message to the runtime actor.\n").
-spec stop(gleam@erlang@process:subject(runtime_msg())) -> nil.
stop(Subject) ->
gleam@otp@actor:send(Subject, stop).
-file("src/pig/agent/runtime.gleam", 145).
?DOC(" Get the agent's current message history.\n").
-spec history(gleam@erlang@process:subject(runtime_msg()), integer()) -> list(pig_protocol@message:message()).
history(Subject, Timeout) ->
gleam@otp@actor:call(
Subject,
Timeout,
fun(Reply_to) -> {get_history, Reply_to} end
).
-file("src/pig/agent/runtime.gleam", 154).
?DOC(
" Send a prompt to the runtime and wait for a response.\n"
" Returns `Error(Nil)` if the call times out or the runtime crashes.\n"
).
-spec try_run(gleam@erlang@process:subject(runtime_msg()), binary(), integer()) -> {ok,
{ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}} |
{error, nil}.
try_run(Subject, Prompt, Timeout) ->
pig_agent_try_call_ffi:try_call(
Subject,
Timeout,
fun(Reply_to) -> {run, Prompt, Reply_to} end
).
-file("src/pig/agent/runtime.gleam", 164).
?DOC(
" Resume the agent loop from its current history and wait for a response.\n"
" Returns `Error(Nil)` if the call times out or the runtime crashes.\n"
).
-spec try_run_continue(gleam@erlang@process:subject(runtime_msg()), integer()) -> {ok,
{ok, pig_protocol@message:message()} |
{error, pig_protocol@error:ai_error()}} |
{error, nil}.
try_run_continue(Subject, Timeout) ->
pig_agent_try_call_ffi:try_call(
Subject,
Timeout,
fun(Reply_to) -> {continue, Reply_to} end
).
-file("src/pig/agent/runtime.gleam", 182).
?DOC(
" Create a ChildSpecification for use with static_supervisor.\n"
"\n"
" Starts a named actor so the Subject can be recovered after\n"
" supervisor start via `process.named_subject(name)`.\n"
).
-spec supervised(
pig@agent@state:agent_config(),
gleam@erlang@process:name(pig@obs@dispatcher:dispatcher_message()),
gleam@erlang@process:name(runtime_msg())
) -> gleam@otp@supervision:child_specification(nil).
supervised(Agent_config, Dispatcher_name, Name) ->
gleam@otp@supervision:worker(
fun() ->
Runtime_config = {runtime_config,
erlang:element(2, Agent_config),
erlang:element(3, Agent_config),
[],
gleam@erlang@process:named_subject(Dispatcher_name),
erlang:element(6, Agent_config),
erlang:element(5, Agent_config)},
Initial_state = {runtime_state,
pig@agent@state:new(Agent_config),
Runtime_config},
Builder = begin
_pipe = gleam@otp@actor:new(Initial_state),
_pipe@1 = gleam@otp@actor:on_message(
_pipe,
fun handle_message/2
),
gleam@otp@actor:named(_pipe@1, Name)
end,
case gleam@otp@actor:start(Builder) of
{ok, Started} ->
{ok, {started, erlang:element(2, Started), nil}};
{error, E} ->
{error, E}
end
end
).