renderers 0.1.13.dev15__tar.gz → 0.1.13.dev17__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 (99) hide show
  1. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/PKG-INFO +1 -1
  2. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/__init__.py +16 -9
  3. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/_version.py +2 -2
  4. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/client.py +9 -6
  5. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/replay.py +1 -1
  6. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_client.py +11 -5
  7. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/.github/workflows/publish-dev.yml +0 -0
  8. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/.github/workflows/publish.yml +0 -0
  9. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/.github/workflows/style.yml +0 -0
  10. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/.github/workflows/test.yml +0 -0
  11. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/.gitignore +0 -0
  12. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/.pre-commit-config.yaml +0 -0
  13. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/LICENSE +0 -0
  14. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/README.md +0 -0
  15. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/docs/renderer-config.md +0 -0
  16. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/examples/README.md +0 -0
  17. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/examples/sglang/multiturn_generate_sglang.py +0 -0
  18. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/examples/sglang/online_multiturn_sglang.py +0 -0
  19. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/examples/tinker/multiturn_generate_tinker.py +0 -0
  20. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/examples/transformers/multiturn_generate_transformers.py +0 -0
  21. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/examples/vllm/multiturn_generate_vllm.py +0 -0
  22. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/pyproject.toml +0 -0
  23. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/base.py +0 -0
  24. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/configs.py +0 -0
  25. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/custom.py +0 -0
  26. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/deepseek_r1.py +0 -0
  27. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/deepseek_v3.py +0 -0
  28. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/deepseek_v4.py +0 -0
  29. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/default.py +0 -0
  30. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/gemma4.py +0 -0
  31. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/glm45.py +0 -0
  32. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/glm5.py +0 -0
  33. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/gpt_oss.py +0 -0
  34. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/hy3.py +0 -0
  35. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/inkling.py +0 -0
  36. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/kimi_k2.py +0 -0
  37. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/kimi_k25.py +0 -0
  38. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/laguna_s21.py +0 -0
  39. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/laguna_xs2.py +0 -0
  40. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/llama_3.py +0 -0
  41. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/minimax_m2.py +0 -0
  42. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/muse_glimmer.py +0 -0
  43. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/nemotron3.py +0 -0
  44. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/parsers.py +0 -0
  45. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/parsing.py +0 -0
  46. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/prime_qwen3.py +0 -0
  47. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/qwen3.py +0 -0
  48. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/qwen35.py +0 -0
  49. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/qwen36.py +0 -0
  50. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/qwen38.py +0 -0
  51. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/qwen3_vl.py +0 -0
  52. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/reasoning.py +0 -0
  53. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/renderers/tagged.py +0 -0
  54. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/audit_prefix_stability.py +0 -0
  55. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/conftest.py +0 -0
  56. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/fixtures/prefix_stability.json +0 -0
  57. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/parity.py +0 -0
  58. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/reference_continuations.py +0 -0
  59. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/reference_rendering.py +0 -0
  60. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_bridge.py +0 -0
  61. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_build_helpers.py +0 -0
  62. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_custom.py +0 -0
  63. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_deepseek_r1.py +0 -0
  64. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_deepseek_v4.py +0 -0
  65. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_disabled_thinking_stability.py +0 -0
  66. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_gemma4.py +0 -0
  67. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_glm53_flash.py +0 -0
  68. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_glm_tool_name_validation.py +0 -0
  69. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_hy3.py +0 -0
  70. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_incremental.py +0 -0
  71. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_inkling.py +0 -0
  72. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_is_content.py +0 -0
  73. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_kimi_k25_tool_schema.py +0 -0
  74. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_laguna_m1.py +0 -0
  75. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_laguna_xs21.py +0 -0
  76. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_llama_3.py +0 -0
  77. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_load_tokenizer.py +0 -0
  78. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_message_indices.py +0 -0
  79. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_message_tool_names.py +0 -0
  80. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_multimodal.py +0 -0
  81. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_muse_glimmer.py +0 -0
  82. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_nemotron35_vlm.py +0 -0
  83. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_nemotron3_ultra.py +0 -0
  84. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_offsetless_tokenizers.py +0 -0
  85. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_parity.py +0 -0
  86. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_parse_response.py +0 -0
  87. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_parse_response_robustness.py +0 -0
  88. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_parsers.py +0 -0
  89. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_preserve_thinking.py +0 -0
  90. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_qwen35_size_coverage.py +0 -0
  91. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_qwen38.py +0 -0
  92. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_reasoning_boundaries.py +0 -0
  93. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_renderer_config.py +0 -0
  94. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_roundtrip.py +0 -0
  95. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_sampled_mask.py +0 -0
  96. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_tagged_rollout.py +0 -0
  97. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_tokens_per_message.py +0 -0
  98. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/tests/test_tool_arg_type_preservation.py +0 -0
  99. {renderers-0.1.13.dev15 → renderers-0.1.13.dev17}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: renderers
