renderers 0.1.11.dev3__tar.gz → 0.1.11.dev5__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 (92) hide show
  1. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/PKG-INFO +3 -3
  2. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/README.md +2 -2
  3. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/docs/renderer-config.md +4 -2
  4. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/pyproject.toml +1 -1
  5. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/__init__.py +4 -0
  6. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/_version.py +2 -2
  7. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/base.py +5 -0
  8. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/configs.py +44 -0
  9. renderers-0.1.11.dev5/renderers/deepseek_v4.py +859 -0
  10. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/parsing.py +190 -0
  11. renderers-0.1.11.dev5/tests/conftest.py +59 -0
  12. renderers-0.1.11.dev5/tests/parity.py +782 -0
  13. renderers-0.1.11.dev5/tests/reference_rendering.py +668 -0
  14. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_bridge.py +2 -23
  15. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_build_helpers.py +3 -14
  16. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_deepseek_r1.py +3 -70
  17. renderers-0.1.11.dev5/tests/test_deepseek_v4.py +565 -0
  18. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_disabled_thinking_stability.py +17 -33
  19. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_gemma4.py +2 -6
  20. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_glm_tool_name_validation.py +2 -4
  21. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_incremental.py +1 -1
  22. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_laguna_m1.py +0 -78
  23. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_llama_3.py +4 -147
  24. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_message_indices.py +1 -1
  25. renderers-0.1.11.dev5/tests/test_parity.py +222 -0
  26. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_parse_response.py +64 -0
  27. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_qwen35_size_coverage.py +4 -91
  28. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_roundtrip.py +12 -49
  29. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_tool_arg_type_preservation.py +2 -10
  30. renderers-0.1.11.dev3/tests/conftest.py +0 -166
  31. renderers-0.1.11.dev3/tests/test_gpt_oss_harmony_parity.py +0 -271
  32. renderers-0.1.11.dev3/tests/test_laguna_s21.py +0 -128
  33. renderers-0.1.11.dev3/tests/test_nemotron3_parity.py +0 -683
  34. renderers-0.1.11.dev3/tests/test_prime_qwen3_parity.py +0 -254
  35. renderers-0.1.11.dev3/tests/test_render_ids.py +0 -411
  36. renderers-0.1.11.dev3/tests/test_renderer_config_parity.py +0 -606
  37. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/.github/workflows/publish-dev.yml +0 -0
  38. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/.github/workflows/publish.yml +0 -0
  39. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/.github/workflows/style.yml +0 -0
  40. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/.github/workflows/test.yml +0 -0
  41. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/.gitignore +0 -0
  42. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/.pre-commit-config.yaml +0 -0
  43. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/LICENSE +0 -0
  44. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/examples/README.md +0 -0
  45. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/examples/sglang/multiturn_generate_sglang.py +0 -0
  46. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/examples/sglang/online_multiturn_sglang.py +0 -0
  47. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/examples/tinker/multiturn_generate_tinker.py +0 -0
  48. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/examples/transformers/multiturn_generate_transformers.py +0 -0
  49. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/examples/vllm/multiturn_generate_vllm.py +0 -0
  50. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/client.py +0 -0
  51. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/deepseek_r1.py +0 -0
  52. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/deepseek_v3.py +0 -0
  53. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/default.py +0 -0
  54. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/gemma4.py +0 -0
  55. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/glm45.py +0 -0
  56. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/glm5.py +0 -0
  57. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/gpt_oss.py +0 -0
  58. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/hy3.py +0 -0
  59. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/inkling.py +0 -0
  60. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/kimi_k2.py +0 -0
  61. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/kimi_k25.py +0 -0
  62. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/laguna_s21.py +0 -0
  63. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/laguna_xs2.py +0 -0
  64. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/llama_3.py +0 -0
  65. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/minimax_m2.py +0 -0
  66. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/nemotron3.py +0 -0
  67. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/parsers.py +0 -0
  68. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/prime_qwen3.py +0 -0
  69. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/qwen3.py +0 -0
  70. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/qwen35.py +0 -0
  71. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/qwen36.py +0 -0
  72. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/qwen38.py +0 -0
  73. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/renderers/qwen3_vl.py +0 -0
  74. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_client.py +0 -0
  75. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_hy3.py +0 -0
  76. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_inkling.py +0 -0
  77. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_is_content.py +0 -0
  78. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_kimi_k25_tool_schema.py +0 -0
  79. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_laguna_xs21.py +0 -0
  80. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_load_tokenizer.py +0 -0
  81. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_message_tool_names.py +0 -0
  82. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_multimodal.py +0 -0
  83. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_nemotron3_ultra.py +0 -0
  84. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_offsetless_tokenizers.py +0 -0
  85. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_parse_response_robustness.py +0 -0
  86. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_parsers.py +0 -0
  87. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_preserve_thinking.py +0 -0
  88. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_qwen38.py +0 -0
  89. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_renderer_config.py +0 -0
  90. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_sampled_mask.py +0 -0
  91. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/tests/test_tokens_per_message.py +0 -0
  92. {renderers-0.1.11.dev3 → renderers-0.1.11.dev5}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: renderers
