renderers 0.2.0__tar.gz → 0.2.1.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.
- renderers-0.2.1.dev1/.github/workflows/test.yml +69 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/PKG-INFO +1 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/pyproject.toml +6 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/_version.py +2 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/base.py +7 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/conftest.py +7 -3
- renderers-0.2.1.dev1/tests/model_assets.py +94 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_bridge.py +9 -4
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_client.py +8 -6
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_custom.py +2 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_deepseek_r1.py +5 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_deepseek_v4.py +5 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_disabled_thinking_stability.py +5 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_gemma4.py +11 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_glm53_flash.py +3 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_glm_tool_name_validation.py +5 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_hy3.py +4 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_inkling.py +4 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_laguna_m1.py +4 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_laguna_xs21.py +3 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_llama_3.py +4 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_load_tokenizer.py +25 -9
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_message_indices.py +3 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_multimodal.py +47 -63
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_muse_glimmer.py +5 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_nemotron35_vlm.py +2 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_offsetless_tokenizers.py +3 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_parity.py +5 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_parse_response.py +5 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_parsers.py +4 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_qwen35_size_coverage.py +3 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_qwen38.py +5 -2
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_reasoning_boundaries.py +5 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_roundtrip.py +4 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_tagged_rollout.py +4 -1
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_tool_arg_type_preservation.py +3 -1
- renderers-0.2.0/.github/workflows/test.yml +0 -41
- {renderers-0.2.0 → renderers-0.2.1.dev1}/.github/workflows/publish-dev.yml +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/.github/workflows/publish.yml +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/.github/workflows/style.yml +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/.gitignore +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/.pre-commit-config.yaml +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/LICENSE +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/README.md +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/docs/renderer-config.md +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/examples/README.md +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/examples/sglang/multiturn_generate_sglang.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/examples/sglang/online_multiturn_sglang.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/examples/tinker/multiturn_generate_tinker.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/examples/transformers/multiturn_generate_transformers.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/examples/vllm/multiturn_generate_vllm.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/__init__.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/client.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/configs.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/custom.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/deepseek_r1.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/deepseek_v3.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/deepseek_v4.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/default.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/gemma4.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/glm45.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/glm5.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/gpt_oss.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/hy3.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/inkling.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/kimi_k2.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/kimi_k25.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/laguna_s21.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/laguna_xs2.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/llama_3.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/minimax_m2.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/muse_glimmer.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/nemotron3.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/parsers.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/parsing.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/prime_qwen3.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/qwen3.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/qwen35.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/qwen36.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/qwen38.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/qwen3_vl.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/reasoning.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/renderers/tagged.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/audit_prefix_stability.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/fixtures/prefix_stability.json +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/parity.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/reference_continuations.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/reference_rendering.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/replay.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_build_helpers.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_incremental.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_is_content.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_kimi_k25_tool_schema.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_message_tool_names.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_nemotron3_ultra.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_parse_response_robustness.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_preserve_thinking.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_renderer_config.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_sampled_mask.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/tests/test_tokens_per_message.py +0 -0
- {renderers-0.2.0 → renderers-0.2.1.dev1}/uv.lock +0 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "**.py"
|
|
8
|
+
- "pyproject.toml"
|
|
9
|
+
- "uv.lock"
|
|
10
|
+
- ".github/workflows/test.yml"
|
|
11
|
+
pull_request:
|
|
12
|
+
branches: [main]
|
|
13
|
+
paths:
|
|
14
|
+
- "**.py"
|
|
15
|
+
- "pyproject.toml"
|
|
16
|
+
- "uv.lock"
|
|
17
|
+
- ".github/workflows/test.yml"
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
unit:
|
|
24
|
+
name: Offline (Python ${{ matrix.python-version }})
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
env:
|
|
27
|
+
HF_HUB_OFFLINE: "1"
|
|
28
|
+
TRANSFORMERS_OFFLINE: "1"
|
|
29
|
+
strategy:
|
|
30
|
+
fail-fast: false
|
|
31
|
+
matrix:
|
|
32
|
+
python-version: ["3.11", "3.12", "3.13", "3.14"]
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: actions/setup-python@v6
|
|
36
|
+
with:
|
|
37
|
+
python-version: ${{ matrix.python-version }}
|
|
38
|
+
- uses: astral-sh/setup-uv@v7
|
|
39
|
+
- run: uv sync --locked
|
|
40
|
+
- run: uv run --no-sync pytest tests/ -m "not model" -v
|
|
41
|
+
|
|
42
|
+
model:
|
|
43
|
+
name: Pinned ${{ matrix.suite }} parity
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
timeout-minutes: 45
|
|
46
|
+
strategy:
|
|
47
|
+
fail-fast: false
|
|
48
|
+
matrix:
|
|
49
|
+
include:
|
|
50
|
+
- suite: text
|
|
51
|
+
selection: "model and not multimodal"
|
|
52
|
+
- suite: multimodal
|
|
53
|
+
selection: "model and multimodal"
|
|
54
|
+
env:
|
|
55
|
+
HF_HUB_DISABLE_PROGRESS_BARS: "1"
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/checkout@v4
|
|
58
|
+
- uses: actions/setup-python@v6
|
|
59
|
+
with:
|
|
60
|
+
python-version: "3.13"
|
|
61
|
+
- uses: astral-sh/setup-uv@v7
|
|
62
|
+
- uses: actions/cache@v4
|
|
63
|
+
with:
|
|
64
|
+
path: |
|
|
65
|
+
~/.cache/huggingface/hub
|
|
66
|
+
~/.cache/huggingface/modules
|
|
67
|
+
key: hf-renderers-${{ runner.os }}-${{ matrix.suite }}-${{ hashFiles('tests/model_assets.py') }}
|
|
68
|
+
- run: uv sync --locked
|
|
69
|
+
- run: uv run --no-sync pytest tests/ -m "${{ matrix.selection }}" -v
|
|
@@ -139,3 +139,9 @@ invalid-key = "warn"
|
|
|
139
139
|
unsupported-operator = "warn"
|
|
140
140
|
invalid-return-type = "warn"
|
|
141
141
|
invalid-assignment = "warn"
|
|
142
|
+
|
|
143
|
+
[tool.pytest.ini_options]
|
|
144
|
+
markers = [
|
|
145
|
+
"model: requires real tokenizer or processor assets at pinned revisions",
|
|
146
|
+
"multimodal: exercises real image/audio/video processors or model sidecars",
|
|
147
|
+
]
|
|
@@ -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.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.1.dev1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 1, 'dev1')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1313,13 +1313,14 @@ def _load_tokenizer_via_auto(model_name_or_path: str, **kwargs) -> Any:
|
|
|
1313
1313
|
return tok
|
|
1314
1314
|
|
|
1315
1315
|
|
|
1316
|
-
def load_tokenizer(model_name_or_path: str):
|
|
1316
|
+
def load_tokenizer(model_name_or_path: str, *, revision: str | None = None):
|
|
1317
1317
|
"""Load a tokenizer with the renderers-package security policy.
|
|
1318
1318
|
|
|
1319
1319
|
Default ``trust_remote_code=False``. Models listed in
|
|
1320
1320
|
``TRUSTED_REVISIONS`` (Moonshot Kimi-K2 family) load with
|
|
1321
1321
|
``trust_remote_code=True`` AND a pinned ``revision=<sha>`` so
|
|
1322
1322
|
transformers only executes the reviewed commit's tokenizer Python.
|
|
1323
|
+
``revision`` pins ordinary assets without overriding reviewed remote code.
|
|
1323
1324
|
|
|
1324
1325
|
``AutoTokenizer.from_pretrained`` eagerly builds the model config to
|
|
1325
1326
|
resolve the tokenizer class. If that construction raises on a
|
|
@@ -1338,6 +1339,11 @@ def load_tokenizer(model_name_or_path: str):
|
|
|
1338
1339
|
_require_transformers("Loading a tokenizer")
|
|
1339
1340
|
load_name_or_path = _tokenizer_source_for(model_name_or_path)
|
|
1340
1341
|
kwargs = _tokenizer_load_kwargs(load_name_or_path)
|
|
1342
|
+
if revision is not None:
|
|
1343
|
+
trusted_revision = kwargs.get("revision")
|
|
1344
|
+
if trusted_revision is not None and revision != trusted_revision:
|
|
1345
|
+
raise ValueError("Cannot override the reviewed remote-code revision")
|
|
1346
|
+
kwargs["revision"] = revision
|
|
1341
1347
|
tok = _load_tokenizer_via_auto(load_name_or_path, **kwargs)
|
|
1342
1348
|
return _preserve_requested_tokenizer_name(
|
|
1343
1349
|
tok,
|
|
@@ -12,9 +12,10 @@ from pathlib import Path
|
|
|
12
12
|
|
|
13
13
|
import pytest
|
|
14
14
|
from parity import models_for
|
|
15
|
+
|
|
15
16
|
from renderers import create_renderer
|
|
16
|
-
from renderers.base import load_tokenizer
|
|
17
17
|
from renderers.configs import config_from_name
|
|
18
|
+
from tests.model_assets import load_test_tokenizer
|
|
18
19
|
|
|
19
20
|
# Backwards-compatible view used by documentation and a few invariant tests.
|
|
20
21
|
RENDERER_MODELS = [(case.model, case.renderer) for case in models_for("shared")]
|
|
@@ -33,7 +34,7 @@ def release_shared_models():
|
|
|
33
34
|
def _load(model_name: str, renderer_name: str):
|
|
34
35
|
key = f"{model_name}:{renderer_name}"
|
|
35
36
|
if key not in _cache:
|
|
36
|
-
tokenizer =
|
|
37
|
+
tokenizer = load_test_tokenizer(model_name)
|
|
37
38
|
renderer = create_renderer(tokenizer, config_from_name(renderer_name))
|
|
38
39
|
_cache[key] = (tokenizer, renderer)
|
|
39
40
|
return _cache[key]
|
|
@@ -54,7 +55,10 @@ def pytest_generate_tests(metafunc):
|
|
|
54
55
|
cases = models_for(suite)
|
|
55
56
|
metafunc.parametrize(
|
|
56
57
|
"model_name,renderer_name",
|
|
57
|
-
[
|
|
58
|
+
[
|
|
59
|
+
pytest.param(case.model, case.renderer, marks=pytest.mark.model)
|
|
60
|
+
for case in cases
|
|
61
|
+
],
|
|
58
62
|
ids=[case.model for case in cases],
|
|
59
63
|
)
|
|
60
64
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Immutable test assets; Llama revisions refer to the audited tokenizer mirrors."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from renderers.base import load_tokenizer
|
|
6
|
+
|
|
7
|
+
MODEL_REVISIONS = {
|
|
8
|
+
"MiniMaxAI/MiniMax-M2.5": "2515a3ea0afec1dd22d0c7794b3a91557804d195",
|
|
9
|
+
"PrimeIntellect/Qwen3-0.6B": "15aba67c8ec68aeac96f57ba7e31d373564ee03c",
|
|
10
|
+
"PrimeIntellect/Qwen3-1.7B": "61a79cd101170642a653d1d44d18f69f88f54e08",
|
|
11
|
+
"Qwen/Qwen2.5-0.5B-Instruct": "7ae557604adf67be50417f59c2c2f167def9a775",
|
|
12
|
+
"Qwen/Qwen3-0.6B": "c1899de289a04d12100db370d81485cdf75e47ca",
|
|
13
|
+
"Qwen/Qwen3-4B": "1cfa9a7208912126459214e8b04321603b3df60c",
|
|
14
|
+
"Qwen/Qwen3-8B": "b968826d9c46dd6066d109eabc6255188de91218",
|
|
15
|
+
"Qwen/Qwen3-VL-30B-A3B-Instruct": "9c4b90e1e4ba969fd3b5378b57d966d725f1b86c",
|
|
16
|
+
"Qwen/Qwen3-VL-4B-Instruct": "ebb281ec70b05090aa6165b016eac8ec08e71b17",
|
|
17
|
+
"Qwen/Qwen3-VL-8B-Instruct": "0c351dd01ed87e9c1b53cbc748cba10e6187ff3b",
|
|
18
|
+
"Qwen/Qwen3.5-0.8B": "2fc06364715b967f1860aea9cf38778875588b17",
|
|
19
|
+
"Qwen/Qwen3.5-122B-A10B": "dc4d348443bc740c68e2d77492492c11606384d5",
|
|
20
|
+
"Qwen/Qwen3.5-2B": "15852e8c16360a2fea060d615a32b45270f8a8fc",
|
|
21
|
+
"Qwen/Qwen3.5-35B-A3B": "59d61f3ce65a6d9863b86d2e96597125219dc754",
|
|
22
|
+
"Qwen/Qwen3.5-397B-A17B": "8472618112abcbd45acbcdc58436aff4233c23f7",
|
|
23
|
+
"Qwen/Qwen3.5-4B": "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a",
|
|
24
|
+
"Qwen/Qwen3.5-9B": "c202236235762e1c871ad0ccb60c8ee5ba337b9a",
|
|
25
|
+
"Qwen/Qwen3.6-35B-A3B": "995ad96eacd98c81ed38be0c5b274b04031597b0",
|
|
26
|
+
"Qwen/Qwen3.8-27B": "1d4bf0f2ff6012fd82039f2fa52739d0dd7c60c0",
|
|
27
|
+
"Qwen/Qwen3.8-Flash-Next": "de4b8e4d43b917e7706784d8bb445c9af86a3540",
|
|
28
|
+
"THUDM/GLM-4.5-Air": "a24ceef6ce4f3536971efe9b778bdaa1bab18daa",
|
|
29
|
+
"deepseek-ai/DeepSeek-R1": "56d4cbbb4d29f4355bab4b9a39ccb717a14ad5ad",
|
|
30
|
+
"deepseek-ai/DeepSeek-V3": "e815299b0bcbac849fa540c768ef21845365c9eb",
|
|
31
|
+
"deepseek-ai/DeepSeek-V4-Flash-0731": "7872f01b1d1fe23eabc4c98b48bffcef5a386062",
|
|
32
|
+
"google/gemma-4-26B-A4B-it": "4d7ae4984b7db7de8f8457170b3f1a419ee76d52",
|
|
33
|
+
"google/gemma-4-31B-it": "842da3794eaa0b77d5f08bae87a17459d91ff475",
|
|
34
|
+
"google/gemma-4-E2B-it": "3e22461f65e89153144f8adb70e3b8c2cc9845a7",
|
|
35
|
+
"google/gemma-4-E4B-it": "ee0ef6023621cff504d758262d4e04895a5af4a2",
|
|
36
|
+
"meta-llama/Llama-3.2-1B-Instruct": "5a8abab4a5d6f164389b1079fb721cfab8d7126c",
|
|
37
|
+
"meta-llama/Llama-3.2-3B-Instruct": "006f5dcd1393c3add266de40994ba96225e9689d",
|
|
38
|
+
"meta-models/Muse-Glimmer-30B": "cec94fcfd6512f2cfe708138c32533b346ea4f69",
|
|
39
|
+
"moonshotai/Kimi-K2-Instruct": "fd1984e2b7a3350dbf7305fe73a4ede25c14de50",
|
|
40
|
+
"moonshotai/Kimi-K2.5": "4d01dfe0332d63057c186e0b262165819efb6611",
|
|
41
|
+
"moonshotai/Kimi-K2.6": "2755962d07cb42aa2d988a35bcb65cd4a9c2de82",
|
|
42
|
+
"nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-BF16": "bf77c3174f68ad409e1c2aa60daeb46e32d1c606",
|
|
43
|
+
"nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16": "2dc98e2afe4face0e4ce40972a915c45368bd34a",
|
|
44
|
+
"nvidia/NVIDIA-Nemotron-3-Ultra-550B-A55B-BF16": "77df655d5e9f8362164ed14dd8b48f8bce657498",
|
|
45
|
+
"nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16": "a9904d24bcc1d289a1950fa9d2b978c47cf903b9",
|
|
46
|
+
"openai/gpt-oss-120b": "b5c939de8f754692c1647ca79fbf85e8c1e70f8a",
|
|
47
|
+
"openai/gpt-oss-20b": "6cee5e81ee83917806bbde320786a8fb61efebee",
|
|
48
|
+
"poolside/Laguna-M.1": "2bf8a4ab6d2e8e1e0d92a41318532a0fee1640d6",
|
|
49
|
+
"poolside/Laguna-S-2.1": "0f573140834b11cfac0c2af97a101a7a69a13e22",
|
|
50
|
+
"poolside/Laguna-XS-2.1": "c5f36269bbdbd3f27fddc9a9f9dbae0cf2cf57db",
|
|
51
|
+
"poolside/Laguna-XS.2": "a397bde04501591d0ac3d74a5d9d0a4ec298ffb8",
|
|
52
|
+
"tencent/Hy3": "a960ebc3da325ba167f069f76c41eb62c9280d22",
|
|
53
|
+
"thinkingmachines/Inkling": "828496eeae4c243ff1a22f7f28ff83694f2f7bc9",
|
|
54
|
+
"thinkingmachines/Inkling-Small": "8cc5877b44d343f88b92086aa1fb72897950f06a",
|
|
55
|
+
"zai-org/GLM-4.7-Flash": "7dd20894a642a0aa287e9827cb1a1f7f91386b67",
|
|
56
|
+
"zai-org/GLM-5": "c183ef8c61faee82855eca1ed9bb3a9a7ce3b0b2",
|
|
57
|
+
"zai-org/GLM-5.1": "26e1bd6e011feb778d25ae34b09b07074139d92d",
|
|
58
|
+
"zai-org/GLM-5.3": "ba32f8c19c8738e2c57c71798c601d477a7bf844",
|
|
59
|
+
"zai-org/GLM-5.3-BF16": "abf42e6c0e53bc29a875511301e7b7fcd220315b",
|
|
60
|
+
"zai-org/GLM-5.3-Flash-BF16": "f9fed3bbc82291073373d67452e9f25317ef7577",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def model_revision(model: str) -> str:
|
|
65
|
+
# The optional private VLM fixture can only resolve an existing local snapshot.
|
|
66
|
+
if model == "nvidia/NVIDIA-Nemotron-3.5-Super-EA-09112026":
|
|
67
|
+
from huggingface_hub import try_to_load_from_cache
|
|
68
|
+
|
|
69
|
+
path = try_to_load_from_cache(model, "tokenizer_config.json")
|
|
70
|
+
if isinstance(path, str):
|
|
71
|
+
return Path(path).parent.name
|
|
72
|
+
try:
|
|
73
|
+
return MODEL_REVISIONS[model]
|
|
74
|
+
except KeyError as exc:
|
|
75
|
+
raise KeyError(f"No immutable test revision registered for {model!r}") from exc
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_test_tokenizer(model: str):
|
|
79
|
+
return load_tokenizer(model, revision=model_revision(model))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_test_processor(model: str):
|
|
83
|
+
from transformers import AutoProcessor
|
|
84
|
+
|
|
85
|
+
return AutoProcessor.from_pretrained(
|
|
86
|
+
model,
|
|
87
|
+
revision=model_revision(model),
|
|
88
|
+
trust_remote_code=model
|
|
89
|
+
in {
|
|
90
|
+
"moonshotai/Kimi-K2.5",
|
|
91
|
+
"moonshotai/Kimi-K2.6",
|
|
92
|
+
"nvidia/NVIDIA-Nemotron-3.5-Super-EA-09112026",
|
|
93
|
+
},
|
|
94
|
+
)
|
|
@@ -27,8 +27,8 @@ _BRIDGE_MODELS = [(case.model, case.renderer) for case in models_for("bridge")]
|
|
|
27
27
|
@lru_cache(maxsize=None)
|
|
28
28
|
def _load(model_name: str, renderer_name: str):
|
|
29
29
|
from renderers import create_renderer
|
|
30
|
-
from renderers.base import load_tokenizer
|
|
31
30
|
from renderers.configs import config_from_name
|
|
31
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
32
32
|
|
|
33
33
|
tok = load_tokenizer(model_name)
|
|
34
34
|
return tok, create_renderer(tok, config_from_name(renderer_name))
|
|
@@ -38,7 +38,10 @@ def pytest_generate_tests(metafunc):
|
|
|
38
38
|
if "br_model" in metafunc.fixturenames:
|
|
39
39
|
metafunc.parametrize(
|
|
40
40
|
"br_model,br_renderer_name",
|
|
41
|
-
|
|
41
|
+
[
|
|
42
|
+
pytest.param(model, renderer, marks=pytest.mark.model)
|
|
43
|
+
for model, renderer in _BRIDGE_MODELS
|
|
44
|
+
],
|
|
42
45
|
ids=[m for m, _ in _BRIDGE_MODELS],
|
|
43
46
|
)
|
|
44
47
|
|
|
@@ -223,6 +226,7 @@ def test_qwen3_renders_openai_text_content_parts(br_renderer, br_tokenizer, br_m
|
|
|
223
226
|
assert text in br_tokenizer.decode(list_ids, skip_special_tokens=False)
|
|
224
227
|
|
|
225
228
|
|
|
229
|
+
@pytest.mark.model
|
|
226
230
|
def test_bridge_declines_across_user_query_when_template_drops_thinking():
|
|
227
231
|
"""Qwen3's template drops a past block's thinking once a new user turn
|
|
228
232
|
arrives. The resolved ``tool_cycle`` bridge policy therefore treats a
|
|
@@ -237,8 +241,8 @@ def test_bridge_declines_across_user_query_when_template_drops_thinking():
|
|
|
237
241
|
- no prior thinking + new user query -> decline; no marker lookback.
|
|
238
242
|
"""
|
|
239
243
|
from renderers import create_renderer
|
|
240
|
-
from renderers.base import load_tokenizer
|
|
241
244
|
from renderers.configs import Qwen3RendererConfig
|
|
245
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
242
246
|
|
|
243
247
|
tok = load_tokenizer("Qwen/Qwen3-8B")
|
|
244
248
|
im_end = tok.convert_tokens_to_ids("<|im_end|>")
|
|
@@ -326,14 +330,15 @@ def test_bridge_declines_across_user_turn_when_thinking_present(br_renderer, br_
|
|
|
326
330
|
assert extended is not None, f"{br_model}: should still bridge within a tool cycle"
|
|
327
331
|
|
|
328
332
|
|
|
333
|
+
@pytest.mark.model
|
|
329
334
|
def test_bridge_keeps_thinking_when_history_kwarg_disables_truncation():
|
|
330
335
|
"""GLM ``clear_thinking=False`` / Nemotron ``truncate_history_thinking=
|
|
331
336
|
False`` keep all past thinking (the template doesn't strip it), so the
|
|
332
337
|
bridge must NOT decline across a user turn — declining would re-render and
|
|
333
338
|
re-tokenize model-sampled thinking bytes."""
|
|
334
339
|
from renderers import create_renderer
|
|
335
|
-
from renderers.base import load_tokenizer
|
|
336
340
|
from renderers.configs import GLM5RendererConfig, Nemotron3RendererConfig
|
|
341
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
337
342
|
|
|
338
343
|
asst = [
|
|
339
344
|
{"role": "assistant", "reasoning_content": "Let me think.", "content": "Hi"}
|
|
@@ -738,6 +738,8 @@ def test_generate_threads_prompt_attribution_through_prebuilt_prompt_path():
|
|
|
738
738
|
# ---------------------------------------------------------------------------
|
|
739
739
|
|
|
740
740
|
|
|
741
|
+
@pytest.mark.model
|
|
742
|
+
@pytest.mark.multimodal
|
|
741
743
|
@pytest.mark.parametrize(
|
|
742
744
|
"model_id,renderer_class_path",
|
|
743
745
|
[
|
|
@@ -762,11 +764,8 @@ def test_generate_serializes_multimodal_features_for_qwen_vl_family(
|
|
|
762
764
|
|
|
763
765
|
import torch as _torch
|
|
764
766
|
|
|
765
|
-
from renderers.base import
|
|
766
|
-
|
|
767
|
-
PlaceholderRange,
|
|
768
|
-
load_tokenizer,
|
|
769
|
-
)
|
|
767
|
+
from renderers.base import MultiModalData, PlaceholderRange
|
|
768
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
770
769
|
|
|
771
770
|
mod_name, cls_name = renderer_class_path.split(":")
|
|
772
771
|
renderer_cls = getattr(importlib.import_module(mod_name), cls_name)
|
|
@@ -832,6 +831,8 @@ def test_generate_serializes_multimodal_features_for_qwen_vl_family(
|
|
|
832
831
|
assert isinstance(item, str) and len(item) > 0
|
|
833
832
|
|
|
834
833
|
|
|
834
|
+
@pytest.mark.model
|
|
835
|
+
@pytest.mark.multimodal
|
|
835
836
|
def test_generate_serializes_multimodal_features_for_gemma4():
|
|
836
837
|
"""Gemma 4's HF image positions are translated to vLLM's field name."""
|
|
837
838
|
pytest.importorskip("torch")
|
|
@@ -839,8 +840,9 @@ def test_generate_serializes_multimodal_features_for_gemma4():
|
|
|
839
840
|
|
|
840
841
|
import torch as _torch
|
|
841
842
|
|
|
842
|
-
from renderers.base import MultiModalData, PlaceholderRange
|
|
843
|
+
from renderers.base import MultiModalData, PlaceholderRange
|
|
843
844
|
from renderers.gemma4 import Gemma4Renderer
|
|
845
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
844
846
|
|
|
845
847
|
renderer = Gemma4Renderer(load_tokenizer("google/gemma-4-31B-it"))
|
|
846
848
|
mm_data = MultiModalData(
|
|
@@ -169,8 +169,9 @@ GLM53_NO_THINKING_SOURCE = textwrap.dedent(
|
|
|
169
169
|
)
|
|
170
170
|
|
|
171
171
|
|
|
172
|
+
@pytest.mark.model
|
|
172
173
|
def test_readme_example_prefills_the_think_block(tmp_path):
|
|
173
|
-
from
|
|
174
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
174
175
|
|
|
175
176
|
path = tmp_path / "glm53_no_thinking.py"
|
|
176
177
|
path.write_text(GLM53_NO_THINKING_SOURCE)
|
|
@@ -7,12 +7,16 @@ stripping of ``</think>`` from historical assistant turns.
|
|
|
7
7
|
|
|
8
8
|
from functools import lru_cache
|
|
9
9
|
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
10
12
|
from renderers import (
|
|
11
13
|
DeepSeekR1Renderer,
|
|
12
14
|
DeepSeekV3Renderer,
|
|
13
15
|
create_renderer,
|
|
14
16
|
)
|
|
15
|
-
from
|
|
17
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
18
|
+
|
|
19
|
+
pytestmark = pytest.mark.model
|
|
16
20
|
|
|
17
21
|
|
|
18
22
|
@lru_cache
|
|
@@ -6,6 +6,7 @@ from functools import lru_cache
|
|
|
6
6
|
|
|
7
7
|
import pytest
|
|
8
8
|
from pydantic import TypeAdapter, ValidationError
|
|
9
|
+
|
|
9
10
|
from renderers import (
|
|
10
11
|
DeepSeekV4Renderer,
|
|
11
12
|
DeepSeekV4RendererConfig,
|
|
@@ -13,9 +14,12 @@ from renderers import (
|
|
|
13
14
|
ToolCallParseStatus,
|
|
14
15
|
create_renderer,
|
|
15
16
|
)
|
|
16
|
-
from renderers.base import MODEL_RENDERER_MAP
|
|
17
|
+
from renderers.base import MODEL_RENDERER_MAP
|
|
18
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
17
19
|
from tests.reference_rendering import render_reference
|
|
18
20
|
|
|
21
|
+
pytestmark = pytest.mark.model
|
|
22
|
+
|
|
19
23
|
MODEL = "deepseek-ai/DeepSeek-V4-Flash-0731"
|
|
20
24
|
BOS = "<|begin▁of▁sentence|>"
|
|
21
25
|
EOS = "<|end▁of▁sentence|>"
|
|
@@ -24,10 +24,14 @@ from __future__ import annotations
|
|
|
24
24
|
|
|
25
25
|
from functools import lru_cache
|
|
26
26
|
|
|
27
|
+
import pytest
|
|
27
28
|
from parity import models_for
|
|
29
|
+
|
|
28
30
|
from renderers import create_renderer
|
|
29
|
-
from renderers.base import load_tokenizer
|
|
30
31
|
from renderers.configs import _config_class_for
|
|
32
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
33
|
+
|
|
34
|
+
pytestmark = pytest.mark.model
|
|
31
35
|
|
|
32
36
|
# One representative model per affected renderer family, each with
|
|
33
37
|
# thinking explicitly disabled.
|
|
@@ -6,8 +6,12 @@ import pytest
|
|
|
6
6
|
from parity import models_for
|
|
7
7
|
|
|
8
8
|
from renderers import Gemma4Renderer, create_renderer
|
|
9
|
-
from renderers.base import MODEL_RENDERER_MAP, MULTIMODAL_MODELS
|
|
9
|
+
from renderers.base import MODEL_RENDERER_MAP, MULTIMODAL_MODELS
|
|
10
10
|
from renderers.configs import Gemma4RendererConfig
|
|
11
|
+
from tests.model_assets import load_test_processor
|
|
12
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
13
|
+
|
|
14
|
+
pytestmark = pytest.mark.model
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
_MODELS = tuple(case.model for case in models_for("gemma-checkpoints"))
|
|
@@ -298,7 +302,9 @@ def test_parser_recovers_prompt_opened_post_tool_reasoning():
|
|
|
298
302
|
def _image_renderer():
|
|
299
303
|
"""A Gemma 4 renderer with a live ``Gemma4Processor``, or a skip."""
|
|
300
304
|
tokenizer, _ = _gemma4()
|
|
301
|
-
renderer = Gemma4Renderer(
|
|
305
|
+
renderer = Gemma4Renderer(
|
|
306
|
+
tokenizer, processor=load_test_processor("google/gemma-4-31B-it")
|
|
307
|
+
)
|
|
302
308
|
try:
|
|
303
309
|
renderer._get_processor()
|
|
304
310
|
except (RuntimeError, OSError) as exc: # pragma: no cover - env dependent
|
|
@@ -312,6 +318,7 @@ def _tiny_image():
|
|
|
312
318
|
return Image.new("RGB", (224, 224), color=(128, 192, 255))
|
|
313
319
|
|
|
314
320
|
|
|
321
|
+
@pytest.mark.multimodal
|
|
315
322
|
@pytest.mark.parametrize("size", [(224, 224), (448, 224), (224, 448)])
|
|
316
323
|
def test_real_processor_keeps_one_batched_row_per_image(size):
|
|
317
324
|
"""Guard the ``MultiModalFieldConfig.batched('image')`` contract with
|
|
@@ -332,6 +339,7 @@ def test_real_processor_keeps_one_batched_row_per_image(size):
|
|
|
332
339
|
assert placeholder.length > 0
|
|
333
340
|
|
|
334
341
|
|
|
342
|
+
@pytest.mark.multimodal
|
|
335
343
|
def test_schema_unified_image_parts_still_expand_image_tokens():
|
|
336
344
|
"""``Dataset.from_list`` unifies the Arrow schema across a content list,
|
|
337
345
|
so an image part round-tripped through a dataset carries ``text: None``
|
|
@@ -374,6 +382,7 @@ def test_schema_unified_image_parts_still_expand_image_tokens():
|
|
|
374
382
|
)
|
|
375
383
|
|
|
376
384
|
|
|
385
|
+
@pytest.mark.multimodal
|
|
377
386
|
def test_schema_unified_tool_response_image_parts_survive():
|
|
378
387
|
"""Same hazard on the tool-response path, which also has to keep
|
|
379
388
|
accepting untyped text parts."""
|
|
@@ -19,6 +19,8 @@ from openai import AsyncOpenAI
|
|
|
19
19
|
|
|
20
20
|
from renderers.client import generate
|
|
21
21
|
|
|
22
|
+
pytestmark = pytest.mark.model
|
|
23
|
+
|
|
22
24
|
MODEL = "zai-org/GLM-5.3-Flash-BF16"
|
|
23
25
|
GLM53_MODELS = ["zai-org/GLM-5.3", MODEL]
|
|
24
26
|
|
|
@@ -45,7 +47,7 @@ _TOOL_CALL = {
|
|
|
45
47
|
@lru_cache(maxsize=None)
|
|
46
48
|
def _load(model=MODEL):
|
|
47
49
|
from renderers import create_renderer
|
|
48
|
-
from
|
|
50
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
49
51
|
|
|
50
52
|
tok = load_tokenizer(model)
|
|
51
53
|
return tok, create_renderer(tok)
|
|
@@ -25,9 +25,13 @@ from __future__ import annotations
|
|
|
25
25
|
|
|
26
26
|
from functools import lru_cache
|
|
27
27
|
|
|
28
|
+
import pytest
|
|
28
29
|
from parity import models_for
|
|
30
|
+
|
|
29
31
|
from renderers.base import ToolCallParseStatus
|
|
30
32
|
|
|
33
|
+
pytestmark = pytest.mark.model
|
|
34
|
+
|
|
31
35
|
# Both GLM renderers share ``parse_glm`` and are served by the same
|
|
32
36
|
# strict vLLM parser (the ``glm45`` and ``glm47`` aliases both resolve
|
|
33
37
|
# to ``Glm47MoeModelToolParser`` in vLLM ≥ 0.24).
|
|
@@ -55,7 +59,7 @@ _TOOLS = [
|
|
|
55
59
|
@lru_cache(maxsize=None)
|
|
56
60
|
def _load(model: str, renderer_name: str):
|
|
57
61
|
from renderers import config_from_name, create_renderer
|
|
58
|
-
from
|
|
62
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
59
63
|
|
|
60
64
|
tok = load_tokenizer(model)
|
|
61
65
|
return tok, create_renderer(tok, config_from_name(renderer_name))
|
|
@@ -15,7 +15,10 @@ from functools import lru_cache
|
|
|
15
15
|
import pytest
|
|
16
16
|
|
|
17
17
|
from renderers import Hy3RendererConfig, create_renderer
|
|
18
|
-
from renderers.base import ToolCallParseStatus
|
|
18
|
+
from renderers.base import ToolCallParseStatus
|
|
19
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
20
|
+
|
|
21
|
+
pytestmark = pytest.mark.model
|
|
19
22
|
|
|
20
23
|
_MODEL = "tencent/Hy3"
|
|
21
24
|
|
|
@@ -22,9 +22,12 @@ import numpy as np
|
|
|
22
22
|
import pytest
|
|
23
23
|
|
|
24
24
|
from renderers import create_renderer
|
|
25
|
-
from renderers.base import ToolCallParseStatus
|
|
25
|
+
from renderers.base import ToolCallParseStatus
|
|
26
26
|
from renderers.configs import InklingRendererConfig
|
|
27
27
|
from renderers.inkling import InklingRenderer
|
|
28
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
29
|
+
|
|
30
|
+
pytestmark = pytest.mark.model
|
|
28
31
|
|
|
29
32
|
_MODEL = "thinkingmachines/Inkling"
|
|
30
33
|
_SMALL_MODEL = "thinkingmachines/Inkling-Small"
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from functools import lru_cache
|
|
6
6
|
|
|
7
|
+
import pytest
|
|
7
8
|
from pydantic import TypeAdapter
|
|
8
9
|
|
|
9
10
|
from renderers import (
|
|
@@ -12,7 +13,9 @@ from renderers import (
|
|
|
12
13
|
RendererConfig,
|
|
13
14
|
create_renderer,
|
|
14
15
|
)
|
|
15
|
-
from
|
|
16
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
17
|
+
|
|
18
|
+
pytestmark = pytest.mark.model
|
|
16
19
|
|
|
17
20
|
_MODEL = "poolside/Laguna-M.1"
|
|
18
21
|
|
|
@@ -22,9 +22,11 @@ from functools import lru_cache
|
|
|
22
22
|
import pytest
|
|
23
23
|
|
|
24
24
|
from renderers import create_renderer
|
|
25
|
-
from renderers.base import load_tokenizer
|
|
26
25
|
from renderers.configs import LagunaXS21RendererConfig, config_from_name
|
|
27
26
|
from renderers.laguna_xs2 import LagunaXS21Renderer
|
|
27
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
28
|
+
|
|
29
|
+
pytestmark = pytest.mark.model
|
|
28
30
|
|
|
29
31
|
_MODEL = "poolside/Laguna-XS-2.1"
|
|
30
32
|
|
|
@@ -15,11 +15,13 @@ from parity import models_for
|
|
|
15
15
|
from renderers import Llama3Renderer, Llama3RendererConfig, create_renderer
|
|
16
16
|
from renderers.base import (
|
|
17
17
|
MODEL_RENDERER_MAP,
|
|
18
|
-
ParsedResponse,
|
|
19
18
|
TOKENIZER_SOURCE_OVERRIDES,
|
|
19
|
+
ParsedResponse,
|
|
20
20
|
ToolCallParseStatus,
|
|
21
|
-
load_tokenizer,
|
|
22
21
|
)
|
|
22
|
+
from tests.model_assets import load_test_tokenizer as load_tokenizer
|
|
23
|
+
|
|
24
|
+
pytestmark = pytest.mark.model
|
|
23
25
|
|
|
24
26
|
# Pinned date for byte-parity tests. Matches the chat template's
|
|
25
27
|
# strftime fallback so we don't have to override on the apply side.
|