renderers 0.1.8.dev61__tar.gz → 0.1.9.dev1__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 (74) hide show
  1. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/PKG-INFO +1 -1
  2. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/_version.py +2 -2
  3. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/kimi_k25.py +5 -3
  4. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/qwen35.py +5 -3
  5. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/qwen3_vl.py +6 -4
  6. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_multimodal.py +19 -0
  7. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/.github/workflows/publish-dev.yml +0 -0
  8. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/.github/workflows/publish.yml +0 -0
  9. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/.github/workflows/style.yml +0 -0
  10. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/.github/workflows/test.yml +0 -0
  11. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/.gitignore +0 -0
  12. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/.pre-commit-config.yaml +0 -0
  13. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/LICENSE +0 -0
  14. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/README.md +0 -0
  15. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/docs/renderer-config.md +0 -0
  16. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/examples/README.md +0 -0
  17. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/examples/sglang/multiturn_generate_sglang.py +0 -0
  18. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/examples/sglang/online_multiturn_sglang.py +0 -0
  19. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/examples/tinker/multiturn_generate_tinker.py +0 -0
  20. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/examples/transformers/multiturn_generate_transformers.py +0 -0
  21. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/examples/vllm/multiturn_generate_vllm.py +0 -0
  22. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/pyproject.toml +0 -0
  23. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/__init__.py +0 -0
  24. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/base.py +0 -0
  25. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/client.py +0 -0
  26. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/configs.py +0 -0
  27. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/deepseek_r1.py +0 -0
  28. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/deepseek_v3.py +0 -0
  29. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/default.py +0 -0
  30. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/glm45.py +0 -0
  31. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/glm5.py +0 -0
  32. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/gpt_oss.py +0 -0
  33. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/hy3.py +0 -0
  34. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/kimi_k2.py +0 -0
  35. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/laguna_xs2.py +0 -0
  36. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/llama_3.py +0 -0
  37. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/minimax_m2.py +0 -0
  38. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/nemotron3.py +0 -0
  39. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/parsers.py +0 -0
  40. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/parsing.py +0 -0
  41. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/prime_qwen3.py +0 -0
  42. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/qwen3.py +0 -0
  43. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/renderers/qwen36.py +0 -0
  44. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/conftest.py +0 -0
  45. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_bridge.py +0 -0
  46. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_build_helpers.py +0 -0
  47. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_client.py +0 -0
  48. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_deepseek_r1.py +0 -0
  49. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_gpt_oss_harmony_parity.py +0 -0
  50. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_hy3.py +0 -0
  51. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_incremental.py +0 -0
  52. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_is_content.py +0 -0
  53. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_kimi_k25_tool_schema.py +0 -0
  54. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_laguna_xs21.py +0 -0
  55. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_llama_3.py +0 -0
  56. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_load_tokenizer.py +0 -0
  57. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_message_indices.py +0 -0
  58. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_message_tool_names.py +0 -0
  59. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_nemotron3_parity.py +0 -0
  60. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_nemotron3_ultra.py +0 -0
  61. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_parse_response.py +0 -0
  62. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_parse_response_robustness.py +0 -0
  63. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_parsers.py +0 -0
  64. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_preserve_thinking.py +0 -0
  65. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_prime_qwen3_parity.py +0 -0
  66. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_qwen35_size_coverage.py +0 -0
  67. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_render_ids.py +0 -0
  68. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_renderer_config.py +0 -0
  69. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_renderer_config_parity.py +0 -0
  70. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_roundtrip.py +0 -0
  71. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_sampled_mask.py +0 -0
  72. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_tokens_per_message.py +0 -0
  73. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/tests/test_tool_arg_type_preservation.py +0 -0
  74. {renderers-0.1.8.dev61 → renderers-0.1.9.dev1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: renderers
3
- Version: 0.1.8.dev61
3
+ Version: 0.1.9.dev1
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
@@ -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.8.dev61'
22
- __version_tuple__ = version_tuple = (0, 1, 8, 'dev61')
21
+ __version__ = version = '0.1.9.dev1'
22
+ __version_tuple__ = version_tuple = (0, 1, 9, 'dev1')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -1183,18 +1183,20 @@ class KimiK25Renderer:
1183
1183
  emit_text("<think></think>", -1)
1184
1184
 
1185
1185
  # Merge prev mm_data (earlier-turn images) with the new turn's items.
1186
+ # Copy the per-modality lists (not just the outer dict) so appending
1187
+ # below never mutates the caller's previous_multi_modal_data.
1186
1188
  merged_hashes: dict[str, list[str]] = (
1187
- dict(previous_multi_modal_data.mm_hashes)
1189
+ {k: list(v) for k, v in previous_multi_modal_data.mm_hashes.items()}
1188
1190
  if previous_multi_modal_data
1189
1191
  else {}
1190
1192
  )
1191
1193
  merged_placeholders: dict[str, list[PlaceholderRange]] = (
1192
- dict(previous_multi_modal_data.mm_placeholders)
1194
+ {k: list(v) for k, v in previous_multi_modal_data.mm_placeholders.items()}
1193
1195
  if previous_multi_modal_data
1194
1196
  else {}
1195
1197
  )
1196
1198
  merged_items: dict[str, list[dict[str, Any]]] = (
1197
- dict(previous_multi_modal_data.mm_items)
1199
+ {k: list(v) for k, v in previous_multi_modal_data.mm_items.items()}
1198
1200
  if previous_multi_modal_data
1199
1201
  else {}
1200
1202
  )
@@ -834,18 +834,20 @@ class Qwen35Renderer:
834
834
  emit_text("\n\n", -1)
835
835
 
836
836
  # Merge prev mm_data (images from earlier turns) with the new turn's.
837
+ # Copy the per-modality lists (not just the outer dict) so appending
838
+ # below never mutates the caller's previous_multi_modal_data.
837
839
  merged_hashes: dict[str, list[str]] = (
838
- dict(previous_multi_modal_data.mm_hashes)
840
+ {k: list(v) for k, v in previous_multi_modal_data.mm_hashes.items()}
839
841
  if previous_multi_modal_data
840
842
  else {}
841
843
  )
842
844
  merged_placeholders: dict[str, list[PlaceholderRange]] = (
843
- dict(previous_multi_modal_data.mm_placeholders)
845
+ {k: list(v) for k, v in previous_multi_modal_data.mm_placeholders.items()}
844
846
  if previous_multi_modal_data
845
847
  else {}
846
848
  )
847
849
  merged_items: dict[str, list[dict[str, Any]]] = (
848
- dict(previous_multi_modal_data.mm_items)
850
+ {k: list(v) for k, v in previous_multi_modal_data.mm_items.items()}
849
851
  if previous_multi_modal_data
850
852
  else {}
851
853
  )
@@ -828,19 +828,21 @@ class Qwen3VLRenderer:
828
828
  em.text("assistant\n", is_sampled=False, is_content=False)
829
829
  em.finalize()
830
830
 
831
- # Merge prev mm_data with the new turn's items.
831
+ # Merge prev mm_data with the new turn's items. Copy the per-modality
832
+ # lists (not just the outer dict) so appending below never mutates the
833
+ # caller's previous_multi_modal_data.
832
834
  merged_hashes = (
833
- dict(previous_multi_modal_data.mm_hashes)
835
+ {k: list(v) for k, v in previous_multi_modal_data.mm_hashes.items()}
834
836
  if previous_multi_modal_data
835
837
  else {}
836
838
  )
837
839
  merged_placeholders = (
838
- dict(previous_multi_modal_data.mm_placeholders)
840
+ {k: list(v) for k, v in previous_multi_modal_data.mm_placeholders.items()}
839
841
  if previous_multi_modal_data
840
842
  else {}
841
843
  )
842
844
  merged_items = (
843
- dict(previous_multi_modal_data.mm_items)
845
+ {k: list(v) for k, v in previous_multi_modal_data.mm_items.items()}
844
846
  if previous_multi_modal_data
845
847
  else {}
846
848
  )
@@ -583,6 +583,12 @@ def test_multimodal_bridge_extends_and_carries_mm_data(
583
583
  ]
584
584
 
585
585
  initial_rendered = renderer.render(initial, add_generation_prompt=True)
586
+ prior_mm = initial_rendered.multi_modal_data
587
+ prior_counts = (
588
+ len(prior_mm.mm_placeholders.get(modality, [])),
589
+ len(prior_mm.mm_items.get(modality, [])),
590
+ len(prior_mm.mm_hashes.get(modality, [])),
591
+ )
586
592
  # ``previous_completion_ids`` mirrors what a sampler would emit
587
593
  # starting AFTER the prompt's assistant role opener — i.e. the
588
594
  # response text followed by ``<|im_end|>``.
@@ -632,6 +638,19 @@ def test_multimodal_bridge_extends_and_carries_mm_data(
632
638
  hashes = bridged_mm.mm_hashes.get(modality, [])
633
639
  assert len(items) == 2 and len(hashes) == 2
634
640
 
641
+ # (2b) The prior turn's sidecar is unchanged — the bridge copies the
642
+ # per-modality lists, so the carried-forward item doesn't grow the
643
+ # caller's previous_multi_modal_data in place.
644
+ assert (
645
+ (
646
+ len(prior_mm.mm_placeholders.get(modality, [])),
647
+ len(prior_mm.mm_items.get(modality, [])),
648
+ len(prior_mm.mm_hashes.get(modality, [])),
649
+ )
650
+ == prior_counts
651
+ == (1, 1, 1)
652
+ ), f"{mm_model_name} / {modality}: bridge mutated previous_multi_modal_data"
653
+
635
654
  # (3) Extension contains the new turn's pad run, and its
636
655
  # placeholder offset lands inside the extension region.
637
656
  pad_id = tokenizer.convert_tokens_to_ids(kit["placeholder_token"])
File without changes
File without changes