"""Prepare V2.1 LM + V3 natural chat + C1r2 conversion without test/blind reads.""" import argparse from collections import Counter import hashlib import json from pathlib import Path import struct import sys import unicodedata import numpy as np import sentencepiece as spm ROOT=Path(__file__).resolve().parents[2] sys.path.insert(0,str(ROOT/'src')) from vimeml.training.data import file_sha,write_json,prepare_indexes from vimeml.training.v41_data import prepare_maps from vimeml.posttraining.data import TOKENIZER_SHA OLD=ROOT/'artifacts/token-data/corpus-v4-8k' CHAT=ROOT/'artifacts/token-data/corpus-v3-half/chat' DEST=ROOT/'artifacts/token-data/corpus-v41-chat-8k' INDEX=ROOT/'artifacts/training-data/corpus-v41-chat-c128' MIX=ROOT/'artifacts/training-data/corpus-v41-mixed-c128' CONV=ROOT/'artifacts/posttrain-data/v4-c1r2' def canonical(text): return ' '.join(unicodedata.normalize('NFKC',text).split()) def key(text): return hashlib.blake2b(canonical(text).encode(),digest_size=16).digest() def main(): parser=argparse.ArgumentParser(description=__doc__) parser.add_argument('--maps-only',action='store_true') args=parser.parse_args() if args.maps_only: prepare_maps(MIX) return for d in (DEST,INDEX,MIX): d.mkdir(parents=True,exist_ok=True) if (MIX/'request.json').exists(): prepare_maps(MIX) return seed=json.loads((ROOT/'outputs/remote/v41-20261016/randomization.json').read_text())['seed'] old=json.loads((OLD/'manifest.json').read_text('utf-8')) chat_old=json.loads((CHAT/'manifest.json').read_text('utf-8')) tokenizer=ROOT/'artifacts/tokenizers/v4-a/ja-unigram-8192/tokenizer.model' assert file_sha(tokenizer)==TOKENIZER_SHA sp=spm.SentencePieceProcessor(model_file=str(tokenizer)) rows={s:[json.loads(line) for line in (CHAT/f'{s}.jsonl').read_text('utf-8').splitlines()] for s in ('train','validation')} assert all(len(rows[s])==chat_old['splits'][s]['sentences'] for s in rows) assert all(r['source'] in ('real-persona-chat','mrmp-chat') for s in rows for r in rows[s]) assert not ({r['doc'] for r in rows['train']}&{r['doc'] for r in rows['validation']}) held={key(line) for line in (ROOT/'outputs/corpus-fast-v1/validation.txt').open(encoding='utf-8')} held.update(key(r['text']) for r in rows['validation']) # Only explicit development/regression provenance; never open old V3 blind filters. for track in ('standard-development','legacy-regression','ajimee','historical-development'): for r in json.loads((ROOT/f'outputs/evaluation-v4-20261015/inputs/{track}.json').read_text('utf-8')): text=r.get('provenance',{}).get('original_message') if text: held.add(key(text)) chat_keys={key(r['text']) for r in rows['train']} overlap=set() keep=np.ones(old['splits']['train']['sentences'],dtype=np.bool_) counts=Counter() print('Scanning only frozen V2.1 training text for validation/cross-pool exact overlap',flush=True) actual=hashlib.sha256() count=0 with (ROOT/'outputs/corpus-fast-v1/train.txt').open('rb') as stream: for i,raw in enumerate(stream): actual.update(raw) fingerprint=key(raw.decode('utf-8').rstrip('\r\n')) if fingerprint in held: keep[i]=False counts['old_heldout_exact']+=1 elif fingerprint in chat_keys: overlap.add(fingerprint) count=i+1 if count%5000000==0: print(json.dumps({'old_rows':count,'removed':int((~keep[:count]).sum())}),flush=True) assert count==len(keep) assert actual.hexdigest()=='9c1b4585adaf0518dc0275e7f8086e967db26f9797fb571e1ff481aeadf2ff8f' np.save(MIX/'old.keep.npy',keep,allow_pickle=False) source_ids={'mrmp-chat':0,'real-persona-chat':1} stats={} for split in ('validation','train'): tokens,offsets,sources=bytearray(),[0],bytearray() chars=content=longest=0 primary=Counter() accepted_records=[] texts=[] for row in rows[split]: fingerprint=key(row['text']) if split=='train' and fingerprint in held: counts['chat_heldout_exact']+=1 continue if split=='train' and fingerprint in overlap: counts['chat_exact_duplicate_of_old']+=1 continue texts.append(row['text']) accepted_records.append(row) for start in range(0,len(texts),10000): batch=texts[start:start+10000] encoded=sp.encode(batch,num_threads=8) decoded=sp.decode(encoded,num_threads=8) for row,text,ids,restored in zip(accepted_records[start:start+10000],batch,encoded,decoded,strict=True): if restored!=text or not ids or any(i<7 or i>=8192 for i in ids): raise ValueError('Chat roundtrip, reserved marker or token ID error') full=[2,*ids,3] tokens.extend(struct.pack(f'<{len(full)}H',*full)) offsets.append(offsets[-1]+len(full)) sources.append(source_ids[row['source']]) chars+=len(text) content+=len(ids) longest=max(longest,len(full)) primary[row['source']]+=1 (DEST/f'{split}.tokens.bin').write_bytes(tokens) np.asarray(offsets,dtype='