renderers 0.1.10.dev4__tar.gz → 0.1.10.dev10__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.dev4 → renderers-0.1.10.dev10}/PKG-INFO +2 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/README.md +1 -1
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/docs/renderer-config.md +2 -1
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/__init__.py +4 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/_version.py +2 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/base.py +5 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/configs.py +34 -0
- renderers-0.1.10.dev10/renderers/laguna_s21.py +33 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/laguna_xs2.py +14 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/conftest.py +4 -0
- renderers-0.1.10.dev10/tests/test_laguna_s21.py +128 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_renderer_config_parity.py +1 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/.github/workflows/style.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/.github/workflows/test.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/.gitignore +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/LICENSE +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/README.md +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/pyproject.toml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/client.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/default.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/glm45.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/glm5.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/hy3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/llama_3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/nemotron3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/parsers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/parsing.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/prime_qwen3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/qwen3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/qwen35.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/qwen36.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_bridge.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_build_helpers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_client.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_disabled_thinking_stability.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_glm_tool_name_validation.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_hy3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_incremental.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_is_content.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_laguna_m1.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_laguna_xs21.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_llama_3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_message_indices.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_multimodal.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_parse_response.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_parsers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_prime_qwen3_parity.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_render_ids.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_roundtrip.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/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.dev10
|
|
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`, `nemotron-3.5`, `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-s-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`, `nemotron-3.5`, `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-s-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
|
|
|
@@ -39,6 +39,7 @@ chat-template kwargs. Those fields are covered by parity tests against
|
|
|
39
39
|
| Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
40
40
|
| Laguna M.1 | `LagunaM1RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
41
41
|
| Laguna XS-2.1 | `LagunaXS21RendererConfig` | `enable_thinking` | - |
|
|
42
|
+
| Laguna S-2.1 | `LagunaS21RendererConfig` | `enable_thinking`, `preserve_thinking` | - |
|
|
42
43
|
| Llama 3 | `Llama3RendererConfig` | `date_string`, `tools_in_user_message` | - |
|
|
43
44
|
| MiniMax M2 | `MiniMaxM2RendererConfig` | `model_identity` | - |
|
|
44
45
|
| Nemotron-3 Nano / Super | `Nemotron3RendererConfig` | `enable_thinking`, `truncate_history_thinking`, `low_effort` | - |
|
|
@@ -138,7 +139,7 @@ the knobs its template actually exposes:
|
|
|
138
139
|
| Nemotron-3 / 3.5 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
139
140
|
| DeepSeek R1 | `template` |
|
|
140
141
|
| MiniMax M2 | `tool_cycle` |
|
|
141
|
-
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1, Llama 3 | `all` |
|
|
142
|
+
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3 | `all` |
|
|
142
143
|
| PrimeIntellect Qwen3 | `all` |
|
|
143
144
|
|
|
144
145
|
Config construction raises when an explicit template knob directly contradicts
|
|
@@ -55,6 +55,7 @@ from renderers.configs import (
|
|
|
55
55
|
KimiK25RendererConfig,
|
|
56
56
|
KimiK2RendererConfig,
|
|
57
57
|
LagunaM1RendererConfig,
|
|
58
|
+
LagunaS21RendererConfig,
|
|
58
59
|
LagunaXS2RendererConfig,
|
|
59
60
|
LagunaXS21RendererConfig,
|
|
60
61
|
Llama3RendererConfig,
|
|
@@ -93,6 +94,7 @@ _LAZY_RENDERERS: dict[str, str] = {
|
|
|
93
94
|
"KimiK25Renderer": "renderers.kimi_k25",
|
|
94
95
|
"KimiK2Renderer": "renderers.kimi_k2",
|
|
95
96
|
"LagunaM1Renderer": "renderers.laguna_xs2",
|
|
97
|
+
"LagunaS21Renderer": "renderers.laguna_s21",
|
|
96
98
|
"LagunaXS21Renderer": "renderers.laguna_xs2",
|
|
97
99
|
"LagunaXS2Renderer": "renderers.laguna_xs2",
|
|
98
100
|
"Llama3Renderer": "renderers.llama_3",
|
|
@@ -151,6 +153,8 @@ __all__ = [
|
|
|
151
153
|
"KimiK2RendererConfig",
|
|
152
154
|
"LagunaM1Renderer",
|
|
153
155
|
"LagunaM1RendererConfig",
|
|
156
|
+
"LagunaS21Renderer",
|
|
157
|
+
"LagunaS21RendererConfig",
|
|
154
158
|
"LagunaXS2Renderer",
|
|
155
159
|
"LagunaXS2RendererConfig",
|
|
156
160
|
"LagunaXS21Renderer",
|
|
@@ -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.dev10'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 10, 'dev10')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1076,11 +1076,12 @@ MODEL_RENDERER_MAP: dict[str, str] = {
|
|
|
1076
1076
|
# construction to pin a different date.
|
|
1077
1077
|
"meta-llama/Llama-3.2-1B-Instruct": "llama-3",
|
|
1078
1078
|
"meta-llama/Llama-3.2-3B-Instruct": "llama-3",
|
|
1079
|
-
# Poolside Laguna. These checkpoints ship distinct chat templates,
|
|
1080
|
-
#
|
|
1079
|
+
# Poolside Laguna. These checkpoints ship distinct chat templates, each
|
|
1080
|
+
# mirrored by its own renderer class/config discriminator.
|
|
1081
1081
|
"poolside/Laguna-XS.2": "laguna-xs.2",
|
|
1082
1082
|
"poolside/Laguna-M.1": "laguna-m.1",
|
|
1083
1083
|
"poolside/Laguna-XS-2.1": "laguna-xs-2.1",
|
|
1084
|
+
"poolside/Laguna-S-2.1": "laguna-s-2.1",
|
|
1084
1085
|
# GPT-OSS.
|
|
1085
1086
|
"openai/gpt-oss-20b": "gpt-oss",
|
|
1086
1087
|
"openai/gpt-oss-120b": "gpt-oss",
|
|
@@ -1328,6 +1329,7 @@ def _populate_registry():
|
|
|
1328
1329
|
from renderers.hy3 import Hy3Renderer
|
|
1329
1330
|
from renderers.kimi_k2 import KimiK2Renderer
|
|
1330
1331
|
from renderers.kimi_k25 import KimiK25Renderer
|
|
1332
|
+
from renderers.laguna_s21 import LagunaS21Renderer
|
|
1331
1333
|
from renderers.laguna_xs2 import (
|
|
1332
1334
|
LagunaM1Renderer,
|
|
1333
1335
|
LagunaXS2Renderer,
|
|
@@ -1366,6 +1368,7 @@ def _populate_registry():
|
|
|
1366
1368
|
"laguna-xs.2": LagunaXS2Renderer,
|
|
1367
1369
|
"laguna-m.1": LagunaM1Renderer,
|
|
1368
1370
|
"laguna-xs-2.1": LagunaXS21Renderer,
|
|
1371
|
+
"laguna-s-2.1": LagunaS21Renderer,
|
|
1369
1372
|
"llama-3": Llama3Renderer,
|
|
1370
1373
|
"nemotron-3": Nemotron3Renderer,
|
|
1371
1374
|
"nemotron-3-ultra": Nemotron3UltraRenderer,
|
|
@@ -546,6 +546,37 @@ class LagunaXS21RendererConfig(BaseRendererConfig):
|
|
|
546
546
|
upstream default."""
|
|
547
547
|
|
|
548
548
|
|
|
549
|
+
class LagunaS21RendererConfig(BaseRendererConfig):
|
|
550
|
+
"""Laguna S-2.1 renderer config.
|
|
551
|
+
|
|
552
|
+
S-2.1 is a larger sibling of XS-2.1 sharing its tokenizer (same vocab,
|
|
553
|
+
special tokens, and merges), but its chat template is *not* byte-identical:
|
|
554
|
+
``enable_thinking`` defaults to ``True`` (XS-2.1 defaults ``False``), and a
|
|
555
|
+
new ``preserve_thinking`` kwarg widens the reasoning-display gate to
|
|
556
|
+
``enable_thinking or preserve_thinking``. The token format is otherwise
|
|
557
|
+
identical, so this is served by
|
|
558
|
+
:class:`renderers.laguna_s21.LagunaS21Renderer`, a thin subclass of
|
|
559
|
+
``LagunaXS21Renderer`` that only overrides that gate.
|
|
560
|
+
"""
|
|
561
|
+
|
|
562
|
+
name: Literal["laguna-s-2.1"] = "laguna-s-2.1"
|
|
563
|
+
|
|
564
|
+
enable_thinking: bool = True
|
|
565
|
+
"""When ``True``, the generation prompt ends with ``<think>`` and every
|
|
566
|
+
assistant turn renders ``<think>{reasoning}</think>``; when ``False``,
|
|
567
|
+
turns open with a bare ``</think>``. Mirrors the template's
|
|
568
|
+
``enable_thinking`` kwarg — note S-2.1's upstream default is ``True``,
|
|
569
|
+
unlike XS-2.1's ``False``."""
|
|
570
|
+
|
|
571
|
+
preserve_thinking: bool = False
|
|
572
|
+
"""When ``True``, assistant turns keep their ``<think>{reasoning}</think>``
|
|
573
|
+
block even while ``enable_thinking`` is ``False`` — the template gates
|
|
574
|
+
reasoning display on ``enable_thinking or preserve_thinking``. With the
|
|
575
|
+
default ``False`` the gate collapses to ``enable_thinking`` and the
|
|
576
|
+
renderer matches XS-2.1 turn-for-turn. Mirrors the template's
|
|
577
|
+
``preserve_thinking`` kwarg and its upstream default."""
|
|
578
|
+
|
|
579
|
+
|
|
549
580
|
class Llama3RendererConfig(BaseRendererConfig):
|
|
550
581
|
"""Llama-3.x Instruct renderer config.
|
|
551
582
|
|
|
@@ -735,6 +766,7 @@ RendererConfig = Annotated[
|
|
|
735
766
|
LagunaXS2RendererConfig,
|
|
736
767
|
LagunaM1RendererConfig,
|
|
737
768
|
LagunaXS21RendererConfig,
|
|
769
|
+
LagunaS21RendererConfig,
|
|
738
770
|
Llama3RendererConfig,
|
|
739
771
|
MiniMaxM2RendererConfig,
|
|
740
772
|
Nemotron3RendererConfig,
|
|
@@ -777,6 +809,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
|
|
|
777
809
|
"laguna-xs.2": LagunaXS2RendererConfig,
|
|
778
810
|
"laguna-m.1": LagunaM1RendererConfig,
|
|
779
811
|
"laguna-xs-2.1": LagunaXS21RendererConfig,
|
|
812
|
+
"laguna-s-2.1": LagunaS21RendererConfig,
|
|
780
813
|
"llama-3": Llama3RendererConfig,
|
|
781
814
|
"minimax-m2": MiniMaxM2RendererConfig,
|
|
782
815
|
"nemotron-3": Nemotron3RendererConfig,
|
|
@@ -825,6 +858,7 @@ __all__ = [
|
|
|
825
858
|
"KimiK25RendererConfig",
|
|
826
859
|
"KimiK2RendererConfig",
|
|
827
860
|
"LagunaM1RendererConfig",
|
|
861
|
+
"LagunaS21RendererConfig",
|
|
828
862
|
"LagunaXS2RendererConfig",
|
|
829
863
|
"LagunaXS21RendererConfig",
|
|
830
864
|
"Llama3RendererConfig",
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Laguna S-2.1 Renderer — a larger sibling of Laguna XS-2.1.
|
|
2
|
+
|
|
3
|
+
S-2.1 shares XS-2.1's tokenizer and token format, so this is a thin subclass of
|
|
4
|
+
:class:`renderers.laguna_xs2.LagunaXS21Renderer`; see that module for the shared
|
|
5
|
+
format. The template delta is two thinking kwargs: ``enable_thinking`` defaults
|
|
6
|
+
to ``True`` (XS-2.1 defaults ``False``), and ``preserve_thinking`` widens the
|
|
7
|
+
reasoning-display gate to ``enable_thinking or preserve_thinking``.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from transformers.tokenization_utils import PreTrainedTokenizer
|
|
13
|
+
|
|
14
|
+
from renderers.configs import LagunaS21RendererConfig
|
|
15
|
+
from renderers.laguna_xs2 import LagunaXS21Renderer
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class LagunaS21Renderer(LagunaXS21Renderer):
|
|
19
|
+
"""Mirrors the ``poolside/Laguna-S-2.1`` chat template."""
|
|
20
|
+
|
|
21
|
+
# Narrows the inherited attribute so the S-2.1-only ``preserve_thinking``
|
|
22
|
+
# resolves; ``__init__`` always stores an S-2.1 config.
|
|
23
|
+
config: LagunaS21RendererConfig
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
tokenizer: PreTrainedTokenizer,
|
|
28
|
+
config: LagunaS21RendererConfig | None = None,
|
|
29
|
+
):
|
|
30
|
+
super().__init__(tokenizer, config or LagunaS21RendererConfig())
|
|
31
|
+
|
|
32
|
+
def _render_history_reasoning(self) -> bool:
|
|
33
|
+
return self.config.enable_thinking or self.config.preserve_thinking
|
|
@@ -41,6 +41,9 @@ Laguna M.1's official template (revision ``2bf8a4ab``) is served by
|
|
|
41
41
|
:class:`LagunaM1Renderer`. It shares XS.2's byte layout, tool syntax, and
|
|
42
42
|
generation prompt, but has no fallback system prompt and reads assistant
|
|
43
43
|
reasoning from ``reasoning`` before falling back to ``reasoning_content``.
|
|
44
|
+
|
|
45
|
+
S-2.1 subclasses :class:`LagunaXS21Renderer` from :mod:`renderers.laguna_s21`,
|
|
46
|
+
overriding only the ``_render_history_reasoning`` seam.
|
|
44
47
|
"""
|
|
45
48
|
|
|
46
49
|
from __future__ import annotations
|
|
@@ -63,6 +66,7 @@ from renderers.base import (
|
|
|
63
66
|
)
|
|
64
67
|
from renderers.configs import (
|
|
65
68
|
LagunaM1RendererConfig,
|
|
69
|
+
LagunaS21RendererConfig,
|
|
66
70
|
LagunaXS2RendererConfig,
|
|
67
71
|
LagunaXS21RendererConfig,
|
|
68
72
|
)
|
|
@@ -120,6 +124,7 @@ class LagunaXS2Renderer:
|
|
|
120
124
|
LagunaXS2RendererConfig
|
|
121
125
|
| LagunaM1RendererConfig
|
|
122
126
|
| LagunaXS21RendererConfig
|
|
127
|
+
| LagunaS21RendererConfig
|
|
123
128
|
| None
|
|
124
129
|
) = None,
|
|
125
130
|
):
|
|
@@ -688,10 +693,17 @@ class LagunaXS21Renderer(LagunaXS2Renderer):
|
|
|
688
693
|
def __init__(
|
|
689
694
|
self,
|
|
690
695
|
tokenizer: PreTrainedTokenizer,
|
|
691
|
-
config: LagunaXS21RendererConfig | None = None,
|
|
696
|
+
config: LagunaXS21RendererConfig | LagunaS21RendererConfig | None = None,
|
|
692
697
|
):
|
|
693
698
|
super().__init__(tokenizer, config or LagunaXS21RendererConfig())
|
|
694
699
|
|
|
700
|
+
def _render_history_reasoning(self) -> bool:
|
|
701
|
+
"""Whether an assistant turn renders its ``<think>{reasoning}</think>``
|
|
702
|
+
block (vs opening with a bare ``</think>``). Mirrors the template's
|
|
703
|
+
reasoning-display gate; XS-2.1 gates this on ``enable_thinking`` alone.
|
|
704
|
+
"""
|
|
705
|
+
return self.config.enable_thinking
|
|
706
|
+
|
|
695
707
|
def render(
|
|
696
708
|
self,
|
|
697
709
|
messages: list[Message],
|
|
@@ -975,7 +987,7 @@ class LagunaXS21Renderer(LagunaXS2Renderer):
|
|
|
975
987
|
# scaffolding the model never samples.
|
|
976
988
|
emit_special(self._assistant, msg_idx, is_sampled=False, is_content=False)
|
|
977
989
|
|
|
978
|
-
if self.
|
|
990
|
+
if self._render_history_reasoning():
|
|
979
991
|
# ``<think>{reasoning}</think>`` renders verbatim, even when
|
|
980
992
|
# the reasoning is empty; the opener is the gen-prompt
|
|
981
993
|
# prefill, the rest the model sampled.
|
|
@@ -44,6 +44,10 @@ RENDERER_MODELS = [
|
|
|
44
44
|
# XS-2.1 resolves to `LagunaXS21Renderer` via the model name — its
|
|
45
45
|
# chat template differs from XS.2's (see renderers/laguna_xs2.py).
|
|
46
46
|
("poolside/Laguna-XS-2.1", "auto"),
|
|
47
|
+
# S-2.1 is a larger sibling of XS-2.1 sharing its tokenizer but NOT its
|
|
48
|
+
# chat template (S-2.1 defaults enable_thinking=True and adds a
|
|
49
|
+
# preserve_thinking gate), so it resolves to LagunaS21Renderer.
|
|
50
|
+
("poolside/Laguna-S-2.1", "auto"),
|
|
47
51
|
# DeepSeek-V3/R1 are intentionally NOT in this shared barrage: their
|
|
48
52
|
# chat templates can't render the barrage's tool-call fixtures (the
|
|
49
53
|
# templates require ``tool['type']`` and a string-serialized
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Laguna-S-2.1 focused tests.
|
|
2
|
+
|
|
3
|
+
S-2.1 shares XS-2.1's tokenizer and token format, so the shared matrices
|
|
4
|
+
(conftest / config-parity) already assert byte parity on the common shapes
|
|
5
|
+
via :class:`LagunaS21Renderer`. This file pins the two behaviours that make
|
|
6
|
+
S-2.1 *differ* from XS-2.1 — and that the shared barrage can't reach because
|
|
7
|
+
it never sets the relevant kwargs:
|
|
8
|
+
|
|
9
|
+
- ``enable_thinking`` defaults to ``True`` (XS-2.1 defaults ``False``), so the
|
|
10
|
+
auto-resolved renderer renders reasoning by default, and
|
|
11
|
+
- the new ``preserve_thinking`` kwarg keeps historical ``<think>`` blocks even
|
|
12
|
+
while ``enable_thinking`` is ``False`` — the template gates reasoning display
|
|
13
|
+
on ``enable_thinking or preserve_thinking``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from functools import lru_cache
|
|
19
|
+
from itertools import product
|
|
20
|
+
|
|
21
|
+
from renderers import create_renderer
|
|
22
|
+
from renderers.base import load_tokenizer
|
|
23
|
+
from renderers.configs import LagunaS21RendererConfig
|
|
24
|
+
from renderers.laguna_s21 import LagunaS21Renderer
|
|
25
|
+
|
|
26
|
+
_MODEL = "poolside/Laguna-S-2.1"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@lru_cache(maxsize=None)
|
|
30
|
+
def _tok():
|
|
31
|
+
return load_tokenizer(_MODEL)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _renderer(**config_kwargs) -> LagunaS21Renderer:
|
|
35
|
+
renderer = create_renderer(_tok(), LagunaS21RendererConfig(**config_kwargs))
|
|
36
|
+
assert isinstance(renderer, LagunaS21Renderer)
|
|
37
|
+
return renderer
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _expected(msgs, *, add_generation_prompt=False, **template_kwargs):
|
|
41
|
+
return list(
|
|
42
|
+
_tok().apply_chat_template(
|
|
43
|
+
msgs,
|
|
44
|
+
add_generation_prompt=add_generation_prompt,
|
|
45
|
+
tokenize=True,
|
|
46
|
+
return_dict=False,
|
|
47
|
+
**template_kwargs,
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_auto_resolves_to_s21_renderer_with_thinking_on_by_default():
|
|
53
|
+
"""``poolside/Laguna-S-2.1`` resolves to :class:`LagunaS21Renderer`, and
|
|
54
|
+
its config defaults ``enable_thinking=True`` / ``preserve_thinking=False``
|
|
55
|
+
— matching S-2.1's template defaults (unlike XS-2.1's thinking-off)."""
|
|
56
|
+
r = create_renderer(_tok()) # auto-resolve via MODEL_RENDERER_MAP
|
|
57
|
+
assert isinstance(r, LagunaS21Renderer)
|
|
58
|
+
assert r.config.name == "laguna-s-2.1"
|
|
59
|
+
assert r.config.enable_thinking is True
|
|
60
|
+
assert r.config.preserve_thinking is False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_default_renders_empty_think_wrapper():
|
|
64
|
+
"""With thinking on by default, a reasoning-free assistant turn still
|
|
65
|
+
opens with the empty ``<think></think>`` wrapper (this is exactly the
|
|
66
|
+
render_ids divergence from the XS-2.1 renderer)."""
|
|
67
|
+
msgs = [
|
|
68
|
+
{"role": "user", "content": "What is 2+2?"},
|
|
69
|
+
{"role": "assistant", "content": "4"},
|
|
70
|
+
]
|
|
71
|
+
r = _renderer() # defaults: enable_thinking=True
|
|
72
|
+
ours = r.render_ids(msgs)
|
|
73
|
+
assert ours == _expected(msgs)
|
|
74
|
+
assert "<assistant><think></think>4</assistant>" in _tok().decode(ours)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_preserve_thinking_keeps_history_when_thinking_off():
|
|
78
|
+
"""``enable_thinking=False`` alone drops reasoning, but with
|
|
79
|
+
``preserve_thinking=True`` the historical ``<think>{reasoning}</think>``
|
|
80
|
+
survives — matching the template's ``enable_thinking or preserve_thinking``
|
|
81
|
+
gate."""
|
|
82
|
+
msgs = [
|
|
83
|
+
{"role": "user", "content": "What is 2+2?"},
|
|
84
|
+
{"role": "assistant", "reasoning_content": "Simple arithmetic", "content": "4"},
|
|
85
|
+
]
|
|
86
|
+
r = _renderer(enable_thinking=False, preserve_thinking=True)
|
|
87
|
+
ours = r.render_ids(msgs)
|
|
88
|
+
assert ours == _expected(msgs, enable_thinking=False, preserve_thinking=True)
|
|
89
|
+
assert "<think>Simple arithmetic</think>" in _tok().decode(ours)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_no_preserve_thinking_drops_history_when_thinking_off():
|
|
93
|
+
"""With both flags off the gate collapses to ``enable_thinking`` and the
|
|
94
|
+
turn opens with a bare ``</think>``, dropping the reasoning — identical to
|
|
95
|
+
the XS-2.1 renderer's default."""
|
|
96
|
+
msgs = [
|
|
97
|
+
{"role": "user", "content": "What is 2+2?"},
|
|
98
|
+
{"role": "assistant", "reasoning_content": "Simple arithmetic", "content": "4"},
|
|
99
|
+
]
|
|
100
|
+
r = _renderer(enable_thinking=False, preserve_thinking=False)
|
|
101
|
+
ours = r.render_ids(msgs)
|
|
102
|
+
assert ours == _expected(msgs, enable_thinking=False, preserve_thinking=False)
|
|
103
|
+
assert "Simple arithmetic" not in _tok().decode(ours)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_all_thinking_flag_combos_match_template():
|
|
107
|
+
"""Byte parity against ``apply_chat_template`` for every
|
|
108
|
+
``enable_thinking`` × ``preserve_thinking`` combination, over a multi-turn
|
|
109
|
+
conversation carrying historical reasoning."""
|
|
110
|
+
msgs = [
|
|
111
|
+
{"role": "user", "content": "Q1"},
|
|
112
|
+
{"role": "assistant", "reasoning_content": "deduce", "content": "A1"},
|
|
113
|
+
{"role": "user", "content": "Q2"},
|
|
114
|
+
{"role": "assistant", "reasoning_content": "more", "content": "A2"},
|
|
115
|
+
]
|
|
116
|
+
for enable_thinking, preserve_thinking in product((False, True), repeat=2):
|
|
117
|
+
r = _renderer(
|
|
118
|
+
enable_thinking=enable_thinking, preserve_thinking=preserve_thinking
|
|
119
|
+
)
|
|
120
|
+
for add_generation_prompt in (False, True):
|
|
121
|
+
assert r.render_ids(
|
|
122
|
+
msgs, add_generation_prompt=add_generation_prompt
|
|
123
|
+
) == _expected(
|
|
124
|
+
msgs,
|
|
125
|
+
add_generation_prompt=add_generation_prompt,
|
|
126
|
+
enable_thinking=enable_thinking,
|
|
127
|
+
preserve_thinking=preserve_thinking,
|
|
128
|
+
), (enable_thinking, preserve_thinking, add_generation_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
|
{renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/examples/sglang/multiturn_generate_sglang.py
RENAMED
|
File without changes
|
|
File without changes
|
{renderers-0.1.10.dev4 → renderers-0.1.10.dev10}/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
|