"""Prototype: frozen-parent encoder/base head with a learned CLS-window selector.""" import torch from torch import nn from tokenfold_select._model import JointGroupModel class WindowGateModel(JointGroupModel): """One head choice per complete encoder window, not per token or group.""" def __init__(self, encoder): super().__init__(encoder) self.window_gate=nn.Linear(encoder.config.hidden_size,1) self.service_head=nn.Linear(encoder.config.hidden_size,1) def choose_head(self, pooled, cls): gate=self.window_gate(cls) original=self.head(pooled) service=self.service_head(pooled) valid=torch.isfinite(gate)&torch.isfinite(original)&torch.isfinite(service) chosen=torch.where(gate>=0,service,original) return torch.where(valid,chosen,torch.full_like(chosen,float('nan'))) def forward(self, input_ids, attention_mask, ranges): if input_ids.ndim!=2 or input_ids.shape[0]!=1 or attention_mask.shape!=input_ids.shape: raise ValueError('single_sequence_required') if not 1<=len(ranges)<=512: raise ValueError('invalid_ranges') previous=0 for start,end in ranges: if not previous<=start