---
title: t5gemma
---

<div class="python-reference">
<a id="fastvideo.models.encoders.t5gemma-classes"></a>
<a id="fastvideo.models.encoders.t5gemma-functions"></a>
<a id="fastvideo.models.encoders.t5gemma"></a>
<p>T5-Gemma encoder wrapper for daVinci-MagiHuman.</p>
<p>MagiHuman uses <code>transformers.models.t5gemma.T5GemmaEncoderModel</code> on
<code>google/t5gemma-9b-9b-ul2</code> (a gated Google repo). This wrapper follows the
same lazy-loading pattern as <code>fastvideo/models/encoders/gemma.py</code>: we keep
the HF module under <code>self._t5gemma_model</code> and exclude it from
<code>named_parameters</code> so FastVideo's weight loader does not try to load
encoder shards from the converted repo directory.</p>
<p>For the base MagiHuman T2V port there are no additional connector layers
on top — the pipeline prompt-preprocessing stage handles pad-or-trim to
<code>text_len</code> and exposes both the padded embedding and the original length.</p>
<h2 id="fastvideo.models.encoders.t5gemma.BaseEncoderOutput"><code>fastvideo.models.encoders.t5gemma.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.t5gemma.TextEncoderConfig"><code>fastvideo.models.encoders.t5gemma.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.t5gemma.TextEncoder"><code>fastvideo.models.encoders.t5gemma.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.t5gemma.TextEncoder.forward"><code>fastvideo.models.encoders.t5gemma.TextEncoder.forward</code></h3>
<pre class="doc-signature"><code>forward(*args: Any, **kwargs: Any) -&gt; TextEncoderOutputT</code></pre>

<h2 id="fastvideo.models.encoders.t5gemma.effective_attention_backend"><code>fastvideo.models.encoders.t5gemma.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.t5gemma.AttentionBackendEnum"><code>fastvideo.models.encoders.t5gemma.AttentionBackendEnum</code></h2>
<pre class="doc-signature"><code>AttentionBackendEnum()</code></pre>

<h2 id="fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel"><code>fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel</code></h2>
<pre class="doc-signature"><code>T5GemmaEncoderModel(config: TextEncoderConfig)</code></pre>
<p>Thin wrapper over HuggingFace's <code>T5GemmaEncoderModel</code>.</p>
<p>On first <code>forward</code>, the wrapper lazily instantiates the upstream encoder
from <code>t5gemma_model_path</code> (defaulting to <code>google/t5gemma-9b-9b-ul2</code>).
Afterwards, forward returns a <code>BaseEncoderOutput</code> with
<code>last_hidden_state = [B, L, 3584]</code> matching MagiHuman's
<code>context.half()</code> output.</p>

