renderers 0.1.10.dev11__tar.gz → 0.1.10.dev15__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 (85) hide show
  1. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/.github/workflows/publish-dev.yml +1 -1
  2. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/.github/workflows/publish.yml +1 -1
  3. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/PKG-INFO +4 -4
  4. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/README.md +2 -2
  5. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/docs/renderer-config.md +16 -6
  6. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/pyproject.toml +4 -0
  7. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/__init__.py +8 -0
  8. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/_version.py +2 -2
  9. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/base.py +39 -6
  10. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/configs.py +185 -22
  11. renderers-0.1.10.dev15/renderers/inkling.py +997 -0
  12. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/parsing.py +132 -0
  13. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/qwen35.py +70 -27
  14. renderers-0.1.10.dev15/renderers/qwen38.py +66 -0
  15. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/conftest.py +3 -0
  16. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_bridge.py +3 -0
  17. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_client.py +2 -1
  18. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_disabled_thinking_stability.py +5 -0
  19. renderers-0.1.10.dev15/tests/test_inkling.py +523 -0
  20. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_multimodal.py +142 -7
  21. renderers-0.1.10.dev15/tests/test_qwen38.py +172 -0
  22. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_renderer_config.py +49 -2
  23. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_renderer_config_parity.py +8 -4
  24. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_roundtrip.py +3 -0
  25. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/uv.lock +48 -50
  26. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/.github/workflows/style.yml +0 -0
  27. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/.github/workflows/test.yml +0 -0
  28. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/.gitignore +0 -0
  29. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/.pre-commit-config.yaml +0 -0
  30. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/LICENSE +0 -0
  31. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/examples/README.md +0 -0
  32. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/examples/sglang/multiturn_generate_sglang.py +0 -0
  33. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/examples/sglang/online_multiturn_sglang.py +0 -0
  34. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/examples/tinker/multiturn_generate_tinker.py +0 -0
  35. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/examples/transformers/multiturn_generate_transformers.py +0 -0
  36. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/examples/vllm/multiturn_generate_vllm.py +0 -0
  37. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/client.py +0 -0
  38. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/deepseek_r1.py +0 -0
  39. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/deepseek_v3.py +0 -0
  40. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/default.py +0 -0
  41. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/gemma4.py +0 -0
  42. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/glm45.py +0 -0
  43. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/glm5.py +0 -0
  44. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/gpt_oss.py +0 -0
  45. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/hy3.py +0 -0
  46. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/kimi_k2.py +0 -0
  47. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/kimi_k25.py +0 -0
  48. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/laguna_s21.py +0 -0
  49. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/laguna_xs2.py +0 -0
  50. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/llama_3.py +0 -0
  51. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/minimax_m2.py +0 -0
  52. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/nemotron3.py +0 -0
  53. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/parsers.py +0 -0
  54. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/prime_qwen3.py +0 -0
  55. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/qwen3.py +0 -0
  56. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/qwen36.py +0 -0
  57. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/renderers/qwen3_vl.py +0 -0
  58. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_build_helpers.py +0 -0
  59. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_deepseek_r1.py +0 -0
  60. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_gemma4.py +0 -0
  61. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_glm_tool_name_validation.py +0 -0
  62. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_gpt_oss_harmony_parity.py +0 -0
  63. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_hy3.py +0 -0
  64. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_incremental.py +0 -0
  65. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_is_content.py +0 -0
  66. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_kimi_k25_tool_schema.py +0 -0
  67. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_laguna_m1.py +0 -0
  68. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_laguna_s21.py +0 -0
  69. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_laguna_xs21.py +0 -0
  70. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_llama_3.py +0 -0
  71. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_load_tokenizer.py +0 -0
  72. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_message_indices.py +0 -0
  73. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_message_tool_names.py +0 -0
  74. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_nemotron3_parity.py +0 -0
  75. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_nemotron3_ultra.py +0 -0
  76. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_parse_response.py +0 -0
  77. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_parse_response_robustness.py +0 -0
  78. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_parsers.py +0 -0
  79. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_preserve_thinking.py +0 -0
  80. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_prime_qwen3_parity.py +0 -0
  81. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_qwen35_size_coverage.py +0 -0
  82. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_render_ids.py +0 -0
  83. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_sampled_mask.py +0 -0
  84. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_tokens_per_message.py +0 -0
  85. {renderers-0.1.10.dev11 → renderers-0.1.10.dev15}/tests/test_tool_arg_type_preservation.py +0 -0
