Skip to navigation

nemo_gym.models.chat_completions_converter

View as Markdown

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

NameDescription
ChatCompletionsConverterConverts between OpenAI Responses API and Chat Completions API formats.
ChatCompletionsConverterState-

Functions

NameDescription
_align_logprobs_to_textKeep the token entries that survive removing the removed character spans.
_chat_logprobs_to_responsesChat choice logprobs in the shape ResponseOutputText.logprobs expects.
_optional_token_countReturn a provider-reported token count without coercing missing/invalid values to zero.
_token_information_from_mappingReturn validated token metadata when the mapping contains it.
_usage_detailRead one canonical nested token detail, then named provider aliases.
chat_completion_usage_to_response_usageConvert one Chat Completions usage block to Responses usage; None when the provider reported none.
message_content_to_textPlain text of a chat message content (a string, or a list of text parts).
split_responses_input_output_items-

Data

CustomToolStrategy

VLLMConverter

VLLMConverterResponsesToChatCompletionsState

_BRIDGE_GRAMMAR_DROP_WARNED

_CUSTOM_TOOL_BRIDGE_PARAMETER

_DROPPABLE_REASONING_CONTEXTS

_IMAGE_OUTPUT_PLACEHOLDER

_REPORTING_ONLY_REASONING_FIELDS

_RESPONSE_NON_BOUNDARY_TYPES

_RESPONSE_OUTPUT_BOUNDARY_TYPES

__getattr__

logger

API

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
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]
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._chat_custom_tool_to_responses(
tool: dict
) -> dict
staticmethod
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._chat_to_responses_tool_choice(
tool_choice: typing.Any
) -> typing.Any
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.

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._custom_tool_to_bridged_function(
tool_dict: dict
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.

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.

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.

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._extract_reasoning_from_content(
content: str
) -> typing.Tuple[typing.List[str], str]
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_custom_tool_call(
m: dict,
) -> 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.

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_function_call(
m: dict,
) -> None
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_function_call_output(
m: dict,
drop_unrepresentable_request_fields: bool = False
) -> None
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_message(
m: dict,
drop_unrepresentable_request_fields: bool = False
) -> None
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._format_reasoning(
m: dict,
) -> 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 for background on reasoning in the Responses API.

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._input_image_to_chat(
part_param: dict
staticmethod
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._parse_think_tags(
content: str
) -> typing.Tuple[typing.List[str], str]
classmethod
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._responses_custom_tool_to_chat(
tool: dict
) -> dict
staticmethod
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._responses_to_chat_tool_choice(
tool_choice: typing.Any
) -> typing.Any
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.

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._tool_reference_to_chat(
tool: dict
) -> dict
staticmethod
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.

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._tool_reference_to_responses(
tool: dict
) -> dict
staticmethod
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter._wrap_reasoning_in_think_tags(
texts: typing.List[str]
) -> str
staticmethod
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.chat_completion_to_response(
preserve_envelope_id: bool = False
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.chat_completion_to_responses_create_params(
nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.chat_completions_messages_to_responses_items(
messages: typing.List[typing.Dict[str, typing.Any]]
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

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

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.postprocess_chat_response(
custom_tool_names: typing.Collection[str] = (),
structured_output: bool = False

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

nemo_gym.models.chat_completions_converter.ChatCompletionsConverter.responses_to_chat_completion_create_params(
drop_unrepresentable_request_fields: bool = False

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.

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)
nemo_gym.models.chat_completions_converter.ChatCompletionsConverterState.flush_assistant() -> None
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.

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.

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.

nemo_gym.models.chat_completions_converter._token_information_from_mapping(
value: typing.Dict[str, typing.Any]

Return validated token metadata when the mapping contains it.

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.

nemo_gym.models.chat_completions_converter.chat_completion_usage_to_response_usage(
usage: typing.Optional[openai.types.completion_usage.CompletionUsage]

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.

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).

nemo_gym.models.chat_completions_converter.split_responses_input_output_items(
nemo_gym.models.chat_completions_converter.CustomToolStrategy = Literal['passthrough', 'function_bridge']
nemo_gym.models.chat_completions_converter.VLLMConverter = ChatCompletionsConverter
nemo_gym.models.chat_completions_converter.VLLMConverterResponsesToChatCompletionsState = ChatCompletionsConverterState
nemo_gym.models.chat_completions_converter._BRIDGE_GRAMMAR_DROP_WARNED: set = set()
nemo_gym.models.chat_completions_converter._CUSTOM_TOOL_BRIDGE_PARAMETER = 'input'
nemo_gym.models.chat_completions_converter._DROPPABLE_REASONING_CONTEXTS = ('auto', 'all_turns')
nemo_gym.models.chat_completions_converter._IMAGE_OUTPUT_PLACEHOLDER = '[image omitted from tool output]'
nemo_gym.models.chat_completions_converter._REPORTING_ONLY_REASONING_FIELDS = ('summary', 'generate_summary')
nemo_gym.models.chat_completions_converter._RESPONSE_NON_BOUNDARY_TYPES: frozenset[str] = frozenset({'additional_tools', 'apply_patch_call_output', 'compaction', 'compact...
nemo_gym.models.chat_completions_converter._RESPONSE_OUTPUT_BOUNDARY_TYPES = frozenset({'code_interpreter_call', 'computer_call', 'custom_tool_call', 'file_s...
nemo_gym.models.chat_completions_converter.__getattr__ = moved_names_getattr(__name__)
nemo_gym.models.chat_completions_converter.logger = logging.getLogger(__name__)