interlens.interp.activation_cache¶
interlens.interp.activation_cache
¶
ActivationCache
¶
A queryable store of captured activations, tagged by conversation structure.
Records know which participant/turn/layer/site they came from, so a downstream probe can ask for "bob's turn
3, layer 18, the answer span" rather than juggling anonymous tensors. This is the object every interp
consumer reads; the harness never puts activations in Message.metadata (that would blow up branch()'s
transcript copy), so the cache is the single home for heavy tensors.
Source code in src/interlens/interp/activation_cache.py
add_batch
¶
Add many records at once, offloading all their tensors in one batched pinned transfer (see
offload_to_cpu). Preferred over a loop of add when a single capture pass produces many records —
it turns N GPU->CPU copies into one per shape-group.
Source code in src/interlens/interp/activation_cache.py
at
¶
Return the single matching tensor, erroring if the filters aren't unique — the ergonomic accessor for "give me exactly this activation".
Source code in src/interlens/interp/activation_cache.py
ActivationRecord
dataclass
¶
ActivationRecord(
participant: str,
message_idx: int,
layer: int,
site: Site,
tensor: Tensor,
token_span: tuple[int, int],
phases: dict[Phase, tuple[int, int]] = dict(),
)
One captured tensor plus everything needed to know what it is.
tensor is [seq, d_model] for one (participant turn, layer, site). phases maps prompt /
reasoning / answer to (start, end) token indices into that sequence, so reasoning-vs-answer
activations are separable for CoT models. token_span is the (prompt_len, seq_len) boundary between
the fed-in prompt and the newly generated tokens.
CaptureSpec
dataclass
¶
CaptureSpec(
sites: tuple[Site, ...] = ("residual",),
layers: tuple[int, ...] | None = None,
offload: OffloadLocation = "cpu",
)
What to capture during a generation: which sites at which layers, and where to keep the tensors.
Capture defaults to a narrow set — you pass the layers/sites you actually want — because capturing all
layers × all tokens × many rollouts OOMs fast. offload='cpu' moves captured tensors off-GPU as they are
recorded (essential for large sweeps); offload=None keeps them on-device.
offload_to_cpu
¶
Move a list of GPU tensors to CPU efficiently, preserving order.
Two wins over per-tensor .to('cpu'): (1) batching — same-shape/dtype tensors are stacked into one D2H
copy, so N transfer launches collapse to one per shape-group; (2) pinned + non_blocking — the copy goes
through a pinned host staging buffer, which hits full PCIe bandwidth (pageable D2H is throttled by CUDA's
internal bounce buffer) and lets the transfer overlap with compute. Results are cloned into pageable memory so
the (limited) pinned buffer is freed immediately rather than pinned for the cache's lifetime. Falls back to a
plain detach/copy when there's nothing to gain (CPU inputs or CUDA unavailable).