Match Qwen Unicode number token boundaries

This commit is contained in:
Georg Bauer committed 2026-10-04 15:37:04 +02:00
1 parent 990abb4755
commit 8965e77ea1
24 files changed
+27460 -3

No files matched your search

+59
View File
@@ -0,0 +1,59 @@
"""Pinned Qwen3 split/token/request IDs and local DS4 family controls; no inference."""
import argparse,hashlib,json,os,struct,subprocess,sys
from pathlib import Path
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('checkout',type=Path)
parser.add_argument('root',type=Path)
parser.add_argument('out',type=Path)
parser.add_argument('--ds4',type=Path)
parser.add_argument('--glm',type=Path)
parser.add_argument('--deepseek',type=Path)
args=parser.parse_args()
checkout,root,out=args.checkout,args.root,args.out
assert subprocess.check_output(['git','-C',str(checkout),'rev-parse','HEAD'],text=True).strip()=='9882703f3105363ddc37eca9f97aa09a1d387112'
sys.path.insert(0,str(checkout))
os.environ.update(HF_HUB_OFFLINE='1',TRANSFORMERS_OFFLINE='1')
from tokenizers import Regex,Tokenizer as BackendTokenizer,pre_tokenizers
from mtplx.runtime import _QWEN3_PRETOKENIZER_SPLIT,_load_tokenizer_resilient
from mtplx.server import openai as server
out.mkdir(parents=True,exist_ok=True)
tokenizer=_load_tokenizer_resilient(root,json.loads((root/'config.json').read_text()))
component=BackendTokenizer.from_str(tokenizer.backend_tokenizer.to_str())
component.normalizer=None
split=pre_tokenizers.Split(Regex(_QWEN3_PRETOKENIZER_SPLIT),behavior='isolated')
scalars=[chr(i) for i in range(0x110000) if not 0xd800<=i<=0xdfff]
text=''.join('A'+c+'A ' for c in scalars)
h=hashlib.sha256();count=0
for piece,_ in split.pre_tokenize_str(text):
b=piece.encode(); h.update(struct.pack('<I',len(b)));h.update(b);count+=1
(out/'scalar-split.json').write_text(json.dumps(dict(scalars=len(scalars),pieces=count,sha256=h.hexdigest(),pattern=_QWEN3_PRETOKENIZER_SPLIT)))
number_split=pre_tokenizers.Split(Regex(r'\p{N}'),behavior='isolated')
numbers=[p for p,_ in number_split.pre_tokenize_str('A'.join(scalars)) if len(p)==1 and p!='A']
texts=[' ①',' Ⅷ',' ²',' 𝟘','Item ①, Ⅷ, ², 𝟘 and 123.','भारत एक विशाल देश है जिसकी संस्कृति बहुत पुरानी है।','ประเทศไทยมีวัฒนธรรมที่เก่าแก่และสวยงามมาก','الْعَرَبِيَّةُ','The quick brown fox jumps over the lazy dog.','中文,标点。','def f(x):\n return x + 123\n',"I'm WE'RE it's he'd I'll you've can't 'ſx",'\u0301a a\u0301 \u0301 १\u0301b','a\r\n b\t\t','a ','a\t \u2003b','a\u0085\u00a0b','x \n \r\n y','😀 ①🙂Ⅷ ²+𝟘']
texts += [prefix+c+suffix for c in numbers for prefix,suffix in [(' ',''),('a','b'),(' \u0301','\u0301x')]]
records=[]
for text in texts:
full_ids=tokenizer.encode(text,add_special_tokens=False)
ids=component.encode(text,add_special_tokens=False).ids
assert component.decode(ids,skip_special_tokens=False)==text,(text,ids)
records.append(dict(text=text,ids=ids,full_ids=full_ids,pieces=[p for p,_ in split.pre_tokenize_str(text)]))
(out/'qwen.json').write_text(json.dumps(dict(number_count=len(numbers),cases=records),ensure_ascii=False))
requests=[]
for effort in ['direct','low','medium','xhigh']:
messages=[dict(role='user',content=' ① Ⅷ ² 𝟘 भारत \u0301a\n '),dict(role='assistant',content='One ①.',reasoning_content='' if effort=='direct' else 'Brief ² thought.'),dict(role='user',content='Continue Ⅷ and 𝟘.\n x')]
ids=server._encode_messages_uncached(tokenizer,[server.ChatMessage(**m) for m in messages],enable_thinking=effort!='direct',reasoning_effort=None if effort=='direct' else effort,preserve_reasoning_history=True,tools=None)
requests.append(dict(effort=effort,messages=messages,ids=ids,rendered=tokenizer.decode(ids,skip_special_tokens=False,clean_up_tokenization_spaces=False)))
(out/'requests.json').write_text(json.dumps(requests,ensure_ascii=False))
print(json.dumps(dict(case='qwen_oracle',number_count=len(numbers),cases=len(records),requests=len(requests),scalar_count=len(scalars),split_count=count,split_sha256=h.hexdigest())),flush=True)
# CPU vocabulary loading only; DS4 remains an external behavioral oracle.
if args.ds4:
assert args.glm and args.deepseek
families=[]
for family,path in [('glm',args.glm),('deepseek',args.deepseek)]:
cases=[]
for text in texts[:19]:
raw=subprocess.check_output([str(args.ds4),'-m',str(path),'--dump-tokens','--raw','-p',text],text=True)
cases.append(dict(text=text,ids=json.loads(raw.splitlines()[0])))
families.append(dict(family=family,model=str(path),cases=cases))
(out/'ds4.json').write_text(json.dumps(dict(revision=subprocess.check_output(['git','-C',str(args.ds4.parent),'rev-parse','HEAD'],text=True).strip(),binary_sha256=hashlib.sha256(args.ds4.read_bytes()).hexdigest(),families=families),ensure_ascii=False))