Normandy.Agents.ToolCallResponse (normandy v2.2.0)

View Source

Response schema for agent outputs that may include tool calls.

This schema allows the LLM to either provide a final text response or request tool executions.

:thinking holds the response's thinking and redacted_thinking blocks in their wire shape (string keys, with signature or data). They are sent back unchanged, first, when the turn is replayed: a thinking model needs them on the assistant message that carries the tool calls.

:prefix_key (see prefix_key/3) records the system prompt, tools and model the response was produced under. The API accepts a thinking block only under that same prefix and model, so Normandy.Components.AgentMemory.drop_stale_thinking/2 drops thinking whose key no longer matches. It is never sent to the API.

:content_blocks holds the assistant turn as the API returned it: string-keyed, API-shaped blocks in their order (thinking, text with its citations, tool_use, server_tool_use, mcp_tool_use and their results), as Claudio.Messages.Response.to_assistant_content/1 makes them. When it is not empty, the turn is replayed from it unchanged, so the next request carries exactly what the model produced; :content, :tool_calls and :thinking are what Normandy reads. A response built without it (by hand, or stored before it existed) is rebuilt from those three fields.

:stop_reason is why the model stopped (Normandy.LLM.Response.stop_reason/0), or nil when the client did not say. The turn continues a :pause_turn response. It is never sent to the API.

Summary

Functions

:content_blocks for a response's content blocks given as maps (a streamed message's string-keyed blocks): Claudio.Messages.Response.to_assistant_content/1 of them, so a streamed turn is replayed in the same shape as a non-streamed one.

A key for the part of a request a thinking block is bound to besides the messages: the system prompt, the tool set and the model. The API binds tools as a set sorted by name, so their order doesn't change the key.

The thinking and redacted_thinking blocks among response content blocks (atom or string keys), in order and in wire shape, for :thinking.

Types

t()

@type t() :: %Normandy.Agents.ToolCallResponse{
  content: String.t() | nil,
  content_blocks: [map()],
  prefix_key: integer() | nil,
  stop_reason: Normandy.LLM.Response.stop_reason() | nil,
  thinking: [map()],
  tool_calls: [Normandy.Components.ToolCall.t()]
}

Functions

api_blocks(blocks)

@spec api_blocks([map()]) :: [map()]

:content_blocks for a response's content blocks given as maps (a streamed message's string-keyed blocks): Claudio.Messages.Response.to_assistant_content/1 of them, so a streamed turn is replayed in the same shape as a non-streamed one.

prefix_key(system_prompt, tools, model)

@spec prefix_key(term(), [map()], String.t() | nil) :: non_neg_integer()

A key for the part of a request a thinking block is bound to besides the messages: the system prompt, the tool set and the model. The API binds tools as a set sorted by name, so their order doesn't change the key.

thinking_blocks(blocks)

@spec thinking_blocks([map()]) :: [map()]

The thinking and redacted_thinking blocks among response content blocks (atom or string keys), in order and in wire shape, for :thinking.