renderers 0.1.10.dev10__tar.gz → 0.1.10.dev13__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.dev10 → renderers-0.1.10.dev13}/PKG-INFO +4 -4
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/README.md +3 -3
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/docs/renderer-config.md +16 -6
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/pyproject.toml +4 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/__init__.py +8 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/_version.py +2 -2
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/base.py +43 -2
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/client.py +66 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/configs.py +166 -22
- renderers-0.1.10.dev13/renderers/gemma4.py +1369 -0
- renderers-0.1.10.dev13/renderers/inkling.py +997 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/parsing.py +132 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/conftest.py +3 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_bridge.py +3 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_build_helpers.py +14 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_client.py +59 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_disabled_thinking_stability.py +10 -2
- renderers-0.1.10.dev13/tests/test_gemma4.py +473 -0
- renderers-0.1.10.dev13/tests/test_inkling.py +523 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_multimodal.py +211 -21
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_render_ids.py +20 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_renderer_config.py +49 -2
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_renderer_config_parity.py +18 -8
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_roundtrip.py +3 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/uv.lock +48 -50
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/.github/workflows/publish.yml +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/.github/workflows/style.yml +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/.github/workflows/test.yml +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/.gitignore +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/.pre-commit-config.yaml +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/LICENSE +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/examples/README.md +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/deepseek_r1.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/deepseek_v3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/default.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/glm45.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/glm5.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/gpt_oss.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/hy3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/kimi_k2.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/kimi_k25.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/laguna_s21.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/laguna_xs2.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/llama_3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/minimax_m2.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/nemotron3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/parsers.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/prime_qwen3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/qwen3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/qwen35.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/qwen36.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/renderers/qwen3_vl.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_deepseek_r1.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_glm_tool_name_validation.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_gpt_oss_harmony_parity.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_hy3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_incremental.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_is_content.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_laguna_m1.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_laguna_s21.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_laguna_xs21.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_llama_3.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_load_tokenizer.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_message_indices.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_message_tool_names.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_nemotron3_parity.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_parse_response.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_parsers.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_prime_qwen3_parity.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_qwen35_size_coverage.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_sampled_mask.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.1.10.dev10 → renderers-0.1.10.dev13}/tests/test_tool_arg_type_preservation.py +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.dev13
|
|
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-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.
|
|
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`, `inkling` / `inkling-small`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `gemma4`, `kimi-k2.5` / `kimi-k2.6`, and the Inkling checkpoints are multimodal (Inkling handles both image **and** audio).
|
|
60
60
|
|
|
61
61
|
## API
|
|
62
62
|
|
|
@@ -159,7 +159,7 @@ renderer = create_renderer(
|
|
|
159
159
|
|
|
160
160
|
Discriminated union: every per-renderer config is a variant of `RendererConfig`, dispatched on the `name` field. Bogus combinations (e.g. `add_vision_id` under `name="qwen3"`) error at construction with a `pydantic.ValidationError`. Downstream pydantic configs (prime-rl orchestrator, verifiers `ClientConfig`) hold a single field typed as `RendererConfig` and inherit the same strict-per-variant validation.
|
|
161
161
|
|
|
162
|
-
When `chat_template_kwargs` is passed with `config=None` / `AutoRendererConfig`, renderers first resolves the concrete renderer from the model name, then validates
|
|
162
|
+
When `chat_template_kwargs` is passed with `config=None` / `AutoRendererConfig`, renderers first resolves the concrete renderer from the model name, then validates each key against that renderer's explicit template-kwarg allowlist. Renderer-only fields such as `image_cache_max` must be passed through the typed config instead. `Auto + unknown model + chat_template_kwargs` fails loudly; use an explicit typed config or explicit `DefaultRendererConfig` for opaque fallback templates.
|
|
163
163
|
|
|
164
164
|
One shared behaviour flag lives on typed renderer configs: `thinking_retention`, an optional bridge-policy override. Leave it unset to derive bridge behaviour from the chat template and its renderer-exposed kwargs.
|
|
165
165
|
|
|
@@ -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-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.
|
|
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`, `inkling` / `inkling-small`, and `prime-qwen3`. Anything else falls back to `DefaultRenderer`, a generic `apply_chat_template` wrapper. `qwen3-vl`, `qwen3.5`, `qwen3.6`, `gemma4`, `kimi-k2.5` / `kimi-k2.6`, and the Inkling checkpoints are multimodal (Inkling handles both image **and** audio).
|
|
44
44
|
|
|
45
45
|
## API
|
|
46
46
|
|
|
@@ -143,7 +143,7 @@ renderer = create_renderer(
|
|
|
143
143
|
|
|
144
144
|
Discriminated union: every per-renderer config is a variant of `RendererConfig`, dispatched on the `name` field. Bogus combinations (e.g. `add_vision_id` under `name="qwen3"`) error at construction with a `pydantic.ValidationError`. Downstream pydantic configs (prime-rl orchestrator, verifiers `ClientConfig`) hold a single field typed as `RendererConfig` and inherit the same strict-per-variant validation.
|
|
145
145
|
|
|
146
|
-
When `chat_template_kwargs` is passed with `config=None` / `AutoRendererConfig`, renderers first resolves the concrete renderer from the model name, then validates
|
|
146
|
+
When `chat_template_kwargs` is passed with `config=None` / `AutoRendererConfig`, renderers first resolves the concrete renderer from the model name, then validates each key against that renderer's explicit template-kwarg allowlist. Renderer-only fields such as `image_cache_max` must be passed through the typed config instead. `Auto + unknown model + chat_template_kwargs` fails loudly; use an explicit typed config or explicit `DefaultRendererConfig` for opaque fallback templates.
|
|
147
147
|
|
|
148
148
|
One shared behaviour flag lives on typed renderer configs: `thinking_retention`, an optional bridge-policy override. Leave it unset to derive bridge behaviour from the chat template and its renderer-exposed kwargs.
|
|
149
149
|
|
|
@@ -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
|
|
@@ -19,8 +19,10 @@ construction.
|
|
|
19
19
|
|
|
20
20
|
## Per-renderer configs
|
|
21
21
|
|
|
22
|
-
Use `type(config).template_field_names()` to inspect the
|
|
23
|
-
|
|
22
|
+
Use `type(config).template_field_names()` to inspect the explicit allowlist of
|
|
23
|
+
fields accepted through `chat_template_kwargs`. Every renderer-specific field
|
|
24
|
+
is classified as either a template field or a renderer-only field at class
|
|
25
|
+
definition time. Template fields are covered by parity tests against
|
|
24
26
|
`apply_chat_template` in `tests/test_renderer_config_parity.py`.
|
|
25
27
|
|
|
26
28
|
| Renderer | Config class | Template fields | Renderer-only fields |
|
|
@@ -30,12 +32,14 @@ chat-template kwargs. Those fields are covered by parity tests against
|
|
|
30
32
|
| Qwen3.5 | `Qwen35RendererConfig` | `enable_thinking`, `add_vision_id` | `image_cache_max` |
|
|
31
33
|
| Qwen3.6 | `Qwen36RendererConfig` | `enable_thinking`, `add_vision_id`, `preserve_thinking` | `image_cache_max` |
|
|
32
34
|
| Qwen3-VL | `Qwen3VLRendererConfig` | `add_vision_id` | `image_cache_max` |
|
|
35
|
+
| Gemma 4 | `Gemma4RendererConfig` | `enable_thinking`, `preserve_thinking` | `image_cache_max` |
|
|
33
36
|
| GLM-5 / 5.1 | `GLM5RendererConfig` / `GLM51RendererConfig` | `enable_thinking`, `clear_thinking` | - |
|
|
34
37
|
| GLM-4.5 | `GLM45RendererConfig` | `enable_thinking` | - |
|
|
35
38
|
| gpt-oss | `GptOssRendererConfig` | `reasoning_effort`, `conversation_start_date` | `use_system_prompt`, `knowledge_cutoff`, `model_identity`, `auto_drop_analysis` |
|
|
36
39
|
| Hy3 | `Hy3RendererConfig` | `reasoning_effort`, `preserved_thinking`, `is_training`, `raw_last_assistant`, `fallback_strategy` | - |
|
|
37
40
|
| Kimi K2 | `KimiK2RendererConfig` | - | `enable_thinking` |
|
|
38
41
|
| Kimi K2.5 / 2.6 | `KimiK25RendererConfig` | `thinking` | `image_cache_max` |
|
|
42
|
+
| Inkling / Inkling-Small | `InklingRendererConfig` | `reasoning_effort` | `image_cache_max`, `audio_cache_max` |
|
|
39
43
|
| Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
40
44
|
| Laguna M.1 | `LagunaM1RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
|
|
41
45
|
| Laguna XS-2.1 | `LagunaXS21RendererConfig` | `enable_thinking` | - |
|
|
@@ -79,8 +83,10 @@ pool = create_renderer_pool(
|
|
|
79
83
|
```
|
|
80
84
|
|
|
81
85
|
Renderers resolves auto configs before applying `chat_template_kwargs`, so the
|
|
82
|
-
kwargs validate against the concrete renderer
|
|
83
|
-
that conflict with an explicit
|
|
86
|
+
kwargs validate against the concrete renderer's template-field allowlist.
|
|
87
|
+
Unknown kwargs, renderer-only fields, or kwargs that conflict with an explicit
|
|
88
|
+
`thinking_retention` fail at construction. Renderer-only options remain valid
|
|
89
|
+
when supplied through the typed config itself.
|
|
84
90
|
|
|
85
91
|
Auto-resolution fails loudly for VLMs without an exact registered renderer.
|
|
86
92
|
Text-only unknown models fall back to `DefaultRenderer`, unless
|
|
@@ -89,7 +95,10 @@ cannot implement selective bridge retention, so that combination raises.
|
|
|
89
95
|
`AutoRendererConfig` with `chat_template_kwargs` also raises for unknown models,
|
|
90
96
|
because renderers cannot validate those kwargs without a concrete renderer.
|
|
91
97
|
Use an explicit model-specific config, or `DefaultRendererConfig(...)` when you
|
|
92
|
-
intentionally want opaque `apply_chat_template` kwargs.
|
|
98
|
+
intentionally want opaque `apply_chat_template` kwargs. Even for the default
|
|
99
|
+
renderer, typed fields such as `tool_parser`, `reasoning_parser`, and
|
|
100
|
+
`thinking_retention` must be passed through the config rather than through the
|
|
101
|
+
opaque kwargs mapping.
|
|
93
102
|
|
|
94
103
|
## `thinking_retention`
|
|
95
104
|
|
|
@@ -131,6 +140,7 @@ the knobs its template actually exposes:
|
|
|
131
140
|
| Qwen3 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
132
141
|
| Qwen3.5 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
133
142
|
| Qwen3.6 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
143
|
+
| Gemma 4 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
134
144
|
| GLM-5 / 5.1 | `clear_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
135
145
|
| GLM-4.5 | `enable_thinking=False -> all`, else `tool_cycle` |
|
|
136
146
|
| gpt-oss | `auto_drop_analysis=False -> all`, else `tool_cycle` |
|
|
@@ -139,7 +149,7 @@ the knobs its template actually exposes:
|
|
|
139
149
|
| Nemotron-3 / 3.5 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
|
|
140
150
|
| DeepSeek R1 | `template` |
|
|
141
151
|
| MiniMax M2 | `tool_cycle` |
|
|
142
|
-
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3 | `all` |
|
|
152
|
+
| DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3, Inkling | `all` |
|
|
143
153
|
| PrimeIntellect Qwen3 | `all` |
|
|
144
154
|
|
|
145
155
|
Config construction raises when an explicit template knob directly contradicts
|
|
@@ -19,6 +19,10 @@ dependencies = [
|
|
|
19
19
|
"openai>=1.108.1",
|
|
20
20
|
"tiktoken",
|
|
21
21
|
"jinja2",
|
|
22
|
+
# Keep this floor compatible with prime-rl's transformers pin. Inkling's
|
|
23
|
+
# tokenizer and text-only renderer work on older releases; image/audio
|
|
24
|
+
# processing fails lazily with an upgrade message when InklingProcessor is
|
|
25
|
+
# unavailable (native support starts in transformers 5.14).
|
|
22
26
|
"transformers>=4.50.0",
|
|
23
27
|
# Used by GptOssRenderer to render and parse harmony tokens. Vendoring
|
|
24
28
|
# OpenAI's reference implementation keeps us byte-identical with vLLM
|
|
@@ -50,8 +50,10 @@ from renderers.configs import (
|
|
|
50
50
|
GLM45RendererConfig,
|
|
51
51
|
GLM51RendererConfig,
|
|
52
52
|
GLM5RendererConfig,
|
|
53
|
+
Gemma4RendererConfig,
|
|
53
54
|
GptOssRendererConfig,
|
|
54
55
|
Hy3RendererConfig,
|
|
56
|
+
InklingRendererConfig,
|
|
55
57
|
KimiK25RendererConfig,
|
|
56
58
|
KimiK2RendererConfig,
|
|
57
59
|
LagunaM1RendererConfig,
|
|
@@ -90,7 +92,9 @@ _LAZY_RENDERERS: dict[str, str] = {
|
|
|
90
92
|
"GLM51Renderer": "renderers.glm5",
|
|
91
93
|
"GLM5Renderer": "renderers.glm5",
|
|
92
94
|
"GptOssRenderer": "renderers.gpt_oss",
|
|
95
|
+
"Gemma4Renderer": "renderers.gemma4",
|
|
93
96
|
"Hy3Renderer": "renderers.hy3",
|
|
97
|
+
"InklingRenderer": "renderers.inkling",
|
|
94
98
|
"KimiK25Renderer": "renderers.kimi_k25",
|
|
95
99
|
"KimiK2Renderer": "renderers.kimi_k2",
|
|
96
100
|
"LagunaM1Renderer": "renderers.laguna_xs2",
|
|
@@ -142,11 +146,15 @@ __all__ = [
|
|
|
142
146
|
"GLM51RendererConfig",
|
|
143
147
|
"GLM5Renderer",
|
|
144
148
|
"GLM5RendererConfig",
|
|
149
|
+
"Gemma4Renderer",
|
|
150
|
+
"Gemma4RendererConfig",
|
|
145
151
|
"GptOssRenderer",
|
|
146
152
|
"GptOssRendererConfig",
|
|
147
153
|
"Hy3Renderer",
|
|
148
154
|
"Hy3RendererConfig",
|
|
149
155
|
"ImagePart",
|
|
156
|
+
"InklingRenderer",
|
|
157
|
+
"InklingRendererConfig",
|
|
150
158
|
"KimiK25Renderer",
|
|
151
159
|
"KimiK25RendererConfig",
|
|
152
160
|
"KimiK2Renderer",
|
|
@@ -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.dev13'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 10, 'dev13')
|
|
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",
|
|
@@ -1085,6 +1093,10 @@ MODEL_RENDERER_MAP: dict[str, str] = {
|
|
|
1085
1093
|
# GPT-OSS.
|
|
1086
1094
|
"openai/gpt-oss-20b": "gpt-oss",
|
|
1087
1095
|
"openai/gpt-oss-120b": "gpt-oss",
|
|
1096
|
+
# Thinking Machines Inkling checkpoints share byte-identical tokenizer,
|
|
1097
|
+
# chat-template, and processor assets (vision + audio; transformers >= 5.14).
|
|
1098
|
+
"thinkingmachines/Inkling": "inkling",
|
|
1099
|
+
"thinkingmachines/Inkling-Small": "inkling",
|
|
1088
1100
|
# Tencent Hunyuan Hy3 (295B-A21B MoE). The FP8 checkpoint shares the same
|
|
1089
1101
|
# tokenizer and chat template. Hy3-preview is deliberately unmapped: it
|
|
1090
1102
|
# ships an older, incompatible template (un-suffixed special tokens,
|
|
@@ -1107,6 +1119,10 @@ MULTIMODAL_MODELS: dict[str, set[str]] = {
|
|
|
1107
1119
|
"Qwen/Qwen3-VL-4B-Instruct": {"image"},
|
|
1108
1120
|
"Qwen/Qwen3-VL-8B-Instruct": {"image"},
|
|
1109
1121
|
"Qwen/Qwen3-VL-30B-A3B-Instruct": {"image"},
|
|
1122
|
+
"google/gemma-4-E2B-it": {"image"},
|
|
1123
|
+
"google/gemma-4-E4B-it": {"image"},
|
|
1124
|
+
"google/gemma-4-26B-A4B-it": {"image"},
|
|
1125
|
+
"google/gemma-4-31B-it": {"image"},
|
|
1110
1126
|
# Qwen3.5 is itself a VLM family (HF tag ``image-text-to-text``,
|
|
1111
1127
|
# processor class ``Qwen3VLProcessor``) — same vision tokens and
|
|
1112
1128
|
# image-processor as Qwen3-VL, with a different tool-call format.
|
|
@@ -1129,6 +1145,8 @@ MULTIMODAL_MODELS: dict[str, set[str]] = {
|
|
|
1129
1145
|
# ``grid_thws``.
|
|
1130
1146
|
"moonshotai/Kimi-K2.5": {"image"},
|
|
1131
1147
|
"moonshotai/Kimi-K2.6": {"image"},
|
|
1148
|
+
"thinkingmachines/Inkling": {"image", "audio"},
|
|
1149
|
+
"thinkingmachines/Inkling-Small": {"image", "audio"},
|
|
1132
1150
|
}
|
|
1133
1151
|
|
|
1134
1152
|
|
|
@@ -1326,7 +1344,9 @@ def _populate_registry():
|
|
|
1326
1344
|
from renderers.glm5 import GLM5Renderer, GLM51Renderer
|
|
1327
1345
|
from renderers.glm45 import GLM45Renderer
|
|
1328
1346
|
from renderers.gpt_oss import GptOssRenderer
|
|
1347
|
+
from renderers.gemma4 import Gemma4Renderer
|
|
1329
1348
|
from renderers.hy3 import Hy3Renderer
|
|
1349
|
+
from renderers.inkling import InklingRenderer
|
|
1330
1350
|
from renderers.kimi_k2 import KimiK2Renderer
|
|
1331
1351
|
from renderers.kimi_k25 import KimiK25Renderer
|
|
1332
1352
|
from renderers.laguna_s21 import LagunaS21Renderer
|
|
@@ -1354,6 +1374,7 @@ def _populate_registry():
|
|
|
1354
1374
|
"qwen3": Qwen3Renderer,
|
|
1355
1375
|
"prime-qwen3": PrimeQwen3Renderer,
|
|
1356
1376
|
"qwen3-vl": Qwen3VLRenderer,
|
|
1377
|
+
"gemma4": Gemma4Renderer,
|
|
1357
1378
|
"qwen3.5": Qwen35Renderer,
|
|
1358
1379
|
"qwen3.6": Qwen36Renderer,
|
|
1359
1380
|
"glm-5": GLM5Renderer,
|
|
@@ -1363,6 +1384,7 @@ def _populate_registry():
|
|
|
1363
1384
|
"deepseek-v3": DeepSeekV3Renderer,
|
|
1364
1385
|
"deepseek-r1": DeepSeekR1Renderer,
|
|
1365
1386
|
"hy3": Hy3Renderer,
|
|
1387
|
+
"inkling": InklingRenderer,
|
|
1366
1388
|
"kimi-k2": KimiK2Renderer,
|
|
1367
1389
|
"kimi-k2.5": KimiK25Renderer,
|
|
1368
1390
|
"laguna-xs.2": LagunaXS2Renderer,
|
|
@@ -1469,12 +1491,31 @@ def _merge_chat_template_kwargs(
|
|
|
1469
1491
|
return config
|
|
1470
1492
|
if not isinstance(chat_template_kwargs, Mapping):
|
|
1471
1493
|
raise TypeError("chat_template_kwargs must be a mapping.")
|
|
1494
|
+
kwargs = dict(chat_template_kwargs)
|
|
1495
|
+
config_cls = type(config)
|
|
1496
|
+
allowed = config_cls.template_field_names()
|
|
1497
|
+
if config_cls._allow_opaque_template_kwargs:
|
|
1498
|
+
reserved = frozenset(config_cls.model_fields) - allowed - {"name"}
|
|
1499
|
+
unsupported = frozenset(kwargs) & reserved
|
|
1500
|
+
else:
|
|
1501
|
+
unsupported = frozenset(kwargs) - allowed
|
|
1502
|
+
if unsupported:
|
|
1503
|
+
allowed_text = (
|
|
1504
|
+
"opaque Jinja kwargs"
|
|
1505
|
+
if config_cls._allow_opaque_template_kwargs
|
|
1506
|
+
else ", ".join(sorted(allowed)) or "(none)"
|
|
1507
|
+
)
|
|
1508
|
+
raise ValueError(
|
|
1509
|
+
f"Unsupported chat_template_kwargs for {config.name!r}: "
|
|
1510
|
+
f"{sorted(unsupported)}. Allowed: {allowed_text}. Pass "
|
|
1511
|
+
"renderer-internal options through the typed config instead."
|
|
1512
|
+
)
|
|
1472
1513
|
data: dict[str, Any] = {"name": config.name}
|
|
1473
1514
|
for field_name in config.__pydantic_fields_set__:
|
|
1474
1515
|
data[field_name] = getattr(config, field_name)
|
|
1475
1516
|
data.update(getattr(config, "model_extra", None) or {})
|
|
1476
|
-
data.update(
|
|
1477
|
-
return
|
|
1517
|
+
data.update(kwargs)
|
|
1518
|
+
return config_cls.model_validate(data)
|
|
1478
1519
|
|
|
1479
1520
|
|
|
1480
1521
|
def _resolve_renderer_config(
|
|
@@ -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]:
|