3
- Version: 0.1.11.dev3
3
+ Version: 0.1.11.dev5
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
@@ -77,7 +77,7 @@ next_prompt_ids = r.bridge_to_next_turn(
77
77
  )
78
78
  ```
79
79
 
80
- Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `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`, `inkling` / `inkling-small`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `kimi-k2.5` / `kimi-k2.6`, and the Inkling checkpoints are multimodal (Inkling handles both image **and** audio).
80
+ Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `deepseek-v4` (V4 Flash 0731), `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`, `inkling` / `inkling-small`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `kimi-k2.5` / `kimi-k2.6`, and the Inkling checkpoints are multimodal (Inkling handles both image **and** audio).
81
81
 
82
82
  ## API
83
83
 
@@ -199,7 +199,7 @@ uv sync --group dev
199
199
  uv run pytest
200
200
  ```
201
201
 
202
- Round-trip parity (render → parse → original) and token-level parity against `apply_chat_template` are tested per renderer. End-to-end validation runs against Reverse-Text, Wordle, OpenCode-Math, and RLM-SWE environments.
202
+ Round-trip parity (render → parse → original) and token-level parity against each model's independent reference encoder are tested per renderer. Most references use `apply_chat_template`; DeepSeek V4 uses its shipped Python encoder, and GPT-OSS uses Harmony.
203
203
 
204
204
  ## License
205
205
 
@@ -57,7 +57,7 @@ next_prompt_ids = r.bridge_to_next_turn(
57
57
  )
58
58
  ```
59
59
 
60
- Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `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`, `inkling` / `inkling-small`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `kimi-k2.5` / `kimi-k2.6`, and the Inkling checkpoints are multimodal (Inkling handles both image **and** audio).
60
+ Hand-coded renderers ship for `qwen3`, `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `glm-5`, `glm-5.1`, `glm-4.5`, `minimax-m2`, `deepseek-v3`, `deepseek-r1`, `deepseek-v4` (V4 Flash 0731), `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`, `inkling` / `inkling-small`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `qwen3.8`, `gemma4`, `kimi-k2.5` / `kimi-k2.6`, and the Inkling checkpoints are multimodal (Inkling handles both image **and** audio).
61
61
 
62
62
  ## API
63
63
 
@@ -179,7 +179,7 @@ uv sync --group dev
179
179
  uv run pytest
