renderers 0.1.8.dev55__tar.gz → 0.1.8.dev57__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.
Files changed (74) hide show
  1. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/PKG-INFO +2 -2
  2. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/README.md +1 -1
  3. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/docs/renderer-config.md +4 -0
  4. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/__init__.py +8 -0
  5. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/_version.py +2 -2
  6. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/base.py +14 -0
  7. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/configs.py +76 -0
  8. renderers-0.1.8.dev57/renderers/hy3.py +706 -0
  9. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/parsing.py +199 -0
  10. renderers-0.1.8.dev57/renderers/prime_qwen3.py +694 -0
  11. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/conftest.py +2 -1
  12. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_bridge.py +3 -1
  13. renderers-0.1.8.dev57/tests/test_hy3.py +486 -0
  14. renderers-0.1.8.dev57/tests/test_prime_qwen3_parity.py +196 -0
  15. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_renderer_config_parity.py +34 -1
  16. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_roundtrip.py +2 -1
  17. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_tool_arg_type_preservation.py +1 -0
  18. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/.github/workflows/publish-dev.yml +0 -0
  19. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/.github/workflows/publish.yml +0 -0
  20. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/.github/workflows/style.yml +0 -0
  21. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/.github/workflows/test.yml +0 -0
  22. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/.gitignore +0 -0
  23. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/.pre-commit-config.yaml +0 -0
  24. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/LICENSE +0 -0
  25. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/examples/README.md +0 -0
  26. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/examples/sglang/multiturn_generate_sglang.py +0 -0
  27. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/examples/sglang/online_multiturn_sglang.py +0 -0
  28. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/examples/tinker/multiturn_generate_tinker.py +0 -0
  29. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/examples/transformers/multiturn_generate_transformers.py +0 -0
  30. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/examples/vllm/multiturn_generate_vllm.py +0 -0
  31. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/pyproject.toml +0 -0
  32. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/client.py +0 -0
  33. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/deepseek_r1.py +0 -0
  34. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/deepseek_v3.py +0 -0
  35. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/default.py +0 -0
  36. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/glm45.py +0 -0
  37. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/glm5.py +0 -0
  38. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/gpt_oss.py +0 -0
  39. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/kimi_k2.py +0 -0
  40. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/kimi_k25.py +0 -0
  41. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/laguna_xs2.py +0 -0
  42. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/llama_3.py +0 -0
  43. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/minimax_m2.py +0 -0
  44. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/nemotron3.py +0 -0
  45. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/parsers.py +0 -0
  46. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/qwen3.py +0 -0
  47. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/qwen35.py +0 -0
  48. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/qwen36.py +0 -0
  49. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/renderers/qwen3_vl.py +0 -0
  50. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_build_helpers.py +0 -0
  51. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_client.py +0 -0
  52. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_deepseek_r1.py +0 -0
  53. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_gpt_oss_harmony_parity.py +0 -0
  54. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_incremental.py +0 -0
  55. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_is_content.py +0 -0
  56. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_kimi_k25_tool_schema.py +0 -0
  57. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_laguna_xs21.py +0 -0
  58. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_llama_3.py +0 -0
  59. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_load_tokenizer.py +0 -0
  60. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_message_indices.py +0 -0
  61. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_message_tool_names.py +0 -0
  62. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_multimodal.py +0 -0
  63. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_nemotron3_parity.py +0 -0
  64. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_nemotron3_ultra.py +0 -0
  65. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_parse_response.py +0 -0
  66. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_parse_response_robustness.py +0 -0
  67. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_parsers.py +0 -0
  68. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_preserve_thinking.py +0 -0
  69. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_qwen35_size_coverage.py +0 -0
  70. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_render_ids.py +0 -0
  71. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_renderer_config.py +0 -0
  72. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_sampled_mask.py +0 -0
  73. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/tests/test_tokens_per_message.py +0 -0
  74. {renderers-0.1.8.dev55 → renderers-0.1.8.dev57}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: renderers
3
- Version: 0.1.8.dev55
3
+ Version: 0.1.8.dev57
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`, and `gpt-oss`. 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`, `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.
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`, and `gpt-oss`. 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`, `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.
44
44
 
45
45
  ## API