@@ -62,6 +62,6 @@ jobs:
62
62
  path: dist/
63
63
 
64
64
  - name: Publish to PyPI
65
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
65
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
66
66
  with:
67
67
  skip-existing: true
@@ -87,4 +87,4 @@ jobs:
87
87
  path: dist/
88
88
 
89
89
  - name: Publish to PyPI
90
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
90
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: renderers
3
- Version: 0.1.10.dev11
3
+ Version: 0.1.10.dev15
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`, `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.
59
+ 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
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 those kwargs against that renderer's config. `Auto + unknown model + chat_template_kwargs` fails loudly; use an explicit typed config or explicit `DefaultRendererConfig` for opaque fallback templates.
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
 
@@ -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`, `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.
43
+ 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).
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 those kwargs against that renderer's config. `Auto + unknown model + chat_template_kwargs` fails loudly; use an explicit typed config or explicit `DefaultRendererConfig` for opaque fallback templates.
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
 
@@ -19,8 +19,10 @@ construction.
19
19
 
20
20
  ## Per-renderer configs
21
21
 
22
- Use `type(config).template_field_names()` to inspect the fields that mirror
23
- chat-template kwargs. Those fields are covered by parity tests against
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 |
@@ -29,6 +31,7 @@ chat-template kwargs. Those fields are covered by parity tests against
29
31
  | PrimeIntellect Qwen3 | `PrimeQwen3RendererConfig` | - | - |
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` |
34
+ | Qwen3.8 | `Qwen38RendererConfig` | `enable_thinking`, `add_vision_id`, `preserve_thinking`, `reasoning_effort` | `image_cache_max` |
32
35
  | Qwen3-VL | `Qwen3VLRendererConfig` | `add_vision_id` | `image_cache_max` |
33
36
  | Gemma 4 | `Gemma4RendererConfig` | `enable_thinking`, `preserve_thinking` | `image_cache_max` |
34
37
  | GLM-5 / 5.1 | `GLM5RendererConfig` / `GLM51RendererConfig` | `enable_thinking`, `clear_thinking` | - |
@@ -37,6 +40,7 @@ chat-template kwargs. Those fields are covered by parity tests against
37
40
  | Hy3 | `Hy3RendererConfig` | `reasoning_effort`, `preserved_thinking`, `is_training`, `raw_last_assistant`, `fallback_strategy` | - |
38
41
  | Kimi K2 | `KimiK2RendererConfig` | - | `enable_thinking` |
39
42
  | Kimi K2.5 / 2.6 | `KimiK25RendererConfig` | `thinking` | `image_cache_max` |
43
+ | Inkling / Inkling-Small | `InklingRendererConfig` | `reasoning_effort` | `image_cache_max`, `audio_cache_max` |
40
44
  | Laguna XS.2 | `LagunaXS2RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
41
45
  | Laguna M.1 | `LagunaM1RendererConfig` | `enable_thinking`, `render_assistant_messages_raw` | - |
42
46
  | Laguna XS-2.1 | `LagunaXS21RendererConfig` | `enable_thinking` | - |