<details><summary>Source</summary><pre><code>class T5GemmaEncoderModel(TextEncoder):
    &quot;&quot;&quot;Thin wrapper over HuggingFace&#x27;s `T5GemmaEncoderModel`.

    On first `forward`, the wrapper lazily instantiates the upstream encoder
    from `t5gemma_model_path` (defaulting to `google/t5gemma-9b-9b-ul2`).
    Afterwards, forward returns a `BaseEncoderOutput` with
    `last_hidden_state = &#91;B, L, 3584&#93;` matching MagiHuman&#x27;s
    `context.half()` output.
    &quot;&quot;&quot;

    _supported_attention_backends = (
        AttentionBackendEnum.FLASH_ATTN,
        AttentionBackendEnum.TORCH_SDPA,
    )

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

    def named_parameters(self, prefix: str = &quot;&quot;, recurse: bool = True):
        # The upstream encoder is loaded lazily and its parameters are
        # managed by HF, not FastVideo&#x27;s loader. Hide them from the parent
        # module-tree traversal so Diffusers-repo weight loading does not
        # try to match them.
        for name, param in super().named_parameters(prefix=prefix, recurse=recurse):
            if name.startswith(&quot;_t5gemma_model.&quot;) or name == &quot;_t5gemma_model&quot;:
                continue
            yield name, param

    def _build_t5gemma_model(self, device: torch.device | None = None):
        from transformers.models.t5gemma import T5GemmaEncoderModel as HFEncoder

        path = self.t5gemma_model_path
        if not path:
            raise ValueError(&quot;t5gemma_model_path must be set. Expected &quot;
                             &quot;`google/t5gemma-9b-9b-ul2` or a local path to an &quot;
                             &quot;equivalent T5-Gemma encoder.&quot;)
        dtype = getattr(torch, self.t5gemma_dtype, torch.bfloat16)
        model = HFEncoder.from_pretrained(
            path,
            is_encoder_decoder=False,
            dtype=dtype,
        )
        if effective_attention_backend(self.config) == AttentionBackendEnum.TORCH_SDPA:
            if hasattr(model.config, &quot;attn_implementation&quot;):
                model.config.attn_implementation = &quot;sdpa&quot;
            if hasattr(model.config, &quot;_attn_implementation&quot;):
                model.config._attn_implementation = &quot;sdpa&quot;
        if device is not None:
            model = model.to(device=device)
        model.eval()
        return model

    @property
    def t5gemma_model(self):
        if self._t5gemma_model is None:
            # Lazy-load on CPU if no device is known yet; `forward` will
            # move the model to the input&#x27;s device on first call.
            self._t5gemma_model = self._build_t5gemma_model()
        return self._t5gemma_model

    def forward(
        self,
        input_ids: torch.Tensor | None = 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:
        # Ensure the lazy-loaded encoder lives on the same device as the
        # input; lazy-loading leaves it on CPU until the first forward.
        ref = input_ids if input_ids is not None else inputs_embeds
        target_device = ref.device if ref is not None else None
        model = self.t5gemma_model
        if target_device is not None:
            first_param = next(model.parameters(), None)
            if first_param is not None and first_param.device != target_device:
                model = model.to(device=target_device)
                self._t5gemma_model = model
        outputs = model(
            input_ids=input_ids,
            position_ids=position_ids,
            attention_mask=attention_mask,
            inputs_embeds=inputs_embeds,
            output_hidden_states=output_hidden_states,
        )
        # MagiHuman casts to fp16 at this point; keep the raw dtype here and
        # leave precision management to the pipeline&#x27;s postprocess stage.
        return BaseEncoderOutput(
            last_hidden_state=outputs&#91;&quot;last_hidden_state&quot;&#93;,
            hidden_states=getattr(outputs, &quot;hidden_states&quot;, None),
            attention_mask=attention_mask,
        )

    def load_weights(self, weights: Iterable&#91;tuple&#91;str, torch.Tensor&#93;&#93;) -&gt; set&#91;str&#93;:
        # The HF T5-Gemma encoder is lazy-loaded from `t5gemma_model_path`
        # (see `_build_t5gemma_model`), so this wrapper owns zero
        # FastVideo-native parameters and `named_parameters()` is filtered
        # to hide the HF submodule. `TextEncoderLoader.load_model()` calls
        # `model.load_weights(...)` unconditionally, so we must define it
        # here. Returning an empty set matches the empty `weights_to_load`
        # set the loader computes from `named_parameters()`, satisfying
        # its strict-load check.
        for _ in weights:
            pass
        return set()</code></pre></details>
<h3 id="fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel.named_parameters"><code>fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel.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):
    # The upstream encoder is loaded lazily and its parameters are
    # managed by HF, not FastVideo&#x27;s loader. Hide them from the parent
    # module-tree traversal so Diffusers-repo weight loading does not
    # try to match them.
    for name, param in super().named_parameters(prefix=prefix, recurse=recurse):
        if name.startswith(&quot;_t5gemma_model.&quot;) or name == &quot;_t5gemma_model&quot;:
            continue
        yield name, param</code></pre></details>
<h3 id="fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel.forward"><code>fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel.forward</code></h3>
<pre class="doc-signature"><code>forward(input_ids: torch.Tensor | None = 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 = 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:
    # Ensure the lazy-loaded encoder lives on the same device as the
    # input; lazy-loading leaves it on CPU until the first forward.
    ref = input_ids if input_ids is not None else inputs_embeds
    target_device = ref.device if ref is not None else None
    model = self.t5gemma_model
    if target_device is not None:
        first_param = next(model.parameters(), None)
        if first_param is not None and first_param.device != target_device:
            model = model.to(device=target_device)
            self._t5gemma_model = model
    outputs = model(
        input_ids=input_ids,
        position_ids=position_ids,
        attention_mask=attention_mask,
        inputs_embeds=inputs_embeds,
        output_hidden_states=output_hidden_states,
    )
    # MagiHuman casts to fp16 at this point; keep the raw dtype here and
    # leave precision management to the pipeline&#x27;s postprocess stage.
    return BaseEncoderOutput(
        last_hidden_state=outputs&#91;&quot;last_hidden_state&quot;&#93;,
        hidden_states=getattr(outputs, &quot;hidden_states&quot;, None),
        attention_mask=attention_mask,
    )</code></pre></details>
<h3 id="fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel.load_weights"><code>fastvideo.models.encoders.t5gemma.T5GemmaEncoderModel.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;:
    # The HF T5-Gemma encoder is lazy-loaded from `t5gemma_model_path`
    # (see `_build_t5gemma_model`), so this wrapper owns zero
    # FastVideo-native parameters and `named_parameters()` is filtered
    # to hide the HF submodule. `TextEncoderLoader.load_model()` calls
    # `model.load_weights(...)` unconditionally, so we must define it
    # here. Returning an empty set matches the empty `weights_to_load`
    # set the loader computes from `named_parameters()`, satisfying
    # its strict-load check.
    for _ in weights:
        pass
    return set()</code></pre></details>
</div>