46
46
 
@@ -26,12 +26,14 @@ chat-template kwargs. Those fields are covered by parity tests against
26
26
  | Renderer | Config class | Template fields | Renderer-only fields |
27
27
  | --- | --- | --- | --- |
28
28
  | Qwen3 | `Qwen3RendererConfig` | `enable_thinking` | - |
29
+ | PrimeIntellect Qwen3 | `PrimeQwen3RendererConfig` | - | - |
29
30
  | Qwen3.5 | `Qwen35RendererConfig` | `enable_thinking`, `add_vision_id` | `image_cache_max` |
30
31
  | Qwen3.6 | `Qwen36RendererConfig` | `enable_thinking`, `add_vision_id`, `preserve_thinking` | `image_cache_max` |
31
32
  | Qwen3-VL | `Qwen3VLRendererConfig` | `add_vision_id` | `image_cache_max` |
32
33
  | GLM-5 / 5.1 | `GLM5RendererConfig` / `GLM51RendererConfig` | `enable_thinking`, `clear_thinking` | - |
33
34
  | GLM-4.5 | `GLM45RendererConfig` | `enable_thinking` | - |
34
35
  | gpt-oss | `GptOssRendererConfig` | `reasoning_effort`, `conversation_start_date` | `use_system_prompt`, `knowledge_cutoff`, `model_identity`, `auto_drop_analysis` |
36
+ | Hy3 | `Hy3RendererConfig` | `reasoning_effort`, `preserved_thinking`, `is_training`, `raw_last_assistant`, `fallback_strategy` | - |
35
37
  | Kimi K2 | `KimiK2RendererConfig` | - | `enable_thinking` |
36
38
  | Kimi K2.5 / 2.6 | `KimiK25RendererConfig` | `thinking` | `image_cache_max` |
37
39
  | Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
