"""Structured conversion codec and mmap dataset. Labels are already shifted.""" import json from pathlib import Path import numpy as np import sentencepiece as spm from .readings import RESERVED TOKENIZER_SHA = "296a2bdf4f4862be8bda848ed1cd261f0b0fd43064181fad5a513c6ce73081b5" FORMAT = "vimeml_conversion_v1" class ConversionCodec: def __init__(self, model): from vimeml.training.data import file_sha if file_sha(model) != TOKENIZER_SHA: raise ValueError("V4 tokenizer identity mismatch") self.sp = spm.SentencePieceProcessor(model_file=str(model)) if [self.sp.piece_to_id(x) for x in ("", "", "")] != [4, 5, 6]: raise ValueError("Structural marker identity mismatch") def encode(self, context, reading, output): if not output or any(s in field for field in (context, reading, output) for s in RESERVED): raise ValueError("Empty target or reserved literal") r, out = self.sp.encode(reading), self.sp.encode(output) budget = 129 - (len(r) + len(out) + 5) if budget < 0: return None ctx, removed = self.sp.encode(context), 0 while len(ctx) > budget: # Unicode scalar boundary, never slicing encoded bytes or OUT/READ. removed += 1 ctx = self.sp.encode(context[removed:]) ids = [2, 4] + ctx + [5] + r + [6] + out + [3] first_output = 4 + len(ctx) + len(r) if self.sp.unk_id() in ids: raise ValueError("Unexpected UNK") return ids, first_output, removed def shifted(ids, first_output): if not (ids[0] == 2 and ids[-1] == 3 and ids[first_output - 1] == 6): raise ValueError("Malformed conversion sequence") labels = [-100] * (first_output - 1) + ids[first_output:] assert len(ids) - 1 == len(labels) <= 128 return {"input_ids": ids[:-1], "labels": labels} class ConversionDataset: def __init__(self, directory, split="train"): self.directory = Path(directory) self.split = split if split not in ("train", "validation"): raise ValueError("Only train/validation are allowed") self.manifest = json.loads((self.directory / "manifest.json").read_text(encoding="utf-8")) if self.manifest["status"] != "complete" or self.manifest["format"] != FORMAT: raise ValueError("Unfrozen conversion data") self.count = self.manifest["splits"][split]["examples"] self.ids = np.memmap(self.directory / f"{split}.tokens.bin", mode="r", dtype="