renderers 0.1.8.dev57__tar.gz → 0.1.8.dev58__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.dev57 → renderers-0.1.8.dev58}/PKG-INFO +1 -1
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/_version.py +2 -2
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/base.py +33 -2
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_build_helpers.py +28 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/.github/workflows/style.yml +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/.github/workflows/test.yml +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/.gitignore +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/LICENSE +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/README.md +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/docs/renderer-config.md +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/examples/README.md +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/pyproject.toml +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/__init__.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/client.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/configs.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/default.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/glm45.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/glm5.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/hy3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/llama_3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/nemotron3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/parsers.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/parsing.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/prime_qwen3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/qwen3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/qwen35.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/qwen36.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/conftest.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_bridge.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_client.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_hy3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_incremental.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_is_content.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_laguna_xs21.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_llama_3.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_message_indices.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_multimodal.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_parse_response.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_parsers.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_prime_qwen3_parity.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_render_ids.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_renderer_config_parity.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/uv.lock +0 -0
|
@@ -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.dev58'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 8, 'dev58')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1594,6 +1594,7 @@ def build_training_sample(
|
|
|
1594
1594
|
role_to_mask: Callable[[Message], bool] | None = None,
|
|
1595
1595
|
tools: list[ToolSpec] | None = None,
|
|
1596
1596
|
content_sft_roles: "set[str] | frozenset[str] | None" = None,
|
|
1597
|
+
ensure_final_stop: bool = False,
|
|
1597
1598
|
) -> RenderedTrainingSample:
|
|
1598
1599
|
"""Build a :class:`RenderedTrainingSample` for supervised training.
|
|
1599
1600
|
|
|
@@ -1649,6 +1650,17 @@ def build_training_sample(
|
|
|
1649
1650
|
or hand-coded renderers that haven't been wired up yet) ignore
|
|
1650
1651
|
``content_sft_roles`` silently — falling back to the original
|
|
1651
1652
|
``role_to_mask`` + ``sampled_mask`` behaviour.
|
|
1653
|
+
|
|
1654
|
+
``ensure_final_stop`` appends the renderer's canonical stop token
|
|
1655
|
+
when the sample ends with an assistant message that the template
|
|
1656
|
+
leaves unterminated. Some templates close an assistant turn only
|
|
1657
|
+
via the *next* message's role marker (e.g. GLM's ``<|user|>`` /
|
|
1658
|
+
``<|observation|>``), so a final assistant message renders with no
|
|
1659
|
+
stop token at all. No-op when the template already closes the turn
|
|
1660
|
+
in-message (ChatML ``<|im_end|>``, Llama ``<|eot_id|>``); where it
|
|
1661
|
+
fires, the output intentionally diverges from ``apply_chat_template``.
|
|
1662
|
+
Ignored for renderers without ``sampled_mask`` (``DefaultRenderer``) —
|
|
1663
|
+
the close of an opaque template can't be located reliably.
|
|
1652
1664
|
"""
|
|
1653
1665
|
rendered = renderer.render(messages, tools=tools)
|
|
1654
1666
|
has_sampled_info = len(rendered.sampled_mask) == len(rendered.token_ids)
|
|
@@ -1689,6 +1701,25 @@ def build_training_sample(
|
|
|
1689
1701
|
else:
|
|
1690
1702
|
loss_mask.append(role_to_mask(msg))
|
|
1691
1703
|
|
|
1704
|
+
token_ids = list(rendered.token_ids)
|
|
1705
|
+
# Requires sampled_mask (opaque templates hide the assistant close)
|
|
1706
|
+
# and a final assistant message the role filter trains.
|
|
1707
|
+
if (
|
|
1708
|
+
ensure_final_stop
|
|
1709
|
+
and has_sampled_info
|
|
1710
|
+
and messages[-1].get("role") == "assistant"
|
|
1711
|
+
and (role_to_mask is None or role_to_mask(messages[-1]))
|
|
1712
|
+
):
|
|
1713
|
+
stop_ids = set(renderer.get_stop_token_ids())
|
|
1714
|
+
last_trainable = next(
|
|
1715
|
+
(k for k in range(len(loss_mask) - 1, -1, -1) if loss_mask[k]), None
|
|
1716
|
+
)
|
|
1717
|
+
if last_trainable is None or token_ids[last_trainable] not in stop_ids:
|
|
1718
|
+
token_ids.append(renderer.get_stop_token_ids()[0])
|
|
1719
|
+
# loss_mask=True marks the token as trainable — the appended
|
|
1720
|
+
# stop is a training target, like any sampled token.
|
|
1721
|
+
loss_mask.append(True)
|
|
1722
|
+
|
|
1692
1723
|
# Surface the multimodal payload for VLM renderers. ``None`` for text
|
|
1693
1724
|
# renderers and for text-only samples (empty media) so downstream
|
|
1694
1725
|
# ``multi_modal_data is not None`` is a reliable "has media" check.
|
|
@@ -1696,12 +1727,12 @@ def build_training_sample(
|
|
|
1696
1727
|
if mm is not None and mm.is_empty():
|
|
1697
1728
|
mm = None
|
|
1698
1729
|
mm_token_type_ids = (
|
|
1699
|
-
_build_mm_token_type_ids(mm.mm_placeholders, len(
|
|
1730
|
+
_build_mm_token_type_ids(mm.mm_placeholders, len(token_ids))
|
|
1700
1731
|
if mm is not None and mm.mm_placeholders
|
|
1701
1732
|
else None
|
|
1702
1733
|
)
|
|
1703
1734
|
return RenderedTrainingSample(
|
|
1704
|
-
token_ids=
|
|
1735
|
+
token_ids=token_ids,
|
|
1705
1736
|
loss_mask=loss_mask,
|
|
1706
1737
|
multi_modal_data=mm,
|
|
1707
1738
|
mm_token_type_ids=mm_token_type_ids,
|
|
@@ -64,6 +64,34 @@ def test_build_training_sample_has_trainable_tokens(model_name, tokenizer, rende
|
|
|
64
64
|
assert len(mask) == len(ids)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
def test_build_training_sample_ensures_final_stop(model_name, tokenizer, renderer):
|
|
68
|
+
"""The final assistant turn ends at a trainable renderer stop token.
|
|
69
|
+
|
|
70
|
+
Templates whose assistant close is part of the message (ChatML, Llama)
|
|
71
|
+
already satisfy this, so the sample stays byte-identical; templates
|
|
72
|
+
that terminate turns with the next message's role marker (GLM) get the
|
|
73
|
+
canonical stop appended as the training target.
|
|
74
|
+
"""
|
|
75
|
+
if not renderer.render([{"role": "user", "content": "x"}]).sampled_mask:
|
|
76
|
+
return # DefaultRenderer: no sampled_mask, role-only masking
|
|
77
|
+
msgs = [
|
|
78
|
+
{"role": "user", "content": "Hi"},
|
|
79
|
+
{"role": "assistant", "content": "Hello!"},
|
|
80
|
+
]
|
|
81
|
+
sample = build_training_sample(renderer, msgs, ensure_final_stop=True)
|
|
82
|
+
stop_ids = set(renderer.get_stop_token_ids())
|
|
83
|
+
last_trainable = max(k for k, m in enumerate(sample.loss_mask) if m)
|
|
84
|
+
assert sample.token_ids[last_trainable] in stop_ids
|
|
85
|
+
|
|
86
|
+
baseline = build_training_sample(renderer, msgs)
|
|
87
|
+
if (
|
|
88
|
+
baseline.token_ids[max(k for k, m in enumerate(baseline.loss_mask) if m)]
|
|
89
|
+
in stop_ids
|
|
90
|
+
):
|
|
91
|
+
# In-message close: ensure_final_stop must not change the bytes.
|
|
92
|
+
assert sample.token_ids == baseline.token_ids
|
|
93
|
+
|
|
94
|
+
|
|
67
95
|
def test_build_trajectory_step_reconstructs_full(model_name, tokenizer, renderer):
|
|
68
96
|
"""prompt_ids + completion_ids must equal the full rendered sequence."""
|
|
69
97
|
prompt = [
|
|
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.dev57 → renderers-0.1.8.dev58}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
|
File without changes
|
{renderers-0.1.8.dev57 → renderers-0.1.8.dev58}/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
|
|
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
|