Skip to content
3 min read · 557 words

@nhtio/adk/batteries/llm/anthropic_messages/adapter

Cross-environment executor adapter for the Anthropic Messages API.

Remarks

Native Anthropic Messages adapter targeting client.messages.create from @anthropic-ai/sdk (a hard static dependency of this battery). Node-first: an Anthropic API key in a browser bundle is unacceptably exposed, so browser is deliberately not a target or gate for this adapter — dangerouslyAllowBrowser exists only for the caller who accepts that risk knowingly.

Structurally a sibling of the native Ollama and OpenAI Chat Completions adapters, with the Anthropic-wire divergences:

  • Transport: the Anthropic SDK owns the HTTP call (client.messages.create), not raw fetch. ADK owns retry (maxRetries: 0 on the client) and timeout (requestTimeoutMs fences an internal AbortController linked with ctx.abortSignal; when requestTimeoutMs is 0 a large explicit sentinel is set on the SDK client so the SDK's own silent 10-minute default is never silently inherited).
  • Streaming: Stream<RawMessageStreamEvent> — an AsyncIterable the SDK already filters ping frames out of; a mid-stream provider error surfaces as a thrown APIError from for await iteration, not a discriminated event variant, so the consumption loop is wrapped in a try/catch.
  • Content-block state machine keyed by index: content_block_start carries the ONLY occurrence of a tool_use block's id/name and a redacted_thinking block's data; content_block_delta carries five delta variants (text_delta, thinking_delta, signature_delta, input_json_delta, citations_delta — the last is out of scope for v1 and is a no-op); content_block_stop finalizes only that index. message_stop is not trusted to arrive; finalization is reachable from stream EOF too.
  • Stop reasons: all seven (end_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal, model_context_window_exceeded) are handled explicitly — refusal is a real terminal HTTP-200 outcome (never mistaken for an error), model_context_window_exceeded maps to E_ANTHROPIC_MESSAGES_CONTEXT_OVERFLOW.
  • Error translation: typed SDK error classes (BadRequestError, AuthenticationError, PermissionDeniedError, NotFoundError, ConflictError, UnprocessableEntityError, RateLimitError, InternalServerError, APIConnectionTimeoutError, APIConnectionError, APIUserAbortError, generic APIError) are narrowed and mapped to ADK exceptions. Context overflow arrives as a 400 BadRequestError and is detected from body text (prompt is too long), not status. A real upstream 529 overloaded_error is retriable (E_ANTHROPIC_MESSAGES_HTTP_ERROR) — never routed into the fatal same-numbered adapter-side exceptions, which use 529 as their own unrelated status code.
  • Thinking persistence: signed thinking blocks persist as a Thought carrying a {variant:'thinking', thinking, signature, prefixFingerprint} payload; redacted_thinking blocks carry {variant:'redacted_thinking', data, prefixFingerprint}. The fingerprint is computed via the exported fingerprintAnthropicMessagesPrefix helper — never reimplemented.
  • No request repair: a tool_choice that appears not to have been honored (forces a name the response never calls) only warns via helpers.log.warnAnthropicMessagesAdapterOptions has no strictToolChoice escape hatch, consistent with the "warn loudly, send anyway" ethos.

Per-iteration flow (steps 1–9 of the plan):

  1. Merge constructor / executor / stash options and re-validate.
  2. Resolve helpers, falling back to bundled default* for each unset field.
  3. Artifact-reader tools: pre-forged onto ctx.tools by the DispatchRunner core; read as-is.
  4. Pre-render every persisted tool-call result into an Anthropic tool-result content block.
  5. When tokenEncoding !== null, sum the token weight of every persisted bucket and throw @nhtio/adk/batteries!E_ANTHROPIC_MESSAGES_CONTEXT_OVERFLOW when the total exceeds contextWindow.
  6. Build the request via buildAnthropicMessagesHistory.
  7. Call client.messages.create with retry/timeout ownership; classify and translate errors.
  8. Streaming path: content-block state machine + usage latching + stop-reason handling.
  9. Non-streaming path: same persistence + tool-execution loop, from a single Message.

Classes

ClassDescription
AnthropicMessagesAdapterOpinionated cross-environment LLM adapter for the Anthropic Messages wire shape.