Anthropic adapter that satisfies the ::ai::chat/run-loop contract:
chat-with-tools(
model: Str,
messages: Vec<::ai::chat/Message>,
system: Str?,
tools: Vec<::ai::tool/Tool>?
): ::ai::chat/ChatReply
The function translates ::ai::chat/Message records into
Anthropic's content-block shape, calls
::anthropic::messages/post, and normalizes the response into a
::ai::chat/ChatReply so that run-loop does not need to know
about Anthropic's wire format.
Values
DEFAULT_MAX_TOKENS
DEFAULT_MAX_TOKENS: Int 1024
EMPTY_STATE
EMPTY_STATE: Map {
text-blocks: {},
tool-blocks: {},
block-order: [],
stop-reason: null,
usage-input: 0,
usage-output: 0
}
Functions
apply-request-options
fn (request: Map, options: ChatRequestOptions): Map
Apply output and effort controls to an Anthropic request map.
block-text
fn (b: Map): Str
Text from a single content block, or empty string.
block-tool-call
fn (b: Map): ::ai::tool/ToolCall?
Convert a tool_use content block into a ::ai::tool/ToolCall, or null otherwise.
chat-with-tools
fn (model: Str, messages: Vec<::ai::chat/Message>, system: Str?, tools: Vec<::ai::tool/Tool>?): ::ai::chat/ChatReply
Tools-aware chat function compatible with ::ai::chat/run-loop.
Parameters
model— Anthropic model id (e.g."claude-sonnet-4-5").messages— conversation history asVec<::ai::chat/Message>.system— optional system prompt.tools— optionalVec<::ai::tool/Tool>. When non-null, tools are advertised to the model andtool_useresponses becomeChatReply.tool-calls.
Returns a normalized ::ai::chat/ChatReply regardless of the
underlying response shape.
Example
::tool ::ai::tool
add fn (x: Int, y: Int): Int { add(x, y) }
opts ::ai::chat/ChatOptions({
chat-fn: ::anthropic::chat-tools/chat-with-tools,
model: "claude-sonnet-4-5",
tools: [::tool/from-fn(add)]
})
answer ::ai::chat/run-loop(opts, "What is 17 + 25?")
chat-with-tools-options
fn (model: Str, messages: Vec<::ai::chat/Message>, system: Str?, tools: Vec<::ai::tool/Tool>?, options: ChatRequestOptions): ::ai::chat/ChatReply
chat-with-tools with an explicit output ceiling and Anthropic effort.
Bind this function in a closure when a session needs model-specific
controls while retaining the normalized four-argument chat contract.
chat-with-tools-stream
fn (model: Str, messages: Vec<::ai::chat/Message>, system: Str?, tools: Vec<::ai::tool/Tool>?, on-delta: Fn): ::ai::chat/ChatReply
Streaming counterpart to chat-with-tools, compatible with
::ai::chat/run-loop-stream. Translates Anthropic SSE events
into provider-agnostic ::ai::chat/ReplyDelta events delivered
via on-delta, and returns a fully-aggregated ChatReply once
the stream terminates.
Parameters
model— Anthropic model id.messages— conversation history asVec<::ai::chat/Message>.system— optional system prompt.tools— optionalVec<::ai::tool/Tool>.on-delta— callback(delta: ReplyDelta): Nullinvoked for every event during streaming. Use::ai::chat/noop-on-deltaif you only care about the finalChatReply.
Example
on-text (delta) {
match delta {
::ai::chat/TextDelta => { print(delta.text) }
=> { null }
}
}
reply chat-with-tools-stream(
"claude-sonnet-4-5",
[::ai-chat/user-message("Hi")],
null,
null,
on-text
)
Then reply.text holds the full assistant text, reply.tool-calls
holds any parsed tool calls (with their input maps reconstructed
from streamed JSON fragments), and reply.usage carries the
provider-reported token totals.
chat-with-tools-stream-options
fn (model: Str, messages: Vec<::ai::chat/Message>, system: Str?, tools: Vec<::ai::tool/Tool>?, on-delta: Fn, options: ChatRequestOptions): ::ai::chat/ChatReply
Streaming chat-with-tools with explicit output and effort controls.
count-tokens
fn (messages: Vec<::ai::chat/Message>, model: Str): Int
Count the input tokens a Vec<::ai::chat/Message> would consume
against model, using Anthropic's stable
/v1/messages/count_tokens endpoint. Returns the exact count
Anthropic would charge.
Compatible with the count-tokens-fn slot on
::ai::chat/ChatOptions:
(messages: Vec<Message>, model: Str): Int
System prompt and tools are not currently included by this helper — pass them via a custom wrapper if you need to model their cost. The caller's pre-call budget check should add the model's reserved overhead separately.
Network calls cost nothing in API credits but they are not free in latency; cache the result when checking budget repeatedly against an unchanged history.
drain-stream
fn (iter: Any, on-delta: Fn, state: Map): Map
Internal: tail-recursively consume iter, accumulating ReplyDelta events into state.
finalize-state
fn (state: Map): ::ai::chat/ChatReply
Internal: convert accumulated stream state into a ChatReply.
Concatenates text blocks in index order, parses the buffered
tool-input JSON for each tool block, and assembles the final
usage map.
handle-content-block-delta
fn (state: Map, idx: Int, delta: Map, on-delta: Fn): Map
Internal: dispatch a content_block_delta SSE event into ReplyDelta + state.
handle-content-block-start
fn (state: Map, idx: Int, block: Map, on-delta: Fn): Map
Internal: dispatch a content_block_start SSE event into ReplyDelta + state.
handle-content-block-stop
fn (state: Map, idx: Int, on-delta: Fn): Map
Internal: dispatch a content_block_stop SSE event into ReplyDelta + state.
handle-event
fn (state: Map, event: Map, on-delta: Fn): Map
Internal: dispatch a single SSE event from Anthropic's
post-stream into the appropriate ReplyDelta emissions and a
new accumulator state.
handle-message-delta
fn (state: Map, data: Map): Map
Internal: capture stop_reason and output token usage from a message_delta event.
handle-message-start
fn (state: Map, data: Map): Map
Internal: capture initial input usage from a message_start event.
response-text
fn (blocks: Vec): Str
Concatenate text content blocks from an Anthropic response.
response-tool-calls
fn (blocks: Vec): Vec<::ai::tool/ToolCall>
Extract ::ai::tool/ToolCall entries from a list of Anthropic content blocks.
response-usage
fn (u: Any): Map?
Normalize Anthropic's usage shape into {input-tokens, output-tokens}.
to-anthropic-message
fn (m: ::ai::chat/Message): Map
Convert a single normalized ::ai::chat/Message into Anthropic's
InputMessage shape. Tool results are emitted as user turns
with a tool_result content block (Anthropic's wire convention).
to-anthropic-tool
fn (t: ::ai::tool/Tool): Map
Convert a ::ai::tool/Tool into Anthropic's Tool shape (rename
input-schema to input_schema, keep name and description).
to-tool-use-block
fn (call: ::ai::tool/ToolCall): Map
Convert a ::ai::tool/ToolCall into an Anthropic tool_use content block.
Types
ChatRequestOptions
ChatRequestOptions type {
max-tokens: Int,
effort: Str?
}
Per-request output controls for Anthropic Messages.
Claude Opus 5 has thinking enabled by default and counts thinking plus
visible text against max_tokens, so callers selecting it should use a
materially larger ceiling than the legacy 1024-token adapter default.