3
- Version: 0.1.13.dev15
3
+ Version: 0.1.13.dev17
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
@@ -6,6 +6,8 @@ except ImportError:
6
6
  # installs always have it.
7
7
  __version__ = "0+unknown"
8
8
 
9
+ from typing import TYPE_CHECKING
10
+
9
11
  from renderers.base import (
10
12
  MULTIMODAL_MODELS,
11
13
  ChatTemplateTokenizer,
@@ -42,7 +44,6 @@ from renderers.base import (
42
44
  template_field_names,
43
45
  trim_to_turn_close,
44
46
  )
45
- from renderers.client import MalformedGenerateResponseError, OverlongPromptError
46
47
  from renderers.configs import (
47
48
  AutoRendererConfig,
48
49
  BaseRendererConfig,
@@ -83,14 +84,18 @@ from renderers.configs import (
83
84
  )
84
85
  from renderers.custom import custom_renderer_config, load_custom_renderer, load_target
85
86
 
86
- # Concrete renderer classes are lazy-loaded so that consumers needing only the
87
- # config layer (``RendererConfig`` discriminated union) don't import every
88
- # renderer module. Renderer tokenizer annotations use the local ``Tokenizer``
89
- # protocols, so resolving a text renderer remains safe when the optional
87
+ if TYPE_CHECKING:
88
+ from renderers.client import MalformedGenerateResponseError, OverlongPromptError
89
+
90
+ # Concrete renderer classes and the HTTP client's errors are lazy-loaded so that
91
+ # consumers needing only the config layer (``RendererConfig`` discriminated union)
92
+ # import neither every renderer module nor the OpenAI client. Renderer tokenizer
93
+ # annotations use the local ``Tokenizer`` protocols, so resolving a text renderer
94
+ # remains safe when the optional
90
95
  # ``transformers`` dependency is absent. ``__getattr__`` (PEP 562) resolves the
91
96
  # names on first attribute access, while ``renderers.base._populate_registry``
92
97
  # handles lazy registration for ``create_renderer``.
93
- _LAZY_RENDERERS: dict[str, str] = {
98
+ _LAZY_EXPORTS: dict[str, str] = {
94
99
  "DeepSeekR1Renderer": "renderers.deepseek_r1",
95
100
  "DeepSeekV3Renderer": "renderers.deepseek_v3",
96
101
  "DeepSeekV4Renderer": "renderers.deepseek_v4",
@@ -111,11 +116,13 @@ _LAZY_RENDERERS: dict[str, str] = {
111
116
  "LagunaXS21Renderer": "renderers.laguna_xs2",
112
117
  "LagunaXS2Renderer": "renderers.laguna_xs2",
113
118
  "Llama3Renderer": "renderers.llama_3",
119
+ "MalformedGenerateResponseError": "renderers.client",
114
120
  "MiniMaxM2Renderer": "renderers.minimax_m2",
115
121
  "MuseGlimmerRenderer": "renderers.muse_glimmer",
116
122
  "Nemotron35Renderer": "renderers.nemotron3",
117
123
  "Nemotron3Renderer": "renderers.nemotron3",
118
124
  "Nemotron3UltraRenderer": "renderers.nemotron3",
125
+ "OverlongPromptError": "renderers.client",
119
126
  "PrimeQwen3Renderer": "renderers.prime_qwen3",
120
127
  "Qwen35Renderer": "renderers.qwen35",
121
128
  "Qwen36Renderer": "renderers.qwen36",
@@ -126,10 +133,10 @@ _LAZY_RENDERERS: dict[str, str] = {
126
133
 
127
134
 
128
135
  def __getattr__(name: str):
129
- if name in _LAZY_RENDERERS:
136
+ if name in _LAZY_EXPORTS:
130
137
  import importlib
131
138
 
132
- module = importlib.import_module(_LAZY_RENDERERS[name])
139
+ module = importlib.import_module(_LAZY_EXPORTS[name])
133
140
  value = getattr(module, name)
134
141
  globals()[name] = value # cache for subsequent lookups
135
142
  return value
@@ -137,7 +144,7 @@ def __getattr__(name: str):
137
144
 
138
145
 
139
146
  def __dir__() -> list[str]:
140
- return sorted(set(globals().keys()) | set(_LAZY_RENDERERS))
147
+ return sorted(set(globals().keys()) | set(_LAZY_EXPORTS))
141
148
 
142
149
 
143
150
  __all__ = [
@@ -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.13.dev15'
22
- __version_tuple__ = version_tuple = (0, 1, 13, 'dev15')
21
+ __version__ = version = '0.1.13.dev17'
22
+ __version_tuple__ = version_tuple = (0, 1, 13, 'dev17')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -157,7 +157,7 @@ def parse_generate_response(raw: bytes) -> dict[str, Any]:
157
157
 
158
158
  def _parse_packed_completion_logprobs(
159
159
  packed: Mapping[str, Any], completion_ids: list[int]
160
- ) -> list[float]:
160
+ ) -> np.ndarray:
161
161
  """prime-rl's packed form: base64 float32 ``{data, shape, dtype}``, one per token."""
162
162
  values = np.frombuffer(base64.b64decode(packed["data"]), dtype=packed["dtype"])
163
163
  valid = np.isfinite(values) & (values > VLLM_LOGPROB_SENTINEL)
@@ -166,12 +166,12 @@ def _parse_packed_completion_logprobs(
166
166
  f"Engine response choice.completion_logprobs: {len(values)} values for "
167
167
  f"{len(completion_ids)} completion tokens, or a non-finite or missing (-9999) logprob."
168
168
  )
169
- return values.astype(np.float64).tolist()
169
+ return values.astype(np.float32, copy=False)
170
170
 
171
171
 
172
172
  def _parse_compact_logprobs(
173
173
  choice: Mapping[str, Any], completion_ids: list[int], k: int | None
174
- ) -> tuple[list[float], list[list[int]] | None, list[list[float]] | None]:
174
+ ) -> tuple[np.ndarray, list[list[int]] | None, list[list[float]] | None]:
175
175
  packed = choice.get("compact_logprobs")
176
176
  if not isinstance(packed, Mapping) or packed.get("format") != "u32-f32-le-v1":
177
177
  raise MalformedGenerateResponseError("Unsupported compact logprob format")
@@ -227,7 +227,7 @@ def _parse_compact_logprobs(
227
227
  top_ids.append(selected_ids)
228
228
  top_logprobs.append(selected_logprobs)
229
229
  return (
230
- sampled,
230
+ np.asarray(sampled, dtype=np.float32),
231
231
  top_ids if k is not None else None,
232
232
  top_logprobs if k is not None else None,
233
233
  )
@@ -245,7 +245,7 @@ def _logprob_token_id(entry: Mapping[str, Any]) -> int | None:
245
245
 
246
246
  def _parse_completion_logprobs(
247
247
  choice: Mapping[str, Any], completion_ids: list[int]
248
- ) -> list[float]:
248
+ ) -> np.ndarray:
249
249
  packed = choice.get("completion_logprobs")
250
250
  if packed is not None:
251
251
  return _parse_packed_completion_logprobs(packed, completion_ids)
@@ -297,7 +297,7 @@ def _parse_completion_logprobs(
297
297
  f"Engine response choice.logprobs.content[{index}].logprob does not contain sampling evidence."
298
298
  )
299
299
  completion_logprobs.append(logprob)
300
- return completion_logprobs
300
+ return np.asarray(completion_logprobs, dtype=np.float32)
301
301
 
302
302
 
303
303
  def _parse_sampling_mask(
@@ -501,6 +501,9 @@ async def generate(
501
501
  mm_placeholders, completion_ids, completion_logprobs, content,
502
502
  reasoning_content, tool_calls, finish_reason, routed_experts,
503
503
  weight_versions, multi_modal_data, prompt_attribution.
504
+ ``completion_logprobs`` is a float32 numpy array, one value per completion
505
+ token: vLLM samples in float32, so the packed form decodes without a copy
506
+ and the JSON forms narrow losslessly.
504
507
  ``renderer_prompt_ids`` is the unexpanded logical prompt when
505
508
  ``process_multimodal=False`` and ``None`` otherwise. ``weight_versions``
506
509
  is vLLM's (>= 0.32) list of ``{version, start, end}`` spans over
@@ -44,5 +44,5 @@ def replay_client(completions, requests):
44
44
 
45
45
  def assert_evidence(result, ids):
46
46
  assert result["completion_ids"] == ids
47
- assert result["completion_logprobs"] == [-0.25] * len(ids)
47
+ assert result["completion_logprobs"].tolist() == [-0.25] * len(ids)
48
48
  assert result["sampling_mask"] == [[tid] for tid in ids]
@@ -122,9 +122,14 @@ def test_parse_generate_response_preserves_multiple_compact_choices(separators):
122
122
  json.dumps({"choices": choices}, separators=separators).encode(),
123
123
  )
124
124
  for i, choice in zip((7, 8), payload["choices"]):
125
- assert _parse_compact_logprobs(choice, [i], None) == ([-0.5], None, None)
125
+ sampled, top_ids, top_logprobs = _parse_compact_logprobs(choice, [i], None)
126
+ assert sampled.dtype == np.float32 and sampled.tolist() == [-0.5]
127
+ assert top_ids is None and top_logprobs is None
126
128
  assert base64.b64decode(choice["routed_experts"]["data"]) == bytes([i])
127
- assert _parse_compact_logprobs(payload["choices"][2], [], 2) == ([], [], [])
129
+ sampled, top_ids, top_logprobs = _parse_compact_logprobs(
130
+ payload["choices"][2], [], 2
131
+ )
132
+ assert sampled.tolist() == [] and top_ids == [] and top_logprobs == []
128
133
 
129
134
 
130
135
  @pytest.mark.parametrize("sampled", [1, 3, 5])
@@ -594,7 +599,7 @@ def test_generate_preserves_zero_completion_logprob():
594
599
 
595
600
  result = _run_generate(client)
596
601
 
597
- assert result["completion_logprobs"] == [0.0, -0.2]
602
+ assert result["completion_logprobs"] == pytest.approx([0.0, -0.2])
598
603
 
599
604
 
600
605
  def test_generate_reads_packed_completion_logprobs():
@@ -609,7 +614,8 @@ def test_generate_reads_packed_completion_logprobs():
609
614
 
610
615
  result = _run_generate(client)
611
616
 
612
- assert result["completion_logprobs"] == values.tolist()
617
+ assert result["completion_logprobs"].dtype == np.float32
618
+ assert result["completion_logprobs"].tolist() == values.tolist()
613
619
 
614
620
  client.choice["completion_logprobs"]["data"] = base64.b64encode(
615
621
  np.array([-0.1, -9999.0], dtype=np.float32).tobytes()
@@ -1141,5 +1147,5 @@ def test_generate_forwards_reasoning_state_without_classifying_acceptance(
1141
1147
  assert result["reasoning_complete"] is reasoning_complete
1142
1148
  assert result["finish_reason"] == finish
1143
1149
  assert result["completion_ids"] == [7, 8]
1144
- assert result["completion_logprobs"] == [-0.1, -0.2]
1150
+ assert result["completion_logprobs"] == pytest.approx([-0.1, -0.2])
1145
1151
  assert result["content"] == ""