180
180
  ```
181
181
 
182
- Round-trip parity (render → parse → original) and token-level parity against `apply_chat_template` are tested per renderer. End-to-end validation runs against Reverse-Text, Wordle, OpenCode-Math, and RLM-SWE environments.
182
+ Round-trip parity (render → parse → original) and token-level parity against each model's independent reference encoder are tested per renderer. Most references use `apply_chat_template`; DeepSeek V4 uses its shipped Python encoder, and GPT-OSS uses Harmony.
183
183
 
184
184
  ## License
185
185
 
@@ -21,8 +21,8 @@ construction.
21
21
  Use `type(config).template_field_names()` to inspect the explicit allowlist of
22
22
  fields accepted through `chat_template_kwargs`. Every renderer-specific field
23
23
  is classified as either a template field or a renderer-only field at class
24
- definition time. Template fields are covered by parity tests against
25
- `apply_chat_template` in `tests/test_renderer_config_parity.py`.
24
+ definition time. Template fields are covered by the model/scenario/kwarg
25
+ product in `tests/test_parity.py`.
26
26
 
27
27
  | Renderer | Config class | Template fields | Renderer-only fields |
28
28
  | --- | --- | --- | --- |
@@ -51,6 +51,7 @@ definition time. Template fields are covered by parity tests against
51
51
  | Nemotron-3.5 Lightning | `Nemotron35RendererConfig` | `enable_thinking`, `truncate_history_thinking` | - |
52
52
  | DeepSeek V3 | `DeepSeekV3RendererConfig` | - | - |
53
53
  | DeepSeek R1 | `DeepSeekR1RendererConfig` | - | - |
54
+ | DeepSeek V4 Flash 0731 | `DeepSeekV4RendererConfig` | `enable_thinking`, `drop_thinking`, `reasoning_effort` | - |
54
55
 
55
56
  Configs are frozen value objects. To override a field, construct a new instance
56
57
  or call `config.model_copy(update={...})`.
@@ -145,6 +146,7 @@ the knobs its template actually exposes:
145
146
  | Kimi K2.5 / 2.6 | `thinking=False -> all`, else `tool_cycle` |
146
147
  | Nemotron-3 / 3.5 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
147
148
  | DeepSeek R1 | `template` |
149
+ | DeepSeek V4 Flash 0731 | `enable_thinking=False` or `drop_thinking=False -> all`, else `tool_cycle` |
148
150
  | MiniMax M2 | `tool_cycle` |
149
151
  | DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3, Inkling | `all` |
150
152
  | PrimeIntellect Qwen3 | `all` |
@@ -27,7 +27,7 @@ dependencies = [
27
27
  # release through 0.5.12.post1 hard-pins ``openai-harmony==0.0.4``, so a
28
28
  # higher floor makes ``renderers`` uninstallable alongside SGLang. The
29
29
  # GptOssRenderer renders byte-identically on 0.0.4 (verified token-for-token
30
- # against 0.0.8) and ``tests/test_gpt_oss_harmony_parity.py`` passes on it,
30
+ # against 0.0.8) and the Harmony cells in ``tests/test_parity.py`` pass,
31
31
  # so the older harmony is safe.
32
32
  "openai-harmony>=0.0.4",
33
33
  # ``BaseRendererConfig`` inherits from ``pydantic_config.BaseConfig`` so
@@ -48,6 +48,7 @@ from renderers.configs import (
48
48
  DefaultRendererConfig,
49
49
  DeepSeekR1RendererConfig,
50
50
  DeepSeekV3RendererConfig,
51
+ DeepSeekV4RendererConfig,
51
52
  GLM45RendererConfig,
52
53
  GLM51RendererConfig,
53
54
  GLM5RendererConfig,
@@ -85,6 +86,7 @@ from renderers.configs import (
85
86
  _LAZY_RENDERERS: dict[str, str] = {
86
87
  "DeepSeekR1Renderer": "renderers.deepseek_r1",
87
88
  "DeepSeekV3Renderer": "renderers.deepseek_v3",
89
+ "DeepSeekV4Renderer": "renderers.deepseek_v4",
88
90
  "DefaultRenderer": "renderers.default",
89
91
  "GLM45Renderer": "renderers.glm45",
90
92
  "GLM51Renderer": "renderers.glm5",
@@ -138,6 +140,8 @@ __all__ = [
138
140
  "DeepSeekR1RendererConfig",
139
141
  "DeepSeekV3Renderer",
140
142
  "DeepSeekV3RendererConfig",
143
+ "DeepSeekV4Renderer",
144
+ "DeepSeekV4RendererConfig",
141
145
  "DefaultRenderer",
142
146
  "DefaultRendererConfig",
143
147
  "GLM45Renderer",
@@ -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.11.dev3'
22
- __version_tuple__ = version_tuple = (0, 1, 11, 'dev3')
21
+ __version__ = version = '0.1.11.dev5'
22
+ __version_tuple__ = version_tuple = (0, 1, 11, 'dev5')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -982,6 +982,9 @@ MODEL_RENDERER_MAP: dict[str, str] = {
982
982
  # DeepSeek R1 (reasoning).
983
983
  "deepseek-ai/DeepSeek-R1": "deepseek-r1",
984
984
  "deepseek-ai/DeepSeek-R1-0528": "deepseek-r1",
985
+ # DeepSeek V4 Flash 0731 uses the repository's Python DSML encoder (the
986
+ # tokenizer intentionally ships no Jinja chat_template).
987
+ "deepseek-ai/DeepSeek-V4-Flash-0731": "deepseek-v4",
985
988
  # Kimi K2 (K2.5 and K2.6 share the K2.5 template, distinct from K2).
986
989
  "moonshotai/Kimi-K2-Instruct": "kimi-k2",
987
990
  "moonshotai/Kimi-K2.5": "kimi-k2.5",
@@ -1299,6 +1302,7 @@ def _populate_registry():
1299
1302
  return
1300
1303
  from renderers.deepseek_r1 import DeepSeekR1Renderer
1301
1304
  from renderers.deepseek_v3 import DeepSeekV3Renderer
1305
+ from renderers.deepseek_v4 import DeepSeekV4Renderer
1302
1306
  from renderers.default import DefaultRenderer
1303
1307
  from renderers.glm5 import GLM5Renderer, GLM51Renderer
1304
1308
  from renderers.glm45 import GLM45Renderer
@@ -1344,6 +1348,7 @@ def _populate_registry():
1344
1348
  "minimax-m2": MiniMaxM2Renderer,
1345
1349
  "deepseek-v3": DeepSeekV3Renderer,
1346
1350
  "deepseek-r1": DeepSeekR1Renderer,
1351
+ "deepseek-v4": DeepSeekV4Renderer,
1347
1352
  "hy3": Hy3Renderer,
1348
1353
  "inkling": InklingRenderer,
1349
1354
  "kimi-k2": KimiK2Renderer,
@@ -930,6 +930,47 @@ class DeepSeekR1RendererConfig(BaseRendererConfig):
930
930
  _template_fields = frozenset()
931
931
 
932
932
 
933
+ class DeepSeekV4RendererConfig(BaseRendererConfig):
934
+ """DeepSeek-V4-Flash-0731 reference-encoder configuration.
935
+
936
+ The checkpoint ships a Python encoder rather than a Jinja template. These
937
+ fields mirror its public controls: chat vs thinking mode, historical
938
+ reasoning dropping, and the opt-in thinking-effort prefix.
939
+ """
940
+
941
+ name: Literal["deepseek-v4"] = "deepseek-v4"
942
+ _template_fields = frozenset(
943
+ {"enable_thinking", "drop_thinking", "reasoning_effort"}
944
+ )
945
+
946
+ enable_thinking: bool = False
947
+ """Select thinking mode. ``False`` matches the official inference script."""
948
+
949
+ drop_thinking: bool = True
950
+ """Drop reasoning before the latest user query when no tools are present.
951
+
952
+ The reference encoder automatically preserves all reasoning whenever tools
953
+ are supplied, regardless of this value.
954
+ """
955
+
956
+ reasoning_effort: Literal["low", "high", "max"] = "low"
957
+ """Thinking-only effort prefix; ``low`` adds no text.
958
+
959
+ ``low`` is the checkpoint Python encoder's default. DeepSeek's hosted API
960
+ independently defaults its thinking effort to ``high``.
961
+ """
962
+
963
+ @model_validator(mode="after")
964
+ def _check_thinking_retention(self):
965
+ _reject_thinking_retention_conflict(
966
+ self,
967
+ "drop_thinking",
968
+ true_implies="tool_cycle",
969
+ false_implies="all",
970
+ )
971
+ return self
972
+
973
+
933
974
  RendererConfig = Annotated[
934
975
  Union[
935
976
  AutoRendererConfig,
@@ -960,6 +1001,7 @@ RendererConfig = Annotated[
960
1001
  Nemotron35RendererConfig,
961
1002
  DeepSeekV3RendererConfig,
962
1003
  DeepSeekR1RendererConfig,
1004
+ DeepSeekV4RendererConfig,
963
1005
  ],
964
1006
  Field(discriminator="name"),
965
1007
  ]
@@ -1006,6 +1048,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
1006
1048
  "nemotron-3.5": Nemotron35RendererConfig,
1007
1049
  "deepseek-v3": DeepSeekV3RendererConfig,
1008
1050
  "deepseek-r1": DeepSeekR1RendererConfig,
1051
+ "deepseek-v4": DeepSeekV4RendererConfig,
1009
1052
  }
1010
1053
 
1011
1054
 
@@ -1039,6 +1082,7 @@ __all__ = [
1039
1082
  "DefaultRendererConfig",
1040
1083
  "DeepSeekR1RendererConfig",
1041
1084
  "DeepSeekV3RendererConfig",
1085
+ "DeepSeekV4RendererConfig",
1042
1086
  "GLM45RendererConfig",
1043
1087
  "GLM51RendererConfig",
1044
1088
  "GLM5RendererConfig",