voidful's picture
Add fail-closed stable speaker inference
7e7df2a
Raw History Blame
3.48 kB
"""Barbet as the Text-Semantic LM (TSLM), with incremental decoding.
VoxCPM2's generation loop decodes one latent patch at a time, so the TSLM needs
a stateful single-step path. As of the open_formosa R2 architecture, Barbet
ships exactly that: a real **Mamba2** mixer and a native ``BarbetCache`` holding
- attention K/V states (rolling window for sliding-window layers),
- the trailing ``d_conv - 1`` causal-conv inputs per Mamba layer, and
- the Mamba2 selective-scan SSM state per Mamba layer.
We delegate stepwise decoding to it rather than re-deriving a cache by hand:
the selective-scan state cannot be reconstructed from a conv ring buffer alone,
so the official cache is the only correct option (and it stays in lockstep with
upstream). ``tests/test_step_equivalence.py`` asserts prefill + forward_step
reproduces the full-sequence forward across all layer types.
"""
from __future__ import annotations
from dataclasses import dataclass
import torch
from torch import nn
from barbet import BarbetConfig, BarbetModel
from barbet.modeling_barbet import BarbetCache
@dataclass
class BarbetStepState:
"""Decoding state: Barbet's native hybrid cache (tracks position itself)."""
cache: BarbetCache
@property
def pos(self) -> int:
return self.cache.seen_tokens
class BarbetTSLM(nn.Module):
"""Barbet backbone with embeddings, full forward, and cached stepwise decode."""
def __init__(self, config: BarbetConfig) -> None:
super().__init__()
self.config = config
self.backbone = BarbetModel(config)
@property
def embed_tokens(self) -> nn.Embedding:
return self.backbone.embed_tokens
@property
def hidden_size(self) -> int:
return self.config.hidden_size
def forward(
self,
inputs_embeds: torch.Tensor,
attention_mask: torch.Tensor | None = None,
) -> torch.Tensor:
"""Full-sequence forward (training / teacher-forcing), no cache."""
out = self.backbone(
inputs_embeds=inputs_embeds,
attention_mask=attention_mask,
use_cache=False,
return_dict=True,
)
return out.last_hidden_state
# ------------------------------------------------------------------ #
# Stepwise decoding (delegated to BarbetCache)
# ------------------------------------------------------------------ #
@torch.no_grad()
def prefill(self, inputs_embeds: torch.Tensor) -> tuple[torch.Tensor, BarbetStepState]:
"""Run the prompt and return (hidden_states, state) with a warm cache."""
cache = BarbetCache(self.config)
out = self.backbone(
inputs_embeds=inputs_embeds,
past_key_values=cache,
use_cache=True,
return_dict=True,
)
return out.last_hidden_state, BarbetStepState(cache=out.past_key_values or cache)
@torch.no_grad()
def forward_step(self, x: torch.Tensor, state: BarbetStepState) -> torch.Tensor:
"""Decode one position. ``x``: [B, H] input embedding -> [B, H] hidden.
The cache tracks the running position (``seen_tokens``), so RoPE offsets
and the sliding-window / conv / SSM states advance automatically.
"""
out = self.backbone(
inputs_embeds=x.unsqueeze(1),
past_key_values=state.cache,
use_cache=True,
return_dict=True,
)
return out.last_hidden_state[:, 0, :]