ExAthena.ToolCalls (ExAthena v0.4.6)

Copy Markdown View Source

Extracts tool calls from a provider response.

Three parsers:

  • ExAthena.ToolCalls.Native — structured tool_calls array from the provider (OpenAI-style function + arguments, Claude tool_use blocks, Ollama's OpenAI-compatible payload).
  • ExAthena.ToolCalls.TextTagged — prompt-engineered ~~~tool_call <json> ~~~ blocks embedded in the assistant's text.
  • ExAthena.ToolCalls.RawJson — bare or ```json-fenced JSON objects emitted by weak open-weight models that ignore both native tool-call APIs and the ~~~tool_call fence format.

Auto-fallback

extract/2 cascades through three tiers based on the response content and provider_capabilities.native_tool_calls:

  1. Structured tool_calls present → Native.parse/1.
  2. Text contains ~~~tool_call fences, or provider declared no native support → TextTagged.parse/1.
  3. Text looks like a raw JSON tool call (has both "name" and "arguments" substrings) → RawJson.parse/1.
  4. No match → {:ok, []}.

The agent loop uses augment_system_prompt/2 to add text-tagged instructions when a provider lacks native support.

Summary

Functions

Append text-tagged tool-call instructions to a system prompt so non-native-tool-call providers know how to respond.

Extract tool calls from a provider response.

Types

provider_response()

@type provider_response() :: %{
  optional(:text) => String.t() | nil,
  optional(:tool_calls) => list() | nil
}

Functions

augment_system_prompt(existing, tools, opts \\ [])

@spec augment_system_prompt(String.t() | nil, [map()], keyword()) :: String.t()

Append text-tagged tool-call instructions to a system prompt so non-native-tool-call providers know how to respond.

The agent loop (Phase 2) calls this when the chosen provider lacks native tool-call support, or when the user has forced TextTagged mode.

Options

  • :compact — when true, emits a one-line-per-tool signature instead of the full JSON schema. Useful for weak open-weight models that degrade past ~4 KB system prompts. Default: false.

extract(response, capabilities \\ %{})

@spec extract(provider_response(), ExAthena.Capabilities.t()) ::
  {:ok, [ExAthena.Messages.ToolCall.t()]} | {:error, term()}

Extract tool calls from a provider response.

Returns {:ok, [ToolCall.t()]} — always a list (possibly empty), never nil. Pass capabilities so the parser can pick the right protocol.