renderers 0.1.10.dev1__tar.gz → 0.1.10.dev4__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.10.dev1 → renderers-0.1.10.dev4}/PKG-INFO +2 -2
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/README.md +1 -1
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/docs/renderer-config.md +2 -1
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/__init__.py +4 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/_version.py +2 -2
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/base.py +9 -1
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/configs.py +34 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/nemotron3.py +22 -3
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/conftest.py +3 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_bridge.py +1 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_nemotron3_parity.py +16 -9
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_nemotron3_ultra.py +32 -7
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_renderer_config_parity.py +3 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_roundtrip.py +2 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/.github/workflows/style.yml +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/.github/workflows/test.yml +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/.gitignore +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/LICENSE +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/examples/README.md +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/pyproject.toml +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/client.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/default.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/glm45.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/glm5.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/hy3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/llama_3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/parsers.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/parsing.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/prime_qwen3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/qwen3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/qwen35.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/qwen36.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_build_helpers.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_client.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_disabled_thinking_stability.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_glm_tool_name_validation.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_hy3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_incremental.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_is_content.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_laguna_m1.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_laguna_xs21.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_llama_3.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_message_indices.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_multimodal.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_parse_response.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_parsers.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_prime_qwen3_parity.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_render_ids.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: renderers
|
|
3
|
-
Version: 0.1.10.
|
|
3
|
+
Version: 0.1.10.dev4
|
|
4
4
|
Summary: Chat template renderers — deterministic message-to-token conversion for LLM training
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -56,7 +56,7 @@ next_prompt_ids = r.bridge_to_next_turn(
|
|
|
56
56
|
)
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `kimi-k2`, `kimi-k2.5` / `kimi-k2.6`, `laguna-xs.2`, `laguna-xs-2.1`, `laguna-m.1`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`, `gpt-oss`, `hy3`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
59
|
+
Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `kimi-k2`, `kimi-k2.5` / `kimi-k2.6`, `laguna-xs.2`, `laguna-xs-2.1`, `laguna-m.1`, `nemotron-3`, `nemotron-3-ultra`, `nemotron-3.5`, `llama-3`, `gpt-oss`, `hy3`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
60
60
|
|
|
61
61
|
## API
|
|
62
62
|
|
|
@@ -40,7 +40,7 @@ next_prompt_ids = r.bridge_to_next_turn(
|
|
|
40
40
|
)
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `kimi-k2`, `kimi-k2.5` / `kimi-k2.6`, `laguna-xs.2`, `laguna-xs-2.1`, `laguna-m.1`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`, `gpt-oss`, `hy3`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
43
|
+
Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `kimi-k2`, `kimi-k2.5` / `kimi-k2.6`, `laguna-xs.2`, `laguna-xs-2.1`, `laguna-m.1`, `nemotron-3`, `nemotron-3-ultra`, `nemotron-3.5`, `llama-3`, `gpt-oss`, `hy3`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
44
44
|
|
|
45
45
|
## API
|
|
46
46
|
|
|
@@ -43,6 +43,7 @@ chat-template kwargs. Those fields are covered by parity tests against
|
|
|
43
43
|
| MiniMax M2 | `MiniMaxM2RendererConfig` | `model_identity` | - |
|
|
44
44
|
| Nemotron-3 Nano / Super | `Nemotron3RendererConfig` | `enable_thinking`, `truncate_history_thinking`, `low_effort` | - |
|
|
45
45
|
| Nemotron-3 Ultra | `Nemotron3UltraRendererConfig` | `enable_thinking`, `truncate_history_thinking`, `medium_effort` | - |
|
|
46
|
+
| Nemotron-3.5 Lightning | `Nemotron35RendererConfig` | `enable_thinking`, `truncate_history_thinking` | - |
|
|
46
47
|
| DeepSeek V3 | `DeepSeekV3RendererConfig` | - | - |
|
|
47
48
|
| DeepSeek R1 | `DeepSeekR1RendererConfig` | - | - |
|
|
48
49
|
|
|
@@ -134,7 +135,7 @@ the knobs its template actually exposes:
|
|
|
134
135
|
| gpt-oss | `auto_drop_analysis=False -> all`, else `tool_cycle` |
|
|
135
136
|
| Hy3 | `preserved_thinking=True -> all`, else `tool_cycle` |
|
|
136
137
|
| Kimi K2.5 / 2.6 | `thinking=False -> all`, else `tool_cycle` |
|
|
137
|
-
| Nemotron-3 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
138
|
+
| Nemotron-3 / 3.5 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
138
139
|
| DeepSeek R1 | `template` |
|
|
139
140
|
| MiniMax M2 | `tool_cycle` |
|
|
140
141
|
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1, Llama 3 | `all` |
|
|
@@ -59,6 +59,7 @@ from renderers.configs import (
|
|
|
59
59
|
LagunaXS21RendererConfig,
|
|
60
60
|
Llama3RendererConfig,
|
|
61
61
|
MiniMaxM2RendererConfig,
|
|
62
|
+
Nemotron35RendererConfig,
|
|
62
63
|
Nemotron3RendererConfig,
|
|
63
64
|
Nemotron3UltraRendererConfig,
|
|
64
65
|
PrimeQwen3RendererConfig,
|
|
@@ -96,6 +97,7 @@ _LAZY_RENDERERS: dict[str, str] = {
|
|
|
96
97
|
"LagunaXS2Renderer": "renderers.laguna_xs2",
|
|
97
98
|
"Llama3Renderer": "renderers.llama_3",
|
|
98
99
|
"MiniMaxM2Renderer": "renderers.minimax_m2",
|
|
100
|
+
"Nemotron35Renderer": "renderers.nemotron3",
|
|
99
101
|
"Nemotron3Renderer": "renderers.nemotron3",
|
|
100
102
|
"Nemotron3UltraRenderer": "renderers.nemotron3",
|
|
101
103
|
"PrimeQwen3Renderer": "renderers.prime_qwen3",
|
|
@@ -162,6 +164,8 @@ __all__ = [
|
|
|
162
164
|
"MiniMaxM2RendererConfig",
|
|
163
165
|
"MultiModalData",
|
|
164
166
|
"MultimodalRenderer",
|
|
167
|
+
"Nemotron35Renderer",
|
|
168
|
+
"Nemotron35RendererConfig",
|
|
165
169
|
"Nemotron3Renderer",
|
|
166
170
|
"Nemotron3RendererConfig",
|
|
167
171
|
"Nemotron3UltraRenderer",
|
|
@@ -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.10.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, 10, '
|
|
21
|
+
__version__ = version = '0.1.10.dev4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 10, 'dev4')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1065,6 +1065,9 @@ MODEL_RENDERER_MAP: dict[str, str] = {
|
|
|
1065
1065
|
"nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16": "nemotron-3",
|
|
1066
1066
|
"nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16": "nemotron-3-ultra",
|
|
1067
1067
|
"nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-FP8": "nemotron-3-ultra",
|
|
1068
|
+
# Nemotron 3.5 (Lightning). Its template is the Ultra variant's minus the
|
|
1069
|
+
# effort kwarg (``nemotron-3.5``).
|
|
1070
|
+
"nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16": "nemotron-3.5",
|
|
1068
1071
|
# Llama 3.2 (Instruct). Tested against the gated meta-llama repos and
|
|
1069
1072
|
# the unrestricted unsloth/... mirror, which ships a byte-identical
|
|
1070
1073
|
# chat template. ``Llama3Renderer`` defaults ``date_string`` to
|
|
@@ -1332,7 +1335,11 @@ def _populate_registry():
|
|
|
1332
1335
|
)
|
|
1333
1336
|
from renderers.llama_3 import Llama3Renderer
|
|
1334
1337
|
from renderers.minimax_m2 import MiniMaxM2Renderer
|
|
1335
|
-
from renderers.nemotron3 import
|
|
1338
|
+
from renderers.nemotron3 import (
|
|
1339
|
+
Nemotron3Renderer,
|
|
1340
|
+
Nemotron3UltraRenderer,
|
|
1341
|
+
Nemotron35Renderer,
|
|
1342
|
+
)
|
|
1336
1343
|
from renderers.prime_qwen3 import PrimeQwen3Renderer
|
|
1337
1344
|
from renderers.qwen3 import Qwen3Renderer
|
|
1338
1345
|
from renderers.qwen3_vl import Qwen3VLRenderer
|
|
@@ -1362,6 +1369,7 @@ def _populate_registry():
|
|
|
1362
1369
|
"llama-3": Llama3Renderer,
|
|
1363
1370
|
"nemotron-3": Nemotron3Renderer,
|
|
1364
1371
|
"nemotron-3-ultra": Nemotron3UltraRenderer,
|
|
1372
|
+
"nemotron-3.5": Nemotron35Renderer,
|
|
1365
1373
|
"gpt-oss": GptOssRenderer,
|
|
1366
1374
|
}
|
|
1367
1375
|
)
|
|
@@ -657,6 +657,37 @@ class Nemotron3UltraRendererConfig(BaseRendererConfig):
|
|
|
657
657
|
user message. Mirrors the Ultra chat template's ``medium_effort`` kwarg."""
|
|
658
658
|
|
|
659
659
|
|
|
660
|
+
class Nemotron35RendererConfig(BaseRendererConfig):
|
|
661
|
+
"""Nemotron-3.5 (Lightning) renderer config.
|
|
662
|
+
|
|
663
|
+
Nemotron 3.5's chat template is the Ultra variant's minus the effort
|
|
664
|
+
kwarg: same reasoning-block glue (no ``\\n`` around ``</think>``, no
|
|
665
|
+
trim on truncated tool-call history), but its Jinja defines no
|
|
666
|
+
reasoning-effort variable at all. It shares the
|
|
667
|
+
:class:`renderers.nemotron3.Nemotron3Renderer` implementation via the
|
|
668
|
+
:class:`renderers.nemotron3.Nemotron35Renderer` subclass and is reached
|
|
669
|
+
via the ``nemotron-3.5`` discriminator.
|
|
670
|
+
"""
|
|
671
|
+
|
|
672
|
+
name: Literal["nemotron-3.5"] = "nemotron-3.5"
|
|
673
|
+
|
|
674
|
+
enable_thinking: bool = True
|
|
675
|
+
"""See :class:`Nemotron3RendererConfig.enable_thinking`."""
|
|
676
|
+
|
|
677
|
+
truncate_history_thinking: bool = True
|
|
678
|
+
"""See :class:`Nemotron3RendererConfig.truncate_history_thinking`."""
|
|
679
|
+
|
|
680
|
+
@model_validator(mode="after")
|
|
681
|
+
def _check_thinking_retention(self):
|
|
682
|
+
_reject_thinking_retention_conflict(
|
|
683
|
+
self,
|
|
684
|
+
"truncate_history_thinking",
|
|
685
|
+
true_implies="tool_cycle",
|
|
686
|
+
false_implies="all",
|
|
687
|
+
)
|
|
688
|
+
return self
|
|
689
|
+
|
|
690
|
+
|
|
660
691
|
class DeepSeekV3RendererConfig(BaseRendererConfig):
|
|
661
692
|
"""DeepSeek-V3 renderer config (non-reasoning).
|
|
662
693
|
|
|
@@ -708,6 +739,7 @@ RendererConfig = Annotated[
|
|
|
708
739
|
MiniMaxM2RendererConfig,
|
|
709
740
|
Nemotron3RendererConfig,
|
|
710
741
|
Nemotron3UltraRendererConfig,
|
|
742
|
+
Nemotron35RendererConfig,
|
|
711
743
|
DeepSeekV3RendererConfig,
|
|
712
744
|
DeepSeekR1RendererConfig,
|
|
713
745
|
],
|
|
@@ -749,6 +781,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
|
|
|
749
781
|
"minimax-m2": MiniMaxM2RendererConfig,
|
|
750
782
|
"nemotron-3": Nemotron3RendererConfig,
|
|
751
783
|
"nemotron-3-ultra": Nemotron3UltraRendererConfig,
|
|
784
|
+
"nemotron-3.5": Nemotron35RendererConfig,
|
|
752
785
|
"deepseek-v3": DeepSeekV3RendererConfig,
|
|
753
786
|
"deepseek-r1": DeepSeekR1RendererConfig,
|
|
754
787
|
}
|
|
@@ -796,6 +829,7 @@ __all__ = [
|
|
|
796
829
|
"LagunaXS21RendererConfig",
|
|
797
830
|
"Llama3RendererConfig",
|
|
798
831
|
"MiniMaxM2RendererConfig",
|
|
832
|
+
"Nemotron35RendererConfig",
|
|
799
833
|
"Nemotron3RendererConfig",
|
|
800
834
|
"Nemotron3UltraRendererConfig",
|
|
801
835
|
"PrimeQwen3RendererConfig",
|
|
@@ -31,7 +31,11 @@ from renderers.base import (
|
|
|
31
31
|
should_rerender_for_thinking_retention,
|
|
32
32
|
trim_to_turn_close,
|
|
33
33
|
)
|
|
34
|
-
from renderers.configs import
|
|
34
|
+
from renderers.configs import (
|
|
35
|
+
Nemotron3RendererConfig,
|
|
36
|
+
Nemotron3UltraRendererConfig,
|
|
37
|
+
Nemotron35RendererConfig,
|
|
38
|
+
)
|
|
35
39
|
from renderers.parsing import parse_qwen35
|
|
36
40
|
|
|
37
41
|
# ---------------------------------------------------------------------------
|
|
@@ -76,10 +80,12 @@ def _render_extra_keys(obj: dict[str, Any], handled_keys: set[str]) -> list[str]
|
|
|
76
80
|
return lines
|
|
77
81
|
|
|
78
82
|
|
|
79
|
-
# The Nemotron
|
|
83
|
+
# The Nemotron family ships three chat-template variants. Nano / Super share
|
|
80
84
|
# one (renderer ``Nemotron3Renderer`` / config ``name="nemotron-3"``); Ultra
|
|
81
85
|
# differs in the reasoning-block glue — no ``\n`` around ``</think>`` — and is
|
|
82
|
-
# the ``Nemotron3UltraRenderer`` subclass (``name="nemotron-3-ultra"``)
|
|
86
|
+
# the ``Nemotron3UltraRenderer`` subclass (``name="nemotron-3-ultra"``);
|
|
87
|
+
# Nemotron 3.5 (Lightning) uses the Ultra glue but defines no effort kwarg and
|
|
88
|
+
# is the ``Nemotron35Renderer`` subclass (``name="nemotron-3.5"``). Which
|
|
83
89
|
# variant a checkpoint uses is carried by ``MODEL_RENDERER_MAP``, so the right
|
|
84
90
|
# renderer class is constructed and the variant is encoded by the class itself.
|
|
85
91
|
|
|
@@ -852,3 +858,16 @@ class Nemotron3UltraRenderer(Nemotron3Renderer):
|
|
|
852
858
|
|
|
853
859
|
_config_cls = Nemotron3UltraRendererConfig
|
|
854
860
|
_ultra = True
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
class Nemotron35Renderer(Nemotron3Renderer):
|
|
864
|
+
"""Renderer for Nemotron **3.5** (Lightning).
|
|
865
|
+
|
|
866
|
+
The 3.5 chat template is the Ultra variant's minus the effort kwarg: same
|
|
867
|
+
reasoning-block glue (carried by the ``_ultra`` class hook), but its Jinja
|
|
868
|
+
defines no reasoning-effort variable, so the config exposes none and the
|
|
869
|
+
effort hint is always empty.
|
|
870
|
+
"""
|
|
871
|
+
|
|
872
|
+
_config_cls = Nemotron35RendererConfig
|
|
873
|
+
_ultra = True
|
|
@@ -36,6 +36,9 @@ RENDERER_MODELS = [
|
|
|
36
36
|
# Ultra resolves to the `nemotron-3-ultra` config variant via the model
|
|
37
37
|
# name (auto → MODEL_RENDERER_MAP → nemotron-3-ultra).
|
|
38
38
|
("nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16", "auto"),
|
|
39
|
+
# Nemotron 3.5 (Lightning): Ultra's template variant minus the effort
|
|
40
|
+
# kwarg (auto → MODEL_RENDERER_MAP → nemotron-3.5).
|
|
41
|
+
("nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16", "auto"),
|
|
39
42
|
("poolside/Laguna-XS.2", "auto"),
|
|
40
43
|
("poolside/Laguna-M.1", "auto"),
|
|
41
44
|
# XS-2.1 resolves to `LagunaXS21Renderer` via the model name — its
|
|
@@ -33,6 +33,7 @@ _BRIDGE_MODELS = [
|
|
|
33
33
|
("moonshotai/Kimi-K2-Instruct", "auto"),
|
|
34
34
|
("moonshotai/Kimi-K2.5", "auto"),
|
|
35
35
|
("nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16", "auto"),
|
|
36
|
+
("nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16", "auto"),
|
|
36
37
|
("tencent/Hy3", "auto"),
|
|
37
38
|
("unsloth/Llama-3.2-1B-Instruct", "llama-3"),
|
|
38
39
|
("openai/gpt-oss-20b", "gpt-oss"),
|
|
@@ -25,9 +25,10 @@ Every assertion compares ``renderer.render_ids(...)`` to
|
|
|
25
25
|
renderer is byte-for-byte faithful for that case. Tokenizers are loaded from
|
|
26
26
|
the local HF cache (offline); no network.
|
|
27
27
|
|
|
28
|
-
The variants split across
|
|
29
|
-
``low_effort``)
|
|
30
|
-
|
|
28
|
+
The variants split across three configs: ``nemotron-3`` (Nano / Super, with
|
|
29
|
+
``low_effort``), ``nemotron-3-ultra`` (Ultra, with ``medium_effort``), and
|
|
30
|
+
``nemotron-3.5`` (Lightning — Ultra's glue, no effort kwarg). The helper
|
|
31
|
+
resolves the right config class per model from ``MODEL_RENDERER_MAP``.
|
|
31
32
|
"""
|
|
32
33
|
|
|
33
34
|
from __future__ import annotations
|
|
@@ -44,7 +45,8 @@ from renderers.configs import _config_class_for
|
|
|
44
45
|
NANO = "nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16"
|
|
45
46
|
SUPER = "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16"
|
|
46
47
|
ULTRA = "nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16"
|
|
47
|
-
|
|
48
|
+
LIGHTNING = "nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16"
|
|
49
|
+
MODELS = [NANO, SUPER, ULTRA, LIGHTNING]
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
@lru_cache
|
|
@@ -107,7 +109,9 @@ def _assert_parity(
|
|
|
107
109
|
)
|
|
108
110
|
|
|
109
111
|
|
|
110
|
-
pytestmark = pytest.mark.parametrize(
|
|
112
|
+
pytestmark = pytest.mark.parametrize(
|
|
113
|
+
"model", MODELS, ids=["nano", "super", "ultra", "lightning"]
|
|
114
|
+
)
|
|
111
115
|
|
|
112
116
|
|
|
113
117
|
TOOLS = [
|
|
@@ -648,8 +652,8 @@ _EFFORT_SHAPES = [
|
|
|
648
652
|
def test_low_effort_kwarg(model, flag, shape, extra):
|
|
649
653
|
"""``low_effort`` appends ``\\n\\n{reasoning effort: low}`` to the last user
|
|
650
654
|
message on **Super**; it's a no-op on **Nano** (its template never defines
|
|
651
|
-
it). Ultra
|
|
652
|
-
if model
|
|
655
|
+
it). The Ultra / Lightning configs have no such field, so they're skipped."""
|
|
656
|
+
if model in (ULTRA, LIGHTNING):
|
|
653
657
|
pytest.skip("low_effort is a nemotron-3 (Nano/Super) kwarg")
|
|
654
658
|
_assert_parity(model, shape, low_effort=flag, **extra)
|
|
655
659
|
|
|
@@ -660,7 +664,7 @@ def test_low_effort_kwarg(model, flag, shape, extra):
|
|
|
660
664
|
)
|
|
661
665
|
def test_medium_effort_kwarg(model, flag, shape, extra):
|
|
662
666
|
"""``medium_effort`` appends ``\\n\\n{reasoning effort: efficient}`` on
|
|
663
|
-
**Ultra**.
|
|
667
|
+
**Ultra**. The other configs have no such field, so they're skipped."""
|
|
664
668
|
if model != ULTRA:
|
|
665
669
|
pytest.skip("medium_effort is a nemotron-3-ultra kwarg")
|
|
666
670
|
_assert_parity(model, shape, medium_effort=flag, **extra)
|
|
@@ -668,9 +672,12 @@ def test_medium_effort_kwarg(model, flag, shape, extra):
|
|
|
668
672
|
|
|
669
673
|
def test_effort_kwarg_lives_on_the_right_variant(model):
|
|
670
674
|
"""Each effort kwarg is declared only on the variant whose template defines
|
|
671
|
-
it — the discriminated union rejects the wrong combination at config load.
|
|
675
|
+
it — the discriminated union rejects the wrong combination at config load.
|
|
676
|
+
Lightning's template defines no effort variable, so its config has neither."""
|
|
672
677
|
fields = _config_cls(model).template_field_names()
|
|
673
678
|
if model == ULTRA:
|
|
674
679
|
assert "medium_effort" in fields and "low_effort" not in fields
|
|
680
|
+
elif model == LIGHTNING:
|
|
681
|
+
assert "low_effort" not in fields and "medium_effort" not in fields
|
|
675
682
|
else:
|
|
676
683
|
assert "low_effort" in fields and "medium_effort" not in fields
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Offline wiring tests for the Nemotron
|
|
1
|
+
"""Offline wiring tests for the Nemotron variant split.
|
|
2
2
|
|
|
3
3
|
Assert the model→renderer mapping, the per-variant typed-config surface, and
|
|
4
4
|
the name-based ``low_effort`` gating WITHOUT loading any tokenizer (no
|
|
@@ -6,15 +6,16 @@ network). This pins the wiring the parity matrix can't reach — in particular
|
|
|
6
6
|
the FP8 Ultra entry, which no test loads a tokenizer for — so it can't
|
|
7
7
|
silently rot.
|
|
8
8
|
|
|
9
|
-
The
|
|
9
|
+
The three variants:
|
|
10
10
|
|
|
11
11
|
* ``nemotron-3`` — Nano / Super, shared template. Config exposes ``low_effort``
|
|
12
12
|
(honoured on Super, a no-op on Nano).
|
|
13
13
|
* ``nemotron-3-ultra`` — Ultra, distinct ``</think>`` glue. Config exposes
|
|
14
14
|
``medium_effort``.
|
|
15
|
+
* ``nemotron-3.5`` — Lightning, Ultra's glue but no effort kwarg at all.
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
All route to the one ``Nemotron3Renderer`` implementation via per-variant
|
|
18
|
+
subclasses.
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
21
|
from types import SimpleNamespace
|
|
@@ -23,9 +24,15 @@ from renderers.base import MODEL_RENDERER_MAP, RENDERER_REGISTRY, _populate_regi
|
|
|
23
24
|
from renderers.configs import (
|
|
24
25
|
Nemotron3RendererConfig,
|
|
25
26
|
Nemotron3UltraRendererConfig,
|
|
27
|
+
Nemotron35RendererConfig,
|
|
26
28
|
_config_class_for,
|
|
27
29
|
)
|
|
28
|
-
from renderers.nemotron3 import
|
|
30
|
+
from renderers.nemotron3 import (
|
|
31
|
+
Nemotron3Renderer,
|
|
32
|
+
Nemotron3UltraRenderer,
|
|
33
|
+
Nemotron35Renderer,
|
|
34
|
+
_is_super,
|
|
35
|
+
)
|
|
29
36
|
|
|
30
37
|
_ULTRA_REPOS = [
|
|
31
38
|
"nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16",
|
|
@@ -35,6 +42,9 @@ _NANO_SUPER_REPOS = [
|
|
|
35
42
|
"nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16",
|
|
36
43
|
"nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16",
|
|
37
44
|
]
|
|
45
|
+
_LIGHTNING_REPOS = [
|
|
46
|
+
"nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16",
|
|
47
|
+
]
|
|
38
48
|
|
|
39
49
|
|
|
40
50
|
def _fake_tok(name):
|
|
@@ -46,18 +56,23 @@ def test_models_map_to_their_variant():
|
|
|
46
56
|
assert MODEL_RENDERER_MAP.get(repo) == "nemotron-3-ultra", repo
|
|
47
57
|
for repo in _NANO_SUPER_REPOS:
|
|
48
58
|
assert MODEL_RENDERER_MAP.get(repo) == "nemotron-3", repo
|
|
59
|
+
for repo in _LIGHTNING_REPOS:
|
|
60
|
+
assert MODEL_RENDERER_MAP.get(repo) == "nemotron-3.5", repo
|
|
49
61
|
|
|
50
62
|
|
|
51
63
|
def test_each_discriminator_maps_to_its_config_and_renderer_class():
|
|
52
64
|
# Config discriminator → config class.
|
|
53
65
|
assert _config_class_for("nemotron-3") is Nemotron3RendererConfig
|
|
54
66
|
assert _config_class_for("nemotron-3-ultra") is Nemotron3UltraRendererConfig
|
|
55
|
-
|
|
56
|
-
#
|
|
67
|
+
assert _config_class_for("nemotron-3.5") is Nemotron35RendererConfig
|
|
68
|
+
# Registry → renderer class (Ultra / 3.5 are sibling subclasses, matching
|
|
69
|
+
# the GLM-5/5.1 and Qwen3.5/3.6 house style — not one class under N names).
|
|
57
70
|
_populate_registry()
|
|
58
71
|
assert RENDERER_REGISTRY["nemotron-3"] is Nemotron3Renderer
|
|
59
72
|
assert RENDERER_REGISTRY["nemotron-3-ultra"] is Nemotron3UltraRenderer
|
|
73
|
+
assert RENDERER_REGISTRY["nemotron-3.5"] is Nemotron35Renderer
|
|
60
74
|
assert issubclass(Nemotron3UltraRenderer, Nemotron3Renderer)
|
|
75
|
+
assert issubclass(Nemotron35Renderer, Nemotron3Renderer)
|
|
61
76
|
|
|
62
77
|
|
|
63
78
|
def test_variant_is_encoded_by_the_class():
|
|
@@ -66,8 +81,10 @@ def test_variant_is_encoded_by_the_class():
|
|
|
66
81
|
# config.name → class). Default config also follows the class.
|
|
67
82
|
assert Nemotron3Renderer._ultra is False
|
|
68
83
|
assert Nemotron3UltraRenderer._ultra is True
|
|
84
|
+
assert Nemotron35Renderer._ultra is True
|
|
69
85
|
assert Nemotron3Renderer._config_cls is Nemotron3RendererConfig
|
|
70
86
|
assert Nemotron3UltraRenderer._config_cls is Nemotron3UltraRendererConfig
|
|
87
|
+
assert Nemotron35Renderer._config_cls is Nemotron35RendererConfig
|
|
71
88
|
|
|
72
89
|
|
|
73
90
|
def test_template_fields_per_variant():
|
|
@@ -80,6 +97,10 @@ def test_template_fields_per_variant():
|
|
|
80
97
|
assert Nemotron3UltraRendererConfig.template_field_names() == frozenset(
|
|
81
98
|
{"enable_thinking", "truncate_history_thinking", "medium_effort"}
|
|
82
99
|
)
|
|
100
|
+
# Lightning's template defines no effort variable at all.
|
|
101
|
+
assert Nemotron35RendererConfig.template_field_names() == frozenset(
|
|
102
|
+
{"enable_thinking", "truncate_history_thinking"}
|
|
103
|
+
)
|
|
83
104
|
|
|
84
105
|
|
|
85
106
|
def test_configs_reject_the_other_variants_effort_kwarg():
|
|
@@ -91,6 +112,10 @@ def test_configs_reject_the_other_variants_effort_kwarg():
|
|
|
91
112
|
Nemotron3RendererConfig(medium_effort=True) # type: ignore[call-arg]
|
|
92
113
|
with pytest.raises(ValidationError):
|
|
93
114
|
Nemotron3UltraRendererConfig(low_effort=True) # type: ignore[call-arg]
|
|
115
|
+
with pytest.raises(ValidationError):
|
|
116
|
+
Nemotron35RendererConfig(low_effort=True) # type: ignore[call-arg]
|
|
117
|
+
with pytest.raises(ValidationError):
|
|
118
|
+
Nemotron35RendererConfig(medium_effort=True) # type: ignore[call-arg]
|
|
94
119
|
# And the removed ``ultra`` selector is gone entirely.
|
|
95
120
|
with pytest.raises(ValidationError):
|
|
96
121
|
Nemotron3RendererConfig(ultra=True) # type: ignore[call-arg]
|
|
@@ -63,6 +63,9 @@ _RENDERER_MODELS = [
|
|
|
63
63
|
# Ultra: auto-resolves to the ``nemotron-3-ultra`` config via the model
|
|
64
64
|
# name; parity asserted against the Ultra apply_chat_template (``medium_effort``).
|
|
65
65
|
("nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16", "auto"),
|
|
66
|
+
# Nemotron 3.5 (Lightning): Ultra's template minus the effort kwarg — its
|
|
67
|
+
# config declares only ``enable_thinking`` / ``truncate_history_thinking``.
|
|
68
|
+
("nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16", "auto"),
|
|
66
69
|
("poolside/Laguna-XS.2", "auto"),
|
|
67
70
|
("poolside/Laguna-M.1", "auto"),
|
|
68
71
|
("poolside/Laguna-XS-2.1", "auto"),
|
|
@@ -46,6 +46,8 @@ _ROUNDTRIP_MODELS = [
|
|
|
46
46
|
# Ultra: parse must recover content after a </think> glued directly to it
|
|
47
47
|
# (no separating newline) — the Ultra-specific glue stresses the round-trip.
|
|
48
48
|
("nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16", "auto"),
|
|
49
|
+
# Nemotron 3.5 (Lightning): same glue as Ultra, distinct renderer class.
|
|
50
|
+
("nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16", "auto"),
|
|
49
51
|
("poolside/Laguna-XS.2", "auto"),
|
|
50
52
|
("poolside/Laguna-M.1", "auto"),
|
|
51
53
|
# Laguna-XS-2.1 is deliberately absent: under the default
|
|
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.10.dev1 → renderers-0.1.10.dev4}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
|
File without changes
|
{renderers-0.1.10.dev1 → renderers-0.1.10.dev4}/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
|