Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions fern/versions/latest/pages/model-server/claude.mdx
Comment thread
ffrujeri marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
---
title: "Claude"
description: "Use Anthropic Claude models through NeMo Gym's Responses API"
position: 4
---

NeMo Gym offers two complementary ways to work with Claude, aimed at different audiences:

- **Egress (`anthropic_model` — this page):** Your harness speaks NeMo Gym's native [Responses API](https://developers.openai.com/api/reference/resources/responses/methods/create) on `/v1/responses` and the backend is Anthropic Claude. Use this when you already run Gym agents or harnesses in Responses format and want Claude as the policy model.
- **Ingress (built into every model server):** Your harness speaks Anthropic's [Messages API](https://docs.anthropic.com/en/api/messages) on `/v1/messages` and the backend is any Gym model server (vLLM, OpenAI, an inference provider, etc.). Every Gym model server exposes `/v1/messages` by default, mapping Messages ↔ Responses around its own `responses()` implementation. Blackbox agents that already talk Anthropic Messages — notably the [Claude Code Agent](https://github.com/NVIDIA-NeMo/Gym/tree/main/responses_api_agents/claude_code_agent) — can target any Gym backend without rewriting to Responses.

The `anthropic_model` server is the egress path: it accepts Responses API requests, translates them to Anthropic `/v1/messages`, and maps Anthropic responses back to Responses objects.

Conversion logic lives in `nemo_gym.anthropic_converter` and is shared with the ingress `/v1/messages` route on other Gym model servers.

<Info>
For **training** workloads that require token IDs and log probabilities, use [vLLM](/model-server/vllm) instead. Anthropic's hosted API does not expose the token-level information needed for RL training.

</Info>

## Supported APIs

This server exposes one endpoint and converts to Anthropic Messages under the hood:

- **OpenAI Responses** — `/v1/responses`

Chat Completions (`/v1/chat/completions`) is not supported on this server.

If your agent speaks Anthropic Messages instead of Responses, you do not need `anthropic_model`. Point the agent at any Gym model server's `/v1/messages` endpoint (see ingress above).

## Set Your Credentials

Store your values in `env.yaml` in the project root (gitignored):

```yaml
policy_base_url: https://api.anthropic.com
policy_api_key: your-api-key
policy_model_name: claude-sonnet-4-6
```

`policy_base_url` accepts either a host-only or `/v1` style URL. Both `https://api.anthropic.com` and `https://api.anthropic.com/v1` resolve to `/v1/messages`.

## Configuration Reference

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `anthropic_base_url` | `str` | `https://api.anthropic.com/v1` | Base URL for the Anthropic API. |
| `anthropic_api_key` | `str` | — | **Required.** Anthropic API key (`x-api-key` header). |
| `anthropic_model` | `str` | — | **Required.** Model identifier (for example, `claude-sonnet-4-6`). |
| `max_tokens` | `int` | `32768` | Maximum tokens to generate per request when `max_output_tokens` is not set on the Responses request. |
| `anthropic_version` | `str` | `2023-06-01` | Value for the `anthropic-version` header. |
| `thinking` | `dict` | `null` | Typed thinking config for modern Claude models (for example, `{type: adaptive}`). |
| `thinking_budget_tokens` | `int` | `null` | Budget for older models that use `thinking: {type: enabled, budget_tokens: ...}`. |
| `max_concurrent_requests` | `int` | `null` | Cap on in-flight upstream requests (per-process). `null` = unlimited. |
| `extra_body` | `dict` | `{}` | Provider-specific fields merged into every Anthropic request body. |

<Note>
**The model is fixed by configuration.** This server always sends the configured `anthropic_model` (from `policy_model_name`) to Anthropic. To run a different model, change the config and start a new server.

Do not set both `thinking` and `thinking_budget_tokens` — the server rejects ambiguous thinking configuration.

</Note>

### Thinking configuration

For modern Claude models, prefer adaptive thinking:

```yaml
thinking:
type: adaptive
```

`thinking_budget_tokens` remains available for older models that require manual `thinking: {type: enabled, budget_tokens: ...}`.

### Model-specific behavior

Claude Opus 4.7 and 4.8 reject configurable sampling parameters (`temperature`, `top_p`, `top_k`). Omit them from requests and use prompting or adaptive thinking/effort controls instead.

Responses `input_image` parts are supported when `image_url` is a base64 data URL. Supported media types are `image/jpeg`, `image/png`, `image/gif`, and `image/webp`; remote image URLs are rejected with HTTP 400.

