ExAthena.Loop.Parallel (ExAthena v0.4.2)

Copy Markdown View Source

Parallel tool-call dispatcher.

When a model emits multiple tool calls in a single response, the kernel groups them:

  • Read-only calls (tool's parallel_safe?/0 returns true) run concurrently via Task.async_stream/3.
  • Mutating calls run serially in the order the model emitted them, to preserve deterministic side-effect ordering.

Regardless of execution order, results are returned in the same order as the input tool calls — the model always sees results aligned with its calls.

Summary

Functions

Emit tool-call / tool-result events and update counters.

Run the pre-tool gate (permissions + PreToolUse hooks) for a single call.

Execute a list of tool calls.

Functions

emit_events(state, call, tool_message)

@spec emit_events(
  map(),
  ExAthena.Messages.ToolCall.t(),
  ExAthena.Messages.Message.t()
) :: :ok

Emit tool-call / tool-result events and update counters.

pre_tool_gate(tool_call, state)

@spec pre_tool_gate(ExAthena.Messages.ToolCall.t(), map()) ::
  :allow | {:deny, term()} | {:halt, term()}

Run the pre-tool gate (permissions + PreToolUse hooks) for a single call.

Returns :allow, {:deny, reason}, or {:halt, reason}.

run(calls, state, runner_fn)

@spec run([ExAthena.Messages.ToolCall.t()], map(), (ExAthena.Messages.ToolCall.t(),
                                              map() ->
                                                {term(), map()})) ::
  {:ok, [term()], map()} | {:halt, term(), map()}

Execute a list of tool calls.

runner_fn is (ToolCall.t(), state -> {result, updated_state}). The result is whatever the single-call runner returns (typically a tool-result message tuple or a :halt tuple). Updated state threads the phase transitions, mistake counter, and budget.

Returns {:ok, results_in_order, final_state} or {:halt, reason, final_state} when any call returns a halt.

Ordering guarantee: results_in_order is ordered the same as the input calls, even though parallel-safe calls may execute out-of-order.