renderers 0.1.8.dev54__tar.gz → 0.1.8.dev56__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.dev54 → renderers-0.1.8.dev56}/PKG-INFO +2 -2
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/README.md +1 -1
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/docs/renderer-config.md +4 -1
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/__init__.py +6 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/_version.py +2 -2
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/base.py +37 -5
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/configs.py +78 -13
- renderers-0.1.8.dev56/renderers/hy3.py +706 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/laguna_xs2.py +384 -13
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/parsing.py +226 -16
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/conftest.py +3 -2
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_bridge.py +2 -0
- renderers-0.1.8.dev56/tests/test_hy3.py +486 -0
- renderers-0.1.8.dev56/tests/test_laguna_xs21.py +376 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_renderer_config_parity.py +35 -1
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_roundtrip.py +5 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/.github/workflows/style.yml +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/.github/workflows/test.yml +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/.gitignore +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/LICENSE +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/examples/README.md +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/pyproject.toml +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/client.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/default.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/glm45.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/glm5.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/llama_3.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/nemotron3.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/parsers.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/qwen3.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/qwen35.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/qwen36.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_build_helpers.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_client.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_incremental.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_is_content.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_llama_3.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_message_indices.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_multimodal.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_parse_response.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_parsers.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_render_ids.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.8.dev54 → renderers-0.1.8.dev56}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: renderers
|
|
3
|
-
Version: 0.1.8.
|
|
3
|
+
Version: 0.1.8.dev56
|
|
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`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`,
|
|
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`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`, `gpt-oss`, and `hy3`. 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`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`,
|
|
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`, `nemotron-3`, `nemotron-3-ultra`, `llama-3`, `gpt-oss`, and `hy3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
44
44
|
|
|
45
45
|
## API
|
|
46
46
|
|
|
@@ -32,9 +32,11 @@ chat-template kwargs. Those fields are covered by parity tests against
|
|
|
32
32
|
| GLM-5 / 5.1 | `GLM5RendererConfig` / `GLM51RendererConfig` | `enable_thinking`, `clear_thinking` | - |
|
|
33
33
|
| GLM-4.5 | `GLM45RendererConfig` | `enable_thinking` | - |
|
|
34
34
|
| gpt-oss | `GptOssRendererConfig` | `reasoning_effort`, `conversation_start_date` | `use_system_prompt`, `knowledge_cutoff`, `model_identity`, `auto_drop_analysis` |
|
|
35
|
+
| Hy3 | `Hy3RendererConfig` | `reasoning_effort`, `preserved_thinking`, `is_training`, `raw_last_assistant`, `fallback_strategy` | - |
|
|
35
36
|
| Kimi K2 | `KimiK2RendererConfig` | - | `enable_thinking` |
|
|
36
37
|
| Kimi K2.5 / 2.6 | `KimiK25RendererConfig` | `thinking` | `image_cache_max` |
|
|
37
38
|
| Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
39
|
+
| Laguna XS-2.1 | `LagunaXS21RendererConfig` | `enable_thinking` | - |
|
|
38
40
|
| Llama 3 | `Llama3RendererConfig` | `date_string`, `tools_in_user_message` | - |
|
|
39
41
|
| MiniMax M2 | `MiniMaxM2RendererConfig` | `model_identity` | - |
|
|
40
42
|
| Nemotron-3 Nano / Super | `Nemotron3RendererConfig` | `enable_thinking`, `truncate_history_thinking`, `low_effort` | - |
|
|
@@ -128,11 +130,12 @@ the knobs its template actually exposes:
|
|
|
128
130
|
| GLM-5 / 5.1 | `clear_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
129
131
|
| GLM-4.5 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
130
132
|
| gpt-oss | `auto_drop_analysis=False -> all`, else `tool_cycle` |
|
|
133
|
+
| Hy3 | `preserved_thinking=True -> all`, else `tool_cycle` |
|
|
131
134
|
| Kimi K2.5 / 2.6 | `thinking=False -> all`, else `tool_cycle` |
|
|
132
135
|
| Nemotron-3 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
133
136
|
| DeepSeek R1 | `template` |
|
|
134
137
|
| MiniMax M2 | `tool_cycle` |
|
|
135
|
-
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2, Llama 3 | `all` |
|
|
138
|
+
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / XS-2.1, Llama 3 | `all` |
|
|
136
139
|
|
|
137
140
|
Config construction raises when an explicit template knob directly contradicts
|
|
138
141
|
an explicit generic bridge policy. For example:
|
|
@@ -51,6 +51,7 @@ from renderers.configs import (
|
|
|
51
51
|
GLM51RendererConfig,
|
|
52
52
|
GLM5RendererConfig,
|
|
53
53
|
GptOssRendererConfig,
|
|
54
|
+
Hy3RendererConfig,
|
|
54
55
|
KimiK25RendererConfig,
|
|
55
56
|
KimiK2RendererConfig,
|
|
56
57
|
LagunaXS2RendererConfig,
|
|
@@ -85,8 +86,10 @@ _LAZY_RENDERERS: dict[str, str] = {
|
|
|
85
86
|
"GLM51Renderer": "renderers.glm5",
|
|
86
87
|
"GLM5Renderer": "renderers.glm5",
|
|
87
88
|
"GptOssRenderer": "renderers.gpt_oss",
|
|
89
|
+
"Hy3Renderer": "renderers.hy3",
|
|
88
90
|
"KimiK25Renderer": "renderers.kimi_k25",
|
|
89
91
|
"KimiK2Renderer": "renderers.kimi_k2",
|
|
92
|
+
"LagunaXS21Renderer": "renderers.laguna_xs2",
|
|
90
93
|
"LagunaXS2Renderer": "renderers.laguna_xs2",
|
|
91
94
|
"Llama3Renderer": "renderers.llama_3",
|
|
92
95
|
"MiniMaxM2Renderer": "renderers.minimax_m2",
|
|
@@ -133,6 +136,8 @@ __all__ = [
|
|
|
133
136
|
"GLM5RendererConfig",
|
|
134
137
|
"GptOssRenderer",
|
|
135
138
|
"GptOssRendererConfig",
|
|
139
|
+
"Hy3Renderer",
|
|
140
|
+
"Hy3RendererConfig",
|
|
136
141
|
"ImagePart",
|
|
137
142
|
"KimiK25Renderer",
|
|
138
143
|
"KimiK25RendererConfig",
|
|
@@ -140,6 +145,7 @@ __all__ = [
|
|
|
140
145
|
"KimiK2RendererConfig",
|
|
141
146
|
"LagunaXS2Renderer",
|
|
142
147
|
"LagunaXS2RendererConfig",
|
|
148
|
+
"LagunaXS21Renderer",
|
|
143
149
|
"LagunaXS21RendererConfig",
|
|
144
150
|
"Llama3Renderer",
|
|
145
151
|
"Llama3RendererConfig",
|
|
@@ -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.dev56'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 8, 'dev56')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1060,13 +1060,19 @@ 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.
|
|
1064
|
-
#
|
|
1063
|
+
# Poolside Laguna. The two checkpoints ship different chat templates,
|
|
1064
|
+
# each mirrored by its own renderer class.
|
|
1065
1065
|
"poolside/Laguna-XS.2": "laguna-xs.2",
|
|
1066
1066
|
"poolside/Laguna-XS-2.1": "laguna-xs-2.1",
|
|
1067
1067
|
# GPT-OSS.
|
|
1068
1068
|
"openai/gpt-oss-20b": "gpt-oss",
|
|
1069
1069
|
"openai/gpt-oss-120b": "gpt-oss",
|
|
1070
|
+
# Tencent Hunyuan Hy3 (295B-A21B MoE). The FP8 checkpoint shares the same
|
|
1071
|
+
# tokenizer and chat template. Hy3-preview is deliberately unmapped: it
|
|
1072
|
+
# ships an older, incompatible template (un-suffixed special tokens,
|
|
1073
|
+
# ``interleaved_thinking`` instead of ``preserved_thinking``).
|
|
1074
|
+
"tencent/Hy3": "hy3",
|
|
1075
|
+
"tencent/Hy3-FP8": "hy3",
|
|
1070
1076
|
}
|
|
1071
1077
|
|
|
1072
1078
|
|
|
@@ -1302,9 +1308,10 @@ def _populate_registry():
|
|
|
1302
1308
|
from renderers.glm5 import GLM5Renderer, GLM51Renderer
|
|
1303
1309
|
from renderers.glm45 import GLM45Renderer
|
|
1304
1310
|
from renderers.gpt_oss import GptOssRenderer
|
|
1311
|
+
from renderers.hy3 import Hy3Renderer
|
|
1305
1312
|
from renderers.kimi_k2 import KimiK2Renderer
|
|
1306
1313
|
from renderers.kimi_k25 import KimiK25Renderer
|
|
1307
|
-
from renderers.laguna_xs2 import LagunaXS2Renderer
|
|
1314
|
+
from renderers.laguna_xs2 import LagunaXS2Renderer, LagunaXS21Renderer
|
|
1308
1315
|
from renderers.llama_3 import Llama3Renderer
|
|
1309
1316
|
from renderers.minimax_m2 import MiniMaxM2Renderer
|
|
1310
1317
|
from renderers.nemotron3 import Nemotron3Renderer, Nemotron3UltraRenderer
|
|
@@ -1326,10 +1333,11 @@ def _populate_registry():
|
|
|
1326
1333
|
"minimax-m2": MiniMaxM2Renderer,
|
|
1327
1334
|
"deepseek-v3": DeepSeekV3Renderer,
|
|
1328
1335
|
"deepseek-r1": DeepSeekR1Renderer,
|
|
1336
|
+
"hy3": Hy3Renderer,
|
|
1329
1337
|
"kimi-k2": KimiK2Renderer,
|
|
1330
1338
|
"kimi-k2.5": KimiK25Renderer,
|
|
1331
1339
|
"laguna-xs.2": LagunaXS2Renderer,
|
|
1332
|
-
"laguna-xs-2.1":
|
|
1340
|
+
"laguna-xs-2.1": LagunaXS21Renderer,
|
|
1333
1341
|
"llama-3": Llama3Renderer,
|
|
1334
1342
|
"nemotron-3": Nemotron3Renderer,
|
|
1335
1343
|
"nemotron-3-ultra": Nemotron3UltraRenderer,
|
|
@@ -1764,6 +1772,8 @@ def _get_offset_tokenizer(tokenizer):
|
|
|
1764
1772
|
def attribute_text_segments(
|
|
1765
1773
|
tokenizer,
|
|
1766
1774
|
segments: "list[tuple[str, bool]]",
|
|
1775
|
+
*,
|
|
1776
|
+
overlap_is_content: bool = False,
|
|
1767
1777
|
) -> "list[tuple[int, bool]]":
|
|
1768
1778
|
"""Tokenize concatenated segments as a single BPE pass and return
|
|
1769
1779
|
``(token_id, is_content)`` pairs.
|
|
@@ -1782,6 +1792,16 @@ def attribute_text_segments(
|
|
|
1782
1792
|
tokens (rare; usually pre-tokenizer artefacts) are attributed to
|
|
1783
1793
|
the most recently entered segment.
|
|
1784
1794
|
|
|
1795
|
+
``overlap_is_content=True`` widens the content bit: a token counts
|
|
1796
|
+
as content when *any* of its source characters fall in a content
|
|
1797
|
+
segment, not just its first. Templates whose wrap glues directly
|
|
1798
|
+
onto the body with no whitespace (e.g. ``<user>{content}</user>``)
|
|
1799
|
+
can merge wrap and body bytes into one token; under the first-char
|
|
1800
|
+
policy such a token would land on the wrap side and the body would
|
|
1801
|
+
no longer be recoverable from the content run. Over-inclusion keeps
|
|
1802
|
+
every body byte inside the ``is_content=True`` run at the cost of a
|
|
1803
|
+
few adjacent wrap bytes.
|
|
1804
|
+
|
|
1785
1805
|
Requires a HuggingFace fast tokenizer with offset tracking. Every
|
|
1786
1806
|
model in ``MODEL_RENDERER_MAP`` ships one, so the offset lookup
|
|
1787
1807
|
always succeeds for tokenizers obtained via :func:`load_tokenizer`.
|
|
@@ -1818,13 +1838,25 @@ def attribute_text_segments(
|
|
|
1818
1838
|
|
|
1819
1839
|
out: list[tuple[int, bool]] = []
|
|
1820
1840
|
last_is_content = spans[-1][2] if spans else False
|
|
1821
|
-
for tok_id, (start,
|
|
1841
|
+
for tok_id, (start, end) in zip(token_ids, offsets):
|
|
1822
1842
|
if start >= total_len:
|
|
1823
1843
|
# Token's character offset is past every segment (shouldn't
|
|
1824
1844
|
# normally happen for add_special_tokens=False, but defensive
|
|
1825
1845
|
# against tokenizer-specific edge cases).
|
|
1826
1846
|
out.append((tok_id, last_is_content))
|
|
1827
1847
|
continue
|
|
1848
|
+
if overlap_is_content and end > start:
|
|
1849
|
+
out.append(
|
|
1850
|
+
(
|
|
1851
|
+
tok_id,
|
|
1852
|
+
any(
|
|
1853
|
+
seg_is_content
|
|
1854
|
+
for seg_start, seg_end, seg_is_content in spans
|
|
1855
|
+
if seg_start < end and start < seg_end
|
|
1856
|
+
),
|
|
1857
|
+
)
|
|
1858
|
+
)
|
|
1859
|
+
continue
|
|
1828
1860
|
# Find the segment that contains `start`. Segments are
|
|
1829
1861
|
# contiguous and ordered, so a linear scan is fine — the inner
|
|
1830
1862
|
# loop runs at most len(segments) times per token and segments
|
|
@@ -310,6 +310,70 @@ class GLM45RendererConfig(BaseRendererConfig):
|
|
|
310
310
|
the chat template's ``enable_thinking`` kwarg."""
|
|
311
311
|
|
|
312
312
|
|
|
313
|
+
class Hy3RendererConfig(BaseRendererConfig):
|
|
314
|
+
"""Tencent Hunyuan Hy3 renderer config.
|
|
315
|
+
|
|
316
|
+
Hy3 reasons via a ``reasoning_effort`` gate rather than a boolean
|
|
317
|
+
``enable_thinking``. ``"no_think"`` (the template default) prefills an
|
|
318
|
+
empty ``<think></think>`` at the generation prompt so the model answers
|
|
319
|
+
directly; ``"low"`` / ``"high"`` prefill only the ``<think>`` opener so
|
|
320
|
+
the model streams reasoning up to a ``</think>`` it emits itself.
|
|
321
|
+
|
|
322
|
+
``preserved_thinking`` mirrors the template kwarg of the same name:
|
|
323
|
+
``True`` keeps ``<think>{reasoning}</think>`` on every historical
|
|
324
|
+
assistant turn; ``False`` collapses past-cycle reasoning to
|
|
325
|
+
``<think></think>``, keeping it only on the in-flight turn (after the
|
|
326
|
+
last user query). ``None`` (default) follows the template's own
|
|
327
|
+
default — ``True`` when ``tools`` are supplied at render time, ``False``
|
|
328
|
+
otherwise. Bridge policy tracks the same resolution: ``"all"`` whenever
|
|
329
|
+
``preserved_thinking`` resolves to ``True`` for the tools at hand,
|
|
330
|
+
``"tool_cycle"`` otherwise.
|
|
331
|
+
"""
|
|
332
|
+
|
|
333
|
+
name: Literal["hy3"] = "hy3"
|
|
334
|
+
|
|
335
|
+
reasoning_effort: Literal["no_think", "low", "high"] = "no_think"
|
|
336
|
+
"""Reasoning gate. Mirrors the chat template's ``reasoning_effort`` kwarg.
|
|
337
|
+
``"no_think"`` prefills ``<think></think>`` at the generation prompt;
|
|
338
|
+
``"low"`` / ``"high"`` prefill just ``<think>``."""
|
|
339
|
+
|
|
340
|
+
preserved_thinking: bool | None = None
|
|
341
|
+
"""Keep historical assistant reasoning. Mirrors the template's
|
|
342
|
+
``preserved_thinking`` kwarg. ``None`` defers to the template default
|
|
343
|
+
(``True`` with tools, ``False`` without). Bridge policy is ``"all"``
|
|
344
|
+
whenever this resolves to ``True`` for the tools at hand."""
|
|
345
|
+
|
|
346
|
+
is_training: bool = False
|
|
347
|
+
"""Mirrors the template's ``is_training`` kwarg. ``True`` renders SFT
|
|
348
|
+
targets: reasoning is kept on every assistant turn (regardless of
|
|
349
|
+
``preserved_thinking`` / position) and the final assistant is terminated
|
|
350
|
+
with ``<|hy_eos|>``. Leave ``False`` for inference-faithful renders;
|
|
351
|
+
the training loss mask is normally derived via ``build_training_sample``
|
|
352
|
+
rather than this flag."""
|
|
353
|
+
|
|
354
|
+
raw_last_assistant: bool = False
|
|
355
|
+
"""Mirrors the template's ``raw_last_assistant`` kwarg. When ``True`` a
|
|
356
|
+
trailing non-tool assistant message is emitted as raw visible content —
|
|
357
|
+
no ``<think>`` wrap, no ``<|hy_eos|>`` — for prefill / continuation."""
|
|
358
|
+
|
|
359
|
+
fallback_strategy: Literal["reasoning_toolcall_retry"] | None = None
|
|
360
|
+
"""Mirrors the template's ``fallback_strategy`` kwarg. The sole active
|
|
361
|
+
value, ``"reasoning_toolcall_retry"``, forces ``reasoning_effort="high"``
|
|
362
|
+
and suppresses the generation prompt (``add_generation_prompt=False``)."""
|
|
363
|
+
|
|
364
|
+
@model_validator(mode="after")
|
|
365
|
+
def _check_thinking_retention(self):
|
|
366
|
+
if self.preserved_thinking is not None and self.thinking_retention is not None:
|
|
367
|
+
implied = "all" if self.preserved_thinking else "tool_cycle"
|
|
368
|
+
if self.thinking_retention != implied:
|
|
369
|
+
raise ValueError(
|
|
370
|
+
f"preserved_thinking={self.preserved_thinking!r} implies "
|
|
371
|
+
f"thinking_retention={implied!r}, which conflicts with "
|
|
372
|
+
f"explicit thinking_retention={self.thinking_retention!r}."
|
|
373
|
+
)
|
|
374
|
+
return self
|
|
375
|
+
|
|
376
|
+
|
|
313
377
|
class GptOssRendererConfig(BaseRendererConfig):
|
|
314
378
|
"""OpenAI gpt-oss (harmony) renderer config.
|
|
315
379
|
|
|
@@ -423,24 +487,22 @@ class LagunaXS2RendererConfig(BaseRendererConfig):
|
|
|
423
487
|
|
|
424
488
|
|
|
425
489
|
class LagunaXS21RendererConfig(BaseRendererConfig):
|
|
426
|
-
"""Laguna XS-2.1 renderer config
|
|
427
|
-
|
|
428
|
-
template
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
tools), the ``<system>`` block is omitted entirely. Shares
|
|
433
|
-
:class:`renderers.laguna_xs2.LagunaXS2Renderer`, which selects the
|
|
434
|
-
variant from ``config.name``.
|
|
490
|
+
"""Laguna XS-2.1 renderer config.
|
|
491
|
+
|
|
492
|
+
XS-2.1's chat template reads a single kwarg, ``enable_thinking``,
|
|
493
|
+
which gates both the generation prompt (``<think>`` vs ``</think>``)
|
|
494
|
+
and whether assistant reasoning is rendered into the history at all.
|
|
495
|
+
Served by :class:`renderers.laguna_xs2.LagunaXS21Renderer`.
|
|
435
496
|
"""
|
|
436
497
|
|
|
437
498
|
name: Literal["laguna-xs-2.1"] = "laguna-xs-2.1"
|
|
438
499
|
|
|
439
500
|
enable_thinking: bool = False
|
|
440
|
-
"""
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
501
|
+
"""When ``True``, the generation prompt ends with ``<think>`` and
|
|
502
|
+
every assistant turn renders ``<think>{reasoning}</think>``; when
|
|
503
|
+
``False``, turns open with a bare ``</think>`` and reasoning is not
|
|
504
|
+
rendered. Mirrors the template's ``enable_thinking`` kwarg and its
|
|
505
|
+
upstream default."""
|
|
444
506
|
|
|
445
507
|
|
|
446
508
|
class Llama3RendererConfig(BaseRendererConfig):
|
|
@@ -594,6 +656,7 @@ RendererConfig = Annotated[
|
|
|
594
656
|
GLM51RendererConfig,
|
|
595
657
|
GLM45RendererConfig,
|
|
596
658
|
GptOssRendererConfig,
|
|
659
|
+
Hy3RendererConfig,
|
|
597
660
|
KimiK2RendererConfig,
|
|
598
661
|
KimiK25RendererConfig,
|
|
599
662
|
LagunaXS2RendererConfig,
|
|
@@ -632,6 +695,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
|
|
|
632
695
|
"glm-5.1": GLM51RendererConfig,
|
|
633
696
|
"glm-4.5": GLM45RendererConfig,
|
|
634
697
|
"gpt-oss": GptOssRendererConfig,
|
|
698
|
+
"hy3": Hy3RendererConfig,
|
|
635
699
|
"kimi-k2": KimiK2RendererConfig,
|
|
636
700
|
"kimi-k2.5": KimiK25RendererConfig,
|
|
637
701
|
"laguna-xs.2": LagunaXS2RendererConfig,
|
|
@@ -679,6 +743,7 @@ __all__ = [
|
|
|
679
743
|
"GLM51RendererConfig",
|
|
680
744
|
"GLM5RendererConfig",
|
|
681
745
|
"GptOssRendererConfig",
|
|
746
|
+
"Hy3RendererConfig",
|
|
682
747
|
"KimiK25RendererConfig",
|
|
683
748
|
"KimiK2RendererConfig",
|
|
684
749
|
"LagunaXS2RendererConfig",
|