Provider-specific Anthropic fields that are not modeled as typed config can be passed through `extra_body`.

## Usage Example

### 1. Set model and environment config

```bash
environment_config="resources_servers/example_single_tool_call/configs/example_single_tool_call.yaml"
model_config="responses_api_models/anthropic_model/configs/anthropic_model.yaml"
```

### 2. Start servers

```bash
ng_run "+config_paths=[${environment_config},${model_config}]"
```

### 3. Evaluate your agent

```bash
mkdir -p results

ng_collect_rollouts +agent_name=example_single_tool_call_simple_agent \
+input_jsonl_fpath=resources_servers/example_single_tool_call/data/example.jsonl \
+output_jsonl_fpath=results/claude_example_single_tool_call_rollouts.jsonl \
+limit=1 \
+num_repeats=1
```

### Smoke test the model server

Once the model server is running, send a direct Responses request:

```bash
curl -s <POLICY_MODEL_URL>/v1/responses \
-H 'Content-Type: application/json' \
-d '{
"input": "Say hello in one short sentence.",
"max_output_tokens": 64
}' | python -m json.tool
```

<Note>
This example uses the simple agent harness because it exercises `anthropic_model` as the policy model server through NeMo Gym's `/v1/responses` interface. For Claude Code workflows that speak Anthropic Messages, use the ingress path: set the agent's `model_server` ref so the CLI targets `/v1/messages` on any Gym model server (see [Claude Code Agent](https://github.com/NVIDIA-NeMo/Gym/tree/main/responses_api_agents/claude_code_agent)).

</Note>
6 changes: 6 additions & 0 deletions fern/versions/latest/pages/model-server/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ Use models hosted on Azure OpenAI deployments.
<Badge minimal outlined>cloud</Badge>
</Card>

<Card title="Claude" href="/model-server/claude">
Use Anthropic Claude models through NeMo Gym's Responses API.

<Badge minimal outlined>cloud</Badge>
</Card>

<Card title="Inference Providers" href="/model-server/inference-providers">
Use hosted providers like Fireworks, Together.ai, OpenRouter, and more.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Inference Providers"
description: "Use hosted inference providers like Fireworks, Together.ai, OpenRouter, and more for eval workloads"
position: 4
position: 5
---

The `inference_provider` server connects NeMo Gym to any hosted inference provider. The server manages the conversion to and from the Responses API: it translates incoming Responses requests to Chat Completions for the provider and converts the reply back into a Responses object — so your agent code stays the same across backends.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Local vLLM Proxy"
description: "Expose one Local vLLM deployment as multiple model servers"
position: 7
position: 8
---

LocalVLLMModelProxy (in `responses_api_models/local_vllm_model_proxy`) is a lightweight model server that forwards requests to an existing [LocalVLLMModel](/model-server/local-vllm) instead of launching its own vLLM engine.
Expand Down
2 changes: 1 addition & 1 deletion fern/versions/latest/pages/model-server/local-vllm.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Local vLLM"
description: "Gym-managed vLLM server deployment"
position: 6
position: 7
---

NeMo Gym can launch and manage the vLLM server for you using LocalVLLMModel (in `responses_api_models/local_vllm_model`).
Expand Down
2 changes: 1 addition & 1 deletion fern/versions/latest/pages/model-server/vllm.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "vLLM"
description: "Wrapper for an existing, external vLLM server"
position: 5
position: 6
---
[vLLM](https://docs.vllm.ai/) is a popular LLM inference engine. The NeMo Gym VLLMModel server wraps vLLM's Chat Completions endpoint and converts requests and responses to NeMo Gym's native format, the OpenAI [Responses API](https://platform.openai.com/docs/api-reference/responses) schema.

Expand Down
19 changes: 10 additions & 9 deletions nemo_gym/anthropic_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ def responses_to_anthropic_response(self, response: NeMoGymResponse, model: str)
if item_type == "message":
content.extend(self._output_message_to_anthropic_blocks(item))
elif item_type == "reasoning":
content.extend(self._reasoning_item_to_anthropic_blocks(item))
content.extend(self._reasoning_item_to_anthropic_blocks(item, default_empty_signature=True))
elif item_type == "function_call":
content.append(self._function_call_to_tool_use(item))
has_tool_use = True
Expand Down Expand Up @@ -712,16 +712,17 @@ def _content_to_text(self, content: Any) -> str:
def _system_parts_to_anthropic_blocks(self, system_parts: List[str]) -> List[Dict[str, str]]:
return [{"type": "text", "text": text} for text in system_parts if text]

def _reasoning_item_to_anthropic_blocks(self, item: Dict[str, Any]) -> List[Dict[str, Any]]:
def _reasoning_item_to_anthropic_blocks(
self, item: Dict[str, Any], default_empty_signature: bool = False
) -> List[Dict[str, Any]]:
blocks = []
for summary in item.get("summary", []):
# Anthropic's ThinkingBlock requires a signature; open-model backends don't
# produce one, so default to "" (the synthesized SSE never emits it anyway).
block = {
"type": "thinking",
"thinking": summary["text"],
"signature": item.get("encrypted_content") or "",
}
block: Dict[str, Any] = {"type": "thinking", "thinking": summary["text"]}
encrypted_content = item.get("encrypted_content")
if encrypted_content:
block["signature"] = encrypted_content
elif default_empty_signature:
block["signature"] = ""
blocks.append(block)
return blocks

Expand Down
70 changes: 70 additions & 0 deletions responses_api_models/anthropic_model/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Description

`anthropic_model` is a native Anthropic Messages API model server behind NeMo Gym's `/v1/responses` interface. It translates NeMo Gym Responses API requests to Anthropic `/v1/messages` payloads and maps Anthropic responses back to NeMo Gym Responses API objects.

It supports text messages, base64 image inputs, system/developer prompt extraction, function tools, previous tool calls/results, thinking blocks, usage mapping, and optional request concurrency limiting. It uses `nemo_gym.server_utils.request()` for raw aiohttp transport instead of the Anthropic Python SDK.

# Usage

Start with a resources server config and the Anthropic model config:

```bash
ng_run "+config_paths=[resources_servers/example_single_tool_call/configs/example_single_tool_call.yaml,responses_api_models/anthropic_model/configs/anthropic_model.yaml]" \
+policy_base_url="$ANTHROPIC_BASE_URL" \
+policy_api_key="$ANTHROPIC_API_KEY" \
+policy_model_name="$ANTHROPIC_MODEL_NAME"
```

`anthropic_base_url` accepts either host-only or `/v1` style URLs. Both `https://api.anthropic.com` and `https://api.anthropic.com/v1` resolve to `/v1/messages`.

This example uses the simple agent harness because it exercises `anthropic_model` as the policy model server through NeMo Gym's `/v1/responses` interface. `claude_code_agent` is a separate agent harness that invokes Claude Code/Anthropic directly, so it is useful for testing Claude Code workflows but does not validate this model server.

For modern Claude models, prefer adaptive thinking with the typed `thinking` config:

```yaml
thinking:
type: adaptive
```

`thinking_budget_tokens` remains available for older models that require manual `thinking: {type: enabled, budget_tokens: ...}`.

Minimal direct smoke test once the model server is running:

```bash
curl -s <POLICY_MODEL_URL>/v1/responses \
-H 'Content-Type: application/json' \
-d '{
"input": "Say hello in one short sentence.",
"max_output_tokens": 64
}' | python -m json.tool
```

Collect one rollout through the simple agent:

```bash
mkdir -p results

ng_collect_rollouts \
+agent_name=example_single_tool_call_simple_agent \
+input_jsonl_fpath=resources_servers/example_single_tool_call/data/example.jsonl \
+output_jsonl_fpath=results/claude_example_single_tool_call_rollouts.jsonl \
+limit=1 \
+num_repeats=1
```

# Notes

Provider-specific Anthropic fields that are not modeled as typed config can be passed through `extra_body`. Some options are model-specific: Claude Opus 4.7 and 4.8 reject configurable sampling parameters (`temperature`, `top_p`, `top_k`), so omit them and use prompting or adaptive thinking/effort controls instead.

Responses `input_image` parts are supported when `image_url` is a base64 data URL. Supported media types are `image/jpeg`, `image/png`, `image/gif`, and `image/webp`; remote image URLs are rejected with a 400.

Anthropic `stop_reason` values are mapped to Responses-compatible `incomplete_details` when possible. `max_tokens` and `model_context_window_exceeded` map to `max_output_tokens`; `refusal` maps to `content_filter`. Other stop reasons such as `end_turn`, `tool_use`, and `pause_turn` remain complete responses.

# Licensing information

Code: Apache 2.0

Data: N/A

Dependencies:
- nemo_gym: Apache 2.0
Loading
Loading