> This page is for version 0.3.0.
> For other versions, use one of these documentation indexes:
> - Main (default): https://docs.nvidia.com/nemo/gym/main/llms.txt
> - 0.7.0: https://docs.nvidia.com/nemo/gym/v0.7.0/llms.txt
> - 0.6.0: https://docs.nvidia.com/nemo/gym/v0.6.0/llms.txt
> - 0.5.1: https://docs.nvidia.com/nemo/gym/v0.5.1/llms.txt
> - 0.5.0: https://docs.nvidia.com/nemo/gym/v0.5.0/llms.txt
> - 0.4.0: https://docs.nvidia.com/nemo/gym/v0.4.0/llms.txt
> - 0.3.0: https://docs.nvidia.com/nemo/gym/v0.3.0/llms.txt
> - 0.2.1: https://docs.nvidia.com/nemo/gym/v0.2.1/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/nemo/gym/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/nemo/gym/_mcp/server.

# nemo_gym.models.chat_completions_converter

Shared Responses API ↔ Chat Completions converter.

This module contains the translation logic between OpenAI's Responses API format
and the Chat Completions API format. It is used by model servers that need to
convert between the two formats (e.g. vllm_model, inference_provider).

## Module Contents

### Classes

| Name                                                                                                         | Description                                                             |
| ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| [`ChatCompletionsConverter`](#nemo_gym-models-chat_completions_converter-ChatCompletionsConverter)           | Converts between OpenAI Responses API and Chat Completions API formats. |
| [`ChatCompletionsConverterState`](#nemo_gym-models-chat_completions_converter-ChatCompletionsConverterState) | -                                                                       |

### Functions

| Name                                                                                                                             | Description                                                                                            |
| -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| [`_align_logprobs_to_text`](#nemo_gym-models-chat_completions_converter-_align_logprobs_to_text)                                 | Keep the token entries that survive removing the `removed` character spans.                            |
| [`_chat_logprobs_to_responses`](#nemo_gym-models-chat_completions_converter-_chat_logprobs_to_responses)                         | Chat choice logprobs in the shape `ResponseOutputText.logprobs` expects.                               |
| [`_optional_token_count`](#nemo_gym-models-chat_completions_converter-_optional_token_count)                                     | Return a provider-reported token count without coercing missing/invalid values to zero.                |
| [`_token_information_from_mapping`](#nemo_gym-models-chat_completions_converter-_token_information_from_mapping)                 | Return validated token metadata when the mapping contains it.                                          |
| [`_usage_detail`](#nemo_gym-models-chat_completions_converter-_usage_detail)                                                     | Read one canonical nested token detail, then named provider aliases.                                   |
| [`chat_completion_usage_to_response_usage`](#nemo_gym-models-chat_completions_converter-chat_completion_usage_to_response_usage) | Convert one Chat Completions `usage` block to Responses usage; `None` when the provider reported none. |
| [`message_content_to_text`](#nemo_gym-models-chat_completions_converter-message_content_to_text)                                 | Plain text of a chat message content (a string, or a list of text parts).                              |
| [`split_responses_input_output_items`](#nemo_gym-models-chat_completions_converter-split_responses_input_output_items)           | -                                                                                                      |

### Data

[`CustomToolStrategy`](#nemo_gym-models-chat_completions_converter-CustomToolStrategy)

[`VLLMConverter`](#nemo_gym-models-chat_completions_converter-VLLMConverter)

[`VLLMConverterResponsesToChatCompletionsState`](#nemo_gym-models-chat_completions_converter-VLLMConverterResponsesToChatCompletionsState)

[`_BRIDGE_GRAMMAR_DROP_WARNED`](#nemo_gym-models-chat_completions_converter-_BRIDGE_GRAMMAR_DROP_WARNED)

[`_CUSTOM_TOOL_BRIDGE_PARAMETER`](#nemo_gym-models-chat_completions_converter-_CUSTOM_TOOL_BRIDGE_PARAMETER)

[`_DROPPABLE_REASONING_CONTEXTS`](#nemo_gym-models-chat_completions_converter-_DROPPABLE_REASONING_CONTEXTS)

[`_IMAGE_OUTPUT_PLACEHOLDER`](#nemo_gym-models-chat_completions_converter-_IMAGE_OUTPUT_PLACEHOLDER)

[`_REPORTING_ONLY_REASONING_FIELDS`](#nemo_gym-models-chat_completions_converter-_REPORTING_ONLY_REASONING_FIELDS)

[`_RESPONSE_NON_BOUNDARY_TYPES`](#nemo_gym-models-chat_completions_converter-_RESPONSE_NON_BOUNDARY_TYPES)

[`_RESPONSE_OUTPUT_BOUNDARY_TYPES`](#nemo_gym-models-chat_completions_converter-_RESPONSE_OUTPUT_BOUNDARY_TYPES)

[`__getattr__`](#nemo_gym-models-chat_completions_converter-__getattr__)

[`logger`](#nemo_gym-models-chat_completions_converter-logger)

### API

```python
class nemo_gym.models.chat_completions_converter.ChatCompletionsConverter()
```

**Bases:** `BaseModel`

Converts between OpenAI Responses API and Chat Completions API formats.

**`THINK_TAG_PATTERN`** `ClassVar = re.compile('<think>(.*?)</think>', re.DOTALL)`

---

**`custom_tool_strategy`** `CustomToolStrategy = 'passthrough'`

---

**`inline_tool_output_images`** `bool = False`

---

**`return_token_id_information`** `bool`

---

**`uses_reasoning_parser`** `bool = True`

---

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._chat_completion_to_responses_tools(
    chat_completions_tools: typing.Optional[typing.List[nemo_gym.core.responses_api_types.NeMoGymChatCompletionToolUnionParam]]
) -> typing.List[openai.types.responses.response_create_params.ToolParam]
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._chat_custom_tool_to_responses(
    tool: dict
) -> dict
```

staticmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._chat_to_responses_tool_choice(
    tool_choice: typing.Any
) -> typing.Any
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._chat_tool_content_part_to_responses(
    part: typing.Dict[str, typing.Any]
) -> typing.Dict[str, typing.Any]
```

staticmethod

A Chat tool message's text or image part as the matching `function_call_output` part.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._custom_tool_to_bridged_function(
    tool_dict: dict
) -> nemo_gym.core.responses_api_types.NeMoGymFunctionDefinition
```

staticmethod

The function-tool lowering of a Responses custom tool definition.

The free-form payload becomes the one required string parameter.
`description` is optional on a custom tool and is carried only when
present. A grammar `format` is dropped, with a warning once per tool
name: no vLLM tool parser enforces a grammar through function calling,
so the constraint falls back to the model's own behavior, the same as a
hosted model that ignores the grammar.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._decode_bridged_input(
    arguments: typing.Optional[str],
    tool_name: str = '',
    call_id: str = ''
) -> str
```

staticmethod

Recover the free-form input from bridged function arguments.

Malformed arguments, not JSON or not the expected one-parameter object,
return the raw string: the model's payload is better delivered unwrapped
than lost, and the harness then sees exactly what was generated. Every
malformed shape, and any dropped extra key, is logged with the tool name
and call id, because the harness then runs something other than a clean
bridged payload while the rollout record looks like a normal call.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._encode_bridged_input(
    input_value: str
) -> str
```

staticmethod

Encode a custom tool call's free-form input as bridged function arguments.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._extract_reasoning_from_content(
    content: str
) -> typing.Tuple[typing.List[str], str]
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_custom_tool_call(
    m: dict,
    state: nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState
) -> None
```

Replay a custom tool call in its function-bridged form.

The bridged arguments re-encode the item's free-form input as the
one-parameter object rather than echoing the byte string the engine
produced. vLLM parses a replayed call's arguments into an object before
rendering the chat template, so a well-formed call re-renders to the
same parsed object even when the byte encodings differ. A malformed
call is the limit: its raw-text lift re-encodes here as
`{"input": "<raw text>"}`, a different object from the one the model
generated.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_function_call(
    m: dict,
    state: nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState
) -> None
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_function_call_output(
    m: dict,
    state: nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState,
    drop_unrepresentable_request_fields: bool = False
) -> None
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_message(
    m: dict,
    state: nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState,
    drop_unrepresentable_request_fields: bool = False
) -> None
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_reasoning(
    m: dict,
    state: nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState
) -> None
```

Collects text from 'reasoning' messages in responses api and appends it to a buffer.

This is done to group together one (or multiple) reasoning message(s) into a single,
cohesive block, later prepending it to a subsequent assistant message.
See: [https://docs.nvidia.com/nemo/gym/main/infrastructure/engineering-notes/responses-api-evolution](https://docs.nvidia.com/nemo/gym/main/infrastructure/engineering-notes/responses-api-evolution)
for background on reasoning in the Responses API.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._input_image_to_chat(
    part_param: dict
) -> nemo_gym.core.responses_api_types.NeMoGymChatCompletionContentPartImageParam
```

staticmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._parse_think_tags(
    content: str
) -> typing.Tuple[typing.List[str], str]
```

classmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._responses_custom_tool_to_chat(
    tool: dict
) -> dict
```

staticmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._responses_to_chat_tool_choice(
    tool_choice: typing.Any
) -> typing.Any
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._tool_output_image_to_chat(
    part: dict
) -> dict
```

What the chat tool message carries for an `input_image` part of a tool call output.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._tool_reference_to_chat(
    tool: dict
) -> dict
```

staticmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._tool_reference_to_chat_lowered(
    tool: dict
) -> dict
```

A Responses tool reference in Chat form, following the bridge's lowering.

Under `function_bridge` the request declares a referenced custom tool
as a function tool, and vLLM's named tool choice admits `type:
"function"` only, so the reference is rewritten to the function form.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._tool_reference_to_responses(
    tool: dict
) -> dict
```

staticmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._wrap_reasoning_in_think_tags(
    texts: typing.List[str]
) -> str
```

staticmethod

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.chat_completion_to_response(
    responses_create_params: nemo_gym.core.responses_api_types.NeMoGymResponseCreateParamsNonStreaming,
    chat_completion: nemo_gym.core.responses_api_types.NeMoGymChatCompletion,
    preserve_envelope_id: bool = False
) -> nemo_gym.core.responses_api_types.NeMoGymResponse
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.chat_completion_to_responses_create_params(
    chat_completion_create_params: nemo_gym.core.responses_api_types.NeMoGymChatCompletionCreateParamsNonStreaming
) -> nemo_gym.core.responses_api_types.NeMoGymResponseCreateParamsNonStreaming
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.chat_completions_messages_to_responses_items(
    messages: typing.List[typing.Dict[str, typing.Any]]
) -> typing.List[nemo_gym.core.responses_api_types.NeMoGymResponseOutputItem]
```

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.postprocess_assistant_message_dict(
    message_dict: typing.Dict[str, typing.Any],
    logprobs: typing.Any = None,
    custom_tool_names: typing.Collection[str] = (),
    structured_output: bool = False
) -> typing.List[nemo_gym.core.responses_api_types.NeMoGymResponseOutputItem]
```

Lift assistant content without interpreting tags inside a structured final answer.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.postprocess_chat_response(
    choice: nemo_gym.core.responses_api_types.NeMoGymChoice,
    custom_tool_names: typing.Collection[str] = (),
    structured_output: bool = False
) -> typing.List[nemo_gym.core.responses_api_types.NeMoGymResponseOutputItem]
```

Lift a chat choice; structured output permits only leading reasoning blocks.

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.responses_to_chat_completion_create_params(
    responses_create_params: nemo_gym.core.responses_api_types.NeMoGymResponseCreateParamsNonStreaming,
    drop_unrepresentable_request_fields: bool = False
) -> nemo_gym.core.responses_api_types.NeMoGymChatCompletionCreateParamsNonStreaming
```

Convert Responses create params into Chat Completions create params.

A Responses field with no Chat Completions representation raises `NotImplementedError`,
so the caller can route the request to a server that serves Responses natively. With
`drop_unrepresentable_request_fields` the reporting-only fields are removed from the
request instead and the conversion proceeds: a non-empty `include` (every value; an
empty one is already treated as absent), `reasoning.summary` and
`reasoning.generate_summary`, a `reasoning.context` of `auto` or `all_turns` (see
`_DROPPABLE_REASONING_CONTEXTS`), and a message `phase`. An image part in a tool's output
becomes the text placeholder `_IMAGE_OUTPUT_PLACEHOLDER`, which does replace that part
of the prompt, unless `inline_tool_output_images` keeps it as an image. Every other
unrepresentable field, a `current_turn` reasoning context included, is still refused.

```python
class nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState()
```

**Bases:** `BaseModel`

**`assistant_item_buffered`** `bool = False`

---

**`content_buffer`** `str = ''`

---

**`messages`** `List[NeMoGymChatCompletionMessageParam] = Field(default_factory=list)`

---

**`refusal_buffer`** `str = ''`

---

**`return_token_id_information`** `bool`

---

**`token_information`** `Optional[TokenIDLogProbMixin] = None`

---

**`tool_calls_buffer`** `List[NeMoGymChatCompletionMessageToolCallParam] = Field(default_factory=list)`

---

```python
nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState.flush_assistant() -> None
```

```python
nemo_gym.models.chat_completions_converter._align_logprobs_to_text(
    logprobs: typing.Optional[typing.List[typing.Dict[str, typing.Any]]],
    original: str,
    removed: typing.List[typing.Tuple[int, int]]
) -> typing.Optional[typing.List[typing.Dict[str, typing.Any]]]
```

Keep the token entries that survive removing the `removed` character spans.

Reasoning extraction shortens the output text; its logprobs must shrink with it or
they describe different text. The kept positions come from the removal itself, never
from searching for the answer text, which may also occur inside the reasoning. If the
tokens do not reconstruct `original` the alignment is unknowable and the logprobs
are dropped rather than misattributed.

```python
nemo_gym.models.chat_completions_converter._chat_logprobs_to_responses(
    logprobs: typing.Any
) -> typing.Optional[typing.List[typing.Dict[str, typing.Any]]]
```

Chat choice logprobs in the shape `ResponseOutputText.logprobs` expects.

Same field names on both sides; the Responses models require `bytes` to be
iterable where chat allows null.

```python
nemo_gym.models.chat_completions_converter._optional_token_count(
    value: typing.Any
) -> typing.Optional[int]
```

Return a provider-reported token count without coercing missing/invalid values to zero.

```python
nemo_gym.models.chat_completions_converter._token_information_from_mapping(
    value: typing.Dict[str, typing.Any]
) -> typing.Optional[nemo_gym.core.responses_api_types.TokenIDLogProbMixin]
```

Return validated token metadata when the mapping contains it.

```python
nemo_gym.models.chat_completions_converter._usage_detail(
    usage: typing.Any,
    detail_group: str,
    detail_name: str,
    top_level_aliases: str = ()
) -> typing.Optional[int]
```

Read one canonical nested token detail, then named provider aliases.

```python
nemo_gym.models.chat_completions_converter.chat_completion_usage_to_response_usage(
    usage: typing.Optional[openai.types.completion_usage.CompletionUsage]
) -> typing.Optional[nemo_gym.core.responses_api_types.NeMoGymResponseUsage]
```

Convert one Chat Completions `usage` block to Responses usage; `None` when the provider reported none.

Agents that make several chat calls per rollout sum these with `accumulate_response_usage`.

```python
nemo_gym.models.chat_completions_converter.message_content_to_text(
    content: typing.Any
) -> str
```

Plain text of a chat message content (a string, or a list of text parts).

```python
nemo_gym.models.chat_completions_converter.split_responses_input_output_items(
    items: typing.List[nemo_gym.core.responses_api_types.NeMoGymResponseOutputItem]
) -> typing.Tuple[typing.List[nemo_gym.core.responses_api_types.NeMoGymResponseOutputItem], typing.List[nemo_gym.core.responses_api_types.NeMoGymResponseOutputItem]]
```

```python
nemo_gym.models.chat_completions_converter.CustomToolStrategy = Literal['passthrough', 'function_bridge']
```

```python
nemo_gym.models.chat_completions_converter.VLLMConverter = ChatCompletionsConverter
```

```python
nemo_gym.models.chat_completions_converter.VLLMConverterResponsesToChatCompletionsState = ChatCompletionsConverterState
```

```python
nemo_gym.models.chat_completions_converter._BRIDGE_GRAMMAR_DROP_WARNED: set = set()
```

```python
nemo_gym.models.chat_completions_converter._CUSTOM_TOOL_BRIDGE_PARAMETER = 'input'
```

```python
nemo_gym.models.chat_completions_converter._DROPPABLE_REASONING_CONTEXTS = ('auto', 'all_turns')
```

```python
nemo_gym.models.chat_completions_converter._IMAGE_OUTPUT_PLACEHOLDER = '[image omitted from tool output]'
```

```python
nemo_gym.models.chat_completions_converter._REPORTING_ONLY_REASONING_FIELDS = ('summary', 'generate_summary')
```

```python
nemo_gym.models.chat_completions_converter._RESPONSE_NON_BOUNDARY_TYPES: frozenset[str] = frozenset({'additional_tools', 'apply_patch_call_output', 'compaction', 'compact...
```

```python
nemo_gym.models.chat_completions_converter._RESPONSE_OUTPUT_BOUNDARY_TYPES = frozenset({'code_interpreter_call', 'computer_call', 'custom_tool_call', 'file_s...
```

```python
nemo_gym.models.chat_completions_converter.__getattr__ = moved_names_getattr(__name__)
```

```python
nemo_gym.models.chat_completions_converter.logger = logging.getLogger(__name__)
```