-module(pig@agent@update). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/pig/agent/update.gleam"). -export([update/2]). -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( " Pure agent state machine — the sans-IO update function.\n" "\n" " `update(state, msg) -> StepResult(msg)` is a pure function:\n" " no provider calls, no tool execution, no telemetry, no hooks.\n" "\n" " The runtime calls this function, interprets the returned effects,\n" " and feeds results back as new messages.\n" ). -file("src/pig/agent/update.gleam", 89). -spec handle_tool_results( pig@agent@state:agent_state(), list({pig_protocol@message:tool_call(), {ok, gleam@json:json()} | {error, pig@tool:tool_error()}}) ) -> pig@agent@step_result:step_result(pig@agent@msg:agent_msg()). handle_tool_results(St, Results) -> Tool_messages = gleam@list:map( Results, fun(Pair) -> {Call, Result} = Pair, Content = case Result of {ok, Json_val} -> gleam@json:to_string(Json_val); {error, Tool_err} -> <<"Tool error: "/utf8, (pig@tool:error_message(Tool_err))/binary>> end, {tool, erlang:element(2, Call), Content} end ), New_st = begin _pipe = Tool_messages, _pipe@1 = gleam@list:fold(_pipe, St, fun pig@agent@state:add_message/2), pig@agent@state:increment_iterations(_pipe@1) end, case pig@agent@state:exceeded_max_iterations(New_st) of true -> {failed, New_st, pig@agent@state:max_iterations_error(New_st)}; false -> Msgs = pig@agent@state:messages_for_provider(New_st), Tools = pig@agent@state:tool_definitions(New_st), {continue, New_st, [{call_provider, Msgs, Tools, fun(R) -> case R of {ok, Ir} -> {provider_responded, {ok, erlang:element(2, Ir)}}; {error, E} -> {provider_responded, {error, E}} end end}]} end. -file("src/pig/agent/update.gleam", 49). -spec handle_provider_responded( pig@agent@state:agent_state(), {ok, pig_protocol@message:message()} | {error, pig_protocol@error:ai_error()} ) -> pig@agent@step_result:step_result(pig@agent@msg:agent_msg()). handle_provider_responded(St, Result) -> case Result of {ok, {assistant, _, Calls, _, _} = Assistant_msg} -> case Calls of [] -> New_st = pig@agent@state:add_message(St, Assistant_msg), {done, New_st, Assistant_msg}; _ -> New_st@1 = pig@agent@state:add_message(St, Assistant_msg), {continue, New_st@1, [{execute_tools, Calls, fun(Results) -> {tool_results, Results} end}]} end; {ok, Other} -> New_st@2 = pig@agent@state:add_message(St, Other), {done, New_st@2, Other}; {error, E} -> {failed, St, E} end. -file("src/pig/agent/update.gleam", 33). -spec handle_user_prompt(pig@agent@state:agent_state(), binary()) -> pig@agent@step_result:step_result(pig@agent@msg:agent_msg()). handle_user_prompt(St, Prompt) -> New_st = pig@agent@state:add_message(St, {user, Prompt}), Msgs = pig@agent@state:messages_for_provider(New_st), Tools = pig@agent@state:tool_definitions(New_st), {continue, New_st, [{call_provider, Msgs, Tools, fun(R) -> case R of {ok, Ir} -> {provider_responded, {ok, erlang:element(2, Ir)}}; {error, E} -> {provider_responded, {error, E}} end end}]}. -file("src/pig/agent/update.gleam", 23). ?DOC( " Pure state transition: given current state and a message, return\n" " the next state and any effects the runtime should execute.\n" "\n" " This is the entire agent loop logic, sans IO.\n" ). -spec update(pig@agent@state:agent_state(), pig@agent@msg:agent_msg()) -> pig@agent@step_result:step_result(pig@agent@msg:agent_msg()). update(St, M) -> case M of {user_prompt, Prompt} -> handle_user_prompt(St, Prompt); {provider_responded, Result} -> handle_provider_responded(St, Result); {tool_results, Results} -> handle_tool_results(St, Results) end.