The single instrumented path for every provider call made inside a run.
ReAct's main turn, PlanAndSolve's planning turn, Reflexion's critique,
the conclusion-distillation micro-call, and the compaction summary all
route through call/3, which uniformly applies:
- Request queue — the call holds a
ExAthena.RequestQueueslot for its full lifetime (per-provider concurrency gating for scarce local GPU slots), with{:queue_wait, …}loop events emitted on real waits. - ChatParams hooks — hosts adjusting params per call (or halting)
see every conversational inference, not just the main turn. Disable
with
chat_params: falsefor runtime utility calls whose fixed micro-budgets must not be reshaped by conversational hooks. - Telemetry — the
[:ex_athena, :chat]span fires for every call, with GenAI-semconv metadata plus a:purposetag (:turn,:planning,:reflection,:conclusion_distillation,:compaction_summary) so metering hosts can attribute cost. - Budget accounting — usage and cost fold into
state.budgetwith full cost extraction (total_cost, orinput_cost/output_costsummed on the fly), and a{:usage, usage}loop event is emitted.
Starvation policy (:starvation — required)
Adapters flag output-starved turns (response.starvation) so the kernel
can retry the iteration once with an escalated max_tokens
(see ExAthena.Loop, issue #194). That escalation raises the run's
request-template completion cap ~4x for the REST of the run and re-runs
the whole iteration — the right response when a full turn starves, and a
disproportionate one when a fixed 256-token utility call does.
Every call site must therefore state its policy explicitly:
starvation: :surface— full, turn-shaped calls (main turn, planning). A starved response returns{:error, {:error_thinking_starved, info, folded_state}}so the kernel escalates; the folded state keeps the starved attempt's token burn on the budget across the retry.starvation: :tolerate— internal micro-calls (critique, distillation, summary). A starved response returns{:ok, response, folded_state}like any other; the caller treats the blank text as a failed micro-call and falls back gracefully. The kernel's escalation must never fire for these — a mis-sized micro-call must not inflate the main turn's completion cap.
Summary
Functions
Fire ChatParams hooks for request. Returns {:ok, request, state}
(with any {:inject, msg} returns appended to both the request's and the
state's messages) or {:halt, reason} when a hook bailed.
Run one provider inference through the shared instrumented path.
Types
Functions
@spec apply_chat_params(ExAthena.Loop.State.t(), ExAthena.Request.t()) :: {:ok, ExAthena.Request.t(), ExAthena.Loop.State.t()} | {:halt, term()}
Fire ChatParams hooks for request. Returns {:ok, request, state}
(with any {:inject, msg} returns appended to both the request's and the
state's messages) or {:halt, reason} when a hook bailed.
Exposed for callers that must fire hooks exactly once outside call/3
(ReAct fires them per iteration, before its transient-error retry path).
@spec call(ExAthena.Loop.State.t(), ExAthena.Request.t(), [option()]) :: {:ok, ExAthena.Response.t(), ExAthena.Loop.State.t()} | {:halt, term()} | {:error, {:error_thinking_starved, map(), ExAthena.Loop.State.t()}} | {:error, term()}
Run one provider inference through the shared instrumented path.
Options:
:purpose(required) — atom tag for telemetry attribution.:starvation(required) —:surfaceor:tolerate; see moduledoc.:chat_params— fire ChatParams hooks (defaulttrue). ReAct passesfalsebecause it applies them once per iteration itself (viaapply_chat_params/2) so a transient-error retry cannot double-fire hooks or double-append{:inject, …}messages.:stream_cb— provider stream callback (fromExAthena.Modes.ReAct.stream_callback/1);nilfor one-shotquery/2calls.
Returns:
{:ok, response, state}— usage + cost folded intostate.budget,{:usage, _}emitted.{:halt, reason}— a ChatParams hook halted before the call.{:error, {:error_thinking_starved, info, state}}— starved response understarvation: :surface; state carries the folded budget.{:error, reason}— provider error, unchanged.