renderers 0.1.10.dev4__tar.gz → 0.1.10.dev11__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.dev11}/PKG-INFO +3 -3
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/README.md +2 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/docs/renderer-config.md +4 -1
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/__init__.py +8 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/_version.py +2 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/base.py +19 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/client.py +66 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/configs.py +64 -0
- renderers-0.1.10.dev11/renderers/gemma4.py +1369 -0
- renderers-0.1.10.dev11/renderers/laguna_s21.py +33 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/laguna_xs2.py +14 -2
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/conftest.py +5 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_bridge.py +1 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_build_helpers.py +14 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_client.py +59 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_disabled_thinking_stability.py +10 -2
- renderers-0.1.10.dev11/tests/test_gemma4.py +473 -0
- renderers-0.1.10.dev11/tests/test_laguna_s21.py +128 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_multimodal.py +69 -14
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_render_ids.py +20 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_renderer_config_parity.py +17 -8
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_roundtrip.py +1 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/.github/workflows/style.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/.github/workflows/test.yml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/.gitignore +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/LICENSE +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/examples/README.md +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/pyproject.toml +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/default.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/glm45.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/glm5.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/hy3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/llama_3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/nemotron3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/parsers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/parsing.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/prime_qwen3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/qwen3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/qwen35.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/qwen36.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_glm_tool_name_validation.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_hy3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_incremental.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_is_content.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_laguna_m1.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_laguna_xs21.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_llama_3.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_message_indices.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_parse_response.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_parsers.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_prime_qwen3_parity.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_renderer_config.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/tests/test_tool_arg_type_preservation.py +0 -0
- {renderers-0.1.10.dev4 → renderers-0.1.10.dev11}/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.dev11
|
|
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`, `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`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
60
60
|
|
|
61
61
|
## API
|
|
62
62
|
|
|
@@ -175,7 +175,7 @@ Fallback for unsupported text-only models. Wraps `apply_chat_template` and accep
|
|
|
175
175
|
|
|
176
176
|
## Roadmap
|
|
177
177
|
|
|
178
|
-
- **VLM expansion.** `ImagePart` support exists for Qwen3-VL
|
|
178
|
+
- **VLM expansion.** `ImagePart` support exists for Qwen3-VL, Qwen3.5-family, Gemma 4, and Kimi K2.5 / K2.6 multimodal templates. Remaining work: audio/video support, broader VLM coverage, and more RL validation. Gemma 4 image preprocessing requires a Transformers release that provides `Gemma4Processor`.
|
|
179
179
|
- **Patched chat templates.** Some shipped templates re-tokenize history or normalize JSON in ways that break token identity. Plan: a `use_patched` opt-in per renderer that renders the same surface form while avoiding known-bad patterns. (Auto-stripping thinking from past turns is *not* one of these — that's intended template behaviour the renderer reproduces; use `thinking_retention` to override it.)
|
|
180
180
|
|
|
181
181
|
## Testing
|
|
@@ -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`, `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`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper.
|
|
44
44
|
|
|
45
45
|
## API
|
|
46
46
|
|
|
@@ -159,7 +159,7 @@ Fallback for unsupported text-only models. Wraps `apply_chat_template` and accep
|
|
|
159
159
|
|
|
160
160
|
## Roadmap
|
|
161
161
|
|
|
162
|
-
- **VLM expansion.** `ImagePart` support exists for Qwen3-VL
|
|
162
|
+
- **VLM expansion.** `ImagePart` support exists for Qwen3-VL, Qwen3.5-family, Gemma 4, and Kimi K2.5 / K2.6 multimodal templates. Remaining work: audio/video support, broader VLM coverage, and more RL validation. Gemma 4 image preprocessing requires a Transformers release that provides `Gemma4Processor`.
|
|
163
163
|
- **Patched chat templates.** Some shipped templates re-tokenize history or normalize JSON in ways that break token identity. Plan: a `use_patched` opt-in per renderer that renders the same surface form while avoiding known-bad patterns. (Auto-stripping thinking from past turns is *not* one of these — that's intended template behaviour the renderer reproduces; use `thinking_retention` to override it.)
|
|
164
164
|
|
|
165
165
|
## Testing
|
|
@@ -30,6 +30,7 @@ chat-template kwargs. Those fields are covered by parity tests against
|
|
|
30
30
|
| Qwen3.5 | `Qwen35RendererConfig` | `enable_thinking`, `add_vision_id` | `image_cache_max` |
|
|
31
31
|
| Qwen3.6 | `Qwen36RendererConfig` | `enable_thinking`, `add_vision_id`, `preserve_thinking` | `image_cache_max` |
|
|
32
32
|
| Qwen3-VL | `Qwen3VLRendererConfig` | `add_vision_id` | `image_cache_max` |
|
|
33
|
+
| Gemma 4 | `Gemma4RendererConfig` | `enable_thinking`, `preserve_thinking` | `image_cache_max` |
|
|
33
34
|
| GLM-5 / 5.1 | `GLM5RendererConfig` / `GLM51RendererConfig` | `enable_thinking`, `clear_thinking` | - |
|
|
34
35
|
| GLM-4.5 | `GLM45RendererConfig` | `enable_thinking` | - |
|
|
35
36
|
| gpt-oss | `GptOssRendererConfig` | `reasoning_effort`, `conversation_start_date` | `use_system_prompt`, `knowledge_cutoff`, `model_identity`, `auto_drop_analysis` |
|
|
@@ -39,6 +40,7 @@ chat-template kwargs. Those fields are covered by parity tests against
|
|
|
39
40
|
| Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
40
41
|
| Laguna M.1 | `LagunaM1RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
41
42
|
| Laguna XS-2.1 | `LagunaXS21RendererConfig` | `enable_thinking` | - |
|
|
43
|
+
| Laguna S-2.1 | `LagunaS21RendererConfig` | `enable_thinking`, `preserve_thinking` | - |
|
|
42
44
|
| Llama 3 | `Llama3RendererConfig` | `date_string`, `tools_in_user_message` | - |
|
|
43
45
|
| MiniMax M2 | `MiniMaxM2RendererConfig` | `model_identity` | - |
|
|
44
46
|
| Nemotron-3 Nano / Super | `Nemotron3RendererConfig` | `enable_thinking`, `truncate_history_thinking`, `low_effort` | - |
|
|
@@ -130,6 +132,7 @@ the knobs its template actually exposes:
|
|
|
130
132
|
| Qwen3 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
131
133
|
| Qwen3.5 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
132
134
|
| Qwen3.6 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
135
|
+
| Gemma 4 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
133
136
|
| GLM-5 / 5.1 | `clear_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
134
137
|
| GLM-4.5 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
135
138
|
| gpt-oss | `auto_drop_analysis=False -> all`, else `tool_cycle` |
|
|
@@ -138,7 +141,7 @@ the knobs its template actually exposes:
|
|
|
138
141
|
| Nemotron-3 / 3.5 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
139
142
|
| DeepSeek R1 | `template` |
|
|
140
143
|
| MiniMax M2 | `tool_cycle` |
|
|
141
|
-
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1, Llama 3 | `all` |
|
|
144
|
+
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3 | `all` |
|
|
142
145
|
| PrimeIntellect Qwen3 | `all` |
|
|
143
146
|
|
|
144
147
|
Config construction raises when an explicit template knob directly contradicts
|
|
@@ -50,11 +50,13 @@ from renderers.configs import (
|
|
|
50
50
|
GLM45RendererConfig,
|
|
51
51
|
GLM51RendererConfig,
|
|
52
52
|
GLM5RendererConfig,
|
|
53
|
+
Gemma4RendererConfig,
|
|
53
54
|
GptOssRendererConfig,
|
|
54
55
|
Hy3RendererConfig,
|
|
55
56
|
KimiK25RendererConfig,
|
|
56
57
|
KimiK2RendererConfig,
|
|
57
58
|
LagunaM1RendererConfig,
|
|
59
|
+
LagunaS21RendererConfig,
|
|
58
60
|
LagunaXS2RendererConfig,
|
|
59
61
|
LagunaXS21RendererConfig,
|
|
60
62
|
Llama3RendererConfig,
|
|
@@ -89,10 +91,12 @@ _LAZY_RENDERERS: dict[str, str] = {
|
|
|
89
91
|
"GLM51Renderer": "renderers.glm5",
|
|
90
92
|
"GLM5Renderer": "renderers.glm5",
|
|
91
93
|
"GptOssRenderer": "renderers.gpt_oss",
|
|
94
|
+
"Gemma4Renderer": "renderers.gemma4",
|
|
92
95
|
"Hy3Renderer": "renderers.hy3",
|
|
93
96
|
"KimiK25Renderer": "renderers.kimi_k25",
|
|
94
97
|
"KimiK2Renderer": "renderers.kimi_k2",
|
|
95
98
|
"LagunaM1Renderer": "renderers.laguna_xs2",
|
|
99
|
+
"LagunaS21Renderer": "renderers.laguna_s21",
|
|
96
100
|
"LagunaXS21Renderer": "renderers.laguna_xs2",
|
|
97
101
|
"LagunaXS2Renderer": "renderers.laguna_xs2",
|
|
98
102
|
"Llama3Renderer": "renderers.llama_3",
|
|
@@ -140,6 +144,8 @@ __all__ = [
|
|
|
140
144
|
"GLM51RendererConfig",
|
|
141
145
|
"GLM5Renderer",
|
|
142
146
|
"GLM5RendererConfig",
|
|
147
|
+
"Gemma4Renderer",
|
|
148
|
+
"Gemma4RendererConfig",
|
|
143
149
|
"GptOssRenderer",
|
|
144
150
|
"GptOssRendererConfig",
|
|
145
151
|
"Hy3Renderer",
|
|
@@ -151,6 +157,8 @@ __all__ = [
|
|
|
151
157
|
"KimiK2RendererConfig",
|
|
152
158
|
"LagunaM1Renderer",
|
|
153
159
|
"LagunaM1RendererConfig",
|
|
160
|
+
"LagunaS21Renderer",
|
|
161
|
+
"LagunaS21RendererConfig",
|
|
154
162
|
"LagunaXS2Renderer",
|
|
155
163
|
"LagunaXS2RendererConfig",
|
|
156
164
|
"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.dev11'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 10, 'dev11')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1035,6 +1035,14 @@ MODEL_RENDERER_MAP: dict[str, str] = {
|
|
|
1035
1035
|
"Qwen/Qwen3-VL-4B-Instruct": "qwen3-vl",
|
|
1036
1036
|
"Qwen/Qwen3-VL-8B-Instruct": "qwen3-vl",
|
|
1037
1037
|
"Qwen/Qwen3-VL-30B-A3B-Instruct": "qwen3-vl",
|
|
1038
|
+
# Gemma 4 instruction checkpoints share Google's canonical turn/tool
|
|
1039
|
+
# grammar and dynamic Gemma4Processor image expansion. E2B/E4B omit the
|
|
1040
|
+
# disabled-thinking empty-channel prefill used by the 26B/31B revision;
|
|
1041
|
+
# Gemma4Renderer detects that small template variant per tokenizer.
|
|
1042
|
+
"google/gemma-4-E2B-it": "gemma4",
|
|
1043
|
+
"google/gemma-4-E4B-it": "gemma4",
|
|
1044
|
+
"google/gemma-4-26B-A4B-it": "gemma4",
|
|
1045
|
+
"google/gemma-4-31B-it": "gemma4",
|
|
1038
1046
|
# GLM-5 family (GLM-4.7 reuses the GLM-5 template).
|
|
1039
1047
|
"zai-org/GLM-5": "glm-5",
|
|
1040
1048
|
"zai-org/GLM-5-FP8": "glm-5",
|
|
@@ -1076,11 +1084,12 @@ MODEL_RENDERER_MAP: dict[str, str] = {
|
|
|
1076
1084
|
# construction to pin a different date.
|
|
1077
1085
|
"meta-llama/Llama-3.2-1B-Instruct": "llama-3",
|
|
1078
1086
|
"meta-llama/Llama-3.2-3B-Instruct": "llama-3",
|
|
1079
|
-
# Poolside Laguna. These checkpoints ship distinct chat templates,
|
|
1080
|
-
#
|
|
1087
|
+
# Poolside Laguna. These checkpoints ship distinct chat templates, each
|
|
1088
|
+
# mirrored by its own renderer class/config discriminator.
|
|
1081
1089
|
"poolside/Laguna-XS.2": "laguna-xs.2",
|
|
1082
1090
|
"poolside/Laguna-M.1": "laguna-m.1",
|
|
1083
1091
|
"poolside/Laguna-XS-2.1": "laguna-xs-2.1",
|
|
1092
|
+
"poolside/Laguna-S-2.1": "laguna-s-2.1",
|
|
1084
1093
|
# GPT-OSS.
|
|
1085
1094
|
"openai/gpt-oss-20b": "gpt-oss",
|
|
1086
1095
|
"openai/gpt-oss-120b": "gpt-oss",
|
|
@@ -1106,6 +1115,10 @@ MULTIMODAL_MODELS: dict[str, set[str]] = {
|
|
|
1106
1115
|
"Qwen/Qwen3-VL-4B-Instruct": {"image"},
|
|
1107
1116
|
"Qwen/Qwen3-VL-8B-Instruct": {"image"},
|
|
1108
1117
|
"Qwen/Qwen3-VL-30B-A3B-Instruct": {"image"},
|
|
1118
|
+
"google/gemma-4-E2B-it": {"image"},
|
|
1119
|
+
"google/gemma-4-E4B-it": {"image"},
|
|
1120
|
+
"google/gemma-4-26B-A4B-it": {"image"},
|
|
1121
|
+
"google/gemma-4-31B-it": {"image"},
|
|
1109
1122
|
# Qwen3.5 is itself a VLM family (HF tag ``image-text-to-text``,
|
|
1110
1123
|
# processor class ``Qwen3VLProcessor``) — same vision tokens and
|
|
1111
1124
|
# image-processor as Qwen3-VL, with a different tool-call format.
|
|
@@ -1325,9 +1338,11 @@ def _populate_registry():
|
|
|
1325
1338
|
from renderers.glm5 import GLM5Renderer, GLM51Renderer
|
|
1326
1339
|
from renderers.glm45 import GLM45Renderer
|
|
1327
1340
|
from renderers.gpt_oss import GptOssRenderer
|
|
1341
|
+
from renderers.gemma4 import Gemma4Renderer
|
|
1328
1342
|
from renderers.hy3 import Hy3Renderer
|
|
1329
1343
|
from renderers.kimi_k2 import KimiK2Renderer
|
|
1330
1344
|
from renderers.kimi_k25 import KimiK25Renderer
|
|
1345
|
+
from renderers.laguna_s21 import LagunaS21Renderer
|
|
1331
1346
|
from renderers.laguna_xs2 import (
|
|
1332
1347
|
LagunaM1Renderer,
|
|
1333
1348
|
LagunaXS2Renderer,
|
|
@@ -1352,6 +1367,7 @@ def _populate_registry():
|
|
|
1352
1367
|
"qwen3": Qwen3Renderer,
|
|
1353
1368
|
"prime-qwen3": PrimeQwen3Renderer,
|
|
1354
1369
|
"qwen3-vl": Qwen3VLRenderer,
|
|
1370
|
+
"gemma4": Gemma4Renderer,
|
|
1355
1371
|
"qwen3.5": Qwen35Renderer,
|
|
1356
1372
|
"qwen3.6": Qwen36Renderer,
|
|
1357
1373
|
"glm-5": GLM5Renderer,
|
|
@@ -1366,6 +1382,7 @@ def _populate_registry():
|
|
|
1366
1382
|
"laguna-xs.2": LagunaXS2Renderer,
|
|
1367
1383
|
"laguna-m.1": LagunaM1Renderer,
|
|
1368
1384
|
"laguna-xs-2.1": LagunaXS21Renderer,
|
|
1385
|
+
"laguna-s-2.1": LagunaS21Renderer,
|
|
1369
1386
|
"llama-3": Llama3Renderer,
|
|
1370
1387
|
"nemotron-3": Nemotron3Renderer,
|
|
1371
1388
|
"nemotron-3-ultra": Nemotron3UltraRenderer,
|
|
@@ -428,6 +428,7 @@ def _build_mm_features(
|
|
|
428
428
|
(``MultiModalData``) is already framework-agnostic and does not need
|
|
429
429
|
to change. Don't pre-build the abstraction with one engine in tree.
|
|
430
430
|
"""
|
|
431
|
+
from renderers.gemma4 import Gemma4Renderer
|
|
431
432
|
from renderers.qwen3_vl import Qwen3VLRenderer
|
|
432
433
|
from renderers.qwen35 import Qwen35Renderer
|
|
433
434
|
|
|
@@ -443,6 +444,8 @@ def _build_mm_features(
|
|
|
443
444
|
# the family default and matches every Qwen-VL processor in tree.
|
|
444
445
|
if issubclass(renderer_cls, (Qwen3VLRenderer, Qwen35Renderer)):
|
|
445
446
|
return _build_qwen_vl_features(mm_data, spatial_merge_size=2)
|
|
447
|
+
if issubclass(renderer_cls, Gemma4Renderer):
|
|
448
|
+
return _build_gemma4_features(mm_data)
|
|
446
449
|
|
|
447
450
|
raise NotImplementedError(
|
|
448
451
|
f"Multimodal serialization not implemented for {renderer_cls.__name__}. "
|
|
@@ -450,6 +453,69 @@ def _build_mm_features(
|
|
|
450
453
|
)
|
|
451
454
|
|
|
452
455
|
|
|
456
|
+
def _build_gemma4_features(mm_data: MultiModalData) -> dict[str, Any]:
|
|
457
|
+
"""vLLM features payload for Gemma 4 image inputs.
|
|
458
|
+
|
|
459
|
+
Hugging Face names the position field ``image_position_ids`` while
|
|
460
|
+
vLLM's Gemma 4 processor schema calls it ``pixel_position_ids``. Keep
|
|
461
|
+
renderer output faithful to the HF processor and translate at this
|
|
462
|
+
engine-specific boundary.
|
|
463
|
+
"""
|
|
464
|
+
try:
|
|
465
|
+
import torch
|
|
466
|
+
from transformers.feature_extraction_utils import BatchFeature
|
|
467
|
+
from vllm.entrypoints.scale_out.token_in_token_out.mm_serde import (
|
|
468
|
+
encode_mm_kwargs_item,
|
|
469
|
+
)
|
|
470
|
+
from vllm.multimodal.inputs import (
|
|
471
|
+
MultiModalFieldConfig,
|
|
472
|
+
MultiModalKwargsItems,
|
|
473
|
+
)
|
|
474
|
+
except ImportError as exc:
|
|
475
|
+
raise RuntimeError(
|
|
476
|
+
"Gemma 4 multimodal generate via /inference/v1/generate requires "
|
|
477
|
+
"a vLLM release with Gemma 4 support and `torch`."
|
|
478
|
+
) from exc
|
|
479
|
+
|
|
480
|
+
out: dict[str, Any] = {
|
|
481
|
+
"mm_hashes": {},
|
|
482
|
+
"mm_placeholders": {},
|
|
483
|
+
"kwargs_data": {},
|
|
484
|
+
}
|
|
485
|
+
image_items = mm_data.mm_items.get("image") or []
|
|
486
|
+
if image_items:
|
|
487
|
+
pixel_values = torch.cat(
|
|
488
|
+
[torch.as_tensor(item["pixel_values"]) for item in image_items], dim=0
|
|
489
|
+
)
|
|
490
|
+
pixel_position_ids = torch.cat(
|
|
491
|
+
[torch.as_tensor(item["image_position_ids"]) for item in image_items],
|
|
492
|
+
dim=0,
|
|
493
|
+
)
|
|
494
|
+
hf_inputs = BatchFeature(
|
|
495
|
+
data={
|
|
496
|
+
"pixel_values": pixel_values,
|
|
497
|
+
"pixel_position_ids": pixel_position_ids,
|
|
498
|
+
}
|
|
499
|
+
)
|
|
500
|
+
field_config = {
|
|
501
|
+
"pixel_values": MultiModalFieldConfig.batched("image"),
|
|
502
|
+
"pixel_position_ids": MultiModalFieldConfig.batched("image"),
|
|
503
|
+
}
|
|
504
|
+
kwargs_items = MultiModalKwargsItems.from_hf_inputs(hf_inputs, field_config)
|
|
505
|
+
out["kwargs_data"]["image"] = [
|
|
506
|
+
encode_mm_kwargs_item(item) for item in kwargs_items["image"]
|
|
507
|
+
]
|
|
508
|
+
out["mm_hashes"]["image"] = list(mm_data.mm_hashes.get("image") or [])
|
|
509
|
+
out["mm_placeholders"]["image"] = [
|
|
510
|
+
{"offset": placeholder.offset, "length": placeholder.length}
|
|
511
|
+
for placeholder in mm_data.mm_placeholders.get("image") or []
|
|
512
|
+
]
|
|
513
|
+
|
|
514
|
+
if not any(out["kwargs_data"].values()):
|
|
515
|
+
out["kwargs_data"] = None
|
|
516
|
+
return out
|
|
517
|
+
|
|
518
|
+
|
|
453
519
|
def _build_qwen_vl_features(
|
|
454
520
|
mm_data: MultiModalData, *, spatial_merge_size: int
|
|
455
521
|
) -> dict[str, Any]:
|
|
@@ -270,6 +270,33 @@ class Qwen3VLRendererConfig(BaseRendererConfig):
|
|
|
270
270
|
_internal_fields = frozenset({"image_cache_max"})
|
|
271
271
|
|
|
272
272
|
|
|
273
|
+
class Gemma4RendererConfig(BaseRendererConfig):
|
|
274
|
+
"""Gemma 4 renderer config."""
|
|
275
|
+
|
|
276
|
+
name: Literal["gemma4"] = "gemma4"
|
|
277
|
+
|
|
278
|
+
enable_thinking: bool = False
|
|
279
|
+
"""Enable Gemma 4's thinking mode. Mirrors the canonical template kwarg."""
|
|
280
|
+
|
|
281
|
+
preserve_thinking: bool = False
|
|
282
|
+
"""Keep thinking on historical tool-call turns when the template permits it."""
|
|
283
|
+
|
|
284
|
+
image_cache_max: int = 256
|
|
285
|
+
"""FIFO bound on processed image entries. Renderer-internal."""
|
|
286
|
+
|
|
287
|
+
_internal_fields = frozenset({"image_cache_max"})
|
|
288
|
+
|
|
289
|
+
@model_validator(mode="after")
|
|
290
|
+
def _check_thinking_retention(self):
|
|
291
|
+
_reject_thinking_retention_conflict(
|
|
292
|
+
self,
|
|
293
|
+
"preserve_thinking",
|
|
294
|
+
true_implies="all",
|
|
295
|
+
false_implies="tool_cycle",
|
|
296
|
+
)
|
|
297
|
+
return self
|
|
298
|
+
|
|
299
|
+
|
|
273
300
|
class GLM5RendererConfig(BaseRendererConfig):
|
|
274
301
|
"""GLM-5 renderer config."""
|
|
275
302
|
|
|
@@ -546,6 +573,37 @@ class LagunaXS21RendererConfig(BaseRendererConfig):
|
|
|
546
573
|
upstream default."""
|
|
547
574
|
|
|
548
575
|
|
|
576
|
+
class LagunaS21RendererConfig(BaseRendererConfig):
|
|
577
|
+
"""Laguna S-2.1 renderer config.
|
|
578
|
+
|
|
579
|
+
S-2.1 is a larger sibling of XS-2.1 sharing its tokenizer (same vocab,
|
|
580
|
+
special tokens, and merges), but its chat template is *not* byte-identical:
|
|
581
|
+
``enable_thinking`` defaults to ``True`` (XS-2.1 defaults ``False``), and a
|
|
582
|
+
new ``preserve_thinking`` kwarg widens the reasoning-display gate to
|
|
583
|
+
``enable_thinking or preserve_thinking``. The token format is otherwise
|
|
584
|
+
identical, so this is served by
|
|
585
|
+
:class:`renderers.laguna_s21.LagunaS21Renderer`, a thin subclass of
|
|
586
|
+
``LagunaXS21Renderer`` that only overrides that gate.
|
|
587
|
+
"""
|
|
588
|
+
|
|
589
|
+
name: Literal["laguna-s-2.1"] = "laguna-s-2.1"
|
|
590
|
+
|
|
591
|
+
enable_thinking: bool = True
|
|
592
|
+
"""When ``True``, the generation prompt ends with ``<think>`` and every
|
|
593
|
+
assistant turn renders ``<think>{reasoning}</think>``; when ``False``,
|
|
594
|
+
turns open with a bare ``</think>``. Mirrors the template's
|
|
595
|
+
``enable_thinking`` kwarg — note S-2.1's upstream default is ``True``,
|
|
596
|
+
unlike XS-2.1's ``False``."""
|
|
597
|
+
|
|
598
|
+
preserve_thinking: bool = False
|
|
599
|
+
"""When ``True``, assistant turns keep their ``<think>{reasoning}</think>``
|
|
600
|
+
block even while ``enable_thinking`` is ``False`` — the template gates
|
|
601
|
+
reasoning display on ``enable_thinking or preserve_thinking``. With the
|
|
602
|
+
default ``False`` the gate collapses to ``enable_thinking`` and the
|
|
603
|
+
renderer matches XS-2.1 turn-for-turn. Mirrors the template's
|
|
604
|
+
``preserve_thinking`` kwarg and its upstream default."""
|
|
605
|
+
|
|
606
|
+
|
|
549
607
|
class Llama3RendererConfig(BaseRendererConfig):
|
|
550
608
|
"""Llama-3.x Instruct renderer config.
|
|
551
609
|
|
|
@@ -725,6 +783,7 @@ RendererConfig = Annotated[
|
|
|
725
783
|
Qwen35RendererConfig,
|
|
726
784
|
Qwen36RendererConfig,
|
|
727
785
|
Qwen3VLRendererConfig,
|
|
786
|
+
Gemma4RendererConfig,
|
|
728
787
|
GLM5RendererConfig,
|
|
729
788
|
GLM51RendererConfig,
|
|
730
789
|
GLM45RendererConfig,
|
|
@@ -735,6 +794,7 @@ RendererConfig = Annotated[
|
|
|
735
794
|
LagunaXS2RendererConfig,
|
|
736
795
|
LagunaM1RendererConfig,
|
|
737
796
|
LagunaXS21RendererConfig,
|
|
797
|
+
LagunaS21RendererConfig,
|
|
738
798
|
Llama3RendererConfig,
|
|
739
799
|
MiniMaxM2RendererConfig,
|
|
740
800
|
Nemotron3RendererConfig,
|
|
@@ -767,6 +827,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
|
|
|
767
827
|
"qwen3.5": Qwen35RendererConfig,
|
|
768
828
|
"qwen3.6": Qwen36RendererConfig,
|
|
769
829
|
"qwen3-vl": Qwen3VLRendererConfig,
|
|
830
|
+
"gemma4": Gemma4RendererConfig,
|
|
770
831
|
"glm-5": GLM5RendererConfig,
|
|
771
832
|
"glm-5.1": GLM51RendererConfig,
|
|
772
833
|
"glm-4.5": GLM45RendererConfig,
|
|
@@ -777,6 +838,7 @@ _CONFIG_BY_NAME: dict[str, type[BaseRendererConfig]] = {
|
|
|
777
838
|
"laguna-xs.2": LagunaXS2RendererConfig,
|
|
778
839
|
"laguna-m.1": LagunaM1RendererConfig,
|
|
779
840
|
"laguna-xs-2.1": LagunaXS21RendererConfig,
|
|
841
|
+
"laguna-s-2.1": LagunaS21RendererConfig,
|
|
780
842
|
"llama-3": Llama3RendererConfig,
|
|
781
843
|
"minimax-m2": MiniMaxM2RendererConfig,
|
|
782
844
|
"nemotron-3": Nemotron3RendererConfig,
|
|
@@ -820,11 +882,13 @@ __all__ = [
|
|
|
820
882
|
"GLM45RendererConfig",
|
|
821
883
|
"GLM51RendererConfig",
|
|
822
884
|
"GLM5RendererConfig",
|
|
885
|
+
"Gemma4RendererConfig",
|
|
823
886
|
"GptOssRendererConfig",
|
|
824
887
|
"Hy3RendererConfig",
|
|
825
888
|
"KimiK25RendererConfig",
|
|
826
889
|
"KimiK2RendererConfig",
|
|
827
890
|
"LagunaM1RendererConfig",
|
|
891
|
+
"LagunaS21RendererConfig",
|
|
828
892
|
"LagunaXS2RendererConfig",
|
|
829
893
|
"LagunaXS21RendererConfig",
|
|
830
894
|
"Llama3RendererConfig",
|