返回 JoyAI-Echo
text_encoder_wrapper.py
根目录 / echo_longvideo / ltx-distillation / src / ltx_distillation / models / text_encoder_wrapper.py
1 """
2 Gemma Text Encoder Wrapper for DMD distillation.
3
4 Provides a simple interface for text encoding without prompt enhancement.
5 Just pure text -> context embedding conversion.
6 """
7
8 from typing import Dict, List, Optional
9 import torch
10 import torch.nn as nn
11
12 from ltx_core.loader.registry import Registry
13
14
15 class GemmaTextEncoderWrapper(nn.Module):
16 """
17 Wrapper for Gemma text encoder to provide DMD-compatible interface.
18
19 This wrapper:
20 - Takes raw text prompts (no enhancement needed)
21 - Returns conditional_dict with video_context and audio_context
22 - Handles batched encoding
23 """
24
25 def __init__(
26 self,
27 text_encoder,
28 embeddings_processor,
29 device: torch.device = None,
30 dtype: torch.dtype = torch.bfloat16,
31 ):
32 """
33 Args:
34 text_encoder: GemmaTextEncoder instance
35 embeddings_processor: EmbeddingsProcessor instance
36 device: Target device
37 dtype: Model dtype
38 """
39 super().__init__()
40 self.text_encoder = text_encoder
41 self.embeddings_processor = embeddings_processor
42 self.device = device
43 self.dtype = dtype
44
45 @torch.no_grad()
46 def forward(
47 self,
48 text_prompts: List[str],
49 padding_side: str = "left",
50 ) -> Dict[str, Optional[torch.Tensor]]:
51 """
52 Encode text prompts to conditioning embeddings.
53
54 Args:
55 text_prompts: List of text prompts (already processed, no enhancement)
56 padding_side: Padding side for tokenizer
57
58 Returns:
59 Dictionary containing:
60 - video_context: [B, seq_len, dim] video conditioning
61 - audio_context: [B, seq_len, dim] audio conditioning
62 - attention_mask: [B, seq_len] attention mask
63 """
64 if not text_prompts:
65 return {
66 "video_context": None,
67 "audio_context": None,
68 "attention_mask": None,
69 }
70
71 hidden_states, attention_mask = self.text_encoder.encode_batch(
72 text_prompts,
73 padding_side=padding_side,
74 )
75 output = self.embeddings_processor.process_hidden_states(
76 hidden_states,
77 attention_mask,
78 padding_side=padding_side,
79 )
80
81 return {
82 "video_context": output.video_encoding,
83 "audio_context": output.audio_encoding,
84 "attention_mask": output.attention_mask,
85 }
86
87 def encode_batch(
88 self,
89 text_prompts: List[str],
90 ) -> Dict[str, torch.Tensor]:
91 """Alias for forward() with default padding."""
92 return self.forward(text_prompts)
93
94
95 def create_text_encoder_wrapper(
96 checkpoint_path: str,
97 gemma_path: str,
98 device: torch.device,
99 dtype: torch.dtype = torch.bfloat16,
100 registry: Registry | None = None,
101 ) -> GemmaTextEncoderWrapper:
102 """
103 Factory function to create GemmaTextEncoderWrapper from checkpoint.
104
105 Args:
106 checkpoint_path: Path to LTX-2 checkpoint
107 gemma_path: Path to Gemma text encoder
108 device: Target device
109 dtype: Model dtype
110
111 Returns:
112 Configured GemmaTextEncoderWrapper
113 """
114 from ltx_pipelines.utils.model_ledger import ModelLedger
115
116 # Load to CPU first to avoid safetensors device issues
117 ledger = ModelLedger(
118 dtype=dtype,
119 device=torch.device("cpu"),
120 checkpoint_path=checkpoint_path,
121 gemma_root_path=gemma_path,
122 registry=registry,
123 )
124
125 text_encoder = ledger.text_encoder().to(device=device, dtype=dtype)
126 embeddings_processor = ledger.gemma_embeddings_processor().to(device=device, dtype=dtype)
127
128 wrapper = GemmaTextEncoderWrapper(
129 text_encoder=text_encoder,
130 embeddings_processor=embeddings_processor,
131 device=device,
132 dtype=dtype,
133 )
134
135 return wrapper
136
137
138 def create_language_only_text_encoder(
139 checkpoint_path: str,
140 gemma_path: str,
141 device: torch.device,
142 dtype: torch.dtype = torch.bfloat16,
143 registry: Registry | None = None,
144 ):
145 """Load only the Gemma language backbone and tokenizer for DMD encoding."""
146 from ltx_pipelines.utils.model_ledger import ModelLedger
147
148 ledger = ModelLedger(
149 dtype=dtype,
150 device=torch.device("cpu"),
151 checkpoint_path=checkpoint_path,
152 gemma_root_path=gemma_path,
153 registry=registry,
154 )
155 return ledger.language_only_text_encoder().to(device=device, dtype=dtype).eval()
156
157
158 def create_text_embeddings_processor(
159 checkpoint_path: str,
160 device: torch.device,
161 dtype: torch.dtype = torch.bfloat16,
162 registry: Registry | None = None,
163 ):
164 """Load only Echo's feature extractor and video/audio text connectors."""
165 from ltx_pipelines.utils.model_ledger import ModelLedger
166
167 ledger = ModelLedger(
168 dtype=dtype,
169 device=torch.device("cpu"),
170 checkpoint_path=checkpoint_path,
171 registry=registry,
172 )
173 return ledger.gemma_embeddings_processor().to(device=device, dtype=dtype).eval()
174
174 lines PYTHON