"""Bounded, inspectable span rewriting with exact preservation of surrounding text.""" import math import time import torch from torch.nn import functional as F from .model import BOS,EOS,MASK from .inference import denoise from .train import amp @torch.inference_mode() def local_nll(model,tokenizer,text,start,end,precision="fp32"): encoded=tokenizer.encode(text) if len(encoded.ids)+1>model.config.max_length: raise ValueError("Text exceeds model context") positions=[i for i,(a,b) in enumerate(encoded.offsets) if b>start and a=b or len(text[a:b].split())>12: raise ValueError("Select 1 to 12 words") if (a>0 and text[a-1].isalnum() and text[a].isalnum()) or (bmodel.config.max_length-32: raise ValueError("Text too long; shorten it before editing") prefix,suffix=text[:a],text[b:] left=tokenizer.encode(prefix.rstrip()).ids right=tokenizer.encode(suffix).ids count=len(tokenizer.encode((' ' if prefix else '')+text[a:b]).ids) if not 1<=count<=16:raise ValueError("Select a shorter passage (at most 16 subword tokens)") before=local_nll(model,tokenizer,text,a,b,precision) candidates=[];seen={text[a:b]};started=time.perf_counter() # Six deterministic attempts; all unique candidates are retained with disclosed ranking. lengths=[count,count,count,max(1,count-1),min(16,count+1),count] for attempt,length in enumerate(lengths): ids=[BOS]+left+[MASK]*length+right+[EOS] result=denoise(model,tokenizer,ids,steps=12,temperature=0 if attempt==0 else .8, seed=seed+attempt,precision=precision,allow_eos=False) fragment=tokenizer.decode(result["tokens"][1+len(left):1+len(left)+length]).strip() if not fragment or fragment in seen or '\ufffd' in fragment or len(fragment.split())>14: continue seen.add(fragment) rewritten=prefix+fragment+suffix score=local_nll(model,tokenizer,rewritten,a,a+len(fragment),precision) trace=[] for row in result["trace"]: # The raw model trace is shown for research inspection, not used to rewrite context. trace.append({"step":row["step"],"remaining":row["remaining"],"model_text":row["text"]}) candidates.append({"replacement":fragment,"text":rewritten,"local_nll":score, "delta_nll":score-before,"seed":seed+attempt,"masked_tokens":length,"trace":trace}) candidates.sort(key=lambda c:c["local_nll"]) return {"original":text,"selection":{"start":a,"end":b,"text":text[a:b]}, "prefix":prefix,"suffix":suffix,"original_local_nll":before,"candidates":candidates, "attempts":len(lengths),"latency_ms":(time.perf_counter()-started)*1000, "method":"Bidirectional denoising; rank by causal NLL of the span and up to 16 following tokens.", "limitation":"Ranking reflects this model's preferences, not verified grammar or semantic correctness."}