File size: 22,189 Bytes
fef393c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
#!/usr/bin/env python3
"""Shared loading, streaming, and sampling helpers for nested byte Mamba tools."""
from __future__ import annotations

import gc
import json
import math
import random
import warnings
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterable, List, Optional, Sequence, Tuple

import torch
import torch.nn.functional as F

from modeling_nested_mamba import (
    ForwardBackwardRepairModel,
)


PAD = 0
BOS = 1
EOS = 2
UNK = 3
BYTE_OFFSET = 4
VOCAB_SIZE = 260


@dataclass
class LoadedNestedModel:
    model: ForwardBackwardRepairModel
    config: Dict[str, object]
    checkpoint_step: int
    trained_tokens: int
    checkpoint_path: Path
    precision: str
    fine_device: torch.device
    model_parallel: bool

    @property
    def core(self):
        return self.model.forward_model


def _dtype_for_precision(precision: str) -> torch.dtype:
    normalized = precision.lower()
    if normalized == "fp16":
        return torch.float16
    if normalized == "bf16":
        return torch.bfloat16
    if normalized == "fp32":
        return torch.float32
    raise ValueError(f"unsupported precision {precision!r}; choose fp16, bf16, or fp32")


def _mamba2_fused_causal_conv_available() -> bool:
    """Whether Mamba-2's combined full-sequence kernel can call causal-conv1d."""
    try:
        from mamba_ssm.ops.triton import ssd_combined

        return getattr(ssd_combined, "causal_conv1d_fwd_function", None) is not None
    except Exception:
        return False


def _configure_mamba2_inference_kernels(model: ForwardBackwardRepairModel) -> bool:
    """Select the portable full-sequence path when fused causal-conv1d is absent."""
    fused_available = _mamba2_fused_causal_conv_available()
    changed = False
    for block_group in (
        model.forward_model.global_blocks,
        model.forward_model.nested_global_blocks,
        model.forward_model.tertiary_global_blocks,
    ):
        for block in block_group:
            if int(getattr(block, "mamba_version", 1)) != 2:
                continue
            if not fused_available and bool(getattr(block.mixer, "use_mem_eff_path", False)):
                block.mixer.use_mem_eff_path = False
                changed = True
    return changed


def _build_model(config: Dict[str, object]) -> ForwardBackwardRepairModel:
    if config.get("training_method") == "diffusionblocks":
        raise ValueError(
            "this checkpoint declares an architecture that is incompatible with "
            "the nested Mamba inference implementation"
        )
    model = ForwardBackwardRepairModel(
        vocab_size=int(config.get("vocab_size", VOCAB_SIZE)),
        dim=int(config["dim"]),
        layers=int(config["layers"]),
        position_bins=int(config.get("position_bins", 8192)),
        use_mamba=not bool(config.get("no_mamba", False)),
        # Old checkpoints predate these fields and are Mamba-1/d_state=16.
        mamba_version=int(config.get("mamba_version", 1)),
        mamba_d_state=int(
            config.get(
                "mamba_d_state",
                64 if int(config.get("mamba_version", 1)) == 2 else 16,
            )
        ),
        mamba2_headdim=int(config.get("mamba2_headdim", 0)),
        num_sections=1,
        min_patch_bytes=int(config.get("blt_min_patch_bytes", 16)),
        max_patch_bytes=int(config.get("blt_max_patch_bytes", 96)),
        patch_change_threshold=int(config.get("blt_patch_change_threshold", 48)),
        close_threshold=float(config.get("blt_close_threshold", 0.98)),
        mid_close_bonus=float(config.get("blt_mid_close_bonus", 0.05)),
        nested_pool_factor=int(config.get("nested_pool_factor", 16)),
        nested_min_pool_factor=int(config.get("nested_min_pool_factor", 0)),
        nested_close_threshold=(
            None if config.get("nested_close_threshold") is None
            else float(config["nested_close_threshold"])
        ),
        nested_layers=int(config.get("nested_layers", 0)),
        tertiary_pool_factor=int(config.get("tertiary_pool_factor", 0)),
        tertiary_min_pool_factor=int(config.get("tertiary_min_pool_factor", 0)),
        tertiary_close_threshold=(
            None if config.get("tertiary_close_threshold") is None
            else float(config["tertiary_close_threshold"])
        ),
        tertiary_layers=int(config.get("tertiary_layers", 0)),
        decoder_dim=int(config.get("decoder_dim", 0)),
        detach_inactive_coarse_gradients=bool(
            config.get("detach_inactive_coarse_gradients", True)
        ),
        decoder_pool_controller=bool(config.get("decoder_pool_controller", False)),
        pool_controller_alpha=float(config.get("pool_controller_alpha", 1.0)),
        pool_controller_beta=float(config.get("pool_controller_beta", 0.5)),
        pool_controller_gamma=float(config.get("pool_controller_gamma", 0.5)),
        # Missing fields identify older nested checkpoints whose routing did
        # not include a short-pool budget.
        short_pool_budget=int(config.get("blt_short_pool_budget", 0)),
        short_pool_window=int(config.get("blt_short_pool_window", 0)),
        secondary_min_patch_bytes=int(config.get("blt_secondary_min_patch_bytes", 16)),
    )
    model.forward_model.mamba2_unfused_inference = _configure_mamba2_inference_kernels(model)
    return model


