renderers 0.1.8.dev52__tar.gz → 0.1.8.dev54__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/PKG-INFO +1 -1
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/__init__.py +4 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/_version.py +2 -2
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/base.py +64 -4
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/configs.py +24 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/laguna_xs2.py +26 -17
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/conftest.py +3 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_build_helpers.py +18 -2
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_is_content.py +2 -1
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/.github/workflows/style.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/.github/workflows/test.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/.gitignore +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/LICENSE +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/README.md +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/docs/renderer-config.md +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/README.md +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/pyproject.toml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/client.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/default.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/glm45.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/glm5.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/llama_3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/nemotron3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/parsers.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/parsing.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/qwen3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/qwen35.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/qwen36.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_bridge.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_client.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_incremental.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_llama_3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_message_indices.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_multimodal.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_parse_response.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_parsers.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_render_ids.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_renderer_config_parity.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/uv.lock +0 -0
|
@@ -19,6 +19,7 @@ from renderers.base import (
|
|
|
19
19
|
PlaceholderRange,
|
|
20
20
|
RenderedConversation,
|
|
21
21
|
RenderedTokens,
|
|
22
|
+
RenderedTrainingSample,
|
|
22
23
|
Renderer,
|
|
23
24
|
RendererPool,
|
|
24
25
|
TextPart,
|
|
@@ -53,6 +54,7 @@ from renderers.configs import (
|
|
|
53
54
|
KimiK25RendererConfig,
|
|
54
55
|
KimiK2RendererConfig,
|
|
55
56
|
LagunaXS2RendererConfig,
|
|
57
|
+
LagunaXS21RendererConfig,
|
|
56
58
|
Llama3RendererConfig,
|
|
57
59
|
MiniMaxM2RendererConfig,
|
|
58
60
|
Nemotron3RendererConfig,
|
|
@@ -138,6 +140,7 @@ __all__ = [
|
|
|
138
140
|
"KimiK2RendererConfig",
|
|
139
141
|
"LagunaXS2Renderer",
|
|
140
142
|
"LagunaXS2RendererConfig",
|
|
143
|
+
"LagunaXS21RendererConfig",
|
|
141
144
|
"Llama3Renderer",
|
|
142
145
|
"Llama3RendererConfig",
|
|
143
146
|
"MULTIMODAL_MODELS",
|
|
@@ -164,6 +167,7 @@ __all__ = [
|
|
|
164
167
|
"Qwen3VLRendererConfig",
|
|
165
168
|
"RenderedConversation",
|
|
166
169
|
"RenderedTokens",
|
|
170
|
+
"RenderedTrainingSample",
|
|
167
171
|
"Renderer",
|
|
168
172
|
"RendererConfig",
|
|
169
173
|
"RendererPool",
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.1.8.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, 8, '
|
|
21
|
+
__version__ = version = '0.1.8.dev54'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 8, 'dev54')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1060,8 +1060,10 @@ MODEL_RENDERER_MAP: dict[str, str] = {
|
|
|
1060
1060
|
# construction to pin a different date.
|
|
1061
1061
|
"meta-llama/Llama-3.2-1B-Instruct": "llama-3",
|
|
1062
1062
|
"meta-llama/Llama-3.2-3B-Instruct": "llama-3",
|
|
1063
|
-
# Poolside Laguna.
|
|
1063
|
+
# Poolside Laguna. XS-2.1's template is byte-identical to XS.2's minus
|
|
1064
|
+
# the default system message; the config name selects the variant.
|
|
1064
1065
|
"poolside/Laguna-XS.2": "laguna-xs.2",
|
|
1066
|
+
"poolside/Laguna-XS-2.1": "laguna-xs-2.1",
|
|
1065
1067
|
# GPT-OSS.
|
|
1066
1068
|
"openai/gpt-oss-20b": "gpt-oss",
|
|
1067
1069
|
"openai/gpt-oss-120b": "gpt-oss",
|
|
@@ -1327,6 +1329,7 @@ def _populate_registry():
|
|
|
1327
1329
|
"kimi-k2": KimiK2Renderer,
|
|
1328
1330
|
"kimi-k2.5": KimiK25Renderer,
|
|
1329
1331
|
"laguna-xs.2": LagunaXS2Renderer,
|
|
1332
|
+
"laguna-xs-2.1": LagunaXS2Renderer,
|
|
1330
1333
|
"llama-3": Llama3Renderer,
|
|
1331
1334
|
"nemotron-3": Nemotron3Renderer,
|
|
1332
1335
|
"nemotron-3-ultra": Nemotron3UltraRenderer,
|
|
@@ -1534,6 +1537,42 @@ def _resolve_auto_config(
|
|
|
1534
1537
|
# ---------------------------------------------------------------------------
|
|
1535
1538
|
|
|
1536
1539
|
|
|
1540
|
+
# Match prime-rl's multimodal token type convention: 0=text, 1=image, 2=video.
|
|
1541
|
+
_MM_TYPE_ID: dict[str, int] = {"image": 1, "video": 2}
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
@dataclass(frozen=True)
|
|
1545
|
+
class RenderedTrainingSample:
|
|
1546
|
+
"""Output of :func:`build_training_sample`.
|
|
1547
|
+
|
|
1548
|
+
``token_ids`` and ``loss_mask`` are always populated. ``multi_modal_data``
|
|
1549
|
+
and ``mm_token_type_ids`` are populated only when a multimodal renderer
|
|
1550
|
+
actually emitted media (both ``None`` for text-only renderers and for
|
|
1551
|
+
text-only samples through a VLM renderer), so the text path is unchanged.
|
|
1552
|
+
"""
|
|
1553
|
+
|
|
1554
|
+
token_ids: list[int]
|
|
1555
|
+
loss_mask: list[bool]
|
|
1556
|
+
multi_modal_data: "MultiModalData | None" = None
|
|
1557
|
+
mm_token_type_ids: list[int] | None = None
|
|
1558
|
+
|
|
1559
|
+
|
|
1560
|
+
def _build_mm_token_type_ids(
|
|
1561
|
+
mm_placeholders: dict[str, list[PlaceholderRange]], length: int
|
|
1562
|
+
) -> list[int]:
|
|
1563
|
+
"""Per-token modality flags (0=text, 1=image, 2=video) from placeholder ranges."""
|
|
1564
|
+
ids = [0] * length
|
|
1565
|
+
for modality, ranges in mm_placeholders.items():
|
|
1566
|
+
type_id = _MM_TYPE_ID.get(modality, 0)
|
|
1567
|
+
if type_id == 0:
|
|
1568
|
+
continue
|
|
1569
|
+
for r in ranges:
|
|
1570
|
+
end = min(r.offset + r.length, length)
|
|
1571
|
+
for i in range(r.offset, end):
|
|
1572
|
+
ids[i] = type_id
|
|
1573
|
+
return ids
|
|
1574
|
+
|
|
1575
|
+
|
|
1537
1576
|
def build_training_sample(
|
|
1538
1577
|
renderer: Renderer,
|
|
1539
1578
|
messages: list[Message],
|
|
@@ -1541,8 +1580,12 @@ def build_training_sample(
|
|
|
1541
1580
|
role_to_mask: Callable[[Message], bool] | None = None,
|
|
1542
1581
|
tools: list[ToolSpec] | None = None,
|
|
1543
1582
|
content_sft_roles: "set[str] | frozenset[str] | None" = None,
|
|
1544
|
-
) ->
|
|
1545
|
-
"""Build
|
|
1583
|
+
) -> RenderedTrainingSample:
|
|
1584
|
+
"""Build a :class:`RenderedTrainingSample` for supervised training.
|
|
1585
|
+
|
|
1586
|
+
Returns ``token_ids`` + ``loss_mask`` (always), plus ``multi_modal_data``
|
|
1587
|
+
and ``mm_token_type_ids`` when the renderer emitted media (``None`` for
|
|
1588
|
+
text — the text token_ids/loss_mask are byte-identical to before).
|
|
1546
1589
|
|
|
1547
1590
|
Single render() call + message_indices → per-token mask.
|
|
1548
1591
|
Replaces build_incremental_token_mask (O(N) renders → O(1)).
|
|
@@ -1631,7 +1674,24 @@ def build_training_sample(
|
|
|
1631
1674
|
loss_mask.append(True)
|
|
1632
1675
|
else:
|
|
1633
1676
|
loss_mask.append(role_to_mask(msg))
|
|
1634
|
-
|
|
1677
|
+
|
|
1678
|
+
# Surface the multimodal payload for VLM renderers. ``None`` for text
|
|
1679
|
+
# renderers and for text-only samples (empty media) so downstream
|
|
1680
|
+
# ``multi_modal_data is not None`` is a reliable "has media" check.
|
|
1681
|
+
mm = rendered.multi_modal_data
|
|
1682
|
+
if mm is not None and mm.is_empty():
|
|
1683
|
+
mm = None
|
|
1684
|
+
mm_token_type_ids = (
|
|
1685
|
+
_build_mm_token_type_ids(mm.mm_placeholders, len(rendered.token_ids))
|
|
1686
|
+
if mm is not None and mm.mm_placeholders
|
|
1687
|
+
else None
|
|
1688
|
+
)
|
|
1689
|
+
return RenderedTrainingSample(
|
|
1690
|
+
token_ids=rendered.token_ids,
|
|
1691
|
+
loss_mask=loss_mask,
|
|
1692
|
+
multi_modal_data=mm,
|
|
1693
|
+
mm_token_type_ids=mm_token_type_ids,
|
|
1694
|
+
)
|
|
1635
1695
|
|
|
1636
1696
|
|
|
1637
1697
|
def _common_prefix_len(a: list[int], b: list[int]) -> int:
|
|
@@ -422,6 +422,27 @@ class LagunaXS2RendererConfig(BaseRendererConfig):
|
|
|
422
422
|
chat template's ``render_assistant_messages_raw`` gate."""
|
|
423
423
|
|
|
424
424
|
|
|
425
|
+
class LagunaXS21RendererConfig(BaseRendererConfig):
|
|
426
|
+
"""Laguna XS-2.1 renderer config — distinct discriminator so auto
|
|
427
|
+
resolution gives XS-2.1 checkpoints the no-default-system-message
|
|
428
|
+
template variant.
|
|
429
|
+
|
|
430
|
+
XS-2.1's chat template is byte-identical to XS.2's except it ships
|
|
431
|
+
no default system message: when the caller provides none (and no
|
|
432
|
+
tools), the ``<system>`` block is omitted entirely. Shares
|
|
433
|
+
:class:`renderers.laguna_xs2.LagunaXS2Renderer`, which selects the
|
|
434
|
+
variant from ``config.name``.
|
|
435
|
+
"""
|
|
436
|
+
|
|
437
|
+
name: Literal["laguna-xs-2.1"] = "laguna-xs-2.1"
|
|
438
|
+
|
|
439
|
+
enable_thinking: bool = False
|
|
440
|
+
"""See :class:`LagunaXS2RendererConfig.enable_thinking`."""
|
|
441
|
+
|
|
442
|
+
render_assistant_messages_raw: bool = False
|
|
443
|
+
"""See :class:`LagunaXS2RendererConfig.render_assistant_messages_raw`."""
|
|
444
|
+
|
|
445
|
+
|
|
425
446
|
class Llama3RendererConfig(BaseRendererConfig):
|
|
426
447
|
"""Llama-3.x Instruct renderer config.
|
|
427
448
|
|
|
@@ -576,6 +597,7 @@ RendererConfig = Annotated[
|
|
|
576
597
|
KimiK2RendererConfig,
|
|
577
598
|
KimiK25RendererConfig,
|
|
578
599
|
LagunaXS2RendererConfig,
|
|
600
|
+
LagunaXS21RendererConfig,
|
|
579
601
|
Llama3RendererConfig,
|
|
580
602
|
MiniMaxM2RendererConfig,
|
|
581
603
|
Nemotron3RendererConfig,
|
|
@@ -613,6 +635,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
|
|
|
613
635
|
"kimi-k2": KimiK2RendererConfig,
|
|
614
636
|
"kimi-k2.5": KimiK25RendererConfig,
|
|
615
637
|
"laguna-xs.2": LagunaXS2RendererConfig,
|
|
638
|
+
"laguna-xs-2.1": LagunaXS21RendererConfig,
|
|
616
639
|
"llama-3": Llama3RendererConfig,
|
|
617
640
|
"minimax-m2": MiniMaxM2RendererConfig,
|
|
618
641
|
"nemotron-3": Nemotron3RendererConfig,
|
|
@@ -659,6 +682,7 @@ __all__ = [
|
|
|
659
682
|
"KimiK25RendererConfig",
|
|
660
683
|
"KimiK2RendererConfig",
|
|
661
684
|
"LagunaXS2RendererConfig",
|
|
685
|
+
"LagunaXS21RendererConfig",
|
|
662
686
|
"Llama3RendererConfig",
|
|
663
687
|
"MiniMaxM2RendererConfig",
|
|
664
688
|
"Nemotron3RendererConfig",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Laguna-XS.2 Renderer.
|
|
1
|
+
"""Laguna-XS.2 / XS-2.1 Renderer.
|
|
2
2
|
|
|
3
3
|
Main properties:
|
|
4
4
|
- Prefix is the single token ``〈|EOS|〉`` (also the EOS / stop token).
|
|
@@ -12,10 +12,13 @@ Main properties:
|
|
|
12
12
|
- Tool calls: ``<tool_call>`` / ``</tool_call>`` ARE single tokens, but the
|
|
13
13
|
inner ``<arg_key>`` / ``</arg_key>`` / ``<arg_value>`` / ``</arg_value>``
|
|
14
14
|
markers are plain text — parsed via regex on the decoded inner block.
|
|
15
|
-
-
|
|
16
|
-
a system message.
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
- XS.2's template bakes in a default system prompt when ``messages[0]`` is
|
|
16
|
+
not a system message; XS-2.1's (otherwise byte-identical) template
|
|
17
|
+
removed it, so no system message and no tools means no ``<system>``
|
|
18
|
+
block at all. The config's ``name`` selects the variant. The system
|
|
19
|
+
block also contains the tools section (under a ``### Tools`` header
|
|
20
|
+
with an ``<available_tools>`` listing and prose format instructions
|
|
21
|
+
that vary on ``enable_thinking``).
|
|
19
22
|
- Reasoning is rendered for every assistant message — no last-user-index
|
|
20
23
|
gating. ``thinking_retention`` is accepted for protocol uniformity but
|
|
21
24
|
is effectively a no-op since past reasoning is preserved by default.
|
|
@@ -39,7 +42,7 @@ from renderers.base import (
|
|
|
39
42
|
resolve_thinking_retention,
|
|
40
43
|
should_rerender_for_thinking_retention,
|
|
41
44
|
)
|
|
42
|
-
from renderers.configs import LagunaXS2RendererConfig
|
|
45
|
+
from renderers.configs import LagunaXS2RendererConfig, LagunaXS21RendererConfig
|
|
43
46
|
from renderers.parsing import parse_laguna_xs2
|
|
44
47
|
|
|
45
48
|
_DEFAULT_SYSTEM_MESSAGE = (
|
|
@@ -81,7 +84,7 @@ class LagunaXS2Renderer:
|
|
|
81
84
|
def __init__(
|
|
82
85
|
self,
|
|
83
86
|
tokenizer: PreTrainedTokenizer,
|
|
84
|
-
config: LagunaXS2RendererConfig | None = None,
|
|
87
|
+
config: LagunaXS2RendererConfig | LagunaXS21RendererConfig | None = None,
|
|
85
88
|
):
|
|
86
89
|
self._tokenizer = tokenizer
|
|
87
90
|
self.config = config or LagunaXS2RendererConfig()
|
|
@@ -89,6 +92,11 @@ class LagunaXS2Renderer:
|
|
|
89
92
|
self.config,
|
|
90
93
|
"all",
|
|
91
94
|
)
|
|
95
|
+
# XS.2's template bakes in a default system prompt; XS-2.1's
|
|
96
|
+
# (otherwise byte-identical) template removed it.
|
|
97
|
+
self._default_system_message = (
|
|
98
|
+
_DEFAULT_SYSTEM_MESSAGE if self.config.name == "laguna-xs.2" else ""
|
|
99
|
+
)
|
|
92
100
|
|
|
93
101
|
self._eos = self._token_id("〈|EOS|〉")
|
|
94
102
|
self._think = self._token_id("<think>")
|
|
@@ -184,7 +192,7 @@ class LagunaXS2Renderer:
|
|
|
184
192
|
emit_special(self._eos, -1, is_sampled=False, is_content=False)
|
|
185
193
|
|
|
186
194
|
# ── System header (absorbs messages[0] if it's a system message) ──
|
|
187
|
-
system_content =
|
|
195
|
+
system_content = self._default_system_message
|
|
188
196
|
system_msg_idx = -1
|
|
189
197
|
caller_has_system = bool(messages and messages[0].get("role") == "system")
|
|
190
198
|
if caller_has_system:
|
|
@@ -196,16 +204,12 @@ class LagunaXS2Renderer:
|
|
|
196
204
|
# gate: when the caller passes an empty system message and no tools,
|
|
197
205
|
# the whole ``<system>...</system>`` block is omitted.
|
|
198
206
|
if has_sys_content or tools:
|
|
199
|
-
# The template emits ``<system>\n`` then conditionally a second
|
|
200
|
-
# ``\n``. Bundle those into one emit so BPE merges ``\n\n`` into
|
|
201
|
-
# its single-token form (rather than two ``\n`` atoms).
|
|
202
|
-
emit_text(
|
|
203
|
-
"<system>\n\n" if has_sys_content else "<system>\n",
|
|
204
|
-
-1,
|
|
205
|
-
is_sampled=False,
|
|
206
|
-
is_content=False,
|
|
207
|
-
)
|
|
208
207
|
if has_sys_content:
|
|
208
|
+
# The template emits ``<system>\n`` then a second ``\n``
|
|
209
|
+
# before the system body. Bundle those into one emit so BPE
|
|
210
|
+
# merges ``\n\n`` into its single-token form (rather than
|
|
211
|
+
# two ``\n`` atoms).
|
|
212
|
+
emit_text("<system>\n\n", -1, is_sampled=False, is_content=False)
|
|
209
213
|
# If the caller provided system content, it's body bytes;
|
|
210
214
|
# otherwise this is the default system prompt (scaffold).
|
|
211
215
|
sys_is_content = caller_has_system
|
|
@@ -224,6 +228,11 @@ class LagunaXS2Renderer:
|
|
|
224
228
|
if self.config.enable_thinking
|
|
225
229
|
else _TOOLS_FOOTER_NO_THINKING
|
|
226
230
|
)
|
|
231
|
+
if not has_sys_content:
|
|
232
|
+
# No system body: ``<system>\n`` runs straight into the
|
|
233
|
+
# tools header's ``\n\n`` — encode them together so BPE
|
|
234
|
+
# merges the ``\n\n\n`` seam as the template does.
|
|
235
|
+
tool_text = "<system>\n" + tool_text
|
|
227
236
|
emit_text(tool_text, -1, is_sampled=False, is_content=False)
|
|
228
237
|
emit_text("\n</system>\n", -1, is_sampled=False, is_content=False)
|
|
229
238
|
|
|
@@ -37,6 +37,9 @@ RENDERER_MODELS = [
|
|
|
37
37
|
# name (auto → MODEL_RENDERER_MAP → nemotron-3-ultra).
|
|
38
38
|
("nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16", "auto"),
|
|
39
39
|
("poolside/Laguna-XS.2", "auto"),
|
|
40
|
+
# XS-2.1 resolves to the `laguna-xs-2.1` config variant (no default
|
|
41
|
+
# system message) via the model name.
|
|
42
|
+
("poolside/Laguna-XS-2.1", "auto"),
|
|
40
43
|
# DeepSeek-V3/R1 are intentionally NOT in this shared barrage: their
|
|
41
44
|
# chat templates can't render the barrage's tool-call fixtures (the
|
|
42
45
|
# templates require ``tool['type']`` and a string-serialized
|
|
@@ -4,6 +4,17 @@ Runs against every (model, renderer) pair.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
from renderers import build_training_sample, build_trajectory_step
|
|
7
|
+
from renderers.base import PlaceholderRange, _build_mm_token_type_ids
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_build_mm_token_type_ids_marks_ranges():
|
|
11
|
+
"""Image runs → 1, video runs → 2, everything else → 0; clips at length."""
|
|
12
|
+
placeholders = {
|
|
13
|
+
"image": [PlaceholderRange(offset=2, length=3)], # tokens 2,3,4
|
|
14
|
+
"video": [PlaceholderRange(offset=7, length=2)], # tokens 7,8
|
|
15
|
+
}
|
|
16
|
+
ids = _build_mm_token_type_ids(placeholders, length=10)
|
|
17
|
+
assert ids == [0, 0, 1, 1, 1, 0, 0, 2, 2, 0]
|
|
7
18
|
|
|
8
19
|
|
|
9
20
|
def _expected(tokenizer, messages, **kwargs):
|
|
@@ -29,10 +40,14 @@ def test_build_training_sample_ids_match(model_name, tokenizer, renderer):
|
|
|
29
40
|
{"role": "user", "content": "Hi"},
|
|
30
41
|
{"role": "assistant", "content": "Hello!"},
|
|
31
42
|
]
|
|
32
|
-
|
|
43
|
+
sample = build_training_sample(
|
|
33
44
|
renderer, msgs, role_to_mask=lambda m: m["role"] == "assistant"
|
|
34
45
|
)
|
|
46
|
+
ids = sample.token_ids
|
|
35
47
|
assert ids == _expected(tokenizer, msgs)
|
|
48
|
+
# text-only sample carries no multimodal payload
|
|
49
|
+
assert sample.multi_modal_data is None
|
|
50
|
+
assert sample.mm_token_type_ids is None
|
|
36
51
|
|
|
37
52
|
|
|
38
53
|
def test_build_training_sample_has_trainable_tokens(model_name, tokenizer, renderer):
|
|
@@ -41,9 +56,10 @@ def test_build_training_sample_has_trainable_tokens(model_name, tokenizer, rende
|
|
|
41
56
|
{"role": "user", "content": "Hi"},
|
|
42
57
|
{"role": "assistant", "content": "Hello!"},
|
|
43
58
|
]
|
|
44
|
-
|
|
59
|
+
sample = build_training_sample(
|
|
45
60
|
renderer, msgs, role_to_mask=lambda m: m["role"] == "assistant"
|
|
46
61
|
)
|
|
62
|
+
ids, mask = sample.token_ids, sample.loss_mask
|
|
47
63
|
assert sum(mask) > 0
|
|
48
64
|
assert len(mask) == len(ids)
|
|
49
65
|
|
|
@@ -353,12 +353,13 @@ def test_build_training_sample_content_sft_roles_picks_up_tool_body(
|
|
|
353
353
|
{"role": "tool", "content": "done", "tool_call_id": "call_z"},
|
|
354
354
|
{"role": "assistant", "content": "OK."},
|
|
355
355
|
]
|
|
356
|
-
|
|
356
|
+
sample = build_training_sample(
|
|
357
357
|
renderer,
|
|
358
358
|
msgs,
|
|
359
359
|
role_to_mask=lambda m: m["role"] == "assistant",
|
|
360
360
|
content_sft_roles={"tool"},
|
|
361
361
|
)
|
|
362
|
+
ids, mask = sample.token_ids, sample.loss_mask
|
|
362
363
|
assert len(mask) == len(ids)
|
|
363
364
|
|
|
364
365
|
# We need at least one trainable tool-body token if the renderer
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
|
File without changes
|
{renderers-0.1.8.dev52 → renderers-0.1.8.dev54}/examples/tinker/multiturn_generate_tinker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|