You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
detokenizer duplicates text when one call holds several messages for the same uid (latent; blocks multi-token emission) #69
Latent. Production can't hit this today. It is a prerequisite fix for any multi-token emission, such as speculative decoding.
DetokenizeManager.detokenize_with_meta (python/freetoken/tokenizer/detokenize.py:116-190) runs in two passes over msgs:
The first pass appends each message's token and builds read_ids / surr_ids from the currentsurr_offset / read_offset.
The second pass advances those offsets.
When one call holds two or more messages for the same uid, every message's slice comes from the same pre-update offsets, so text is emitted twice.
Repro (CPU only):
fromtransformersimportAutoTokenizerfromfreetoken.messageimportDetokenizeMsgfromfreetoken.tokenizer.detokenizeimportDetokenizeManagertok=AutoTokenizer.from_pretrained("/home/gdevenyi/models/Qwen3.8-Flash-Next-NVFP4")
ids=tok.encode("Hello world, this is a speculative decoding test.")
defrun(groups):
m, out=DetokenizeManager(tok), []
forgingroups:
out.extend(m.detokenize([DetokenizeMsg(uid=1, next_token=t, finished=False) forting]))
return"".join(out)
print(repr(run([[t] fortinids]))) # 'Hello world, this is a speculative decoding test.'print(repr(run([ids[i:i+2] foriinrange(0, len(ids), 2)]))) # 'HelloHello world,, this is is a speculative speculative decoding test test.'
Why it is latent:
The tokenizer worker only merges queued messages while len(pending_msg) < local_bs (tokenizer/server.py:178), and local_bs is hard-coded to 1 (server/launch.py:185,205).
The scheduler emits one DetokenizeMsg per request per step (scheduler/scheduler.py:398).
So a single call never carries the same uid twice today.
Anything that emits several tokens per request per step would trigger it: speculative decoding, local_bs > 1, or batched replies.
Related:
The think-end check (detokenize.py:135-140) only looks at next_token.
completion_tokens_delta is hard-coded to 1 per message (tokenizer/server.py:236).
Status tok/s counts len(batch.reqs) per step (scheduler/status.py:106).
Fix direction: process same-uid messages sequentially (or group by uid first), and carry token_ids per message. Upstream FlashML-org#71 has a DetokenizeMsg.token_ids approach. FlashML-org#258's version compares against endswith("") (the U+FFFD was stripped), so its offsets never advance; don't copy that one.
Found during the speculative-decoding investigation (2026-09-26).
Fixed on 5080deploy by #76: upstream FlashML-org#464 adds a same-uid guard at the top of detokenize_with_meta, which handles a batch that repeats a uid one message at a time, in order. The guard's test is 1080deploy 577427a, carried in #76.
The repro from this issue, run with the Qwen3.8-Flash-Next tokenizer:
old 5080deploy (8ba20f6): 'HelloHello world,, this is is a speculative speculative decoding test test.'
new 5080deploy (2224380): 'Hello world, this is a speculative decoding test.' for both one-token and two-token groups.
Activity
gdevenyi commentedon Oct 2, 2026
Fixed on 5080deploy by #76: upstream FlashML-org#464 adds a same-uid guard at the top of
detokenize_with_meta, which handles a batch that repeats a uid one message at a time, in order. The guard's test is 1080deploy577427a, carried in #76.The repro from this issue, run with the Qwen3.8-Flash-Next tokenizer:
8ba20f6):'HelloHello world,, this is is a speculative speculative decoding test test.'2224380):'Hello world, this is a speculative decoding test.'for both one-token and two-token groups.🤖 Generated with Claude Code
https://claude.ai/code/session_01Bu6LgoxLR4wETqb7RPR2vt