def _parse_device_list(value: Optional[str]) -> List[torch.device]:
    if not value:
        return []
    return [torch.device(item.strip()) for item in value.split(",") if item.strip()]


def _validate_cuda_devices(devices: Sequence[torch.device]) -> None:
    if not torch.cuda.is_available():
        raise RuntimeError(
            "CUDA is unavailable. mamba_ssm selective-scan inference requires CUDA in this environment."
        )
    count = torch.cuda.device_count()
    for device in devices:
        if device.type != "cuda" or device.index is None or device.index >= count:
            raise ValueError(f"requested device {device} is unavailable; CUDA device count is {count}")


def load_nested_checkpoint(
    checkpoint_path: str,
    *,
    precision: str = "fp16",
    device: str = "cuda:0",
    fine_device: Optional[str] = None,
    nested_devices: Optional[str] = None,
    tertiary_device: Optional[str] = None,
    use_saved_placement: bool = False,
) -> LoadedNestedModel:
    """Load weights without materializing checkpoint optimizer tensors.

    By default all inference runs on ``--device``. Model-parallel placement is
    enabled by supplying ``fine_device`` and ``nested_devices``, or by opting
    into the placement recorded in the checkpoint.
    """
    path = Path(checkpoint_path).expanduser().resolve()
    checkpoint = None
    if path.is_dir():
        config_path = path / "config.json"
        if not config_path.is_file():
            raise FileNotFoundError(f"model config not found: {config_path}")
        config = json.loads(config_path.read_text(encoding="utf-8"))
    elif path.is_file():
        # Backward-compatible local loading for an original training checkpoint.
        warnings.warn(
            "Loading a PyTorch .pt checkpoint requires pickle deserialization. "
            "Only load .pt files that you created or obtained from a trusted source; "
            "use the published SafeTensors directory for untrusted downloads.",
            UserWarning,
            stacklevel=2,
        )
        checkpoint = torch.load(path, map_location="cpu", weights_only=False, mmap=True)
        config = dict(checkpoint.get("config") or {})
    else:
        raise FileNotFoundError(f"model directory or checkpoint not found: {path}")
    if config.get("architecture") not in (None, "byte_latent_mamba_nested_jsonl") and config.get(
        "architecture_label"
    ) != "byte_latent_mamba_nested_jsonl":
        raise ValueError(f"{path} is not identified as a nested JSONL checkpoint")

    with torch.device("meta"):
        model = _build_model(config)
    if bool(getattr(model.forward_model, "mamba2_unfused_inference", False)):
        print(
            "Mamba-2 fused causal-conv1d is unavailable; using the portable "
            "batched convolution + SSD scan path."
        )
    if path.is_dir():
        from safetensors.torch import load_file

        index_path = path / "model.safetensors.index.json"
        single_path = path / "model.safetensors"
        if index_path.is_file():
            index = json.loads(index_path.read_text(encoding="utf-8"))
            weight_map = dict(index.get("weight_map") or {})
            expected = set(model.state_dict().keys())
            published = set(weight_map.keys())
            if expected != published:
                missing = sorted(expected - published)[:8]
                unexpected = sorted(published - expected)[:8]
                raise RuntimeError(
                    f"SafeTensors index does not match architecture; "
                    f"missing={missing}, unexpected={unexpected}"
                )
            for filename in dict.fromkeys(weight_map.values()):
                shard_path = path / filename
                shard = load_file(str(shard_path), device="cpu")
                model.load_state_dict(shard, strict=False, assign=True)
                del shard
        elif single_path.is_file():
            state = load_file(str(single_path), device="cpu")
            model.load_state_dict(state, strict=True, assign=True)
            del state
        else:
            raise FileNotFoundError(
                f"no model.safetensors or model.safetensors.index.json in {path}"
            )
        meta_names = [
            name for name, value in model.state_dict().items() if value.device.type == "meta"
        ]
        if meta_names:
            raise RuntimeError(f"unloaded model tensors remain: {meta_names[:8]}")
        step = 0
        trained_tokens = 0
    else:
        model.load_state_dict(checkpoint["model"], strict=True, assign=True)
        step = int(checkpoint.get("step", 0))
        trained_tokens = int(checkpoint.get("trained_tokens", 0))
        del checkpoint
    gc.collect()

    dtype = _dtype_for_precision(precision)
    if int(config.get("mamba_version", 1)) == 2 and dtype == torch.float16:
        print(
            "Mamba-2 FP16 inference: cached SSM accumulators will remain FP32; "
            "BF16 is recommended when the GPU supports it."
        )
    if use_saved_placement:
        fine_device = fine_device or str(config.get("fine_device") or "cuda:0")
        if nested_devices is None:
            saved_nested = config.get("nested_devices") or []
            nested_devices = ",".join(str(item) for item in saved_nested)
        tertiary_device = tertiary_device or (
            str(config["tertiary_device"]) if config.get("tertiary_device") else None
        )

    coarse = _parse_device_list(nested_devices)
    if coarse:
        if not fine_device:
            raise ValueError("--fine-device is required with --nested-devices")
        fine = torch.device(fine_device)
        tertiary = torch.device(tertiary_device) if tertiary_device else None
        requested = [fine, *coarse, *([tertiary] if tertiary else [])]
        _validate_cuda_devices(requested)
        # Cast on CPU first so a large FP32 checkpoint is never temporarily
        # placed in full on the root GPU.
        model.to(dtype=dtype)
        model.configure_model_parallel(fine, coarse, tertiary_device=tertiary)
        root = fine
        parallel = True
    else:
        root = torch.device(device)
        _validate_cuda_devices([root])
        model.to(root, dtype=dtype)
        parallel = False

    model.eval()
    return LoadedNestedModel(
        model=model,
        config=config,
        checkpoint_step=step,
        trained_tokens=trained_tokens,
        checkpoint_path=path,
        precision=precision,
        fine_device=root,
        model_parallel=parallel,
    )


