renderers 0.1.8.dev52__tar.gz → 0.1.8.dev53__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.dev53}/PKG-INFO +1 -1
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/__init__.py +2 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/_version.py +2 -2
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/base.py +60 -3
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_build_helpers.py +18 -2
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_is_content.py +2 -1
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/.github/workflows/style.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/.github/workflows/test.yml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/.gitignore +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/LICENSE +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/README.md +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/docs/renderer-config.md +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/examples/README.md +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/pyproject.toml +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/client.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/configs.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/default.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/glm45.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/glm5.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/llama_3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/nemotron3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/parsers.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/parsing.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/qwen3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/qwen35.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/qwen36.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/conftest.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_bridge.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_client.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_incremental.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_llama_3.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_message_indices.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_multimodal.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_parse_response.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_parsers.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_render_ids.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_renderer_config_parity.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/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,
|
|
@@ -164,6 +165,7 @@ __all__ = [
|
|
|
164
165
|
"Qwen3VLRendererConfig",
|
|
165
166
|
"RenderedConversation",
|
|
166
167
|
"RenderedTokens",
|
|
168
|
+
"RenderedTrainingSample",
|
|
167
169
|
"Renderer",
|
|
168
170
|
"RendererConfig",
|
|
169
171
|
"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.dev53'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 8, 'dev53')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1534,6 +1534,42 @@ def _resolve_auto_config(
|
|
|
1534
1534
|
# ---------------------------------------------------------------------------
|
|
1535
1535
|
|
|
1536
1536
|
|
|
1537
|
+
# Match prime-rl's multimodal token type convention: 0=text, 1=image, 2=video.
|
|
1538
|
+
_MM_TYPE_ID: dict[str, int] = {"image": 1, "video": 2}
|
|
1539
|
+
|
|
1540
|
+
|
|
1541
|
+
@dataclass(frozen=True)
|
|
1542
|
+
class RenderedTrainingSample:
|
|
1543
|
+
"""Output of :func:`build_training_sample`.
|
|
1544
|
+
|
|
1545
|
+
``token_ids`` and ``loss_mask`` are always populated. ``multi_modal_data``
|
|
1546
|
+
and ``mm_token_type_ids`` are populated only when a multimodal renderer
|
|
1547
|
+
actually emitted media (both ``None`` for text-only renderers and for
|
|
1548
|
+
text-only samples through a VLM renderer), so the text path is unchanged.
|
|
1549
|
+
"""
|
|
1550
|
+
|
|
1551
|
+
token_ids: list[int]
|
|
1552
|
+
loss_mask: list[bool]
|
|
1553
|
+
multi_modal_data: "MultiModalData | None" = None
|
|
1554
|
+
mm_token_type_ids: list[int] | None = None
|
|
1555
|
+
|
|
1556
|
+
|
|
1557
|
+
def _build_mm_token_type_ids(
|
|
1558
|
+
mm_placeholders: dict[str, list[PlaceholderRange]], length: int
|
|
1559
|
+
) -> list[int]:
|
|
1560
|
+
"""Per-token modality flags (0=text, 1=image, 2=video) from placeholder ranges."""
|
|
1561
|
+
ids = [0] * length
|
|
1562
|
+
for modality, ranges in mm_placeholders.items():
|
|
1563
|
+
type_id = _MM_TYPE_ID.get(modality, 0)
|
|
1564
|
+
if type_id == 0:
|
|
1565
|
+
continue
|
|
1566
|
+
for r in ranges:
|
|
1567
|
+
end = min(r.offset + r.length, length)
|
|
1568
|
+
for i in range(r.offset, end):
|
|
1569
|
+
ids[i] = type_id
|
|
1570
|
+
return ids
|
|
1571
|
+
|
|
1572
|
+
|
|
1537
1573
|
def build_training_sample(
|
|
1538
1574
|
renderer: Renderer,
|
|
1539
1575
|
messages: list[Message],
|
|
@@ -1541,8 +1577,12 @@ def build_training_sample(
|
|
|
1541
1577
|
role_to_mask: Callable[[Message], bool] | None = None,
|
|
1542
1578
|
tools: list[ToolSpec] | None = None,
|
|
1543
1579
|
content_sft_roles: "set[str] | frozenset[str] | None" = None,
|
|
1544
|
-
) ->
|
|
1545
|
-
"""Build
|
|
1580
|
+
) -> RenderedTrainingSample:
|
|
1581
|
+
"""Build a :class:`RenderedTrainingSample` for supervised training.
|
|
1582
|
+
|
|
1583
|
+
Returns ``token_ids`` + ``loss_mask`` (always), plus ``multi_modal_data``
|
|
1584
|
+
and ``mm_token_type_ids`` when the renderer emitted media (``None`` for
|
|
1585
|
+
text — the text token_ids/loss_mask are byte-identical to before).
|
|
1546
1586
|
|
|
1547
1587
|
Single render() call + message_indices → per-token mask.
|
|
1548
1588
|
Replaces build_incremental_token_mask (O(N) renders → O(1)).
|
|
@@ -1631,7 +1671,24 @@ def build_training_sample(
|
|
|
1631
1671
|
loss_mask.append(True)
|
|
1632
1672
|
else:
|
|
1633
1673
|
loss_mask.append(role_to_mask(msg))
|
|
1634
|
-
|
|
1674
|
+
|
|
1675
|
+
# Surface the multimodal payload for VLM renderers. ``None`` for text
|
|
1676
|
+
# renderers and for text-only samples (empty media) so downstream
|
|
1677
|
+
# ``multi_modal_data is not None`` is a reliable "has media" check.
|
|
1678
|
+
mm = rendered.multi_modal_data
|
|
1679
|
+
if mm is not None and mm.is_empty():
|
|
1680
|
+
mm = None
|
|
1681
|
+
mm_token_type_ids = (
|
|
1682
|
+
_build_mm_token_type_ids(mm.mm_placeholders, len(rendered.token_ids))
|
|
1683
|
+
if mm is not None and mm.mm_placeholders
|
|
1684
|
+
else None
|
|
1685
|
+
)
|
|
1686
|
+
return RenderedTrainingSample(
|
|
1687
|
+
token_ids=rendered.token_ids,
|
|
1688
|
+
loss_mask=loss_mask,
|
|
1689
|
+
multi_modal_data=mm,
|
|
1690
|
+
mm_token_type_ids=mm_token_type_ids,
|
|
1691
|
+
)
|
|
1635
1692
|
|
|
1636
1693
|
|
|
1637
1694
|
def _common_prefix_len(a: list[int], b: list[int]) -> int:
|
|
@@ -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.dev53}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
|
File without changes
|
{renderers-0.1.8.dev52 → renderers-0.1.8.dev53}/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
|