---
title: gemma
---

<div class="python-reference">
<a id="fastvideo.models.encoders.gemma-classes"></a>
<a id="fastvideo.models.encoders.gemma-functions"></a>
<a id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel-functions"></a>
<a id="fastvideo.models.encoders.gemma"></a>

<h2 id="fastvideo.models.encoders.gemma.BaseEncoderOutput"><code>fastvideo.models.encoders.gemma.BaseEncoderOutput</code></h2>
<pre class="doc-signature"><code>BaseEncoderOutput(last_hidden_state: torch.FloatTensor | None = None, pooler_output: torch.FloatTensor | None = None, hidden_states: tuple&#91;torch.FloatTensor, ...&#93; | None = None, attentions: tuple&#91;torch.FloatTensor, ...&#93; | None = None, attention_mask: torch.Tensor | None = None)</code></pre>


<h2 id="fastvideo.models.encoders.gemma.TextEncoderConfig"><code>fastvideo.models.encoders.gemma.TextEncoderConfig</code></h2>
<pre class="doc-signature"><code>TextEncoderConfig(arch_config: ArchConfig = TextEncoderArchConfig(), prefix: str = &#x27;&#x27;, quant_config: QuantizationConfig | None = None, lora_config: Any | None = None, is_chat_model: bool = False, treat_empty_as_dot: bool = False, *, _resolved_attention_backend: AttentionBackendEnum | None = None, chat_template_enable_thinking: bool = False)</code></pre>


<h2 id="fastvideo.models.encoders.gemma.TextEncoder"><code>fastvideo.models.encoders.gemma.TextEncoder</code></h2>
<pre class="doc-signature"><code>TextEncoder(config: TextEncoderConfig)</code></pre>
<p>Base for native encoders with a model-specific forward output contract.</p>

<h3 id="fastvideo.models.encoders.gemma.TextEncoder.forward"><code>fastvideo.models.encoders.gemma.TextEncoder.forward</code></h3>
<pre class="doc-signature"><code>forward(*args: Any, **kwargs: Any) -&gt; TextEncoderOutputT</code></pre>

<h2 id="fastvideo.models.encoders.gemma.FeedForward"><code>fastvideo.models.encoders.gemma.FeedForward</code></h2>
<pre class="doc-signature"><code>FeedForward(dim: int, dim_out: int, mult: int = 4, bias: bool = True, quant_config: QuantizationConfig | None = None, prefix: str = &#x27;&#x27;)</code></pre>
<p>LTX-2 FFN: GELUApprox -&gt; Identity -&gt; Linear.</p>

<h3 id="fastvideo.models.encoders.gemma.FeedForward.forward"><code>fastvideo.models.encoders.gemma.FeedForward.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor) -&gt; torch.Tensor</code></pre>

<h2 id="fastvideo.models.encoders.gemma.LTXRopeType"><code>fastvideo.models.encoders.gemma.LTXRopeType</code></h2>
<pre class="doc-signature"><code>LTXRopeType()</code></pre>
<p>LTX-2 rotary variants (interleaved vs split).</p>
<h2 id="fastvideo.models.encoders.gemma.apply_ltx_rotary_emb"><code>fastvideo.models.encoders.gemma.apply_ltx_rotary_emb</code></h2>
<pre class="doc-signature"><code>apply_ltx_rotary_emb(input_tensor: torch.Tensor, freqs_cis: tuple&#91;torch.Tensor, torch.Tensor&#93;, rope_type: LTXRopeType = LTXRopeType.INTERLEAVED) -&gt; torch.Tensor</code></pre>
<p>Apply LTX-2 rotary embeddings to a tensor.</p>
<h2 id="fastvideo.models.encoders.gemma.generate_ltx_freq_grid_float64"><code>fastvideo.models.encoders.gemma.generate_ltx_freq_grid_float64</code></h2>
<pre class="doc-signature"><code>generate_ltx_freq_grid_float64(positional_embedding_theta: float, positional_embedding_max_pos_count: int, inner_dim: int) -&gt; torch.Tensor</code></pre>
<p>Generate an eager-cached, compile-safe float64 LTX-2 rotary grid.</p>
<h2 id="fastvideo.models.encoders.gemma.generate_ltx_freq_grid_pytorch"><code>fastvideo.models.encoders.gemma.generate_ltx_freq_grid_pytorch</code></h2>
<pre class="doc-signature"><code>generate_ltx_freq_grid_pytorch(positional_embedding_theta: float, positional_embedding_max_pos_count: int, inner_dim: int) -&gt; torch.Tensor</code></pre>
<p>Generate an eager-cached, compile-safe float32 LTX-2 rotary grid.</p>
<h2 id="fastvideo.models.encoders.gemma.precompute_ltx_freqs_cis"><code>fastvideo.models.encoders.gemma.precompute_ltx_freqs_cis</code></h2>
<pre class="doc-signature"><code>precompute_ltx_freqs_cis(indices_grid: torch.Tensor, dim: int, out_dtype: torch.dtype, theta: float = 10000.0, max_pos: list&#91;int&#93; | None = None, use_middle_indices_grid: bool = False, num_attention_heads: int = 32, rope_type: LTXRopeType = LTXRopeType.INTERLEAVED, freq_grid_generator: Callable&#91;&#91;float, int, int&#93;, torch.Tensor&#93; = generate_ltx_freq_grid_pytorch) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;</code></pre>
<p>Precompute LTX-2 rotary cos/sin grids for (t, x, y) positions.</p>
<h2 id="fastvideo.models.encoders.gemma.default_weight_loader"><code>fastvideo.models.encoders.gemma.default_weight_loader</code></h2>
<pre class="doc-signature"><code>default_weight_loader(param: torch.Tensor, loaded_weight: torch.Tensor) -&gt; None</code></pre>
<p>Default weight loader.</p>
<h2 id="fastvideo.models.encoders.gemma.effective_attention_backend"><code>fastvideo.models.encoders.gemma.effective_attention_backend</code></h2>
<pre class="doc-signature"><code>effective_attention_backend(config: object) -&gt; AttentionBackendEnum | None</code></pre>
<p>Return the attention backend that a component built from <code>config</code> follows.</p>
<p>Some components choose part of their own structure by backend: Wan picks
its VSA transformer block, LTX-2 its distributed-attention path, and the
Gemma and T5-Gemma text encoders switch Hugging Face attention to SDPA.
That choice must match the attention layers inside the component, and
those layers resolve through :func:<code>get_attn_backend</code>. This function
applies the same order:</p>
<ol>
<li>The decision a loader recorded on <code>config</code> with
   :func:<code>record_resolved_attention_backend</code>. Every component built by a
   loader has one when a backend was requested.</li>
<li>While a loader is building a component (a
   <code>_component_attention_backend_scope</code> is active), that scope's rule:
   its backend, and the environment variable only when the scope consults
   it. A scope that deliberately ignores the environment variable, such as
   the dense DMD teacher and critic, keeps ignoring it here.</li>
<li>With no loader involved, the <code>FASTVIDEO_ATTENTION_BACKEND</code>
   environment variable. This covers components constructed directly,
   such as MagiHuman's lazily built T5-Gemma encoder or a script that
   builds a transformer itself.</li>
</ol>
<p>Reading only the recorded decision is not enough: a directly constructed
component has no recorded decision (<code>None</code>), so it would ignore the
environment variable while its attention layers still follow it, and the
component's structure would not match its layers.</p>
<h2 id="fastvideo.models.encoders.gemma.AttentionBackendEnum"><code>fastvideo.models.encoders.gemma.AttentionBackendEnum</code></h2>
<pre class="doc-signature"><code>AttentionBackendEnum()</code></pre>

<h2 id="fastvideo.models.encoders.gemma.get_local_torch_device"><code>fastvideo.models.encoders.gemma.get_local_torch_device</code></h2>
<pre class="doc-signature"><code>get_local_torch_device() -&gt; torch.device</code></pre>
<p>Return the torch device for the current rank.</p>
<h2 id="fastvideo.models.encoders.gemma.GemmaConnectorConfig"><code>fastvideo.models.encoders.gemma.GemmaConnectorConfig</code></h2>
<pre class="doc-signature"><code>GemmaConnectorConfig(num_attention_heads: int, attention_head_dim: int, num_layers: int, positional_embedding_theta: float, positional_embedding_max_pos: list&#91;int&#93;, rope_type: LTXRopeType, double_precision_rope: bool, num_learnable_registers: int | None, apply_gated_attention: bool = False, ff_bias: bool = True)</code></pre>


<details><summary>Source</summary><pre><code>@dataclass(frozen=True)
class GemmaConnectorConfig:
    num_attention_heads: int
    attention_head_dim: int
    num_layers: int
    positional_embedding_theta: float
    positional_embedding_max_pos: list&#91;int&#93;
    rope_type: LTXRopeType
    double_precision_rope: bool
    num_learnable_registers: int | None
    # LTX-2.3 connector gated attention (default False == LTX-2.0 behavior).
    apply_gated_attention: bool = False
    ff_bias: bool = True</code></pre></details>
<h2 id="fastvideo.models.encoders.gemma.GemmaFeaturesExtractorProjLinear"><code>fastvideo.models.encoders.gemma.GemmaFeaturesExtractorProjLinear</code></h2>
<pre class="doc-signature"><code>GemmaFeaturesExtractorProjLinear(in_features: int, out_features: int)</code></pre>
<p>Linear projection that aggregates stacked Gemma hidden states.</p>

<details><summary>Source</summary><pre><code>class GemmaFeaturesExtractorProjLinear(nn.Module):
    &quot;&quot;&quot;Linear projection that aggregates stacked Gemma hidden states.&quot;&quot;&quot;

    def __init__(self, in_features: int, out_features: int) -&gt; None:
        super().__init__()
        self.aggregate_embed = nn.Linear(in_features, out_features, bias=False)

    def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
        return self.aggregate_embed(x)</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.GemmaFeaturesExtractorProjLinear.forward"><code>fastvideo.models.encoders.gemma.GemmaFeaturesExtractorProjLinear.forward</code></h3>
<pre class="doc-signature"><code>forward(x: torch.Tensor) -&gt; torch.Tensor</code></pre>

<details><summary>Source</summary><pre><code>def forward(self, x: torch.Tensor) -&gt; torch.Tensor:
    return self.aggregate_embed(x)</code></pre></details>
<h2 id="fastvideo.models.encoders.gemma.Embeddings1DConnector"><code>fastvideo.models.encoders.gemma.Embeddings1DConnector</code></h2>
<pre class="doc-signature"><code>Embeddings1DConnector(config: GemmaConnectorConfig)</code></pre>
<p>Transformer connector that refines Gemma embeddings for LTX-2.</p>

<details><summary>Source</summary><pre><code>class Embeddings1DConnector(nn.Module):
    &quot;&quot;&quot;Transformer connector that refines Gemma embeddings for LTX-2.&quot;&quot;&quot;

    _supports_gradient_checkpointing = True

    def __init__(self, config: GemmaConnectorConfig) -&gt; None:
        super().__init__()
        self.num_attention_heads = config.num_attention_heads
        self.inner_dim = config.num_attention_heads * config.attention_head_dim
        self.positional_embedding_theta = config.positional_embedding_theta
        self.positional_embedding_max_pos = config.positional_embedding_max_pos
        self.rope_type = config.rope_type
        self.double_precision_rope = config.double_precision_rope
        self.transformer_1d_blocks = nn.ModuleList(&#91;
            _BasicTransformerBlock1D(
                dim=self.inner_dim,
                heads=config.num_attention_heads,
                dim_head=config.attention_head_dim,
                rope_type=config.rope_type,
                apply_gated_attention=config.apply_gated_attention,
                ff_bias=config.ff_bias,
            ) for _ in range(config.num_layers)
        &#93;)
        self.num_learnable_registers = config.num_learnable_registers
        if self.num_learnable_registers:
            self.learnable_registers = nn.Parameter(
                torch.rand(
                    self.num_learnable_registers,
                    self.inner_dim,
                    dtype=torch.bfloat16,
                ) * 2.0 - 1.0)

    def _replace_padded_with_learnable_registers(
        self,
        hidden_states: torch.Tensor,
        attention_mask: torch.Tensor,
    ) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
        assert hidden_states.shape&#91;1&#93; % self.num_learnable_registers == 0, (
            f&quot;Hidden states sequence length &#123;hidden_states.shape&#91;1&#93;&#125; must be divisible by &quot;
            f&quot;num_learnable_registers &#123;self.num_learnable_registers&#125;.&quot;)

        num_registers_duplications = (hidden_states.shape&#91;1&#93; // self.num_learnable_registers)
        learnable_registers = torch.tile(self.learnable_registers, (num_registers_duplications, 1))
        seq_len = hidden_states.shape&#91;1&#93;
        valid = attention_mask.squeeze(1).squeeze(1) &gt;= -9000.0  # (batch, seq)

        # Left-align each row&#x27;s valid tokens, then fill the tail with registers.
        # A boolean index cannot express this: every row may keep a different
        # number of tokens, so the result is not rectangular. Gathering a stable
        # descending argsort of the mask moves the valid tokens to the front of
        # each row while preserving their original relative order.
        order = torch.argsort(valid.to(torch.int8), dim=1, descending=True, stable=True)
        adjusted_hidden_states = torch.gather(hidden_states, 1, order.unsqueeze(-1).expand_as(hidden_states))

        # After the gather, row b holds its valid tokens in slots
        # &#91;0, valid&#91;b&#93;.sum()). Deriving the keep-mask from that per-row count is
        # what makes this correct when rows have different token counts.
        keep = (torch.arange(seq_len, device=hidden_states.device).unsqueeze(0)
                &lt; valid.sum(dim=1, keepdim=True)).unsqueeze(-1).to(hidden_states.dtype)
        hidden_states = keep * adjusted_hidden_states + (1 - keep) * learnable_registers

        attention_mask = torch.full_like(
            attention_mask,
            0.0,
            dtype=attention_mask.dtype,
            device=attention_mask.device,
        )

        return hidden_states, attention_mask

    def forward(
        self,
        hidden_states: torch.Tensor,
        attention_mask: torch.Tensor | None = None,
    ) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
        if self.num_learnable_registers:
            hidden_states, attention_mask = (self._replace_padded_with_learnable_registers(
                hidden_states, attention_mask))

        indices_grid = torch.arange(
            hidden_states.shape&#91;1&#93;,
            dtype=torch.float32,
            device=hidden_states.device,
        )
        indices_grid = indices_grid&#91;None, None, :&#93;
        freq_grid_generator = (generate_ltx_freq_grid_float64
                               if self.double_precision_rope else generate_ltx_freq_grid_pytorch)
        freqs_cis = precompute_ltx_freqs_cis(
            indices_grid=indices_grid,
            dim=self.inner_dim,
            out_dtype=hidden_states.dtype,
            theta=self.positional_embedding_theta,
            max_pos=self.positional_embedding_max_pos,
            num_attention_heads=self.num_attention_heads,
            rope_type=self.rope_type,
            freq_grid_generator=freq_grid_generator,
        )

        for block in self.transformer_1d_blocks:
            hidden_states = block(hidden_states, attention_mask=attention_mask, pe=freqs_cis)

        hidden_states = torch.nn.functional.rms_norm(hidden_states, (hidden_states.shape&#91;-1&#93;, ), eps=1e-6)

        return hidden_states, attention_mask</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.Embeddings1DConnector.forward"><code>fastvideo.models.encoders.gemma.Embeddings1DConnector.forward</code></h3>
<pre class="doc-signature"><code>forward(hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;</code></pre>

<details><summary>Source</summary><pre><code>def forward(
    self,
    hidden_states: torch.Tensor,
    attention_mask: torch.Tensor | None = None,
) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
    if self.num_learnable_registers:
        hidden_states, attention_mask = (self._replace_padded_with_learnable_registers(
            hidden_states, attention_mask))

    indices_grid = torch.arange(
        hidden_states.shape&#91;1&#93;,
        dtype=torch.float32,
        device=hidden_states.device,
    )
    indices_grid = indices_grid&#91;None, None, :&#93;
    freq_grid_generator = (generate_ltx_freq_grid_float64
                           if self.double_precision_rope else generate_ltx_freq_grid_pytorch)
    freqs_cis = precompute_ltx_freqs_cis(
        indices_grid=indices_grid,
        dim=self.inner_dim,
        out_dtype=hidden_states.dtype,
        theta=self.positional_embedding_theta,
        max_pos=self.positional_embedding_max_pos,
        num_attention_heads=self.num_attention_heads,
        rope_type=self.rope_type,
        freq_grid_generator=freq_grid_generator,
    )

    for block in self.transformer_1d_blocks:
        hidden_states = block(hidden_states, attention_mask=attention_mask, pe=freqs_cis)

    hidden_states = torch.nn.functional.rms_norm(hidden_states, (hidden_states.shape&#91;-1&#93;, ), eps=1e-6)

    return hidden_states, attention_mask</code></pre></details>
<h2 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel</code></h2>
<pre class="doc-signature"><code>LTX2GemmaTextEncoderModel(config: TextEncoderConfig)</code></pre>


<details><summary>Source</summary><pre><code>class LTX2GemmaTextEncoderModel(TextEncoder):
    _supported_attention_backends = (
        AttentionBackendEnum.FLASH_ATTN,
        AttentionBackendEnum.TORCH_SDPA,
    )

    def __init__(self, config: TextEncoderConfig) -&gt; None:
        super().__init__(config)
        arch = config.arch_config

        # LTX-2.3 routes the caption projection before the connector and uses
        # separate per-modality feature extractor linears. Default (False)
        # keeps the single LTX-2.0 GemmaFeaturesExtractorProjLinear.
        self.use_v2_feature_extractor = bool(getattr(arch, &quot;caption_proj_before_connector&quot;, False))
        if self.use_v2_feature_extractor:
            video_out_features = (getattr(arch, &quot;video_feature_extractor_out_features&quot;, None)
                                  or arch.feature_extractor_out_features)
            audio_out_features = (getattr(arch, &quot;audio_feature_extractor_out_features&quot;, None) or video_out_features)
            self.video_feature_extractor_linear = nn.Linear(
                arch.feature_extractor_in_features,
                video_out_features,
                bias=True,
            )
            self.audio_feature_extractor_linear = nn.Linear(
                arch.feature_extractor_in_features,
                audio_out_features,
                bias=True,
            )
        else:
            self.feature_extractor_linear = GemmaFeaturesExtractorProjLinear(
                in_features=arch.feature_extractor_in_features,
                out_features=arch.feature_extractor_out_features,
            )

        connector_apply_gated_attention = bool(getattr(arch, &quot;connector_apply_gated_attention&quot;, False))
        connector_ff_bias = bool(getattr(arch, &quot;connector_ff_bias&quot;, True))
        video_connector_config = GemmaConnectorConfig(
            num_attention_heads=arch.connector_num_attention_heads,
            attention_head_dim=arch.connector_attention_head_dim,
            num_layers=arch.connector_num_layers,
            positional_embedding_theta=arch.connector_positional_embedding_theta,
            positional_embedding_max_pos=arch.connector_positional_embedding_max_pos,
            rope_type=LTXRopeType(arch.connector_rope_type),
            double_precision_rope=arch.connector_double_precision_rope,
            num_learnable_registers=arch.connector_num_learnable_registers,
            apply_gated_attention=connector_apply_gated_attention,
            ff_bias=connector_ff_bias,
        )
        audio_connector_config = GemmaConnectorConfig(
            num_attention_heads=getattr(arch, &quot;audio_connector_num_attention_heads&quot;, None)
            or arch.connector_num_attention_heads,
            attention_head_dim=getattr(arch, &quot;audio_connector_attention_head_dim&quot;, None)
            or arch.connector_attention_head_dim,
            num_layers=getattr(arch, &quot;audio_connector_num_layers&quot;, None) or arch.connector_num_layers,
            positional_embedding_theta=arch.connector_positional_embedding_theta,
            positional_embedding_max_pos=arch.connector_positional_embedding_max_pos,
            rope_type=LTXRopeType(arch.connector_rope_type),
            double_precision_rope=arch.connector_double_precision_rope,
            num_learnable_registers=arch.connector_num_learnable_registers,
            apply_gated_attention=connector_apply_gated_attention,
            ff_bias=connector_ff_bias,
        )
        self.embeddings_connector = Embeddings1DConnector(video_connector_config)
        self.audio_embeddings_connector = Embeddings1DConnector(audio_connector_config)

        self.gemma_model_path = arch.gemma_model_path
        self.gemma_dtype = arch.gemma_dtype
        self.padding_side = arch.padding_side
        self._gemma_model: PreTrainedModel | None = None

    def named_parameters(self, prefix: str = &quot;&quot;, recurse: bool = True):
        for name, param in super().named_parameters(prefix=prefix, recurse=recurse):
            if name.startswith(&quot;_gemma_model.&quot;):
                continue
            yield name, param

    def prepare_for_compile(self) -&gt; None:
        # Load Gemma outside Dynamo so torch.compile does not trace HF file-system checks.
        model = self.gemma_model
        # Run one tiny eager forward before torch.compile. FastVideo calls the text encoder with
        # Transformers&#x27; output_hidden_states path, which wraps each Gemma layer
        # forward method and restores it by setting an instance-level forward
        # attribute. If that attribute appears after Dynamo captures guards, the
        # next text-encoder call recompiles; doing it here makes the first
        # compiled call see the stable layer state.
        token_id = getattr(model.config, &quot;eos_token_id&quot;, None)
        if isinstance(token_id, (list, tuple)):
            token_id = token_id&#91;0&#93; if token_id else None
        if token_id is None:
            token_id = getattr(model.config, &quot;pad_token_id&quot;, 0)
        input_ids = torch.full(
            (1, 1),
            int(token_id or 0),
            dtype=torch.long,
            device=model.device,
        )
        attention_mask = torch.ones_like(input_ids)
        with torch.no_grad():
            model(
                input_ids=input_ids,
                attention_mask=attention_mask,
                output_hidden_states=True,
                return_dict=True,
            )

    @property
    def gemma_model(self) -&gt; PreTrainedModel:
        if self._gemma_model is None:
            gemma_path = self.gemma_model_path
            if not gemma_path:
                raise ValueError(&quot;gemma_model_path must be set (expected text_encoder/gemma).&quot;)
            dtype = getattr(torch, self.gemma_dtype, torch.bfloat16)
            self._gemma_model = AutoModelForImageTextToText.from_pretrained(
                gemma_path,
                local_files_only=True,
                torch_dtype=dtype,
            )
            # Configure model-level attention implementation when using TORCH_SDPA.
            # Note: torch.backends.cuda.enable_*_sdp() settings should be configured
            # at application/pipeline initialization level, not here, to avoid
            # unexpected side effects across the application.
            if effective_attention_backend(self.config) == AttentionBackendEnum.TORCH_SDPA:
                if hasattr(self._gemma_model.config, &quot;attn_implementation&quot;):
                    self._gemma_model.config.attn_implementation = &quot;sdpa&quot;
                if hasattr(self._gemma_model.config, &quot;_attn_implementation&quot;):
                    self._gemma_model.config._attn_implementation = &quot;sdpa&quot;
            if self.use_v2_feature_extractor:
                device = next(self.video_feature_extractor_linear.parameters()).device
            else:
                device = next(self.feature_extractor_linear.parameters()).device
            self._gemma_model.to(device=device)
            self._gemma_model.eval()
        return self._gemma_model

    def _run_feature_extractor(
        self,
        hidden_states: tuple&#91;torch.Tensor, ...&#93;,
        attention_mask: torch.Tensor,
        padding_side: str,
    ) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
        encoded_text_features = torch.stack(hidden_states, dim=-1)
        if envs.FASTVIDEO_LTX2_GEMMA_LOG.get():
            for idx, layer in enumerate(hidden_states):
                _debug_gemma_log_line(f&quot;fastvideo:gemma_hidden_state_&#123;idx&#125;&quot;
                                      f&quot;:sum=&#123;layer.float().sum().item():.6f&#125;&quot;)
            _debug_gemma_log_line(&quot;fastvideo:gemma_hidden_states_stack&quot;
                                  f&quot;:sum=&#123;encoded_text_features.float().sum().item():.6f&#125;&quot;)
        encoded_text_features_dtype = encoded_text_features.dtype
        if self.use_v2_feature_extractor:
            # LTX-2.3: per-token RMS norm then separate video/audio linears.
            normed_text_features = _norm_and_concat_per_token_rms(encoded_text_features,
                                                                  attention_mask).to(encoded_text_features_dtype)
            video_out_dim = self.video_feature_extractor_linear.out_features
            audio_out_dim = self.audio_feature_extractor_linear.out_features
            video_features = self.video_feature_extractor_linear(
                _rescale_norm(
                    normed_text_features,
                    target_dim=video_out_dim,
                    source_dim=self.config.arch_config.hidden_size,
                ))
            audio_features = self.audio_feature_extractor_linear(
                _rescale_norm(
                    normed_text_features,
                    target_dim=audio_out_dim,
                    source_dim=self.config.arch_config.hidden_size,
                ))
            return video_features, audio_features

        # LTX-2.0: shared min-max norm + single aggregate linear. The two
        # returned tensors are the same object (video == audio source).
        sequence_lengths = attention_mask.sum(dim=-1)
        normed_text_features = _norm_and_concat_padded_batch(encoded_text_features,
                                                             sequence_lengths,
                                                             padding_side=padding_side)
        shared_features = self.feature_extractor_linear(normed_text_features.to(encoded_text_features_dtype))
        return shared_features, shared_features

    def _convert_to_additive_mask(self, attention_mask: torch.Tensor, dtype: torch.dtype) -&gt; torch.Tensor:
        return (attention_mask - 1).to(dtype).reshape(
            (attention_mask.shape&#91;0&#93;, 1, -1, attention_mask.shape&#91;-1&#93;)) * torch.finfo(dtype).max

    def _run_connectors(
        self,
        video_features: torch.Tensor,
        audio_features: torch.Tensor | None,
        attention_mask: torch.Tensor,
    ) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;:
        connector_attention_mask = self._convert_to_additive_mask(attention_mask, video_features.dtype)
        encoded, encoded_connector_attention_mask = self.embeddings_connector(video_features, connector_attention_mask)

        attention_mask = (encoded_connector_attention_mask &lt; 0.000001).to(torch.int64)
        attention_mask = attention_mask.reshape(&#91;encoded.shape&#91;0&#93;, encoded.shape&#91;1&#93;, 1&#93;)
        encoded = encoded * attention_mask

        # For LTX-2.0 audio_features is the same tensor as video_features, so
        # this reproduces the prior behavior (audio connector fed the shared
        # features). For LTX-2.3 it is fed the separate audio features.
        audio_features = (audio_features if audio_features is not None else video_features)
        encoded_for_audio, _ = self.audio_embeddings_connector(audio_features, connector_attention_mask)

        return encoded, encoded_for_audio, attention_mask.squeeze(-1)

    @torch.no_grad()
    def preprocess_text_embeddings(
        self,
        prompts: str | list&#91;str&#93;,
        tokenizer: AutoTokenizer,
        tokenizer_kwargs: dict&#91;str, Any&#93; | None = None,
        padding_side: str | None = None,
    ) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
        &quot;&quot;&quot;Compute pre-connector text embeddings for LTX-2 training preprocessing.&quot;&quot;&quot;
        if isinstance(prompts, str):
            prompts = &#91;prompts&#93;

        model = self.gemma_model
        kwargs: dict&#91;str, Any&#93; = &#123;
            &quot;padding&quot;: &quot;max_length&quot;,
            &quot;truncation&quot;: True,
            &quot;return_tensors&quot;: &quot;pt&quot;,
        &#125;
        if tokenizer_kwargs is not None:
            kwargs.update(tokenizer_kwargs)
        if &quot;max_length&quot; not in kwargs:
            kwargs&#91;&quot;max_length&quot;&#93; = self.config.arch_config.text_len

        original_padding_side = tokenizer.padding_side
        target_padding_side = padding_side or self.padding_side
        tokenizer.padding_side = target_padding_side
        try:
            text_inputs = tokenizer(prompts, **kwargs)
        finally:
            tokenizer.padding_side = original_padding_side

        input_ids = text_inputs&#91;&quot;input_ids&quot;&#93;.to(device=model.device)
        attention_mask = text_inputs&#91;&quot;attention_mask&quot;&#93;.to(device=model.device)
        input_ids, attention_mask = _ensure_leading_bos(
            input_ids,
            attention_mask,
            bos_token_id=_get_bos_token_id(model.config),
        )
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            output_hidden_states=True,
            return_dict=True,
        )
        video_features, _ = self._run_feature_extractor(
            outputs.hidden_states,
            attention_mask,
            padding_side=target_padding_side,
        )
        return video_features, attention_mask

    def run_connectors(
        self,
        encoded_input: torch.Tensor,
        attention_mask: torch.Tensor,
    ) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;:
        &quot;&quot;&quot;Apply embedding connectors to precomputed Gemma features.&quot;&quot;&quot;
        return self._run_connectors(encoded_input, encoded_input, attention_mask)

    def forward(
        self,
        input_ids: torch.Tensor | None,
        position_ids: torch.Tensor | None = None,
        attention_mask: torch.Tensor | None = None,
        inputs_embeds: torch.Tensor | None = None,
        output_hidden_states: bool | None = None,
        **kwargs,
    ) -&gt; BaseEncoderOutput:
        if input_ids is None:
            raise ValueError(&quot;input_ids is required for Gemma text encoding.&quot;)
        if attention_mask is None:
            attention_mask = torch.ones_like(input_ids)

        model = self.gemma_model
        input_ids, attention_mask = _ensure_leading_bos(
            input_ids,
            attention_mask,
            bos_token_id=_get_bos_token_id(model.config),
        )
        target_device = get_local_torch_device()
        # Do not invoke model.to() inside the compiled forward path.
        # _parse_to returns a non-Tensor torch.device, which Dynamo cannot
        # trace under fullgraph=True. The model is already moved to device
        # when first loaded (see gemma_model property + prepare_for_compile),
        # so this guard is a runtime no-op and Dynamo can DCE it.
        if model.device != target_device:
            model.to(device=target_device)
        outputs = model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            output_hidden_states=True,
            return_dict=True,
        )

        encoded_video, encoded_audio = self._run_feature_extractor(
            outputs.hidden_states,
            attention_mask,
            padding_side=self.padding_side,
        )
        video_encoding, audio_encoding, attention_mask = self._run_connectors(encoded_video, encoded_audio,
                                                                              attention_mask)

        hidden_states = (audio_encoding, ) if output_hidden_states else None
        return BaseEncoderOutput(
            last_hidden_state=video_encoding,
            hidden_states=hidden_states,
            attention_mask=attention_mask,
        )

    def load_weights(self, weights: Iterable&#91;tuple&#91;str, torch.Tensor&#93;&#93;) -&gt; set&#91;str&#93;:
        params_dict = dict(self.named_parameters())
        loaded_params: set&#91;str&#93; = set()
        for name, loaded_weight in weights:
            if name == &quot;aggregate_embed.weight&quot;:
                name = &quot;feature_extractor_linear.aggregate_embed.weight&quot;
            # LTX-2.3 separate video/audio feature extractors. These names
            # do not appear in LTX-2.0 checkpoints, so they are no-ops there.
            elif name == &quot;video_aggregate_embed.weight&quot;:
                name = &quot;video_feature_extractor_linear.weight&quot;
            elif name == &quot;video_aggregate_embed.bias&quot;:
                name = &quot;video_feature_extractor_linear.bias&quot;
            elif name == &quot;audio_aggregate_embed.weight&quot;:
                name = &quot;audio_feature_extractor_linear.weight&quot;
            elif name == &quot;audio_aggregate_embed.bias&quot;:
                name = &quot;audio_feature_extractor_linear.bias&quot;
            elif name == &quot;feature_extractor.video_aggregate_embed.weight&quot;:
                name = &quot;video_feature_extractor_linear.weight&quot;
            elif name == &quot;feature_extractor.video_aggregate_embed.bias&quot;:
                name = &quot;video_feature_extractor_linear.bias&quot;
            elif name == &quot;feature_extractor.audio_aggregate_embed.weight&quot;:
                name = &quot;audio_feature_extractor_linear.weight&quot;
            elif name == &quot;feature_extractor.audio_aggregate_embed.bias&quot;:
                name = &quot;audio_feature_extractor_linear.bias&quot;
            elif name == &quot;feature_extractor.aggregate_embed.weight&quot;:
                name = &quot;feature_extractor_linear.aggregate_embed.weight&quot;
            elif name.startswith(&quot;video_connector.&quot;):
                name = name.replace(&quot;video_connector.&quot;, &quot;embeddings_connector.&quot;, 1)
            elif name.startswith(&quot;audio_connector.&quot;):
                name = name.replace(&quot;audio_connector.&quot;, &quot;audio_embeddings_connector.&quot;, 1)
            if name not in params_dict:
                continue
            param = params_dict&#91;name&#93;
            weight_loader = getattr(param, &quot;weight_loader&quot;, default_weight_loader)
            weight_loader(param, loaded_weight)
            loaded_params.add(name)
        return loaded_params</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.named_parameters"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.named_parameters</code></h3>
<pre class="doc-signature"><code>named_parameters(prefix: str = &#x27;&#x27;, recurse: bool = True)</code></pre>

<details><summary>Source</summary><pre><code>def named_parameters(self, prefix: str = &quot;&quot;, recurse: bool = True):
    for name, param in super().named_parameters(prefix=prefix, recurse=recurse):
        if name.startswith(&quot;_gemma_model.&quot;):
            continue
        yield name, param</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.prepare_for_compile"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.prepare_for_compile</code></h3>
<pre class="doc-signature"><code>prepare_for_compile() -&gt; None</code></pre>

<details><summary>Source</summary><pre><code>def prepare_for_compile(self) -&gt; None:
    # Load Gemma outside Dynamo so torch.compile does not trace HF file-system checks.
    model = self.gemma_model
    # Run one tiny eager forward before torch.compile. FastVideo calls the text encoder with
    # Transformers&#x27; output_hidden_states path, which wraps each Gemma layer
    # forward method and restores it by setting an instance-level forward
    # attribute. If that attribute appears after Dynamo captures guards, the
    # next text-encoder call recompiles; doing it here makes the first
    # compiled call see the stable layer state.
    token_id = getattr(model.config, &quot;eos_token_id&quot;, None)
    if isinstance(token_id, (list, tuple)):
        token_id = token_id&#91;0&#93; if token_id else None
    if token_id is None:
        token_id = getattr(model.config, &quot;pad_token_id&quot;, 0)
    input_ids = torch.full(
        (1, 1),
        int(token_id or 0),
        dtype=torch.long,
        device=model.device,
    )
    attention_mask = torch.ones_like(input_ids)
    with torch.no_grad():
        model(
            input_ids=input_ids,
            attention_mask=attention_mask,
            output_hidden_states=True,
            return_dict=True,
        )</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.preprocess_text_embeddings"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.preprocess_text_embeddings</code></h3>
<pre class="doc-signature"><code>preprocess_text_embeddings(prompts: str | list&#91;str&#93;, tokenizer: AutoTokenizer, tokenizer_kwargs: dict&#91;str, Any&#93; | None = None, padding_side: str | None = None) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;</code></pre>
<p>Compute pre-connector text embeddings for LTX-2 training preprocessing.</p>
<details><summary>Source</summary><pre><code>@torch.no_grad()
def preprocess_text_embeddings(
    self,
    prompts: str | list&#91;str&#93;,
    tokenizer: AutoTokenizer,
    tokenizer_kwargs: dict&#91;str, Any&#93; | None = None,
    padding_side: str | None = None,
) -&gt; tuple&#91;torch.Tensor, torch.Tensor&#93;:
    &quot;&quot;&quot;Compute pre-connector text embeddings for LTX-2 training preprocessing.&quot;&quot;&quot;
    if isinstance(prompts, str):
        prompts = &#91;prompts&#93;

    model = self.gemma_model
    kwargs: dict&#91;str, Any&#93; = &#123;
        &quot;padding&quot;: &quot;max_length&quot;,
        &quot;truncation&quot;: True,
        &quot;return_tensors&quot;: &quot;pt&quot;,
    &#125;
    if tokenizer_kwargs is not None:
        kwargs.update(tokenizer_kwargs)
    if &quot;max_length&quot; not in kwargs:
        kwargs&#91;&quot;max_length&quot;&#93; = self.config.arch_config.text_len

    original_padding_side = tokenizer.padding_side
    target_padding_side = padding_side or self.padding_side
    tokenizer.padding_side = target_padding_side
    try:
        text_inputs = tokenizer(prompts, **kwargs)
    finally:
        tokenizer.padding_side = original_padding_side

    input_ids = text_inputs&#91;&quot;input_ids&quot;&#93;.to(device=model.device)
    attention_mask = text_inputs&#91;&quot;attention_mask&quot;&#93;.to(device=model.device)
    input_ids, attention_mask = _ensure_leading_bos(
        input_ids,
        attention_mask,
        bos_token_id=_get_bos_token_id(model.config),
    )
    outputs = model(
        input_ids=input_ids,
        attention_mask=attention_mask,
        output_hidden_states=True,
        return_dict=True,
    )
    video_features, _ = self._run_feature_extractor(
        outputs.hidden_states,
        attention_mask,
        padding_side=target_padding_side,
    )
    return video_features, attention_mask</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.run_connectors"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.run_connectors</code></h3>
<pre class="doc-signature"><code>run_connectors(encoded_input: torch.Tensor, attention_mask: torch.Tensor) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;</code></pre>
<p>Apply embedding connectors to precomputed Gemma features.</p>
<details><summary>Source</summary><pre><code>def run_connectors(
    self,
    encoded_input: torch.Tensor,
    attention_mask: torch.Tensor,
) -&gt; tuple&#91;torch.Tensor, torch.Tensor, torch.Tensor&#93;:
    &quot;&quot;&quot;Apply embedding connectors to precomputed Gemma features.&quot;&quot;&quot;
    return self._run_connectors(encoded_input, encoded_input, attention_mask)</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.forward"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.forward</code></h3>
<pre class="doc-signature"><code>forward(input_ids: torch.Tensor | None, position_ids: torch.Tensor | None = None, attention_mask: torch.Tensor | None = None, inputs_embeds: torch.Tensor | None = None, output_hidden_states: bool | None = None, **kwargs) -&gt; BaseEncoderOutput</code></pre>

<details><summary>Source</summary><pre><code>def forward(
    self,
    input_ids: torch.Tensor | None,
    position_ids: torch.Tensor | None = None,
    attention_mask: torch.Tensor | None = None,
    inputs_embeds: torch.Tensor | None = None,
    output_hidden_states: bool | None = None,
    **kwargs,
) -&gt; BaseEncoderOutput:
    if input_ids is None:
        raise ValueError(&quot;input_ids is required for Gemma text encoding.&quot;)
    if attention_mask is None:
        attention_mask = torch.ones_like(input_ids)

    model = self.gemma_model
    input_ids, attention_mask = _ensure_leading_bos(
        input_ids,
        attention_mask,
        bos_token_id=_get_bos_token_id(model.config),
    )
    target_device = get_local_torch_device()
    # Do not invoke model.to() inside the compiled forward path.
    # _parse_to returns a non-Tensor torch.device, which Dynamo cannot
    # trace under fullgraph=True. The model is already moved to device
    # when first loaded (see gemma_model property + prepare_for_compile),
    # so this guard is a runtime no-op and Dynamo can DCE it.
    if model.device != target_device:
        model.to(device=target_device)
    outputs = model(
        input_ids=input_ids,
        attention_mask=attention_mask,
        output_hidden_states=True,
        return_dict=True,
    )

    encoded_video, encoded_audio = self._run_feature_extractor(
        outputs.hidden_states,
        attention_mask,
        padding_side=self.padding_side,
    )
    video_encoding, audio_encoding, attention_mask = self._run_connectors(encoded_video, encoded_audio,
                                                                          attention_mask)

    hidden_states = (audio_encoding, ) if output_hidden_states else None
    return BaseEncoderOutput(
        last_hidden_state=video_encoding,
        hidden_states=hidden_states,
        attention_mask=attention_mask,
    )</code></pre></details>
<h3 id="fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.load_weights"><code>fastvideo.models.encoders.gemma.LTX2GemmaTextEncoderModel.load_weights</code></h3>
<pre class="doc-signature"><code>load_weights(weights: Iterable&#91;tuple&#91;str, torch.Tensor&#93;&#93;) -&gt; set&#91;str&#93;</code></pre>

<details><summary>Source</summary><pre><code>def load_weights(self, weights: Iterable&#91;tuple&#91;str, torch.Tensor&#93;&#93;) -&gt; set&#91;str&#93;:
    params_dict = dict(self.named_parameters())
    loaded_params: set&#91;str&#93; = set()
    for name, loaded_weight in weights:
        if name == &quot;aggregate_embed.weight&quot;:
            name = &quot;feature_extractor_linear.aggregate_embed.weight&quot;
        # LTX-2.3 separate video/audio feature extractors. These names
        # do not appear in LTX-2.0 checkpoints, so they are no-ops there.
        elif name == &quot;video_aggregate_embed.weight&quot;:
            name = &quot;video_feature_extractor_linear.weight&quot;
        elif name == &quot;video_aggregate_embed.bias&quot;:
            name = &quot;video_feature_extractor_linear.bias&quot;
        elif name == &quot;audio_aggregate_embed.weight&quot;:
            name = &quot;audio_feature_extractor_linear.weight&quot;
        elif name == &quot;audio_aggregate_embed.bias&quot;:
            name = &quot;audio_feature_extractor_linear.bias&quot;
        elif name == &quot;feature_extractor.video_aggregate_embed.weight&quot;:
            name = &quot;video_feature_extractor_linear.weight&quot;
        elif name == &quot;feature_extractor.video_aggregate_embed.bias&quot;:
            name = &quot;video_feature_extractor_linear.bias&quot;
        elif name == &quot;feature_extractor.audio_aggregate_embed.weight&quot;:
            name = &quot;audio_feature_extractor_linear.weight&quot;
        elif name == &quot;feature_extractor.audio_aggregate_embed.bias&quot;:
            name = &quot;audio_feature_extractor_linear.bias&quot;
        elif name == &quot;feature_extractor.aggregate_embed.weight&quot;:
            name = &quot;feature_extractor_linear.aggregate_embed.weight&quot;
        elif name.startswith(&quot;video_connector.&quot;):
            name = name.replace(&quot;video_connector.&quot;, &quot;embeddings_connector.&quot;, 1)
        elif name.startswith(&quot;audio_connector.&quot;):
            name = name.replace(&quot;audio_connector.&quot;, &quot;audio_embeddings_connector.&quot;, 1)
        if name not in params_dict:
            continue
        param = params_dict&#91;name&#93;
        weight_loader = getattr(param, &quot;weight_loader&quot;, default_weight_loader)
        weight_loader(param, loaded_weight)
        loaded_params.add(name)
    return loaded_params</code></pre></details>
</div>