@@ -80,8 +84,10 @@ pool = create_renderer_pool(
80
84
  ```
81
85
 
82
86
  Renderers resolves auto configs before applying `chat_template_kwargs`, so the
83
- kwargs validate against the concrete renderer config. Unknown kwargs, or kwargs
84
- that conflict with an explicit `thinking_retention`, fail at construction.
87
+ kwargs validate against the concrete renderer's template-field allowlist.
88
+ Unknown kwargs, renderer-only fields, or kwargs that conflict with an explicit
89
+ `thinking_retention` fail at construction. Renderer-only options remain valid
90
+ when supplied through the typed config itself.
85
91
 
86
92
  Auto-resolution fails loudly for VLMs without an exact registered renderer.
87
93
  Text-only unknown models fall back to `DefaultRenderer`, unless
@@ -90,7 +96,10 @@ cannot implement selective bridge retention, so that combination raises.
90
96
  `AutoRendererConfig` with `chat_template_kwargs` also raises for unknown models,
91
97
  because renderers cannot validate those kwargs without a concrete renderer.
92
98
  Use an explicit model-specific config, or `DefaultRendererConfig(...)` when you
93
- intentionally want opaque `apply_chat_template` kwargs.
99
+ intentionally want opaque `apply_chat_template` kwargs. Even for the default
100
+ renderer, typed fields such as `tool_parser`, `reasoning_parser`, and
101
+ `thinking_retention` must be passed through the config rather than through the
102
+ opaque kwargs mapping.
94
103
 
95
104
  ## `thinking_retention`
96
105
 
@@ -132,6 +141,7 @@ the knobs its template actually exposes:
132
141
  | Qwen3 | `enable_thinking=False -> all`, else `tool_cycle` |
133
142
  | Qwen3.5 | `enable_thinking=False -> all`, else `tool_cycle` |
134
143
  | Qwen3.6 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
144
+ | Qwen3.8 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
135
145
  | Gemma 4 | `preserve_thinking=True -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
136
146
  | GLM-5 / 5.1 | `clear_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
137
147
  | GLM-4.5 | `enable_thinking=False -> all`, else `tool_cycle` |
@@ -141,7 +151,7 @@ the knobs its template actually exposes:
141
151
  | Nemotron-3 / 3.5 | `truncate_history_thinking=False -> all`; else `enable_thinking=False -> all`; else `tool_cycle` |
142
152
  | DeepSeek R1 | `template` |
143
153
  | MiniMax M2 | `tool_cycle` |
144
- | DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3 | `all` |
154
+ | DeepSeek V3, Qwen3-VL, Kimi K2, Laguna XS.2 / M.1 / XS-2.1 / S-2.1, Llama 3, Inkling | `all` |
145
155
  | PrimeIntellect Qwen3 | `all` |
146
156
 
147
157
  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
@@ -53,6 +53,7 @@ from renderers.configs import (
53
53
  Gemma4RendererConfig,
54
54
  GptOssRendererConfig,
55
55
  Hy3RendererConfig,
56
+ InklingRendererConfig,
56
57
  KimiK25RendererConfig,
57
58
  KimiK2RendererConfig,
58
59
  LagunaM1RendererConfig,
@@ -67,6 +68,7 @@ from renderers.configs import (
67
68
  PrimeQwen3RendererConfig,
68
69
  Qwen35RendererConfig,
69
70
  Qwen36RendererConfig,
71
+ Qwen38RendererConfig,
70
72
  Qwen3RendererConfig,
71
73
  Qwen3VLRendererConfig,
72
74
  RendererConfig,
@@ -93,6 +95,7 @@ _LAZY_RENDERERS: dict[str, str] = {
93
95
  "GptOssRenderer": "renderers.gpt_oss",
94
96
  "Gemma4Renderer": "renderers.gemma4",
95
97
  "Hy3Renderer": "renderers.hy3",
98
+ "InklingRenderer": "renderers.inkling",
96
99
  "KimiK25Renderer": "renderers.kimi_k25",
97
100
  "KimiK2Renderer": "renderers.kimi_k2",
98
101
  "LagunaM1Renderer": "renderers.laguna_xs2",
@@ -107,6 +110,7 @@ _LAZY_RENDERERS: dict[str, str] = {
107
110
  "PrimeQwen3Renderer": "renderers.prime_qwen3",
108
111
  "Qwen35Renderer": "renderers.qwen35",
109
112
  "Qwen36Renderer": "renderers.qwen36",
113
+ "Qwen38Renderer": "renderers.qwen38",
110
114
  "Qwen3Renderer": "renderers.qwen3",
111
115
  "Qwen3VLRenderer": "renderers.qwen3_vl",
112
116
  }
@@ -151,6 +155,8 @@ __all__ = [
151
155
  "Hy3Renderer",
152
156
  "Hy3RendererConfig",
153
157
  "ImagePart",
158
+ "InklingRenderer",
159
+ "InklingRendererConfig",
154
160
  "KimiK25Renderer",
155
161
  "KimiK25RendererConfig",
156
162
  "KimiK2Renderer",
@@ -188,6 +194,8 @@ __all__ = [
188
194
  "Qwen35RendererConfig",
189
195
  "Qwen36Renderer",
190
196
  "Qwen36RendererConfig",
197
+ "Qwen38Renderer",
198
+ "Qwen38RendererConfig",
191
199
  "Qwen3Renderer",
192
200
  "Qwen3RendererConfig",
193
201
  "Qwen3VLRenderer",
@@ -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.dev11'
22
- __version_tuple__ = version_tuple = (0, 1, 10, 'dev11')
21
+ __version__ = version = '0.1.10.dev15'
22
+ __version_tuple__ = version_tuple = (0, 1, 10, 'dev15')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -794,10 +794,10 @@ class MultimodalRenderer(Renderer, Protocol):
794
794
  """A :class:`Renderer` that supports multimodal inputs (images, video).
795
795
 
796
796
  Concrete classes (``Qwen3VLRenderer``, ``Qwen35Renderer``,
797
- ``Qwen36Renderer``, ``KimiK25Renderer``) implement this Protocol
798
- structurally — no explicit inheritance required. Callers that need
799
- to drive vLLM's ``multi_modal_data`` features field or carry images
800
- forward across turns should dispatch on ``isinstance(r,
797
+ ``Qwen36Renderer``, ``Qwen38Renderer``, ``KimiK25Renderer``) implement
798
+ this Protocol structurally — no explicit inheritance required.
799
+ Callers that need to drive vLLM's ``multi_modal_data`` features field or
800
+ carry images forward across turns should dispatch on ``isinstance(r,
801
801
  MultimodalRenderer)`` and use the extended ``bridge_to_next_turn``
802
802
  signature below.
803
803
  """
@@ -1031,6 +1031,8 @@ MODEL_RENDERER_MAP: dict[str, str] = {
1031
1031
  "Qwen/Qwen3.5-397B-A17B": "qwen3.5",
1032
1032
  # Qwen3.6.
1033
1033
  "Qwen/Qwen3.6-35B-A3B": "qwen3.6",
1034
+ # Qwen3.8.
1035
+ "Qwen/Qwen3.8-27B": "qwen3.8",
1034
1036
  # Qwen3-VL.
1035
1037
  "Qwen/Qwen3-VL-4B-Instruct": "qwen3-vl",
1036
1038
  "Qwen/Qwen3-VL-8B-Instruct": "qwen3-vl",
@@ -1093,6 +1095,10 @@ MODEL_RENDERER_MAP: dict[str, str] = {
1093
1095
  # GPT-OSS.
1094
1096
  "openai/gpt-oss-20b": "gpt-oss",
1095
1097
  "openai/gpt-oss-120b": "gpt-oss",
1098
+ # Thinking Machines Inkling checkpoints share byte-identical tokenizer,
1099
+ # chat-template, and processor assets (vision + audio; transformers >= 5.14).
1100
+ "thinkingmachines/Inkling": "inkling",
1101
+ "thinkingmachines/Inkling-Small": "inkling",
1096
1102
  # Tencent Hunyuan Hy3 (295B-A21B MoE). The FP8 checkpoint shares the same
1097
1103
  # tokenizer and chat template. Hy3-preview is deliberately unmapped: it
1098
1104
  # ships an older, incompatible template (un-suffixed special tokens,
@@ -1132,6 +1138,8 @@ MULTIMODAL_MODELS: dict[str, set[str]] = {
1132
1138
  # Qwen3.6 extends Qwen3.5's chat template; same VL bits, only
1133
1139
  # tool-call argument serialization differs.
1134
1140
  "Qwen/Qwen3.6-35B-A3B": {"image"},
1141
+ # Qwen3.8 adds reasoning-effort control and preserves thinking by default.
1142
+ "Qwen/Qwen3.8-27B": {"image"},
1135
1143
  # Kimi K2.5 / K2.6 are unified VLMs (HF tag ``image-text-to-text``)
1136
1144
  # with custom processor (``KimiK25Processor`` + ``KimiK25VisionProcessor``).
1137
1145
  # Vision wrap is different from Qwen-VL:
@@ -1141,6 +1149,8 @@ MULTIMODAL_MODELS: dict[str, set[str]] = {
1141
1149
  # ``grid_thws``.
1142
1150
  "moonshotai/Kimi-K2.5": {"image"},
1143
1151
  "moonshotai/Kimi-K2.6": {"image"},
1152
+ "thinkingmachines/Inkling": {"image", "audio"},
1153
+ "thinkingmachines/Inkling-Small": {"image", "audio"},
1144
1154
  }
1145
1155
 
1146
1156
 
@@ -1340,6 +1350,7 @@ def _populate_registry():
1340
1350
  from renderers.gpt_oss import GptOssRenderer
1341
1351
  from renderers.gemma4 import Gemma4Renderer
1342
1352
  from renderers.hy3 import Hy3Renderer
1353
+ from renderers.inkling import InklingRenderer
1343
1354
  from renderers.kimi_k2 import KimiK2Renderer
1344
1355
  from renderers.kimi_k25 import KimiK25Renderer
1345
1356
  from renderers.laguna_s21 import LagunaS21Renderer
@@ -1360,6 +1371,7 @@ def _populate_registry():
1360
1371
  from renderers.qwen3_vl import Qwen3VLRenderer
1361
1372
  from renderers.qwen35 import Qwen35Renderer
1362
1373
  from renderers.qwen36 import Qwen36Renderer
1374
+ from renderers.qwen38 import Qwen38Renderer
1363
1375
 
1364
1376
  RENDERER_REGISTRY.update(
1365
1377
  {
@@ -1370,6 +1382,7 @@ def _populate_registry():
1370
1382
  "gemma4": Gemma4Renderer,
1371
1383
  "qwen3.5": Qwen35Renderer,
1372
1384
  "qwen3.6": Qwen36Renderer,
1385
+ "qwen3.8": Qwen38Renderer,
1373
1386
  "glm-5": GLM5Renderer,
1374
1387
  "glm-5.1": GLM51Renderer,
1375
1388
  "glm-4.5": GLM45Renderer,
@@ -1377,6 +1390,7 @@ def _populate_registry():
1377
1390
  "deepseek-v3": DeepSeekV3Renderer,
1378
1391
  "deepseek-r1": DeepSeekR1Renderer,
1379
1392
  "hy3": Hy3Renderer,
1393
+ "inkling": InklingRenderer,
1380
1394
  "kimi-k2": KimiK2Renderer,
1381
1395
  "kimi-k2.5": KimiK25Renderer,
1382
1396
  "laguna-xs.2": LagunaXS2Renderer,
@@ -1483,12 +1497,31 @@ def _merge_chat_template_kwargs(
1483
1497
  return config
1484
1498
  if not isinstance(chat_template_kwargs, Mapping):
1485
1499
  raise TypeError("chat_template_kwargs must be a mapping.")
1500
+ kwargs = dict(chat_template_kwargs)
1501
+ config_cls = type(config)
1502
+ allowed = config_cls.template_field_names()
1503
+ if config_cls._allow_opaque_template_kwargs:
1504
+ reserved = frozenset(config_cls.model_fields) - allowed - {"name"}
1505
+ unsupported = frozenset(kwargs) & reserved
1506
+ else:
1507
+ unsupported = frozenset(kwargs) - allowed
1508
+ if unsupported:
1509
+ allowed_text = (
1510
+ "opaque Jinja kwargs"
1511
+ if config_cls._allow_opaque_template_kwargs
1512
+ else ", ".join(sorted(allowed)) or "(none)"
1513
+ )
1514
+ raise ValueError(
1515
+ f"Unsupported chat_template_kwargs for {config.name!r}: "
1516
+ f"{sorted(unsupported)}. Allowed: {allowed_text}. Pass "
1517
+ "renderer-internal options through the typed config instead."
1518
+ )
1486
1519
  data: dict[str, Any] = {"name": config.name}
1487
1520
  for field_name in config.__pydantic_fields_set__:
1488
1521
  data[field_name] = getattr(config, field_name)
1489
1522
  data.update(getattr(config, "model_extra", None) or {})
1490
- data.update(dict(chat_template_kwargs))
1491
- return type(config).model_validate(data)
1523
+ data.update(kwargs)
1524
+ return config_cls.model_validate(data)
1492
1525
 
1493
1526
 
1494
1527
  def _resolve_renderer_config(