Skip to content

feat: v1/messages support, part 2 (client): add native Anthropic v1/messages client - #2283

Open
RobGeada wants to merge 2 commits into
NVIDIA-NeMo:developfrom
RobGeada:feat/anthropic-client
Open

feat: v1/messages support, part 2 (client): add native Anthropic v1/messages client#2283
RobGeada wants to merge 2 commits into
NVIDIA-NeMo:developfrom
RobGeada:feat/anthropic-client

Conversation

@RobGeada

@RobGeada RobGeada commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Description

This adds a native Anthropic client similar to the existing openai_chat.py, which allows for native support of v1/messages APIs for both inference and guardrail models.

Changes:

  • Adds an AnthropicClient and AnthropicChatModel for calling the Anthropic Messages API through the default framework
  • Registers the 'anthropic' provider (base URL, API key env var, and client pooling) in DefaultFramework
  • Adds anthropic_utils for converting between Anthropic and NeMo message formats.
  • Adds documentation for the new Anthropic client

Note: this depends on PR #2282 - only the second commit in this PR is net-new

Related Issue(s)

#2226

Verification

  • Could successfully communicate with v1/messages servers when using this branch

AI Assistance

  • No AI tools were used.
  • AI tools were used; a human reviewed and can explain every change (tool: Claude code).

Checklist

  • I've read the CONTRIBUTING guidelines.
  • This PR links to a triaged issue assigned to me.
  • My PR title follows the project commit convention.
  • I've updated the documentation if applicable.
  • I've added tests if applicable.
  • I've noted any verification beyond CI and any checks I couldn't run.
  • I did not update generated changelog files manually.
  • I addressed all CodeRabbit, Greptile, and other review comments, or replied with why no change is needed.
  • @mentions of the person or team responsible for reviewing proposed changes.

Add an optional reasoning field to the ChatMessage dataclass so
reasoning content survives round-tripping through to_dict/from_dict,
and extract it from OpenAI-compatible responses via the 'reasoning'
key in addition to 'reasoning_content'.

Signed-off-by: Rob Geada <rob@geada.net>
@github-actions github-actions Bot added status: needs triage New issues that have not yet been reviewed or categorized. size: L needs: signing labels Aug 12, 2026
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

PR merge guidance

@RobGeada thanks for the PR. GitHub is currently blocking merge for one or more repository requirements:

  • This branch has merge conflicts with develop. Please rebase your branch on the latest develop, resolve the conflicts locally, and force-push the updated branch.
  • 2 commits do not have a verified signature (26e6b0e, 8f415a4). Please sign the commits and force-push the updated branch.

Relevant guide:

Comment thread nemoguardrails/llm/frameworks/default.py
Comment thread nemoguardrails/llm/clients/anthropic.py Outdated
Signed-off-by: Rob Geada <rob@geada.net>
@christinaexyou

Copy link
Copy Markdown
Contributor

thanks, @RobGeada - a couple of comments that require follow-up:

  • currently, this does not support tool calls - it forwards along OpenAI-shaped tools and tool_choice params to llm_call. the Anthropic messages API expects the following shapes tools: {name, description, input_schema} and tool_choice: {type: "auto"|"any"|"tool"|"none"}. to resolve this we can either 1) add a Anthropic parser in _TOOL_PARSERS or 2) remove tool calls from the scope of this PR and do a follow-up PR

  • in general, we should validate **kwargs in _build_payload. a user could potentially define OpenAI params on accident and they would be forwarded along to the model backend causing a 40x error

{"role": "assistant", "content": blocks if blocks else [{"type": "text", "text": ""}]}
)

elif msg.role == Role.TOOL:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iiuc this parses every tool as its own user message. i think anthropic requires multiple tool results to be part of one consecutive user turn (https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview#handling-tool-use-and-tool-result-content-blocks)

if response.headers:
provider_metadata["response_headers"] = dict(response.headers)
if thinking_blocks:
provider_metadata["thinking_blocks"] = thinking_blocks

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is a multi-turn scenario, i think this would raise a 40x error on the Anthropic side. iiuc, Anthropic uses signatures in thinking blocks to keep track of multi-turn messages.

this line adds the signed thinking blocks to provider_metadata which the ChatMessage conversion drops so it never is passed onto the next prompt

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would keep the signed blocks: LLMResponse.reasoning = thinking_blocks

continue

if delta_type == "thinking_delta":
yield LLMResponseChunk(delta_reasoning=delta.get("thinking"))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're also dropping signatures here

perhaps we can update this line to include content_block_start + thinking_delta + signature_delta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs: rebase needs: signing size: L status: needs triage New issues that have not yet been reviewed or categorized.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants