renderers 0.1.11.dev1__tar.gz → 0.1.11.dev3__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 (86) hide show
  1. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/PKG-INFO +31 -17
  2. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/README.md +25 -15
  3. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/docs/renderer-config.md +2 -7
  4. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/pyproject.toml +21 -5
  5. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/__init__.py +13 -15
  6. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/_version.py +2 -2
  7. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/base.py +221 -212
  8. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/client.py +10 -36
  9. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/deepseek_v3.py +7 -5
  10. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/default.py +2 -3
  11. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/gemma4.py +8 -8
  12. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/glm45.py +7 -5
  13. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/glm5.py +7 -5
  14. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/gpt_oss.py +7 -5
  15. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/hy3.py +45 -14
  16. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/inkling.py +8 -8
  17. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/kimi_k2.py +7 -5
  18. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/kimi_k25.py +13 -12
  19. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/laguna_s21.py +2 -3
  20. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/laguna_xs2.py +57 -12
  21. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/llama_3.py +7 -5
  22. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/minimax_m2.py +22 -9
  23. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/nemotron3.py +7 -5
  24. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/prime_qwen3.py +17 -8
  25. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/qwen3.py +7 -5
  26. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/qwen35.py +9 -9
  27. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/qwen3_vl.py +10 -10
  28. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_load_tokenizer.py +16 -11
  29. renderers-0.1.11.dev3/tests/test_offsetless_tokenizers.py +223 -0
  30. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_renderer_config.py +0 -26
  31. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/uv.lock +14 -1
  32. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/.github/workflows/publish-dev.yml +0 -0
  33. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/.github/workflows/publish.yml +0 -0
  34. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/.github/workflows/style.yml +0 -0
  35. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/.github/workflows/test.yml +0 -0
  36. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/.gitignore +0 -0
  37. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/.pre-commit-config.yaml +0 -0
  38. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/LICENSE +0 -0
  39. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/examples/README.md +0 -0
  40. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/examples/sglang/multiturn_generate_sglang.py +0 -0
  41. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/examples/sglang/online_multiturn_sglang.py +0 -0
  42. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/examples/tinker/multiturn_generate_tinker.py +0 -0
  43. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/examples/transformers/multiturn_generate_transformers.py +0 -0
  44. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/examples/vllm/multiturn_generate_vllm.py +0 -0
  45. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/configs.py +0 -0
  46. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/deepseek_r1.py +0 -0
  47. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/parsers.py +0 -0
  48. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/parsing.py +0 -0
  49. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/qwen36.py +0 -0
  50. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/renderers/qwen38.py +0 -0
  51. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/conftest.py +0 -0
  52. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_bridge.py +0 -0
  53. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_build_helpers.py +0 -0
  54. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_client.py +0 -0
  55. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_deepseek_r1.py +0 -0
  56. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_disabled_thinking_stability.py +0 -0
  57. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_gemma4.py +0 -0
  58. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_glm_tool_name_validation.py +0 -0
  59. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_gpt_oss_harmony_parity.py +0 -0
  60. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_hy3.py +0 -0
  61. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_incremental.py +0 -0
  62. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_inkling.py +0 -0
  63. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_is_content.py +0 -0
  64. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_kimi_k25_tool_schema.py +0 -0
  65. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_laguna_m1.py +0 -0
  66. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_laguna_s21.py +0 -0
  67. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_laguna_xs21.py +0 -0
  68. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_llama_3.py +0 -0
  69. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_message_indices.py +0 -0
  70. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_message_tool_names.py +0 -0
  71. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_multimodal.py +0 -0
  72. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_nemotron3_parity.py +0 -0
  73. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_nemotron3_ultra.py +0 -0
  74. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_parse_response.py +0 -0
  75. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_parse_response_robustness.py +0 -0
  76. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_parsers.py +0 -0
  77. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_preserve_thinking.py +0 -0
  78. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_prime_qwen3_parity.py +0 -0
  79. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_qwen35_size_coverage.py +0 -0
  80. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_qwen38.py +0 -0
  81. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_render_ids.py +0 -0
  82. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_renderer_config_parity.py +0 -0
  83. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_roundtrip.py +0 -0
  84. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_sampled_mask.py +0 -0
  85. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_tokens_per_message.py +0 -0
  86. {renderers-0.1.11.dev1 → renderers-0.1.11.dev3}/tests/test_tool_arg_type_preservation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: renderers