def new_stream(loaded: LoadedNestedModel, maximum_input_bytes: int) -> Dict[str, object]:
    minimum = max(1, int(loaded.config.get("blt_min_patch_bytes", 16)))
    max_patches = math.ceil((int(maximum_input_bytes) + 1) / minimum) + 8
    return loaded.core.new_stream_state({}, max_patches=max_patches)


def stream_token(
    loaded: LoadedNestedModel,
    stream: Dict[str, object],
    token_id: int,
) -> torch.Tensor:
    token = torch.tensor(
        [[int(token_id)]], dtype=torch.long, device=loaded.fine_device
    )
    return loaded.core.stream_step(stream, {"x": token})[0, 0]


def apply_sampling_filters(
    logits: torch.Tensor,
    *,
    temperature: float,
    top_p: float,
    top_k: int,
    repeat_penalty: float,
    recent_tokens: Sequence[int],
) -> torch.Tensor:
    filtered = logits.float().clone()
    filtered[PAD] = filtered[BOS] = filtered[UNK] = -torch.inf
    if repeat_penalty > 1.0:
        for token_id in set(int(value) for value in recent_tokens):
            if 0 <= token_id < filtered.numel():
                value = filtered[token_id]
                filtered[token_id] = (
                    value / repeat_penalty if value >= 0 else value * repeat_penalty
                )
    filtered /= max(1e-5, float(temperature))
    if top_k > 0 and top_k < filtered.numel():
        threshold = torch.topk(filtered, int(top_k)).values[-1]
        filtered[filtered < threshold] = -torch.inf
    if 0.0 < top_p < 1.0:
        probabilities = torch.softmax(filtered, dim=-1)
        sorted_probabilities, sorted_indices = torch.sort(probabilities, descending=True)
        remove = torch.cumsum(sorted_probabilities, dim=0) > float(top_p)
        remove[0] = False
        filtered[sorted_indices[remove]] = -torch.inf
    return filtered


