renderers 0.1.8.dev55__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.
Files changed (72) hide show
  1. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/PKG-INFO +2 -2
  2. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/README.md +1 -1
  3. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/docs/renderer-config.md +2 -0
  4. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/__init__.py +4 -0
  5. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/_version.py +2 -2
  6. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/base.py +8 -0
  7. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/configs.py +67 -0
  8. renderers-0.1.8.dev56/renderers/hy3.py +706 -0
  9. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/parsing.py +199 -0
  10. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/conftest.py +1 -0
  11. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_bridge.py +2 -0
  12. renderers-0.1.8.dev56/tests/test_hy3.py +486 -0
  13. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_renderer_config_parity.py +34 -1
  14. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_roundtrip.py +1 -0
  15. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/.github/workflows/publish-dev.yml +0 -0
  16. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/.github/workflows/publish.yml +0 -0
  17. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/.github/workflows/style.yml +0 -0
  18. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/.github/workflows/test.yml +0 -0
  19. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/.gitignore +0 -0
  20. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/.pre-commit-config.yaml +0 -0
  21. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/LICENSE +0 -0
  22. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/examples/README.md +0 -0
  23. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/examples/sglang/multiturn_generate_sglang.py +0 -0
  24. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/examples/sglang/online_multiturn_sglang.py +0 -0
  25. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/examples/tinker/multiturn_generate_tinker.py +0 -0
  26. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/examples/transformers/multiturn_generate_transformers.py +0 -0
  27. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/examples/vllm/multiturn_generate_vllm.py +0 -0
  28. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/pyproject.toml +0 -0
  29. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/client.py +0 -0
  30. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/deepseek_r1.py +0 -0
  31. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/deepseek_v3.py +0 -0
  32. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/default.py +0 -0
  33. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/glm45.py +0 -0
  34. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/glm5.py +0 -0
  35. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/gpt_oss.py +0 -0
  36. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/kimi_k2.py +0 -0
  37. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/kimi_k25.py +0 -0
  38. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/laguna_xs2.py +0 -0
  39. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/llama_3.py +0 -0
  40. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/minimax_m2.py +0 -0
  41. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/nemotron3.py +0 -0
  42. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/parsers.py +0 -0
  43. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/qwen3.py +0 -0
  44. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/qwen35.py +0 -0
  45. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/qwen36.py +0 -0
  46. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/renderers/qwen3_vl.py +0 -0
  47. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_build_helpers.py +0 -0
  48. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_client.py +0 -0
  49. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_deepseek_r1.py +0 -0
  50. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_gpt_oss_harmony_parity.py +0 -0
  51. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_incremental.py +0 -0
  52. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_is_content.py +0 -0
  53. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_kimi_k25_tool_schema.py +0 -0
  54. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_laguna_xs21.py +0 -0
  55. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_llama_3.py +0 -0
  56. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_load_tokenizer.py +0 -0
  57. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_message_indices.py +0 -0
  58. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_message_tool_names.py +0 -0
  59. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_multimodal.py +0 -0
  60. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_nemotron3_parity.py +0 -0
  61. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_nemotron3_ultra.py +0 -0
  62. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_parse_response.py +0 -0
  63. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_parse_response_robustness.py +0 -0
  64. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_parsers.py +0 -0
  65. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_preserve_thinking.py +0 -0
  66. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_qwen35_size_coverage.py +0 -0
  67. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_render_ids.py +0 -0
  68. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_renderer_config.py +0 -0
  69. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_sampled_mask.py +0 -0
  70. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_tokens_per_message.py +0 -0
  71. {renderers-0.1.8.dev55 → renderers-0.1.8.dev56}/tests/test_tool_arg_type_preservation.py +0 -0
  72. {renderers-0.1.8.dev55 → 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.dev55
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`, 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`, 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`, 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`, and `hy3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
44
44
 
45
45
  ## API
46
46
 
@@ -32,6 +32,7 @@ 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` | - |
@@ -129,6 +130,7 @@ the knobs its template actually exposes:
129
130
  | GLM-5 / 5.1 | `clear_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
130
131
  | GLM-4.5 | `enable_thinking=False -> all`, else `tool_cycle` |
131
132
  | gpt-oss | `auto_drop_analysis=False -> all`, else `tool_cycle` |
133
+ | Hy3 | `preserved_thinking=True -> all`, else `tool_cycle` |
132
134
  | Kimi K2.5 / 2.6 | `thinking=False -> all`, else `tool_cycle` |
133
135
  | Nemotron-3 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
134
136
  | DeepSeek R1 | `template` |
@@ -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,6 +86,7 @@ _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",
90
92
  "LagunaXS21Renderer": "renderers.laguna_xs2",
@@ -134,6 +136,8 @@ __all__ = [
134
136
  "GLM5RendererConfig",
135
137
  "GptOssRenderer",
136
138
  "GptOssRendererConfig",
139
+ "Hy3Renderer",
140
+ "Hy3RendererConfig",
137
141
  "ImagePart",
138
142
  "KimiK25Renderer",
139
143
  "KimiK25RendererConfig",
@@ -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.dev56'
22
+ __version_tuple__ = version_tuple = (0, 1, 8, 'dev56')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -1067,6 +1067,12 @@ MODEL_RENDERER_MAP: dict[str, str] = {
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,6 +1308,7 @@ 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
1314
  from renderers.laguna_xs2 import LagunaXS2Renderer, LagunaXS21Renderer
@@ -1326,6 +1333,7 @@ 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,
@@ -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
 
@@ -592,6 +656,7 @@ RendererConfig = Annotated[
592
656
  GLM51RendererConfig,
593
657
  GLM45RendererConfig,
594
658
  GptOssRendererConfig,
659
+ Hy3RendererConfig,
595
660
  KimiK2RendererConfig,
596
661
  KimiK25RendererConfig,
597
662
  LagunaXS2RendererConfig,
@@ -630,6 +695,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
630
695
  "glm-5.1": GLM51RendererConfig,
631
696
  "glm-4.5": GLM45RendererConfig,
632
697
  "gpt-oss": GptOssRendererConfig,
698
+ "hy3": Hy3RendererConfig,
633
699
  "kimi-k2": KimiK2RendererConfig,
634
700
  "kimi-k2.5": KimiK25RendererConfig,
635
701
  "laguna-xs.2": LagunaXS2RendererConfig,
@@ -677,6 +743,7 @@ __all__ = [
677
743
  "GLM51RendererConfig",
678
744
  "GLM5RendererConfig",
679
745
  "GptOssRendererConfig",
746
+ "Hy3RendererConfig",
680
747
  "KimiK25RendererConfig",
681
748
  "KimiK2RendererConfig",
682
749
  "LagunaXS2RendererConfig",