3
- Version: 0.1.11.dev1
3
+ Version: 0.1.11.dev3
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
@@ -11,7 +11,11 @@ Requires-Dist: openai-harmony>=0.0.4
11
11
  Requires-Dist: openai>=1.108.1
12
12
  Requires-Dist: prime-pydantic-config>=0.3.0.dev83
13
13
  Requires-Dist: tiktoken
14
- Requires-Dist: transformers>=4.50.0
14
+ Provides-Extra: multimodal
15
+ Requires-Dist: pillow>=12.2.0; extra == 'multimodal'
16
+ Requires-Dist: transformers>=4.50.0; extra == 'multimodal'
17
+ Provides-Extra: transformers
18
+ Requires-Dist: transformers>=4.50.0; extra == 'transformers'
15
19
  Description-Content-Type: text/markdown
16
20
 
17
21
  # renderers
@@ -26,13 +30,30 @@ Standalone on PyPI, and portable across training and inference stacks (transform
26
30
  uv add renderers
27
31
  ```
28
32
 
33
+ The base install supports text renderers with a bring-your-own tokenizer. Add
34
+ the Hugging Face integration for the tokenizer-loading helpers, or the complete
35
+ media stack for image/audio rendering:
36
+
37
+ ```bash
38
+ uv add 'renderers[transformers]'
39
+ uv add 'renderers[multimodal]'
40
+ ```
41
+
42
+ A BYO tokenizer must expose `encode`, `decode`, `convert_tokens_to_ids`, and
43
+ token IDs such as `eos_token_id`. Character offsets are optional: tokenizers
44
+ supporting `return_offsets_mapping=True` also receive precise per-token
45
+ `is_content` attribution; without offsets, renderers return `is_content=[]`.
46
+ `DefaultRenderer` additionally requires `apply_chat_template`. This includes
47
+ text-only Inkling training: `InklingRenderer` loads its Transformers processor
48
+ only when image or audio content is actually rendered.
49
+
29
50
  ## At a glance
30
51
 
31
52
  ```python
32
- from transformers import AutoTokenizer
33
53
  from renderers import create_renderer
54
+ from renderers.base import load_tokenizer
34
55
 
35
- tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")
56
+ tok = load_tokenizer("Qwen/Qwen3-8B") # renderers[transformers]
36
57
  r = create_renderer(tok) # → Qwen3Renderer (auto-resolved)
37
58
 
38
59
  prompt_ids = r.render_ids(
@@ -92,17 +113,10 @@ r = create_renderer(tok) # AutoRendererConfig is the implicit def
92
113
 
93
114
  Auto-detect matches `tokenizer.name_or_path` against `MODEL_RENDERER_MAP` by **exact match**. Prefix matching is intentionally off — same architecture can ship different chat templates (base vs instruct, fine-tune renames). Fine-tunes must pass an explicit typed config (e.g. `Qwen3RendererConfig()`). Unknown text-only names fall back to `DefaultRenderer`, unless `AutoRendererConfig(thinking_retention=...)` was set; the default renderer cannot implement that bridge policy.
94
115
 
95
- ### Pools
96
-
97
- ```python
98
- from renderers import create_renderer_pool
99
-
100
- pool = create_renderer_pool("Qwen/Qwen3-8B", size=16)
101
- with pool.checkout() as r:
102
- ids = r.render_ids(messages)
103
- ```
104
-
105
- Each slot owns its own tokenizer copy. Construction fans out across a thread pool so a 32-slot pool doesn't serially eat ~10–15s of `from_pretrained` calls at startup.
116
+ Without the `transformers` extra, exact-match registered models still
117
+ auto-resolve. For an unknown name, renderers cannot safely probe `AutoConfig`
118
+ to distinguish a text model from an unknown VLM; pass an explicit typed config
119
+ such as `DefaultRendererConfig()` for a known text-only model.
106
120
 
107
121
  ## Why use a renderer
108
122
 
@@ -125,7 +139,7 @@ Each break fragments a rollout into multiple training samples — every fragment
125
139
 
126
140
  ## Typed renderer configs
127
141
 
128
- Each renderer accepts a typed pydantic config at construction. Some fields mirror chat-template kwargs; others configure renderer-only behavior such as image caching, parsers, or Harmony preamble construction. `create_renderer` and `create_renderer_pool` take one positional `config` argument and an optional keyword-only `chat_template_kwargs` mapping:
142
+ Each renderer accepts a typed pydantic config at construction. Some fields mirror chat-template kwargs; others configure renderer-only behavior such as image caching, parsers, or Harmony preamble construction. `create_renderer` takes one positional `config` argument and an optional keyword-only `chat_template_kwargs` mapping:
129
143
 
130
144
  ```python
131
145
  from renderers import (
@@ -175,7 +189,7 @@ Fallback for unsupported text-only models. Wraps `apply_chat_template` and accep
175
189
 
176
190
  ## Roadmap
177
191
 
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`.
192
+ - **VLM expansion.** `ImagePart` support exists for Qwen3-VL, Qwen3.5-family, Gemma 4, and Kimi K2.5 / K2.6 multimodal templates. Install `renderers[multimodal]` for Pillow and the Hugging Face processors. Remaining work: audio/video support, broader VLM coverage, and more RL validation. Gemma 4 image preprocessing requires a Transformers release that provides `Gemma4Processor`.
179
193
  - **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
194
 
181
195
  ## Testing
@@ -10,13 +10,30 @@ Standalone on PyPI, and portable across training and inference stacks (transform
10
10
  uv add renderers
11
11
  ```
12
12
 
13
+ The base install supports text renderers with a bring-your-own tokenizer. Add
14
+ the Hugging Face integration for the tokenizer-loading helpers, or the complete
15
+ media stack for image/audio rendering:
16
+
17
+ ```bash
18
+ uv add 'renderers[transformers]'
19
+ uv add 'renderers[multimodal]'
20
+ ```
21
+
22
+ A BYO tokenizer must expose `encode`, `decode`, `convert_tokens_to_ids`, and
23
+ token IDs such as `eos_token_id`. Character offsets are optional: tokenizers
24
+ supporting `return_offsets_mapping=True` also receive precise per-token
25
+ `is_content` attribution; without offsets, renderers return `is_content=[]`.
26
+ `DefaultRenderer` additionally requires `apply_chat_template`. This includes
27
+ text-only Inkling training: `InklingRenderer` loads its Transformers processor
28
+ only when image or audio content is actually rendered.
29
+
13
30
  ## At a glance
14
31
 
15
32
  ```python
16
- from transformers import AutoTokenizer
17
33
  from renderers import create_renderer
34
+ from renderers.base import load_tokenizer
18
35
 
19
- tok = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")
36
+ tok = load_tokenizer("Qwen/Qwen3-8B") # renderers[transformers]
20
37
  r = create_renderer(tok) # → Qwen3Renderer (auto-resolved)
21
38
 
22
39
  prompt_ids = r.render_ids(
@@ -76,17 +93,10 @@ r = create_renderer(tok) # AutoRendererConfig is the implicit def
76
93
 
77
94
  Auto-detect matches `tokenizer.name_or_path` against `MODEL_RENDERER_MAP` by **exact match**. Prefix matching is intentionally off — same architecture can ship different chat templates (base vs instruct, fine-tune renames). Fine-tunes must pass an explicit typed config (e.g. `Qwen3RendererConfig()`). Unknown text-only names fall back to `DefaultRenderer`, unless `AutoRendererConfig(thinking_retention=...)` was set; the default renderer cannot implement that bridge policy.
78
95
 
79
- ### Pools
80
-
81
- ```python
82
- from renderers import create_renderer_pool
83
-
84
- pool = create_renderer_pool("Qwen/Qwen3-8B", size=16)
85
- with pool.checkout() as r:
86
- ids = r.render_ids(messages)
87
- ```
88
-
89
- Each slot owns its own tokenizer copy. Construction fans out across a thread pool so a 32-slot pool doesn't serially eat ~10–15s of `from_pretrained` calls at startup.
96
+ Without the `transformers` extra, exact-match registered models still
97
+ auto-resolve. For an unknown name, renderers cannot safely probe `AutoConfig`
98
+ to distinguish a text model from an unknown VLM; pass an explicit typed config
99
+ such as `DefaultRendererConfig()` for a known text-only model.
90
100
 
91
101
  ## Why use a renderer
92
102
 
@@ -109,7 +119,7 @@ Each break fragments a rollout into multiple training samples — every fragment
109
119
 
110
120
  ## Typed renderer configs
111
121
 
112
- Each renderer accepts a typed pydantic config at construction. Some fields mirror chat-template kwargs; others configure renderer-only behavior such as image caching, parsers, or Harmony preamble construction. `create_renderer` and `create_renderer_pool` take one positional `config` argument and an optional keyword-only `chat_template_kwargs` mapping:
122
+ Each renderer accepts a typed pydantic config at construction. Some fields mirror chat-template kwargs; others configure renderer-only behavior such as image caching, parsers, or Harmony preamble construction. `create_renderer` takes one positional `config` argument and an optional keyword-only `chat_template_kwargs` mapping:
113
123
 
114
124
  ```python
115
125
  from renderers import (
@@ -159,7 +169,7 @@ Fallback for unsupported text-only models. Wraps `apply_chat_template` and accep
159
169
 
160
170
  ## Roadmap
161
171
 
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`.
172
+ - **VLM expansion.** `ImagePart` support exists for Qwen3-VL, Qwen3.5-family, Gemma 4, and Kimi K2.5 / K2.6 multimodal templates. Install `renderers[multimodal]` for Pillow and the Hugging Face processors. Remaining work: audio/video support, broader VLM coverage, and more RL validation. Gemma 4 image preprocessing requires a Transformers release that provides `Gemma4Processor`.
163
173
  - **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
174
 
165
175
  ## Testing
@@ -1,8 +1,7 @@
1
1
  # Renderer config
2
2
 
3
- `renderers.RendererConfig` is the typed input to `create_renderer` and
4
- `create_renderer_pool`. It pins the renderer choice and its config at
5
- construction time.
3
+ `renderers.RendererConfig` is the typed input to `create_renderer`. It pins the
4
+ renderer choice and its config at construction time.
6
5
 
7
6
  ```python
8
7
  from renderers import create_renderer, Qwen35RendererConfig
@@ -77,10 +76,6 @@ r = create_renderer(
77
76
  tokenizer,
78
77
  chat_template_kwargs={"enable_thinking": False},
79
78
  )
80
- pool = create_renderer_pool(
81
- "Qwen/Qwen3-8B",
82
- chat_template_kwargs={"enable_thinking": False},
83
- )
84
79
  ```
85
80
 
86
81
  Renderers resolves auto configs before applying `chat_template_kwargs`, so the
@@ -19,11 +19,6 @@ 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).
26
- "transformers>=4.50.0",
27
22
  # Used by GptOssRenderer to render and parse harmony tokens. Vendoring
28
23
  # OpenAI's reference implementation keeps us byte-identical with vLLM
29
24
  # (which also uses it) and saves us mirroring a 330-line Jinja template.
@@ -42,6 +37,25 @@ dependencies = [
42
37
  "prime-pydantic-config>=0.3.0.dev83",
43
38
  ]
44
39
 
40
+ [project.optional-dependencies]
41
+ # Tokenizer loading uses Hugging Face. Text-only renderers can instead be
42
+ # constructed with a compatible BYO tokenizer and do not import this
43
+ # dependency. Character offsets are optional.
44
+ transformers = [
45
+ # Keep this floor compatible with prime-rl's transformers pin. Inkling's
46
+ # tokenizer and text-only renderer work on older releases; image/audio
47
+ # processing fails lazily with an upgrade message when InklingProcessor is
48
+ # unavailable (native support starts in transformers 5.14).
49
+ "transformers>=4.50.0",
50
+ ]
51
+
52
+ # Image/audio renderers also need Pillow to resolve media inputs. Keep this as
53
+ # a separate convenience extra so tokenizer-only users do not pull it in.
54
+ multimodal = [
55
+ "pillow>=12.2.0",
56
+ "transformers>=4.50.0",
57
+ ]
58
+
45
59
  [tool.hatch.version]
46
60
  source = "vcs"
47
61
  # Tags look like ``renderers-v0.1.8`` (prefix matches the publish.yml
@@ -90,6 +104,8 @@ dev = [
90
104
  "torch>=2.11.0",
91
105
  "torchvision>=0.26.0",
92
106
  "ty>=0.0.1a29,<0.0.22",
107
+ # Optional for consumers, but required by tokenizer/parity/VLM tests.
108
+ "transformers>=4.50.0",
93
109
  ]
94
110
 
95
111
  [tool.uv]
@@ -7,6 +7,7 @@ except ImportError:
7
7
  __version__ = "0+unknown"
8
8
 
9
9
  from renderers.base import (
10
+ ChatTemplateTokenizer,
10
11
  Content,
11
12
  ContentPart,
12
13
  ImagePart,
@@ -14,6 +15,7 @@ from renderers.base import (
14
15
  Message,
15
16
  MultiModalData,
16
17
  MultimodalRenderer,
18
+ OffsetTokenizer,
17
19
  ParsedResponse,
18
20
  ParsedToolCall,
19
21
  PlaceholderRange,
@@ -21,9 +23,9 @@ from renderers.base import (
21
23
  RenderedTokens,
22
24
  RenderedTrainingSample,
23
25
  Renderer,
24
- RendererPool,
25
26
  TextPart,
26
27
  ThinkingPart,
28
+ Tokenizer,
27
29
  ToolCall,
28
30
  ToolCallFunction,
29
31
  ToolCallParseStatus,
@@ -33,7 +35,6 @@ from renderers.base import (
33
35
  build_training_sample,
34
36
  build_trajectory_step,
35
37
  create_renderer,
36
- create_renderer_pool,
37
38
  extract_message_tool_names,
38
39
  is_multimodal,
39
40
  reject_assistant_in_extension,
@@ -74,17 +75,13 @@ from renderers.configs import (
74
75
  RendererConfig,
75
76
  )
76
77
 
77
- # Concrete renderer classes are lazy-loaded so that consumers needing
78
- # only the config layer (``RendererConfig`` discriminated union) don't
79
- # pay the ``transformers`` import cost. Each renderer module does
80
- # ``from transformers.tokenization_utils import PreTrainedTokenizer``
81
- # at module level, so eager imports here would drag ``transformers``
82
- # into every downstream ``import renderers``. ``__getattr__`` (PEP 562)
83
- # resolves the names on first attribute access, so ``from renderers
84
- # import DefaultRenderer`` and ``renderers.DefaultRenderer`` both work
85
- # transparently. ``create_renderer`` doesn't depend on these eager
86
- # imports — ``renderers.base._populate_registry`` lazy-imports the
87
- # concrete classes itself when a renderer is instantiated.
78
+ # Concrete renderer classes are lazy-loaded so that consumers needing only the
79
+ # config layer (``RendererConfig`` discriminated union) don't import every
80
+ # renderer module. Renderer tokenizer annotations use the local ``Tokenizer``
81
+ # protocols, so resolving a text renderer remains safe when the optional
82
+ # ``transformers`` dependency is absent. ``__getattr__`` (PEP 562) resolves the
83
+ # names on first attribute access, while ``renderers.base._populate_registry``
84
+ # handles lazy registration for ``create_renderer``.
88
85
  _LAZY_RENDERERS: dict[str, str] = {
89
86
  "DeepSeekR1Renderer": "renderers.deepseek_r1",
90
87
  "DeepSeekV3Renderer": "renderers.deepseek_v3",
@@ -134,6 +131,7 @@ def __dir__() -> list[str]:
134
131
  __all__ = [
135
132
  "AutoRendererConfig",
136
133
  "BaseRendererConfig",
134
+ "ChatTemplateTokenizer",
137
135
  "Content",
138
136
  "ContentPart",
139
137
  "DeepSeekR1Renderer",
@@ -184,6 +182,7 @@ __all__ = [
184
182
  "Nemotron3RendererConfig",
185
183
  "Nemotron3UltraRenderer",
186
184
  "Nemotron3UltraRendererConfig",
185
+ "OffsetTokenizer",
187
186
  "OverlongPromptError",
188
187
  "ParsedResponse",
189
188
  "ParsedToolCall",
@@ -205,9 +204,9 @@ __all__ = [
205
204
  "RenderedTrainingSample",
206
205
  "Renderer",
207
206
  "RendererConfig",
208
- "RendererPool",
209
207
  "TextPart",
210
208
  "ThinkingPart",
209
+ "Tokenizer",
211
210
  "ToolCall",
212
211
  "ToolCallFunction",
213
212
  "ToolCallParseStatus",
@@ -219,7 +218,6 @@ __all__ = [
219
218
  "build_trajectory_step",
220
219
  "config_from_name",
221
220
  "create_renderer",
222
- "create_renderer_pool",
223
221
  "extract_message_tool_names",
224
222
  "is_multimodal",
225
223
  "reject_assistant_in_extension",
@@ -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.dev1'
22
- __version_tuple__ = version_tuple = (0, 1, 11, 'dev1')
21
+ __version__ = version = '0.1.11.dev3'
22
+ __version_tuple__ = version_tuple = (0, 1, 11, 'dev3')
23
23
 
24
24
  __commit_id__ = commit_id = None