Test refactor PR2: instruct tokenizer tests, request builders, golden registry (OSS-108) - #280
Open
juliendenize wants to merge 2 commits into
Open
Test refactor PR2: instruct tokenizer tests, request builders, golden registry (OSS-108)#280juliendenize wants to merge 2 commits into
juliendenize wants to merge 2 commits into
Conversation
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 27, 2026 10:11
eea5a3e to
e52c3a3
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 27, 2026 10:39
cde98be to
b41fb82
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 27, 2026 12:31
e52c3a3 to
c135aa4
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 27, 2026 12:45
b41fb82 to
1e17969
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
3 times, most recently
from
July 27, 2026 19:09
2a2ffa5 to
164924f
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 27, 2026 19:39
1e17969 to
575b378
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
2 times, most recently
from
July 27, 2026 22:09
4f59961 to
b2bc57b
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
2 times, most recently
from
July 27, 2026 22:58
83656e6 to
74c9708
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
5 times, most recently
from
July 28, 2026 12:35
f666452 to
585677d
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 28, 2026 14:04
74c9708 to
195993c
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 28, 2026 14:11
585677d to
d1cc455
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 28, 2026 16:01
195993c to
36253d6
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 28, 2026 16:11
d1cc455 to
48364b5
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 28, 2026 18:06
36253d6 to
687fffd
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 28, 2026 18:20
48364b5 to
bacab9a
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 28, 2026 21:44
687fffd to
d729a36
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 29, 2026 07:48
bacab9a to
89a7bc7
Compare
- tests/utils: versions (TestConfig, version lists, config_id) and tokenizer builders (quick_vocab, get_special_tokens, build_tekkenizer, load_sentencepiece, write_tekkenizer_model) - root tests/conftest.py session fixtures (dummy_v3_tekkenizer, spm_v7, make_tekkenizer) - structured library tests in tests/tokenizers (tekken, sentencepiece) at 100% per-file - util self-tests in tests/utils_tests - route legacy test_tekken.py and integrations TestConfig/helpers through shared utils - coverage config + CI per-file gate and diff-cover
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
from
July 29, 2026 09:47
d729a36 to
39ec63d
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
3 times, most recently
from
July 29, 2026 12:13
af42b43 to
e0df115
Compare
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
4 times, most recently
from
July 30, 2026 17:17
448d48a to
d3ca4a4
Compare
…ruct tokenizer tests
juliendenize
force-pushed
the
test-refactor-pr2-instruct
branch
from
July 30, 2026 17:31
d3ca4a4 to
49d2706
Compare
juliendenize
force-pushed
the
test-refactor-pr1-foundation
branch
3 times, most recently
from
August 6, 2026 15:58
6dce84e to
748fbe0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR2 — Instruct + FIM tokenizer tests (OSS-108)
Second PR of the test-suite refactor. Stacked on
test-refactor-pr1-foundation(#279); base is that branch, notmain. When PR1 merges this rebases ontomain. Mostly additive on PR1's files: it modifiesquick_vocab's body intests/utils/tokenizers.py(addserrors="replace", needed by the SPM sentinel merge) and removes now-superseded helpers fromtests/integrations/chat_templates/helpers.py. An earlier claim of "AST diff: modified=[], removed=[]" was wrong — that check compared top-level names, not bodies, so it could not detect what it claimed to rule out. All legacy tests stay green; nosrc/behaviour changes.1. Shared request builders —
tests/utils/requests/Mirrors
src/mistral_common/protocol/.instruct.py— single source of truth forChatCompletionRequestconversations. Folds in the integrationsfixtures_data.py(REQUEST_*+get_conversations) plus deterministic dummy image/audio inputs,math_interpreter_tools(), and the registry request set.fim.py—FIMRequestbuilders.tests/integrations/chat_templates/rewired to consume the shared module (fixtures_data.pyshows as a rename). Collected integration ids unchanged from PR1: 271.Message-literal dedup: 36 distinct
messages=[...]literals → 20; the 9 that were duplicated across 22 sites → 0.2. One golden mechanism
tests/utils/registry.py+ one file per tokenizer key:Each record colocates the input with both outputs, so a golden is reviewable — you see the request that produced the ids instead of a bare 314-int list:
{ "abcd_multi_turn": { "request": { "messages": [ ... ] }, "text": "<s>\u2581[INST]\u2581a[/INST]\u2581b</s>...", "token_ids": [1, 733, 16289, ...] } }The stored
requestis asserted, not decorative —TestInstructGoldens/TestFimGoldenscompare each builder's output against it across all 135 scenarios. A decorative copy would drift from the builder while looking authoritative; asserting it makes a builder edit fail loudly and forces a look at whether the tokens should have moved too. The Python builders stay the source of truth for inputs (they are shared with the chat_templates tests and give type checking).Base64 image/audio payloads are redacted from the stored request: PNG encoding is not byte-stable across zlib versions, so the same pixels serialize differently per Python build and the golden would pass on some CI runners and fail on others (it did, on 3.14). Nothing is lost — processed images stay pinned by the
.npzgoldens and audio by the token ids.An earlier iteration had two parallel golden shapes — a per-key matrix and a flat
scenarios.json. That was pure duplication, and the matrix structurally could not express v11–v15 because model settings were not part of the key. Collapsed into a singleScenariodataclass +SCENARIOS+PROTOCOLS+SUPPORTED_PROTOCOLS+PROTOCOL_ENCODERS, with model settings baked into the key (v13_think,v15_think,v15_aud,v15_img_think). One key = one tokenizer = one golden directory. All 23 pre-existing scenario values verified byte-identical through the collapse..npz, compared withnp.testing.assert_allclose(arrays stored; floats never hashed).tests/utils/regenerate_registry.py, never run by the suite, so goldens are never auto-overwritten.135 scenarios across 17 instruct keys and 6 FIM keys.
3. Expected outputs live in JSON, not in test files
Every full-request encode expectation is now golden-backed, and every golden assertion checks both token ids and text — one-sided assertions and inline copies of existing goldens are gone.
The remaining literals are sub-unit outputs —
encode_system_messagethendecode, andencode_assistant_messageundercontinue_message— which the request-keyed registry cannot expresswithout a second mechanism. The
encode_assistant_messagecase previously reused a full-requestgolden via
[1:]to strip the bos; that coupling was removed, since a prefix-token change would havesilently shifted the slice while the test kept passing.
Migration safety, enforced for every value moved:
src/to prove the old expectations still hold, which pins the f-string cases that cannot be compared as literals.4. Migrated tests
tests/tokenizers/— v1/v2/v3 (+multimodal), v7, v11, v13, v15, and FIM.tests/tokenizers/conftest.pyowns every tokenizer fixture. Adds an executable version × protocol coverage test (TestProtocolCoverage) andTestRefusalScenarios.FIM inputs widened 2 → 7 (
prompt_suffix,prompt_only,empty_suffix,empty_prompt,newline_suffix,leading_space_suffix,unicode_suffix); FIM tests 17 → 155 (70 intest_fim.pyplus 85 registry cases).Legacy
tests/test_tokenize_v*.py,test_tokenizer_v*.py,test_fim_tokenizer.pyleft untouched (retired in a later PR).5. v2 has no FIM support
Shipped SPM v2 lacks
[SUFFIX]/[PREFIX]—piece_to_idreturns 0 (<unk>) silently, with no exception — and tekken v2 never existed. Rather than invent a refusal assertion for behaviour that does not refuse,SUPPORTED_PROTOCOLS[v2] = {"instruct"}withSILENT_UNSUPPORTED_PROTOCOLS = {(v2, "fim")}. The synthetic tekken v2 FIM key and its goldens were removed.6. Naming: the
☺sentinel is a SentencePiece artifact_encode_infillingprefixes☺and slices[2:]to strip SentencePiece's synthetic▁.InstructTokenizerV2is generic over both backends, so tekken inherits a hack it does not need. Test infrastructure renamed accordingly (FIM_SENTINEL*→SPM_INFILLING_SENTINEL*,build_fim_capable_*→ SPM-artifact naming) so later readers are not misled.The pinned invariant is now
decode(_encode_infilling(x)) == xacross whitespace/newline/unicode/punctuation/digit suffixes;len(encode(sentinel)) == 2is kept as a secondary guard on the hardcoded slice. A tekken characterisation test records the current (lossless but suboptimal) behaviour.Verification
ruff check .+ruff format --check .clean.mypy .clean (142 files).pytest --cov=mistral_common tests/ --ignore=tests/integrations(the gated CI command) → 88.84%, gate 88, exits 0.pytest tests/→ 2583 passed, 17 skipped; whole-suite branch coverage 93.8%.pytest tests/integrations/→ 271 collected ids, unchanged from PR1.pytest --doctest-modules ./src→ 52 passed.Known SPM FIM behaviour, characterised not changed
Two long-standing SentencePiece behaviours are pinned by this suite rather than treated as defects, since mistral-common is the reference tokenizer and its output defines what the models were trained on:
_encode_infillingstrips one leading space from a FIM suffix (deliberate, per its docstring).tests/tokenizers/test_fim.pyasserts the round-trip for tab/newline/unicode/punctuation/digit suffixes and excludes a leading space, with a comment recording why.[SUFFIX]/[PREFIX]pieces andencode_fimemits<unk>without raising. Recorded asSUPPORTED_PROTOCOLS[v2] = {"instruct"}plusSILENT_UNSUPPORTED_PROTOCOLS = {(v2, "fim")}.Linear: OSS-108