Skip to content

Strip the metaspace prefix space only when it is unambiguous - #47862

Open
ArthurZucker wants to merge 2 commits into
mainfrom
fix-47487-metaspace-prefix-space-decode
Open

ArthurZucker wants to merge 2 commits into
mainfrom
fix-47487-metaspace-prefix-space-decode

Conversation

@ArthurZucker

@ArthurZucker ArthurZucker commented Aug 10, 2026 •

Copy link
Copy Markdown
Collaborator

CPU CI GPU run-slow

What does this PR do?

Fixes #47487 without touching the encoding pipeline. Follow-up to #47861, which reverted #47488 — this is the version of that fix which does not change any ids.

The actual bug

Metaspace only prepends ▁ when the text does not already start with one. So with two or more leading spaces there is no synthetic prefix at all, and the trailing Strip(content=" ", left=1) removed one of the user's own spaces:

"  hello"  ->  ▁▁hello  ->  "  hello"  ->  Strip(left=1)  ->  " hello"

Encoding was already lossless here — only the decoder was wrong. That is why this does not need a pipeline change.

The fix

-decoders.Strip(content=" ", left=1)
+decoders.Replace(Regex(r"\A (?! )"), "")

Remove the leading space only when there is exactly one, which is exactly the case where it is synthetic. The count of leading ▁ separates the two cases perfectly, because the prepend is guarded by that same starts_with condition.

\A and not ^: the regex engine is Oniguruma and multiline, so ^ eats the indentation of every line, not just the first. test_integration catches it if you get this wrong.

Why this is safe

  • Encoding is untouched. Verified on hf-internal-testing/llama-tokenizer, NousResearch/Llama-2-13b-hf and codellama/CodeLlama-7b-hf: encode ids are identical to main on every probe, including text containing special tokens. No id changes, no added-token changes, no checkpoint changes.
  • It still serializes into tokenizer.json, so tokenizers-rs and transformers.js get the fix too — unlike a Python-side _decode override.

A single leading space stays unrecoverable: " hello" and "hello" both encode to [22172]. That is documented SentencePiece behaviour and is deliberately unchanged; #47487 asks for it to stay that way too.

Tests

test_decode_keeps_real_leading_whitespace added for both models. Each fails on main (' hello' != ' hello') and passes here. It also pins the two things worth not regressing: text with no leading whitespace must not gain a space, and later lines must keep their indentation.

tests/models/{llama,code_llama,moshi,helium} — 182 passed, 33 skipped.

The same Strip appears three times in convert_slow_tokenizer.py (Llama, Moshi, Helium converters) and is swapped there too. The three ggml.py sites are deliberately left alone: two of them have no Fuse before the strip, so they already strip per token ("▁def", "▁f" -> deff) — a separate pre-existing bug in the GGUF path, worth its own issue.

Who can review?

@itazap

`Metaspace` only prepends `▁` when the text does not already start with one, so with two
or more leading spaces there is no synthetic prefix at all and the trailing
`Strip(content=" ", left=1)` decoder removed one of the user's own spaces:

    "  hello" -> ▁▁hello -> "  hello" -> Strip -> " hello"

Encoding was already lossless here; only the decoder was wrong. Replacing the
unconditional strip with `Replace(Regex(r"\A (?! )"), "")` removes the prefix space only
when there is exactly one, which is precisely the case where it is synthetic.

Encoding is untouched, so ids, added tokens and shipped checkpoints are unchanged, and
the decoder still serializes into `tokenizer.json` so the other bindings get the fix too.

`\A` rather than `^` because the regex engine is multiline and `^` would eat the
indentation of every line rather than just the first.

A single leading space stays unrecoverable: `" hello"` and `"hello"` encode to the same
ids, which is the documented SentencePiece behaviour and is not changed here.

Fixes #47487
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.

@itazap itazap left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes thank youuuuu

@ArthurZucker ArthurZucker left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just need to clear the AI slope from it before I can merge!

@github-actions

Copy link
Copy Markdown
Contributor

[For maintainers] Suggested jobs to run (before merge)

run-slow: code_llama, llama

@github-actions

Copy link
Copy Markdown
Contributor

CI recap

Dashboard: View test results in Grafana
Latest run: 33030667780:1
Result: failure | Jobs: 2 | Tests: 22 | Failures: 1 | Duration: 3m 40s

Code quality check failed: test jobs were skipped. Fix the code quality issues and push again to run tests.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CodeLlama tokenizer drops leading whitespace on decode round-trip

3 participants