def choose_token(filtered_logits: torch.Tensor, greedy: bool = False) -> int:
    finite = torch.isfinite(filtered_logits)
    if not bool(finite.any().item()):
        raise FloatingPointError(
            "sampling has no finite logits; the recurrent inference state became "
            "non-finite. Retry with --precision bf16 (recommended for Mamba-2) "
            "or --precision fp32."
        )
    if greedy:
        return int(filtered_logits.argmax())
    probabilities = torch.softmax(filtered_logits, dim=-1)
    if not bool(torch.isfinite(probabilities).all().item()):
        raise FloatingPointError(
            "sampling probabilities became non-finite. Retry with --precision "
            "bf16 (recommended for Mamba-2) or --precision fp32."
        )
    return int(torch.multinomial(probabilities, 1))


def hierarchy_token_attribution(
    loaded: LoadedNestedModel,
    stream: Dict[str, object],
    logits: torch.Tensor,
    token_id: int,
) -> Dict[str, object]:
    """Measure direct L2/L3 decoder influence on one selected next byte.

    This reuses the cached streaming state and only reruns the small decoder
    and LM head. Positive deltas mean the dynamic hierarchy increased the
    selected token's log probability relative to its BOE counterfactual.
    """
    parts = list(loaded.core.last_stream_decode_parts)
    decoder_device = parts[0].device
    normal_log_probability = float(
        F.log_softmax(logits.float(), dim=-1)[int(token_id)].detach().cpu().item()
    )

    def counterfactual(*, remove_l2: bool, remove_l3: bool) -> float:
        altered = list(parts)
        if remove_l2:
            initial_nested = stream["initial_nested_global"]
            if initial_nested.device != decoder_device:
                initial_nested = initial_nested.to(decoder_device, non_blocking=True)
            altered[3] = initial_nested
        if remove_l3 and len(altered) > 4:
            initial_tertiary = stream["initial_tertiary_global"]
            if initial_tertiary.device != decoder_device:
                initial_tertiary = initial_tertiary.to(
                    decoder_device, non_blocking=True
                )
            altered[4] = initial_tertiary
        altered_logits = loaded.core.lm_head(
            loaded.core.decoder(torch.cat(altered, dim=-1))
        )[0, 0].float()
        return float(
            F.log_softmax(altered_logits, dim=-1)[int(token_id)].detach().cpu().item()
        )

    level2_active = int(stream["completed_nested_patches"]) > 0
    level3_active = int(stream.get("completed_tertiary_patches", 0)) > 0
    without_l2 = (
        counterfactual(remove_l2=True, remove_l3=False)
        if level2_active
        else normal_log_probability
    )
    without_l3 = (
        counterfactual(remove_l2=False, remove_l3=True)
        if level3_active
        else normal_log_probability
    )
    without_hierarchy = (
        counterfactual(remove_l2=True, remove_l3=True)
        if level3_active
        else without_l2
    )
    return {
        "level2_active": level2_active,
        "level3_active": level3_active,
        "level2_delta_logp": normal_log_probability - without_l2,
        "level3_delta_logp": normal_log_probability - without_l3,
        "hierarchy_delta_logp": normal_log_probability - without_hierarchy,
        "selected_logp": normal_log_probability,
    }