@@ -129,11 +131,13 @@ the knobs its template actually exposes:
129
131
  | GLM-5 / 5.1 | `clear_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
130
132
  | GLM-4.5 | `enable_thinking=False -> all`, else `tool_cycle` |
131
133
  | gpt-oss | `auto_drop_analysis=False -> all`, else `tool_cycle` |
134
+ | Hy3 | `preserved_thinking=True -> all`, else `tool_cycle` |
132
135
  | Kimi K2.5 / 2.6 | `thinking=False -> all`, else `tool_cycle` |
133
136
  | Nemotron-3 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
134
137
  | DeepSeek R1 | `template` |
135
138
  | MiniMax M2 | `tool_cycle` |
136
139
  | DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / XS-2.1, Llama 3 | `all` |
140
+ | PrimeIntellect Qwen3 | `all` |
137
141
 
138
142
  Config construction raises when an explicit template knob directly contradicts
139
143
  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,
@@ -59,6 +60,7 @@ from renderers.configs import (
59
60
  MiniMaxM2RendererConfig,
60
61
  Nemotron3RendererConfig,
61
62
  Nemotron3UltraRendererConfig,
63
+ PrimeQwen3RendererConfig,
62
64
  Qwen35RendererConfig,
63
65
  Qwen36RendererConfig,
64
66
  Qwen3RendererConfig,
@@ -85,6 +87,7 @@ _LAZY_RENDERERS: dict[str, str] = {
85
87
  "GLM51Renderer": "renderers.glm5",
86
88
  "GLM5Renderer": "renderers.glm5",
87
89
  "GptOssRenderer": "renderers.gpt_oss",
90
+ "Hy3Renderer": "renderers.hy3",
88
91
  "KimiK25Renderer": "renderers.kimi_k25",
89
92
  "KimiK2Renderer": "renderers.kimi_k2",
90
93
  "LagunaXS21Renderer": "renderers.laguna_xs2",
@@ -93,6 +96,7 @@ _LAZY_RENDERERS: dict[str, str] = {
93
96
  "MiniMaxM2Renderer": "renderers.minimax_m2",
94
97
  "Nemotron3Renderer": "renderers.nemotron3",
95
98
  "Nemotron3UltraRenderer": "renderers.nemotron3",
99
+ "PrimeQwen3Renderer": "renderers.prime_qwen3",
96
100
  "Qwen35Renderer": "renderers.qwen35",
97
101
  "Qwen36Renderer": "renderers.qwen36",
98
102
  "Qwen3Renderer": "renderers.qwen3",
@@ -134,6 +138,8 @@ __all__ = [
134
138
  "GLM5RendererConfig",
135
139
  "GptOssRenderer",
136
140
  "GptOssRendererConfig",
141
+ "Hy3Renderer",
142
+ "Hy3RendererConfig",
137
143
  "ImagePart",
138
144
  "KimiK25Renderer",
139
145
  "KimiK25RendererConfig",
@@ -159,6 +165,8 @@ __all__ = [
159
165
  "ParsedResponse",
160
166
  "ParsedToolCall",
161
167
  "PlaceholderRange",
168
+ "PrimeQwen3Renderer",
169
+ "PrimeQwen3RendererConfig",
162
170
  "Qwen35Renderer",
163
171
  "Qwen35RendererConfig",
164
172
  "Qwen36Renderer",
@@ -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.dev55'
22
- __version_tuple__ = version_tuple = (0, 1, 8, 'dev55')
21
+ __version__ = version = '0.1.8.dev57'
22
+ __version_tuple__ = version_tuple = (0, 1, 8, 'dev57')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -999,6 +999,10 @@ MODEL_RENDERER_MAP: dict[str, str] = {
999
999
  "Qwen/Qwen3-30B-A3B-Instruct-2507": "qwen3",
1000
1000
  "Qwen/Qwen3-30B-A3B-Thinking-2507": "qwen3",
1001
1001
  "Qwen/Qwen3-235B-A22B": "qwen3",
1002
+ # PrimeIntellect Qwen3 — both sizes share the same Qwen3-Coder-style
1003
+ # template with XML tool definitions and calls.
1004
+ "PrimeIntellect/Qwen3-0.6B": "prime-qwen3",
1005
+ "PrimeIntellect/Qwen3-1.7B": "prime-qwen3",
1002
1006
  # Qwen3.5. All seven sizes share the same renderer. The 4B / 9B /
1003
1007
  # 35B-A3B / 122B-A10B / 397B-A17B chat template defaults
1004
1008
  # ``enable_thinking=true`` (open ``<think>\n`` at the gen prompt);
@@ -1067,6 +1071,12 @@ MODEL_RENDERER_MAP: dict[str, str] = {
1067
1071
  # GPT-OSS.
1068
1072
  "openai/gpt-oss-20b": "gpt-oss",
1069
1073
  "openai/gpt-oss-120b": "gpt-oss",
1074
+ # Tencent Hunyuan Hy3 (295B-A21B MoE). The FP8 checkpoint shares the same
1075
+ # tokenizer and chat template. Hy3-preview is deliberately unmapped: it
1076
+ # ships an older, incompatible template (un-suffixed special tokens,
1077
+ # ``interleaved_thinking`` instead of ``preserved_thinking``).
1078
+ "tencent/Hy3": "hy3",
1079
+ "tencent/Hy3-FP8": "hy3",
1070
1080
  }
1071
1081
 
1072
1082
 
@@ -1302,12 +1312,14 @@ def _populate_registry():
1302
1312
  from renderers.glm5 import GLM5Renderer, GLM51Renderer
1303
1313
  from renderers.glm45 import GLM45Renderer
1304
1314
  from renderers.gpt_oss import GptOssRenderer
1315
+ from renderers.hy3 import Hy3Renderer
1305
1316
  from renderers.kimi_k2 import KimiK2Renderer
1306
1317
  from renderers.kimi_k25 import KimiK25Renderer
1307
1318
  from renderers.laguna_xs2 import LagunaXS2Renderer, LagunaXS21Renderer
1308
1319
  from renderers.llama_3 import Llama3Renderer
1309
1320
  from renderers.minimax_m2 import MiniMaxM2Renderer
1310
1321
  from renderers.nemotron3 import Nemotron3Renderer, Nemotron3UltraRenderer
1322
+ from renderers.prime_qwen3 import PrimeQwen3Renderer
1311
1323
  from renderers.qwen3 import Qwen3Renderer
1312
1324
  from renderers.qwen3_vl import Qwen3VLRenderer
1313
1325
  from renderers.qwen35 import Qwen35Renderer
@@ -1317,6 +1329,7 @@ def _populate_registry():
1317
1329
  {
1318
1330
  "default": DefaultRenderer,
1319
1331
  "qwen3": Qwen3Renderer,
1332
+ "prime-qwen3": PrimeQwen3Renderer,
1320
1333
  "qwen3-vl": Qwen3VLRenderer,
1321
1334
  "qwen3.5": Qwen35Renderer,
1322
1335
  "qwen3.6": Qwen36Renderer,
@@ -1326,6 +1339,7 @@ def _populate_registry():
1326
1339
  "minimax-m2": MiniMaxM2Renderer,
1327
1340
  "deepseek-v3": DeepSeekV3Renderer,
1328
1341
  "deepseek-r1": DeepSeekR1Renderer,
1342
+ "hy3": Hy3Renderer,
1329
1343
  "kimi-k2": KimiK2Renderer,
1330
1344
  "kimi-k2.5": KimiK25Renderer,
1331
1345
  "laguna-xs.2": LagunaXS2Renderer,
@@ -181,6 +181,12 @@ class Qwen3RendererConfig(BaseRendererConfig):
181
181
  ``enable_thinking`` kwarg."""
182
182
 
183
183
 
184
+ class PrimeQwen3RendererConfig(BaseRendererConfig):
185
+ """PrimeIntellect Qwen3 renderer config."""
186
+
187
+ name: Literal["prime-qwen3"] = "prime-qwen3"
188
+
189
+
184
190
  class Qwen35RendererConfig(BaseRendererConfig):
185
191
  """Qwen3.5 renderer config."""
186
192
 
@@ -310,6 +316,70 @@ class GLM45RendererConfig(BaseRendererConfig):
310
316
  the chat template's ``enable_thinking`` kwarg."""
311
317
 
312
318
 
319
+ class Hy3RendererConfig(BaseRendererConfig):
320
+ """Tencent Hunyuan Hy3 renderer config.
321
+
322
+ Hy3 reasons via a ``reasoning_effort`` gate rather than a boolean
323
+ ``enable_thinking``. ``"no_think"`` (the template default) prefills an
324
+ empty ``<think></think>`` at the generation prompt so the model answers
325
+ directly; ``"low"`` / ``"high"`` prefill only the ``<think>`` opener so
326
+ the model streams reasoning up to a ``</think>`` it emits itself.
327
+
328
+ ``preserved_thinking`` mirrors the template kwarg of the same name:
329
+ ``True`` keeps ``<think>{reasoning}</think>`` on every historical
330
+ assistant turn; ``False`` collapses past-cycle reasoning to
331
+ ``<think></think>``, keeping it only on the in-flight turn (after the
332
+ last user query). ``None`` (default) follows the template's own
333
+ default — ``True`` when ``tools`` are supplied at render time, ``False``
334
+ otherwise. Bridge policy tracks the same resolution: ``"all"`` whenever
335
+ ``preserved_thinking`` resolves to ``True`` for the tools at hand,
336
+ ``"tool_cycle"`` otherwise.
337
+ """
338
+
339
+ name: Literal["hy3"] = "hy3"
340
+
341
+ reasoning_effort: Literal["no_think", "low", "high"] = "no_think"
342
+ """Reasoning gate. Mirrors the chat template's ``reasoning_effort`` kwarg.
343
+ ``"no_think"`` prefills ``<think></think>`` at the generation prompt;
344
+ ``"low"`` / ``"high"`` prefill just ``<think>``."""
345
+
346
+ preserved_thinking: bool | None = None
347
+ """Keep historical assistant reasoning. Mirrors the template's
348
+ ``preserved_thinking`` kwarg. ``None`` defers to the template default
349
+ (``True`` with tools, ``False`` without). Bridge policy is ``"all"``
350
+ whenever this resolves to ``True`` for the tools at hand."""
351
+
352
+ is_training: bool = False
353
+ """Mirrors the template's ``is_training`` kwarg. ``True`` renders SFT
354
+ targets: reasoning is kept on every assistant turn (regardless of
355
+ ``preserved_thinking`` / position) and the final assistant is terminated
356
+ with ``<|hy_eos|>``. Leave ``False`` for inference-faithful renders;
357
+ the training loss mask is normally derived via ``build_training_sample``
358
+ rather than this flag."""
359
+
360
+ raw_last_assistant: bool = False
361
+ """Mirrors the template's ``raw_last_assistant`` kwarg. When ``True`` a
362
+ trailing non-tool assistant message is emitted as raw visible content —
363
+ no ``<think>`` wrap, no ``<|hy_eos|>`` — for prefill / continuation."""
364
+
365
+ fallback_strategy: Literal["reasoning_toolcall_retry"] | None = None
366
+ """Mirrors the template's ``fallback_strategy`` kwarg. The sole active
367
+ value, ``"reasoning_toolcall_retry"``, forces ``reasoning_effort="high"``
368
+ and suppresses the generation prompt (``add_generation_prompt=False``)."""
369
+
370
+ @model_validator(mode="after")
371
+ def _check_thinking_retention(self):
372
+ if self.preserved_thinking is not None and self.thinking_retention is not None:
373
+ implied = "all" if self.preserved_thinking else "tool_cycle"
374
+ if self.thinking_retention != implied:
375
+ raise ValueError(
376
+ f"preserved_thinking={self.preserved_thinking!r} implies "
377
+ f"thinking_retention={implied!r}, which conflicts with "
378
+ f"explicit thinking_retention={self.thinking_retention!r}."
379
+ )
380
+ return self
381
+
382
+
313
383
  class GptOssRendererConfig(BaseRendererConfig):
314
384
  """OpenAI gpt-oss (harmony) renderer config.
315
385
 
@@ -585,6 +655,7 @@ RendererConfig = Annotated[
585
655
  AutoRendererConfig,
586
656
  DefaultRendererConfig,
587
657
  Qwen3RendererConfig,
658
+ PrimeQwen3RendererConfig,
588
659
  Qwen35RendererConfig,
589
660
  Qwen36RendererConfig,
590
661
  Qwen3VLRendererConfig,
@@ -592,6 +663,7 @@ RendererConfig = Annotated[
592
663
  GLM51RendererConfig,
593
664
  GLM45RendererConfig,
594
665
  GptOssRendererConfig,
666
+ Hy3RendererConfig,
595
667
  KimiK2RendererConfig,
596
668
  KimiK25RendererConfig,
597
669
  LagunaXS2RendererConfig,
@@ -623,6 +695,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
623
695
  "auto": AutoRendererConfig,
624
696
  "default": DefaultRendererConfig,
625
697
  "qwen3": Qwen3RendererConfig,
698
+ "prime-qwen3": PrimeQwen3RendererConfig,
626
699
  "qwen3.5": Qwen35RendererConfig,
627
700
  "qwen3.6": Qwen36RendererConfig,
628
701
  "qwen3-vl": Qwen3VLRendererConfig,
@@ -630,6 +703,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
630
703
  "glm-5.1": GLM51RendererConfig,
631
704
  "glm-4.5": GLM45RendererConfig,
632
705
  "gpt-oss": GptOssRendererConfig,
706
+ "hy3": Hy3RendererConfig,
633
707
  "kimi-k2": KimiK2RendererConfig,
634
708
  "kimi-k2.5": KimiK25RendererConfig,
635
709
  "laguna-xs.2": LagunaXS2RendererConfig,
@@ -677,6 +751,7 @@ __all__ = [
677
751
  "GLM51RendererConfig",
678
752
  "GLM5RendererConfig",
679
753
  "GptOssRendererConfig",
754
+ "Hy3RendererConfig",
680
755
  "KimiK25RendererConfig",
681
756
  "KimiK2RendererConfig",
682
757
  "LagunaXS2RendererConfig",
@@ -685,6 +760,7 @@ __all__ = [
685
760
  "MiniMaxM2RendererConfig",
686
761
  "Nemotron3RendererConfig",
687
762
  "Nemotron3UltraRendererConfig",
763
+ "PrimeQwen3RendererConfig",
688
764
  "Qwen35RendererConfig",
689
765
  "Qwen36RendererConfig",
690
766
  "Qwen3RendererConfig",