"""logits-processor: the generation last-mile, loadable through `kernels`. Two fused ops that every serving stack runs on the hot path: - ``apply_token_bitmask`` - structured / guided decoding. Set the logits of disallowed tokens to -inf from a packed allow-mask (XGrammar convention: bit 1 = allowed), so the next softmax cannot pick them. This is the kernel behind constrained JSON and tool-calling output. - ``sample`` - temperature, then the intersection of top-k / top-p (nucleus) / min-p filtering, then a multinomial draw, fused into one logits -> token op with a counter-based RNG that is deterministic in ``(seed, row, offset)``. ``temperature <= 0`` is greedy (argmax). Both take ``[B, V]`` logits in float / half / bf16. Sampling parameters are per-row: pass a scalar to broadcast, or a length-B tensor for a heterogeneous batch (continuous batching, per-request grammars). """ from typing import Union import torch from ._ops import ops __all__ = ["apply_token_bitmask", "sample"] Param = Union[float, int, torch.Tensor] def _row(x: Param, B: int, device, dtype) -> torch.Tensor: if isinstance(x, torch.Tensor): return x.to(device=device, dtype=dtype).contiguous() return torch.full((B,), x, device=device, dtype=dtype) def apply_token_bitmask(logits: torch.Tensor, bitmask: torch.Tensor) -> torch.Tensor: """In place: set disallowed tokens' logits to -inf. logits: ``[B, V]`` (float/half/bf16). bitmask: ``[B, ceil(V/32)]`` int32, bit 1 = token allowed. Returns ``logits`` for chaining. """ ops.apply_token_bitmask_inplace(logits, bitmask.to(torch.int32).contiguous()) return logits def sample(logits: torch.Tensor, temperature: Param = 1.0, top_k: Param = 0, top_p: Param = 1.0, min_p: Param = 0.0, seed: int = 0, offset: int = 0) -> torch.Tensor: """Fused temperature + top-k ∩ top-p ∩ min-p + multinomial sample. top_k <= 0, top_p >= 1, and min_p <= 0 each disable that filter. temperature <= 0 selects the argmax. Returns int64 token ids ``[B]``. Same ``(seed, offset)`` and logits give the same tokens. """ assert logits.dim() == 2, "logits must be [B, V]" B, dev = logits.size(0), logits.device return ops.sample( logits.contiguous(), _row(temperature, B, dev, torch.float32), _row(top_p, B, dev, torch.float32), _row(top_k, B, dev, torch.int32), _row(min_p, B, dev, torch.float32), int(seed), int(offset), )