def hierarchy_mode_logits(
    loaded: LoadedNestedModel,
    stream: Dict[str, object],
    logits: torch.Tensor,
    mode: str,
) -> torch.Tensor:
    """Return next-token logits with selected hierarchy readouts disabled."""
    normalized = str(mode).lower()
    if normalized == "full":
        return logits
    if normalized not in {"level1", "level12"}:
        raise ValueError("hierarchy mode must be 'level1', 'level12', or 'full'")
    parts = list(loaded.core.last_stream_decode_parts)
    decoder_device = parts[0].device
    if normalized == "level1":
        initial_nested = stream["initial_nested_global"]
        if initial_nested.device != decoder_device:
            initial_nested = initial_nested.to(decoder_device, non_blocking=True)
        parts[3] = initial_nested
    if len(parts) > 4:
        initial_tertiary = stream["initial_tertiary_global"]
        if initial_tertiary.device != decoder_device:
            initial_tertiary = initial_tertiary.to(decoder_device, non_blocking=True)
        parts[4] = initial_tertiary
    return loaded.core.lm_head(
        loaded.core.decoder(torch.cat(parts, dim=-1))
    )[0, 0]


def generate_bytes(
    loaded: LoadedNestedModel,
    prompt: bytes,
    *,
    max_new_bytes: int,
    temperature: float = 0.8,
    top_p: float = 0.9,
    top_k: int = 0,
    repeat_penalty: float = 1.05,
    repeat_window: int = 256,
    greedy: bool = False,
    seed: int = 1234,
    collect_hierarchy_attribution: bool = False,
    hierarchy_mode: str = "full",
) -> Tuple[bytes, Dict[str, object]]:
    if collect_hierarchy_attribution and hierarchy_mode != "full":
        raise ValueError("hierarchy attribution is defined for the full hierarchy rollout")
    torch.manual_seed(seed)
    random.seed(seed)
    stream = new_stream(loaded, len(prompt) + max_new_bytes + 2)
    with torch.inference_mode():
        logits = stream_token(loaded, stream, BOS)
        for value in prompt:
            logits = stream_token(loaded, stream, BYTE_OFFSET + int(value))
        output = bytearray()
        attributions: List[Dict[str, object]] = []
        recent: List[int] = [BYTE_OFFSET + int(value) for value in prompt[-repeat_window:]]
        for generated_position in range(max(0, int(max_new_bytes))):
            sampling_logits = hierarchy_mode_logits(
                loaded, stream, logits, hierarchy_mode
            )
            if not bool(torch.isfinite(sampling_logits).all().item()):
                finite_count = int(torch.isfinite(sampling_logits).sum().item())
                raise FloatingPointError(
                    "non-finite generation logits before sampling: "
                    f"generated_byte={generated_position} mode={hierarchy_mode} "
                    f"precision={loaded.precision} "
                    f"finite_logits={finite_count}/{sampling_logits.numel()}. "
                    "Retry with --precision bf16 (recommended for Mamba-2) or "
                    "--precision fp32."
                )
            filtered = apply_sampling_filters(
                sampling_logits,
                temperature=temperature,
                top_p=top_p,
                top_k=top_k,
                repeat_penalty=repeat_penalty,
                recent_tokens=recent[-repeat_window:],
            )
            token_id = choose_token(filtered, greedy=greedy)
            if token_id == EOS:
                break
            if not BYTE_OFFSET <= token_id < BYTE_OFFSET + 256:
                continue
            if collect_hierarchy_attribution:
                attributions.append(
                    hierarchy_token_attribution(
                        loaded, stream, sampling_logits, token_id
                    )
                )
            output.append(token_id - BYTE_OFFSET)
            recent.append(token_id)
            logits = stream_token(loaded, stream, token_id)
    stream["generated_attribution"] = attributions
    stream["hierarchy_mode"] = hierarchy_mode
    return bytes(output), stream


def load_jsonl_texts(
    path: str,
    *,
    text_field: str = "text",
    max_documents: Optional[int] = None,
) -> Iterable[Tuple[int, str]]:
    jsonl = Path(path).expanduser().resolve()
    if not jsonl.is_file():
        raise FileNotFoundError(f"JSONL file not found: {jsonl}")
    yielded = 0
    with jsonl.open("r", encoding="utf-8") as handle:
        for line_number, line in enumerate(handle, 1):
            if not line.strip():
                continue
            try:
                record = json.loads(line)
            except json.JSONDecodeError as error:
                raise ValueError(f"invalid JSON at {jsonl}:{line_number}: {error.msg}") from error
            text = record.get(text_field)
            if not isinstance(text, str):
                raise ValueError(
                    f"{jsonl}:{line_number} must contain a string field {text_field!r}"
                )
            yield line_number, text
            yielded += 1
            if max_documents is not None and yielded >= int(max_documents):
                return