bridge.data.conversation_processing#
Shared conversation rendering, masking, and multimodal normalization.
Module Contents#
Classes#
Token boundary fallback configuration for assistant loss masks. |
|
Source-normalized VLM sample consumed by shared processing. |
|
Token ids and an unshifted assistant-only loss mask for one chat row. |
Functions#
Normalize an Energon |
|
Normalize a HF-style VLM dataset example into the shared sample contract. |
|
Convert a normalized VLM sample into the HF-style collate example schema. |
|
Read a declared attribute without triggering dynamic |
|
Return the HF-style tokenizer attached to a processor or Megatron tokenizer wrapper. |
|
Return optional HF chat-template kwargs stored alongside a conversation. |
|
Return dataset-wide chat-template kwargs shared by a processor batch. |
|
Normalize supported text-chat schemas to OpenAI-style messages. |
|
Return whether a supported chat row contains no media content parts. |
|
Apply one chat template with compatibility fallbacks. |
|
Return the number of identical leading token IDs. |
|
Locate a template-truncated token window in its untruncated rendering. |
|
Map the final assistant-turn boundary into a possibly truncated render. |
|
Map terminal assistant content, excluding its rendered role header. |
|
Render, tokenize, and construct the assistant mask for one chat row. |
|
Apply an explicit chat loss policy to one unpadded token sequence. |
|
Find the first |
|
Extract assistant text segments from a structured VLM conversation. |
|
Tokenize text using a HF-like tokenizer without adding special tokens. |
|
Infer assistant role boundaries from known HF chat-template formats. |
|
Build explicit assistant boundary config from model-declared marker strings. |
|
Return HF generation-block assistant mask when it aligns to |
|
Align an HF assistant mask to possibly padded processor batch ids. |
|
Skip complete token sequences that appear contiguously at a span start. |
|
Build an unshifted assistant-only loss mask. |
|
Build next-token labels and shifted loss mask for Megatron training. |
|
Attach |
|
Ensure a collated batch has 2D position IDs. |
|
Move processor visual tensor keys into |
Data#
API#
- bridge.data.conversation_processing.CHATML_ASSISTANT_ROLE#
‘assistant’
- class bridge.data.conversation_processing.AssistantMaskBoundaryConfig#
Token boundary fallback configuration for assistant loss masks.
Expected input format:
role_start_tokensmaps each chat role to the token-id sequence that immediately precedes that role’s content in already-renderedinput_ids.role_end_tokensmaps each chat role to the token-id sequence that terminates that role’s turn.loss_rolesnames top-level chat roles that should contribute to loss. This follows the common SFT convention used by chat-template based pipelines: once a role contributes to loss, the role’s full rendered content contributes, including formatted tool-call or reasoning spans inside that role.include_*_tokens_for_rolesoptionally include boundary tokens in the loss. By default, assistant turn end tokens are included because they are generated by the assistant and teach the model when to stop.trim_leading_token_idsremoves repeated single-token delimiters from the start of loss-role content.trim_leading_token_sequencesremoves exact leading token sequences, for example an empty<think></think>wrapper without masking a non-empty<think>reasoningspan.role_end_token_variantsprovides ordered fallback end sequences for formats whose final turn may omit a trailing delimiter such as a newline. The primaryrole_end_tokenssequence is always preferred.Example:: AssistantMaskBoundaryConfig( role_start_tokens={"user": [10], "assistant": [11]}, role_end_tokens={"user": [12], "assistant": [12]}, loss_roles=("assistant",), include_end_tokens_for_roles=("assistant",), trim_leading_token_ids=(198,), trim_leading_token_sequences=([55, 56],), )Output format: When passed to :func:
build_assistant_loss_mask, the fallback scans renderedinput_idsfor role boundaries and returns an unshifted float mask with1.0only on role spans whose role appears inloss_roles. Boundary marker tokens are included only when requested by the correspondinginclude_*fields.- role_start_tokens: collections.abc.Mapping[str, collections.abc.Sequence[int]]#
None
- role_end_tokens: collections.abc.Mapping[str, collections.abc.Sequence[int]]#
None
- loss_roles: collections.abc.Sequence[str]#
()
- include_start_tokens_for_roles: collections.abc.Sequence[str]#
()
- include_end_tokens_for_roles: collections.abc.Sequence[str]#
()
- trim_leading_token_ids: collections.abc.Sequence[int]#
()
- trim_leading_token_sequences: collections.abc.Sequence[collections.abc.Sequence[int]]#
()
- role_end_token_variants: collections.abc.Mapping[str, collections.abc.Sequence[collections.abc.Sequence[int]]]#
‘field(…)’
- class bridge.data.conversation_processing.NormalizedVLMSample#
Source-normalized VLM sample consumed by shared processing.
Expected input format: Instances are produced by source adapters such as :func:
normalize_energon_vlm_sampleand :func:normalize_hf_vlm_example.conversationmust be a structured list of chat turns in HF processor format, for example::[ {"role": "user", "content": "describe <image>"}, {"role": "assistant", "content": "a red car"}, ] ``content`` may also be a multimodal content list accepted by ``processor.apply_chat_template``, for example:: [{"type": "image", "image": image_obj}, {"type": "text", "text": "describe"}] ``images`` and ``videos`` are optional processor-ready modality payloads. ``tools`` contains optional top-level tool definitions passed to the HF chat template. Message-level ``tool_calls`` remain in ``conversation``. Energon adapters convert WDS tensors to PIL objects before populating these fields; HF adapters may leave them ``None`` when media already lives inline in ``conversation`` content.Output format: Shared processing treats this as the single boundary contract before model-specific tokenization and vision preprocessing. It does not contain batched tensors; model-specific collators convert it into model input tensors.
- conversation: list[dict[str, Any]]#
None
- images: list[Any] | None#
None
- videos: list[Any] | None#
None
- audio: Any | None#
None
- tools: list[dict[str, Any]] | None#
None
- class bridge.data.conversation_processing.TokenizedConversation#
Token ids and an unshifted assistant-only loss mask for one chat row.
- input_ids: torch.Tensor#
None
- assistant_mask: torch.Tensor#
None
- conversation: list[dict[str, Any]]#
None
- final_assistant_start: int | None#
None
- bridge.data.conversation_processing.normalize_energon_vlm_sample(
- sample: Any,
Normalize an Energon
ChatMLSampleinto the shared VLM sample contract.Expected input format:
sampleis expected to expose the EnergonChatMLSamplefields:- ``conversation``: JSON string accepted by ``cook_chatml_sample``. The JSON may use either ``{"role": ..., "content": ...}`` turns or ``{"from": ..., "value": ...}`` turns. - ``imgs``: optional WDS decoded image tensor/list payload. - ``videos``: optional WDS decoded video tensor/list payload. - ``audio``: optional audio payload, passed through unchanged. - ``tools``: optional top-level tool-definition list. Wrapped ``conversation`` JSON may also carry this field.Output format: Returns
NormalizedVLMSamplewhereconversationis a list of{"role": str, "content": str | list[dict]}turns,imagesare PIL/list processor inputs orNone,videosare nested PIL/list processor inputs orNone, andaudiois copied from the source sample when present.
- bridge.data.conversation_processing.normalize_hf_vlm_example(
- example: collections.abc.Mapping[str, Any],
Normalize a HF-style VLM dataset example into the shared sample contract.
Expected input format:
examplemust contain"conversation"as a structured list of chat turns already produced by an HF schema adapter, for example::{ "conversation": [ {"role": "user", "content": [{"type": "image", "image": img}, {"type": "text", "text": "Q"}]}, {"role": "assistant", "content": [{"type": "text", "text": "A"}]}, ], "audio": optional_audio, } Optional top-level ``images``/``image`` and ``videos``/``video`` fields are accepted for adapter variants that do not embed media inline in the conversation. Optional top-level ``tools`` are copied for chat-template rendering.Output format: Returns
NormalizedVLMSamplewith a deep-copied structuredconversationlist, optional list-valuedimagesandvideospayloads, and optionalaudio. The adapter does not callcook_chatml_samplebecause HF adapters have already normalized the chat schema.- Raises:
ValueError – If
example["conversation"]is missing or is not a list.
- bridge.data.conversation_processing.normalized_vlm_sample_to_hf_example(
- sample: bridge.data.conversation_processing.NormalizedVLMSample,
- *,
- media_first: bool = False,
Convert a normalized VLM sample into the HF-style collate example schema.
Expected input format:
samplefollowsNormalizedVLMSample:conversationis a list of chat turns, and optionalimages/videoscontain processor-ready media payloads such as PIL images or decoded video frame lists. Text turns may contain literal<image>/<video>placeholders.Output format: Returns a dictionary suitable for VLM HF collate functions::
{ "conversation": [ { "role": "user", "content": [ {"type": "image", "image": image_obj}, {"type": "text", "text": "describe"}, ], }, {"role": "assistant", "content": [{"type": "text", "text": "answer"}]}, ], "images": [image_obj], # present when sample.images is not None "videos": [video_obj], # present when sample.videos is not None "audio": audio_obj, # present when sample.audio is not None "tools": [...], # present when sample.tools is not None } Inline media parts are populated from ``sample.images`` and ``sample.videos`` in placeholder order. When ``media_first=True``, media parts are moved before text parts within each turn to preserve Qwen Energon's legacy media-before-text ordering while still using the shared HF collate function.
- bridge.data.conversation_processing._get_explicit_attribute(
- obj: Any,
- name: str,
Read a declared attribute without triggering dynamic
__getattr__fabrication.
- bridge.data.conversation_processing.get_processor_tokenizer(processor: Any) Any#
Return the HF-style tokenizer attached to a processor or Megatron tokenizer wrapper.
- bridge.data.conversation_processing._as_token_id_list(token_ids: Any) list[int]#
- bridge.data.conversation_processing._conversation_from_example(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- bridge.data.conversation_processing.chat_template_kwargs_from_example(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
Return optional HF chat-template kwargs stored alongside a conversation.
- examples: collections.abc.Sequence[collections.abc.Mapping[str, Any]],
Return dataset-wide chat-template kwargs shared by a processor batch.
Processor-batched VLM collators accept one tool schema for the whole microbatch. Datasets using these collators must therefore use a shared tool registry instead of varying tool definitions per row.
- Raises:
ValueError – If rows in one processor batch use different template kwargs.
- bridge.data.conversation_processing.normalize_chat_conversation(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
Normalize supported text-chat schemas to OpenAI-style messages.
Rows may use
messages, singularconversation, or legacy pluralconversationswithfrom/valuekeys.
- bridge.data.conversation_processing.is_text_only_chat_example(
- example: collections.abc.Mapping[str, Any],
Return whether a supported chat row contains no media content parts.
- bridge.data.conversation_processing._chat_template_input_ids(tokenized_chat: Any) list[int]#
- bridge.data.conversation_processing._apply_tokenized_chat_template(
- template_owner: Any,
- conversation: collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- tokenize_kwargs: collections.abc.Mapping[str, Any],
Apply one chat template with compatibility fallbacks.
- bridge.data.conversation_processing._common_token_prefix_length(
- left: collections.abc.Sequence[int],
- right: collections.abc.Sequence[int],
Return the number of identical leading token IDs.
- bridge.data.conversation_processing._rendered_window_start(
- full_ids: collections.abc.Sequence[int],
- rendered_ids: collections.abc.Sequence[int],
- template_owner: Any,
Locate a template-truncated token window in its untruncated rendering.
- bridge.data.conversation_processing._infer_final_assistant_start(
- template_owner: Any,
- conversation: collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- tokenize_kwargs: collections.abc.Mapping[str, Any],
- rendered_ids: collections.abc.Sequence[int],
- *,
- terminal_only: bool,
Map the final assistant-turn boundary into a possibly truncated render.
- bridge.data.conversation_processing._infer_terminal_assistant_content_start(
- template_owner: Any,
- conversation: collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- tokenize_kwargs: collections.abc.Mapping[str, Any],
- rendered_ids: collections.abc.Sequence[int],
Map terminal assistant content, excluding its rendered role header.
- bridge.data.conversation_processing.tokenize_chat_example(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- processor: Any,
- *,
- tool_schemas: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]] | None = None,
- max_length: int | None = None,
- skipped_tokens: torch.Tensor | None = None,
- boundary_config: bridge.data.conversation_processing.AssistantMaskBoundaryConfig | None = None,
- warn_on_all_masked: bool = True,
- loss_mode: Literal[assistant, last_turn, full] = 'assistant',
- return_final_assistant_start: bool = False,
Render, tokenize, and construct the assistant mask for one chat row.
This is the canonical preprocessing entry point shared by
GPTSFTDatasetand direct Hugging Face SFT. Padding, label shifting, and sequence packing remain the responsibility of their respective dataset/collate layers. GPT dataset callers may request the final assistant boundary for generation metadata;last_turnloss infers it automatically.
- bridge.data.conversation_processing.apply_chat_loss_mode(
- assistant_mask: torch.Tensor,
- input_ids: torch.Tensor,
- *,
- loss_mode: Literal[assistant, last_turn, full],
- skipped_tokens: torch.Tensor | None = None,
- last_turn_start: int | None = None,
Apply an explicit chat loss policy to one unpadded token sequence.
last_turnretains assistant tokens after the final assistant-turn boundary inferred from a prefix render. If a template cannot expose that boundary, it falls back to the final contiguous assistant-mask span.- Parameters:
assistant_mask – Assistant-only mask produced by the chat template or boundary inference.
input_ids – Rendered token IDs aligned with
assistant_mask.loss_mode – Assistant-only, final-assistant-turn, or full-sequence loss.
skipped_tokens – Optional token IDs that must never contribute to loss.
last_turn_start – Optional token boundary inferred by rendering the conversation prefix before its final assistant turn.
- Returns:
Boolean loss mask aligned with
input_ids.
- bridge.data.conversation_processing.find_token_span(
- sequence: collections.abc.Sequence[int] | torch.Tensor,
- pattern: collections.abc.Sequence[int],
- start: int = 0,
Find the first
[start, end)token span matchingpattern.- Parameters:
sequence – Token id sequence to search.
pattern – Token id pattern to locate.
start – Index to begin searching from.
- Returns:
(start, end)for the first match, or(-1, -1)when no match exists.
- bridge.data.conversation_processing.gather_assistant_text_segments(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
Extract assistant text segments from a structured VLM conversation.
- bridge.data.conversation_processing.tokenize_text_without_special_tokens(
- tokenizer: Any,
- text: str,
Tokenize text using a HF-like tokenizer without adding special tokens.
- bridge.data.conversation_processing.infer_assistant_mask_boundary_config(
- processor: Any,
Infer assistant role boundaries from known HF chat-template formats.
- bridge.data.conversation_processing.assistant_mask_boundary_config_from_markers(
- processor: Any,
- *,
- assistant_start: str,
- assistant_end: str,
- assistant_end_fallbacks: collections.abc.Sequence[str] = (),
- role_start_markers: collections.abc.Mapping[str, str] | None = None,
- loss_roles: collections.abc.Sequence[str] = (CHATML_ASSISTANT_ROLE,),
- include_start_tokens_for_roles: collections.abc.Sequence[str] = (),
- include_end_tokens_for_roles: collections.abc.Sequence[str] = (CHATML_ASSISTANT_ROLE,),
- trim_leading_token_ids: collections.abc.Sequence[int] = (),
- trim_leading_token_sequences: collections.abc.Sequence[collections.abc.Sequence[int]] = (),
Build explicit assistant boundary config from model-declared marker strings.
- bridge.data.conversation_processing._apply_skipped_tokens(
- mask: torch.Tensor,
- ids: collections.abc.Sequence[int],
- skipped_tokens: torch.Tensor | None,
- bridge.data.conversation_processing._warn_if_all_masked(
- mask: torch.Tensor,
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- *,
- warn_on_all_masked: bool,
- bridge.data.conversation_processing._get_chat_template(*objects: Any) str | None#
- bridge.data.conversation_processing._assistant_mask_from_hf_chat_template(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- ids: collections.abc.Sequence[int],
- processor: Any,
- tokenizer: Any,
Return HF generation-block assistant mask when it aligns to
ids.
- bridge.data.conversation_processing._align_assistant_mask_to_padded_ids(
- candidate_ids: collections.abc.Sequence[int],
- assistant_mask: collections.abc.Sequence[int],
- ids: collections.abc.Sequence[int],
- tokenizer: Any,
Align an HF assistant mask to possibly padded processor batch ids.
- bridge.data.conversation_processing._token_map_from_boundary_config(
- token_map: collections.abc.Mapping[str, collections.abc.Sequence[int]] | None,
- bridge.data.conversation_processing._trim_leading_token_ids(
- ids: collections.abc.Sequence[int],
- start: int,
- end: int,
- trim_token_ids: collections.abc.Sequence[int],
- bridge.data.conversation_processing._trim_leading_token_sequences(
- ids: collections.abc.Sequence[int],
- start: int,
- end: int,
- trim_token_sequences: collections.abc.Sequence[collections.abc.Sequence[int]],
Skip complete token sequences that appear contiguously at a span start.
This is intentionally sequence-based instead of token-set based. For Kimi, it can remove a leading empty
<think></think>block while preserving a non-empty<think>reasoning...</think>block as supervised content.- Parameters:
ids – Rendered token ids to inspect.
start – Inclusive span start.
end – Exclusive span end.
trim_token_sequences – Candidate token-id sequences to skip when they match exactly at the current span start.
- Returns:
The first index after all matched leading token sequences.
- bridge.data.conversation_processing._assistant_mask_from_boundary_config(
- ids: collections.abc.Sequence[int],
- boundary_config: bridge.data.conversation_processing.AssistantMaskBoundaryConfig,
- *,
- include_content: bool = True,
- bridge.data.conversation_processing.build_assistant_loss_mask(
- example_or_conversation: collections.abc.Mapping[str, Any] | collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- input_ids: collections.abc.Sequence[int] | torch.Tensor,
- processor: Any,
- skipped_tokens: torch.Tensor | None = None,
- *,
- boundary_config: bridge.data.conversation_processing.AssistantMaskBoundaryConfig | None = None,
- warn_on_all_masked: bool = True,
Build an unshifted assistant-only loss mask.
The preferred path uses HF chat templates with
{% generation %}blocks. When those are unavailable, callers may provideboundary_configto scan explicit role token boundaries from renderedinput_ids.- Raises:
ValueError – If neither a HF generation mask nor a valid explicit boundary mask can be built.
- bridge.data.conversation_processing.build_shifted_labels_and_loss_mask(
- input_ids: torch.Tensor,
- assistant_loss_mask: torch.Tensor,
- skipped_tokens: torch.Tensor | None = None,
- *,
- ignore_index: int = IGNORE_INDEX,
Build next-token labels and shifted loss mask for Megatron training.
- bridge.data.conversation_processing.apply_assistant_labels_to_batch(
- batch: collections.abc.MutableMapping[str, Any],
- examples: collections.abc.Sequence[collections.abc.Mapping[str, Any]],
- processor: Any,
- skipped_tokens: torch.Tensor,
- *,
- boundary_config: bridge.data.conversation_processing.AssistantMaskBoundaryConfig | None = None,
- unmask_last_token: bool = False,
Attach
labelsandloss_maskto a collated HF VLM batch.
- bridge.data.conversation_processing.ensure_position_ids(
- batch: collections.abc.MutableMapping[str, Any],
Ensure a collated batch has 2D position IDs.
- bridge.data.conversation_processing.pop_generic_visual_inputs(
- batch: collections.abc.MutableMapping[str, Any],
- visual_keys: collections.abc.Sequence[str],
Move processor visual tensor keys into
GenericVisualInputs.