rkazants commited on
Commit
ba09a26
·
1 Parent(s): 69e9673

Add tiny random LFM3 model for optimum-intel tests (#1)

Browse files

- Add tiny random LFM3 model for optimum-intel tests (cf7edf4c3dd449e2ca23f93b6ce8e9ae99b097d4)

.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
chat_template.jinja ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {{- bos_token -}}
2
+ {%- set preserve_thinking = preserve_thinking | default(false) -%}
3
+ {%- set enable_thinking = enable_thinking | default(true) -%}
4
+
5
+ {%- macro render_value(v) -%}
6
+ {%- if v is string -%}
7
+ {{- v -}}
8
+ {%- elif v is boolean -%}
9
+ {{- "true" if v else "false" -}}
10
+ {%- elif v is none -%}
11
+ {{- "null" -}}
12
+ {%- elif v is mapping or (v is sequence and v is not string) -%}
13
+ {{- v | tojson -}}
14
+ {%- else -%}
15
+ {{- v -}}
16
+ {%- endif -%}
17
+ {%- endmacro -%}
18
+
19
+ {%- macro parse_content(content) -%}
20
+ {%- if content is string -%}
21
+ {{- content -}}
22
+ {%- elif content is mapping and content.get("type") == "image" -%}
23
+ {{- "<|vision_start|><|image|><|vision_end|>" -}}
24
+ {%- elif content is mapping and content.get("type") == "video" -%}
25
+ {{- "<|vision_start|><|video|><|vision_end|>" -}}
26
+ {%- elif content is mapping and content.get("type") == "text" -%}
27
+ {{- (content.get("text") or "") | string -}}
28
+ {%- elif content is mapping -%}
29
+ {{- content | tojson -}}
30
+ {%- elif content is iterable -%}
31
+ {%- set _ns = namespace(result="") -%}
32
+ {%- for item in content -%}
33
+ {%- if item is string -%}
34
+ {%- set _ns.result = _ns.result + item -%}
35
+ {%- elif item is mapping and item.get("type") == "image" -%}
36
+ {%- set _ns.result = _ns.result + "<|vision_start|><|image|><|vision_end|>" -%}
37
+ {%- elif item is mapping and item.get("type") == "video" -%}
38
+ {%- set _ns.result = _ns.result + "<|vision_start|><|video|><|vision_end|>" -%}
39
+ {%- elif item is mapping and item.get("type") == "text" -%}
40
+ {%- set _ns.result = _ns.result + ((item.get("text") or "") | string) -%}
41
+ {%- else -%}
42
+ {%- set _ns.result = _ns.result + (item | tojson) -%}
43
+ {%- endif -%}
44
+ {%- endfor -%}
45
+ {{- _ns.result -}}
46
+ {%- endif -%}
47
+ {%- endmacro -%}
48
+
49
+ {%- macro render_tool_calls(tool_calls) -%}
50
+ {%- for tool_call in tool_calls -%}
51
+ {%- if not loop.first -%}{{- "\n\n" -}}{%- endif -%}
52
+ {%- set func = tool_call["function"] if "function" in tool_call else tool_call -%}
53
+ {%- set func_args = func.get("arguments") -%}
54
+ {%- if func_args is string and (func_args | trim) not in ["", "{}", "null"] -%}
55
+ {{- raise_exception("Tool call arguments must be a mapping, got a JSON-encoded string: parse arguments with json.loads() before applying the chat template") -}}
56
+ {%- endif -%}
57
+ {{- "<tool_call>\n<function=" + func["name"] + ">\n" -}}
58
+ {%- if func_args is mapping -%}
59
+ {%- for arg_name, arg_value in func_args.items() -%}
60
+ {{- "<parameter=" + arg_name + ">\n" + render_value(arg_value) + "\n</parameter>\n" -}}
61
+ {%- endfor -%}
62
+ {%- endif -%}
63
+ {{- "</function>\n</tool_call>" -}}
64
+ {%- endfor -%}
65
+ {%- endmacro -%}
66
+
67
+ {%- macro tool_to_json(tool) -%}
68
+ {%- set fn = tool.function if tool.function is defined else tool -%}
69
+ {%- set ns_t = namespace(first=true) -%}
70
+ {{- "{" -}}
71
+ {%- for k, v in fn.items() -%}
72
+ {%- if k != "defer_loading" -%}
73
+ {%- if not ns_t.first -%}{{- ", " -}}{%- endif -%}
74
+ {%- set ns_t.first = false -%}
75
+ {{- k | tojson -}}{{- ": " -}}{{- v | tojson -}}
76
+ {%- endif -%}
77
+ {%- endfor -%}
78
+ {{- "}" -}}
79
+ {%- endmacro -%}
80
+
81
+ {%- macro render_tool_response(content) -%}
82
+ {#- content that already carries the tag (pre-formatted by the caller) is
83
+ passed through as-is instead of being wrapped a second time -#}
84
+ {%- if content is string and "<tool_response>" in content -%}
85
+ {{- content -}}
86
+ {%- else -%}
87
+ {{- "<tool_response>\n" -}}
88
+ {%- if content is string -%}
89
+ {{- content -}}
90
+ {%- elif content is mapping -%}
91
+ {{- parse_content(content) -}}
92
+ {%- elif content is iterable -%}
93
+ {%- for item in content -%}
94
+ {%- if not loop.first -%}{{- "\n" -}}{%- endif -%}
95
+ {%- if item is mapping and item.result is defined and item.result is mapping and item.result.tools is defined -%}
96
+ {{- "<additional_tools>\n" -}}
97
+ {%- set _tl = item.result.tools -%}
98
+ {%- if _tl is string -%}
99
+ {{- _tl -}}
100
+ {%- elif _tl is mapping -%}
101
+ {{- tool_to_json(_tl) -}}
102
+ {%- else -%}
103
+ {%- for t in _tl -%}
104
+ {%- if not loop.first -%}{{- "\n" -}}{%- endif -%}
105
+ {{- tool_to_json(t) -}}
106
+ {%- endfor -%}
107
+ {%- endif -%}
108
+ {{- "\n</additional_tools>" -}}
109
+ {%- else -%}
110
+ {{- parse_content(item) -}}
111
+ {%- endif -%}
112
+ {%- endfor -%}
113
+ {%- endif -%}
114
+ {{- "\n</tool_response>" -}}
115
+ {%- endif -%}
116
+ {%- endmacro -%}
117
+
118
+ {%- set ns = namespace(system_prompt="", last_user_index=-1) -%}
119
+ {%- if messages and messages[0]["role"] == "system" -%}
120
+ {%- if messages[0].get("content") -%}
121
+ {%- set ns.system_prompt = parse_content(messages[0]["content"]) -%}
122
+ {%- endif -%}
123
+ {%- set messages = messages[1:] -%}
124
+ {%- endif -%}
125
+ {%- if tools -%}
126
+ {%- set tools_ns = namespace(lines=[]) -%}
127
+ {%- for tool in tools -%}
128
+ {%- set fn = tool.function if tool.function is defined else tool -%}
129
+ {%- set _dyn = (fn.defer_loading is defined and fn.defer_loading) or (tool.defer_loading is defined and tool.defer_loading) -%}
130
+ {%- if not _dyn -%}
131
+ {%- set tools_ns.lines = tools_ns.lines + [tool_to_json(fn)] -%}
132
+ {%- endif -%}
133
+ {%- endfor -%}
134
+ {%- if tools_ns.lines -%}
135
+ {%- set ns.system_prompt = ns.system_prompt + ("\n\n" if ns.system_prompt else "") + "<tools>\n" + (tools_ns.lines | join("\n")) + "\n</tools>" -%}
136
+ {%- endif -%}
137
+ {%- endif -%}
138
+ {%- if ns.system_prompt -%}
139
+ {{- "<|im_start|>system\n" + ns.system_prompt + "<|im_end|>\n" -}}
140
+ {%- endif -%}
141
+ {%- for message in messages -%}
142
+ {%- if message["role"] == "user" -%}
143
+ {%- set ns.last_user_index = loop.index0 -%}
144
+ {%- endif -%}
145
+ {%- endfor -%}
146
+ {%- for message in messages -%}
147
+ {{- "<|im_start|>" + message.role + "\n" -}}
148
+ {%- if message.role == "assistant" -%}
149
+ {%- generation -%}
150
+ {%- set keep_thinking = preserve_thinking or loop.index0 > ns.last_user_index -%}
151
+ {%- set thinking = message.thinking or message.reasoning or message.reasoning_content -%}
152
+ {%- set thinking = thinking if thinking is string else "" -%}
153
+ {%- if thinking and keep_thinking -%}
154
+ {{- "<think>" + thinking + "</think>" -}}
155
+ {%- endif -%}
156
+ {%- set _cfm_tag = "CONTINUE_FINAL_MESSAGE_TAG " -%}
157
+ {%- set _has_cfm = false -%}
158
+ {%- set content = "" -%}
159
+ {%- if message.get("content") -%}
160
+ {%- set content = parse_content(message.content) -%}
161
+ {%- endif -%}
162
+ {%- if not keep_thinking -%}
163
+ {%- if "</think>" in content -%}
164
+ {%- set content = content.split("</think>")[-1] | trim -%}
165
+ {%- endif -%}
166
+ {%- set content = "<think></think>" + content -%}
167
+ {%- endif -%}
168
+ {%- if content.endswith(_cfm_tag) -%}
169
+ {%- set _has_cfm = true -%}
170
+ {%- set _trunc_len = (content | length) - (_cfm_tag | length) -%}
171
+ {%- set content = content[:_trunc_len] -%}
172
+ {%- endif -%}
173
+ {{- content -}}
174
+ {%- if message.tool_calls -%}
175
+ {{- render_tool_calls(message.tool_calls) -}}
176
+ {%- endif -%}
177
+ {%- if _has_cfm -%}
178
+ {{- _cfm_tag -}}
179
+ {%- endif -%}
180
+ {{- "<|im_end|>\n" -}}
181
+ {%- endgeneration -%}
182
+ {%- elif message.role == "tool" -%}
183
+ {%- if message.get("additional_tools") -%}
184
+ {%- set _at = message["additional_tools"] -%}
185
+ {#- definitions may live in additional_tools itself, or in content
186
+ with additional_tools used as a plain truthy flag -#}
187
+ {%- if _at is mapping or (_at is sequence and _at is not string) -%}
188
+ {%- set _src = _at -%}
189
+ {%- else -%}
190
+ {%- set _src = message.get("content") -%}
191
+ {%- endif -%}
192
+ {#- nothing to reveal: skip the wrapper entirely rather than
193
+ emitting an empty <additional_tools> block -#}
194
+ {%- if _src is string and "<tool_response>" in _src -%}
195
+ {{- _src -}}
196
+ {%- elif _src is string and "<additional_tools>" in _src -%}
197
+ {{- "<tool_response>\n" + _src + "\n</tool_response>" -}}
198
+ {%- elif _src -%}
199
+ {{- "<tool_response>\n<additional_tools>\n" -}}
200
+ {%- if _src is string -%}
201
+ {{- _src -}}
202
+ {%- elif _src is mapping -%}
203
+ {{- tool_to_json(_src) -}}
204
+ {%- else -%}
205
+ {%- for t in _src -%}
206
+ {%- if not loop.first -%}{{- "\n" -}}{%- endif -%}
207
+ {{- tool_to_json(t) -}}
208
+ {%- endfor -%}
209
+ {%- endif -%}
210
+ {{- "\n</additional_tools>\n</tool_response>" -}}
211
+ {%- endif -%}
212
+ {%- elif message.get("content") -%}
213
+ {{- render_tool_response(message["content"]) -}}
214
+ {%- endif -%}
215
+ {{- "<|im_end|>\n" -}}
216
+ {%- else %}
217
+ {%- if message.get("content") -%}
218
+ {{- parse_content(message["content"]) -}}
219
+ {%- endif -%}
220
+ {{- "<|im_end|>\n" -}}
221
+ {%- endif %}
222
+ {%- endfor -%}
223
+ {%- if add_generation_prompt -%}
224
+ {{- "<|im_start|>assistant\n" -}}
225
+ {%- if enable_thinking -%}
226
+ {{- "<think>" -}}
227
+ {%- else -%}
228
+ {{- "<think></think>" -}}
229
+ {%- endif -%}
230
+ {%- endif -%}
config.json ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "Lfm3ForCausalLM"
4
+ ],
5
+ "auto_map": {
6
+ "AutoConfig": "configuration_lfm3.Lfm3Config",
7
+ "AutoModelForCausalLM": "modeling_lfm3.Lfm3ForCausalLM"
8
+ },
9
+ "bos_token_id": 124894,
10
+ "conv_L_cache": 7,
11
+ "conv_bias": false,
12
+ "dtype": "float32",
13
+ "eos_token_id": 124900,
14
+ "head_dim": 8,
15
+ "hidden_size": 16,
16
+ "intermediate_size": 32,
17
+ "layer_types": [
18
+ "conv",
19
+ "conv",
20
+ "full_attention",
21
+ "conv",
22
+ "sliding_attention",
23
+ "conv",
24
+ "sliding_attention"
25
+ ],
26
+ "max_position_embeddings": 196608,
27
+ "model_type": "lfm3",
28
+ "norm_eps": 1e-05,
29
+ "num_attention_heads": 4,
30
+ "num_hidden_layers": 7,
31
+ "num_key_value_heads": 2,
32
+ "pad_token_id": 124893,
33
+ "rope_parameters": {
34
+ "full_attention": {
35
+ "partial_rotary_factor": 0.5,
36
+ "rope_theta": 1000000.0,
37
+ "rope_type": "default"
38
+ },
39
+ "sliding_attention": {
40
+ "rope_theta": 10000,
41
+ "rope_type": "default"
42
+ }
43
+ },
44
+ "sliding_window": 4,
45
+ "tie_word_embeddings": true,
46
+ "transformers_version": "5.20.0",
47
+ "use_cache": true,
48
+ "use_sandwich_norm": true,
49
+ "vocab_size": 128000
50
+ }
configuration_lfm3.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Union
2
+
3
+ from transformers.configuration_utils import PretrainedConfig
4
+ from transformers.modeling_rope_utils import RopeParameters
5
+
6
+
7
+ class Lfm3Config(PretrainedConfig):
8
+ r"""
9
+ Configuration class for [`Lfm3Model`] / [`Lfm3ForCausalLM`].
10
+
11
+ LFM3 dense is a hybrid architecture composed of:
12
+ - GQA full-attention layers (with headwise gated attention)
13
+ - sliding-window attention layers (may use a different rope_theta from global)
14
+ - gated short-conv layers (depthwise conv with B*x -> conv -> C*conv_out)
15
+
16
+ Args:
17
+ vocab_size (`int`, *optional*, defaults to 65536):
18
+ Vocabulary size.
19
+ hidden_size (`int`, *optional*, defaults to 2048):
20
+ Hidden / residual stream dimension.
21
+ intermediate_size (`int`, *optional*, defaults to 7168):
22
+ SwiGLU MLP intermediate dimension.
23
+ num_hidden_layers (`int`, *optional*, defaults to 32):
24
+ Total number of decoder layers.
25
+ num_attention_heads (`int`, *optional*, defaults to 32):
26
+ Number of query heads.
27
+ num_key_value_heads (`int`, *optional*, defaults to 8):
28
+ Number of KV heads (GQA) for full-attention layers.
29
+ head_dim (`int`, *optional*):
30
+ Per-head dim. When `None`, defaults to `hidden_size // num_attention_heads`.
31
+ May differ from that quotient (e.g. dim=1536, num_heads=16, head_dim=128).
32
+ max_position_embeddings (`int`, *optional*, defaults to 128_000):
33
+ Maximum sequence length the model is designed for.
34
+ norm_eps (`float`, *optional*, defaults to 1e-5):
35
+ RMSNorm epsilon.
36
+ use_cache (`bool`, *optional*, defaults to `True`):
37
+ Whether to return past_key_values.
38
+ pad_token_id, bos_token_id, eos_token_id (`int`, *optional*):
39
+ Special token ids.
40
+ tie_word_embeddings (`bool`, *optional*, defaults to `True`):
41
+ Tie input/output embeddings.
42
+ rope_parameters (`RopeParameters` or `dict[str, RopeParameters]`, *optional*):
43
+ Either a single RopeParameters dict (legacy form — broadcast to every
44
+ attention layer type) or a Gemma3-style dict keyed by `layer_type`:
45
+ `{"full_attention": {"rope_type": "default", "rope_theta": 1_000_000,
46
+ "partial_rotary_factor": 0.5}, "sliding_attention":
47
+ {"rope_type": "default", "rope_theta": 10_000}}`. Partial RoPE is a
48
+ global-attention-only knob (Gemma 4 convention); SWA layers always
49
+ use full RoPE, so the SWA entry never carries `partial_rotary_factor`.
50
+ For backward compatibility, callers may also pass a legacy flat
51
+ dict together with a separate `swa_rope_parameters` kwarg; both forms
52
+ are migrated to the nested representation in `__init__`.
53
+ conv_bias (`bool`, *optional*, defaults to `False`):
54
+ Whether the depthwise conv layers use a bias term.
55
+ conv_L_cache (`int`, *optional*, defaults to 3):
56
+ Convolution kernel size and the number of conv-state slots cached per layer.
57
+ sliding_window (`int`, *optional*):
58
+ Sliding-window size used by sliding-window-attention layers.
59
+ full_attn_idxs (`list[int]`, *optional*):
60
+ Convenience: layer indices that use full attention. Only used to build the
61
+ default `layer_types` when `layer_types` is not explicitly provided.
62
+ layer_types (`list[str]`, *optional*):
63
+ Per-layer operator type. Each entry is one of
64
+ `"full_attention"`, `"sliding_attention"`, `"conv"`.
65
+ When `None`, defaults to `"full_attention"` for indices in `full_attn_idxs`
66
+ and `"conv"` for the rest.
67
+ use_sandwich_norm (`bool`, *optional*, defaults to `False`):
68
+ Whether to apply an additional RMSNorm AFTER the operator and AFTER the
69
+ FFN (sandwich norm; quantization-friendly variant).
70
+ """
71
+
72
+ model_type = "lfm3"
73
+ keys_to_ignore_at_inference = ["past_key_values"]
74
+ # Attention layer types that consume a per-layer-type entry in
75
+ # `rope_parameters`. `"conv"` layers don't use RoPE.
76
+ _attention_layer_types = ("full_attention", "sliding_attention")
77
+
78
+ def __init__(
79
+ self,
80
+ vocab_size: int = 65536,
81
+ hidden_size: int = 2048,
82
+ intermediate_size: int = 7168,
83
+ num_hidden_layers: int = 32,
84
+ num_attention_heads: int = 32,
85
+ num_key_value_heads: int = 8,
86
+ head_dim: Optional[int] = None,
87
+ max_position_embeddings: int = 128_000,
88
+ norm_eps: float = 1e-5,
89
+ use_cache: bool = True,
90
+ pad_token_id: int = 0,
91
+ bos_token_id: int = 1,
92
+ eos_token_id: int = 2,
93
+ tie_word_embeddings: bool = True,
94
+ rope_parameters: Optional[Union[RopeParameters, dict[str, RopeParameters]]] = None,
95
+ conv_bias: bool = False,
96
+ conv_L_cache: int = 3,
97
+ sliding_window: Optional[int] = None,
98
+ full_attn_idxs: Optional[list[int]] = None,
99
+ layer_types: Optional[list[str]] = None,
100
+ use_sandwich_norm: bool = False,
101
+ **kwargs,
102
+ ):
103
+ self.vocab_size = vocab_size
104
+ self.hidden_size = hidden_size
105
+ self.intermediate_size = intermediate_size
106
+ self.num_hidden_layers = num_hidden_layers
107
+ self.num_attention_heads = num_attention_heads
108
+ self.num_key_value_heads = num_key_value_heads
109
+ self.head_dim = head_dim
110
+ self.max_position_embeddings = max_position_embeddings
111
+ self.norm_eps = norm_eps
112
+ self.use_cache = use_cache
113
+
114
+ self.conv_bias = conv_bias
115
+ self.conv_L_cache = conv_L_cache
116
+
117
+ self.sliding_window = sliding_window
118
+
119
+ self.use_sandwich_norm = use_sandwich_norm
120
+
121
+ self.layer_types = layer_types
122
+ if self.layer_types is None:
123
+ full_attn_idxs = (
124
+ full_attn_idxs if full_attn_idxs is not None else list(range(num_hidden_layers))
125
+ )
126
+ self.layer_types = [
127
+ "full_attention" if i in full_attn_idxs else "conv"
128
+ for i in range(num_hidden_layers)
129
+ ]
130
+
131
+ # Catch the inconsistent combination at construction time so users see
132
+ # a clear error here instead of an opaque one from
133
+ # `create_sliding_window_causal_mask` deep in the forward pass.
134
+ if "sliding_attention" in self.layer_types and sliding_window is None:
135
+ raise ValueError(
136
+ "Lfm3Config has `sliding_attention` in `layer_types` but "
137
+ "`sliding_window` is None. Set `sliding_window` to the window size "
138
+ "(in tokens) you trained with."
139
+ )
140
+
141
+ # rope_parameters is the Gemma3-style nested dict keyed by attention
142
+ # layer_type (e.g. {"full_attention": {...}, "sliding_attention": {...}}).
143
+ # The trainer→HF conversion writes this nested form directly.
144
+ self.rope_parameters = self._validate_rope_parameters(rope_parameters)
145
+
146
+ tie_word_embeddings = kwargs.get("tie_embedding", tie_word_embeddings)
147
+ super().__init__(
148
+ pad_token_id=pad_token_id,
149
+ bos_token_id=bos_token_id,
150
+ eos_token_id=eos_token_id,
151
+ tie_word_embeddings=tie_word_embeddings,
152
+ **kwargs,
153
+ )
154
+
155
+ def _validate_rope_parameters(
156
+ self,
157
+ rope_parameters: Optional[dict[str, dict]],
158
+ ) -> Optional[dict[str, dict]]:
159
+ """Keep the per-attention-layer-type rope entries present in the model.
160
+
161
+ `rope_parameters` must be the Gemma3-style nested form keyed by attention
162
+ `layer_type`, e.g. ``{"full_attention": {"rope_type": "default",
163
+ "rope_theta": 1e6, "partial_rotary_factor": 0.5}, "sliding_attention":
164
+ {"rope_type": "default", "rope_theta": 1e4}}``. Conv layers have no rope.
165
+ Partial RoPE is a full-attention-only knob — the SWA entry never carries
166
+ `partial_rotary_factor`.
167
+ """
168
+ if rope_parameters is None:
169
+ return None
170
+
171
+ attention_types_in_model = [
172
+ lt for lt in (self.layer_types or []) if lt in self._attention_layer_types
173
+ ]
174
+ # Conv-only models don't use RoPE.
175
+ if not attention_types_in_model:
176
+ return None
177
+
178
+ if not (
179
+ isinstance(rope_parameters, dict)
180
+ and all(k in self._attention_layer_types for k in rope_parameters)
181
+ ):
182
+ raise ValueError(
183
+ "Lfm3 expects nested per-layer-type `rope_parameters` keyed by "
184
+ "attention layer_type (e.g. {'full_attention': {...}, "
185
+ "'sliding_attention': {...}}); got "
186
+ f"{list(rope_parameters) if isinstance(rope_parameters, dict) else type(rope_parameters).__name__}."
187
+ )
188
+
189
+ return {
190
+ k: dict(v) for k, v in rope_parameters.items()
191
+ if k in attention_types_in_model
192
+ } or None
193
+
194
+ def convert_rope_params_to_dict(self, **kwargs):
195
+ """Override the base PretrainedConfig hook so it doesn't pollute our
196
+ per-layer-type `rope_parameters` dict with top-level `rope_theta` /
197
+ `rope_type` keys.
198
+
199
+ The base implementation (modeling_rope_utils.py
200
+ `RotaryEmbeddingConfigMixin.convert_rope_params_to_dict`) does
201
+ `self.rope_parameters.setdefault("rope_theta", ...)` directly on
202
+ whatever we set — fine for legacy flat configs, but it puts top-level
203
+ keys onto our nested dict, which then makes `validate_rope` treat the
204
+ whole thing as flat and warn about the layer-type keys as
205
+ "Unrecognized". This override mirrors Gemma3Config: when the user
206
+ already supplied a nested dict, we trust it and only run validation.
207
+ """
208
+ rope_scaling = kwargs.pop("rope_scaling", None)
209
+ if rope_scaling is not None and self.rope_parameters is None:
210
+ self.rope_parameters = rope_scaling
211
+
212
+ if self.rope_parameters is None:
213
+ # Conv-only models don't carry RoPE. Skipping the base helpers
214
+ # avoids a noisy "no RoPE parameters were found" log on every
215
+ # config construction.
216
+ return kwargs
217
+
218
+ is_nested = isinstance(self.rope_parameters, dict) and all(
219
+ k in self._attention_layer_types for k in self.rope_parameters.keys()
220
+ )
221
+ if is_nested:
222
+ # Ensure each entry has rope_type; default to "default" if missing.
223
+ for entry in self.rope_parameters.values():
224
+ entry.setdefault("rope_type", "default")
225
+ # validate_rope reads self.ignore_keys_at_rope_validation internally.
226
+ self.validate_rope()
227
+ return kwargs
228
+
229
+ # Flat / legacy form — fall back to the base behavior.
230
+ return super().convert_rope_params_to_dict(**kwargs)
231
+
232
+ def standardize_rope_params(self):
233
+ """Override the base PretrainedConfig hook to skip non-attention
234
+ layer types (e.g. ``"conv"``).
235
+
236
+ Callers like vLLM's ``patch_rope_parameters`` (in
237
+ ``vllm/transformers_utils/config.py``) invoke
238
+ ``config.standardize_rope_params()`` directly after loading our
239
+ config, bypassing our ``convert_rope_params_to_dict`` override.
240
+
241
+ The base implementation in
242
+ ``transformers/modeling_rope_utils.py:standardize_rope_params``
243
+ Case 2 (nested rope) does:
244
+
245
+ for layer_type in set(layer_types):
246
+ rope_parameters[layer_type].setdefault("rope_type", ...)
247
+
248
+ This iterates EVERY entry in ``layer_types`` and indexes
249
+ ``rope_parameters[layer_type]`` blindly. Lfm3 has ``"conv"`` in
250
+ ``layer_types`` (conv layers don't use RoPE), and our nested
251
+ ``rope_parameters`` correctly omits a ``"conv"`` entry — so the
252
+ blind index raises ``KeyError: 'conv'`` and vLLM fails to load.
253
+
254
+ Gemma3 dodges this because its ``layer_types`` only contains
255
+ attention types. Upstream LFM2 dodges it because its
256
+ ``rope_parameters`` is flat (Case 1), not nested (Case 2).
257
+
258
+ Our override: iterate only the layer types that actually have an
259
+ entry in ``rope_parameters``, applying the same defaulting logic
260
+ the base does. For the flat / legacy form, defer to base.
261
+ """
262
+ rope_parameters = getattr(self, "rope_parameters", None)
263
+ if not rope_parameters:
264
+ return
265
+
266
+ is_nested = isinstance(rope_parameters, dict) and all(
267
+ k in self._attention_layer_types for k in rope_parameters.keys()
268
+ )
269
+ if not is_nested:
270
+ return super().standardize_rope_params()
271
+
272
+ rope_theta = getattr(self, "rope_theta", None)
273
+ partial_rotary_factor = getattr(self, "partial_rotary_factor", None)
274
+ for layer_type in rope_parameters:
275
+ entry = rope_parameters[layer_type]
276
+ entry.setdefault("rope_type", entry.get("type", "default"))
277
+ if rope_theta is not None:
278
+ entry.setdefault("rope_theta", rope_theta)
279
+ # Partial RoPE is a global-attention-only knob (Gemma 4 invariant);
280
+ # never apply it to SWA, even if a top-level
281
+ # `partial_rotary_factor` is set as a legacy attribute.
282
+ if partial_rotary_factor is not None and layer_type == "full_attention":
283
+ entry["partial_rotary_factor"] = partial_rotary_factor
284
+ if entry["rope_type"] in ("llama3", "yarn", "longrope"):
285
+ entry.setdefault(
286
+ "original_max_position_embeddings", self.max_position_embeddings
287
+ )
288
+
289
+ @property
290
+ def rope_scaling(self):
291
+ """Backward compatibility property: rope_scaling maps to rope_parameters."""
292
+ return self.rope_parameters
293
+
294
+ @rope_scaling.setter
295
+ def rope_scaling(self, value):
296
+ self.rope_parameters = value
297
+
298
+
299
+ __all__ = ["Lfm3Config"]
generation_config.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "bos_token_id": 124894,
4
+ "eos_token_id": 124900,
5
+ "output_attentions": false,
6
+ "output_hidden_states": false,
7
+ "pad_token_id": 124893,
8
+ "transformers_version": "5.20.0",
9
+ "use_cache": true
10
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e19a9477b4572d46575e1ccf66cf843c14c133a18a2e5d7f72e7ec9c74aa55e9
3
+ size 8283088
modeling_lfm3.py ADDED
@@ -0,0 +1,830 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # LFM3 dense model.
2
+ from typing import Any, Callable, Optional, Union
3
+
4
+ import torch
5
+ import torch.nn.functional as F
6
+ from torch import nn
7
+
8
+ from transformers.cache_utils import Cache, DynamicCache
9
+ from transformers.generation import GenerationMixin
10
+ from transformers.integrations import (
11
+ use_kernel_forward_from_hub,
12
+ use_kernel_func_from_hub,
13
+ use_kernelized_func,
14
+ )
15
+ from transformers.masking_utils import create_causal_mask, create_sliding_window_causal_mask
16
+ from transformers.modeling_layers import GradientCheckpointingLayer
17
+ from transformers.modeling_outputs import BaseModelOutputWithPast, CausalLMOutputWithPast
18
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS, dynamic_rope_update
19
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
20
+ from transformers.processing_utils import Unpack
21
+ from transformers.utils import TransformersKwargs, auto_docstring, can_return_tuple
22
+ from transformers.utils.generic import check_model_inputs, maybe_autocast
23
+ from transformers.utils.import_utils import is_causal_conv1d_available, is_torchdynamo_compiling
24
+
25
+ from .configuration_lfm3 import Lfm3Config
26
+
27
+
28
+ if is_causal_conv1d_available():
29
+ from causal_conv1d import causal_conv1d_fn, causal_conv1d_update
30
+ else:
31
+ causal_conv1d_fn, causal_conv1d_update = None, None
32
+
33
+
34
+ @use_kernel_forward_from_hub("RMSNorm")
35
+ class Lfm3RMSNorm(nn.Module):
36
+ def __init__(self, hidden_size, eps=1e-6):
37
+ super().__init__()
38
+ self.weight = nn.Parameter(torch.ones(hidden_size))
39
+ self.variance_epsilon = eps
40
+
41
+ def forward(self, hidden_states):
42
+ input_dtype = hidden_states.dtype
43
+ hidden_states = hidden_states.to(torch.float32)
44
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
45
+ hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
46
+ return self.weight * hidden_states.to(input_dtype)
47
+
48
+ def extra_repr(self):
49
+ return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
50
+
51
+
52
+ class Lfm3RotaryEmbedding(nn.Module):
53
+ """Per-layer-type rotary embedding (Gemma 3-style).
54
+
55
+ `config.rope_parameters` is a dict keyed by `layer_type`
56
+ (`"full_attention"`, `"sliding_attention"`). For each layer type
57
+ present in `config.layer_types`, an `{layer_type}_inv_freq` buffer is
58
+ registered. `forward(x, position_ids, layer_type=...)` selects the right
59
+ one. Partial RoPE (`partial_rotary_factor < 1.0`) follows the Gemma 4
60
+ convention: the first `rope_angles = int(partial_rotary_factor * dim) // 2`
61
+ frequency pairs receive the standard rotation, the remaining
62
+ `dim//2 - rope_angles` entries are zero (cos=1, sin=0 → identity). The
63
+ SWA entry must never carry `partial_rotary_factor` — the Lfm3Config
64
+ normalizer strips it during migration of legacy configs.
65
+ """
66
+
67
+ def __init__(self, config: Lfm3Config, device=None):
68
+ super().__init__()
69
+ self.max_seq_len_cached = config.max_position_embeddings
70
+ self.original_max_seq_len = config.max_position_embeddings
71
+ self.config = config
72
+
73
+ # Deterministic dedupe of attention-layer types present in this model.
74
+ # `dict.fromkeys` preserves insertion order so the buffer-registration
75
+ # order is stable across processes / Python releases — `set()` would
76
+ # not, which matters for reproducible test output.
77
+ layer_types_in_model = list(dict.fromkeys(config.layer_types or []))
78
+ self.attention_layer_types = [
79
+ lt for lt in layer_types_in_model if lt in config._attention_layer_types
80
+ ]
81
+ self.rope_type: dict[str, str] = {}
82
+
83
+ rope_parameters = config.rope_parameters or {}
84
+ for layer_type in self.attention_layer_types:
85
+ entry = rope_parameters.get(layer_type)
86
+ if entry is None:
87
+ continue
88
+ self.rope_type[layer_type] = entry["rope_type"]
89
+ if entry["rope_type"] != "default":
90
+ rope_init_fn: Callable = ROPE_INIT_FUNCTIONS[entry["rope_type"]]
91
+ inv_freq, attention_scaling = rope_init_fn(self.config, device)
92
+ else:
93
+ inv_freq, attention_scaling = self.compute_default_rope_parameters(
94
+ self.config, device, layer_type=layer_type
95
+ )
96
+ self.register_buffer(f"{layer_type}_inv_freq", inv_freq, persistent=False)
97
+ self.register_buffer(
98
+ f"{layer_type}_original_inv_freq", inv_freq.clone(), persistent=False
99
+ )
100
+ setattr(self, f"{layer_type}_attention_scaling", attention_scaling)
101
+
102
+ @staticmethod
103
+ def compute_default_rope_parameters(
104
+ config: Optional[Lfm3Config] = None,
105
+ device: Optional["torch.device"] = None,
106
+ seq_len: Optional[int] = None,
107
+ layer_type: Optional[str] = None,
108
+ ) -> tuple["torch.Tensor", float]:
109
+ entry = config.rope_parameters[layer_type]
110
+ base = entry["rope_theta"]
111
+ partial_rotary_factor = entry.get("partial_rotary_factor", 1.0)
112
+ dim = getattr(config, "head_dim", None) or config.hidden_size // config.num_attention_heads
113
+ attention_factor = 1.0
114
+
115
+ rope_angles = int(partial_rotary_factor * dim) // 2
116
+ assert 0 < rope_angles <= dim // 2, (
117
+ f"partial_rotary_factor={partial_rotary_factor} with dim={dim} produced "
118
+ f"rope_angles={rope_angles}, must be in (0, dim//2={dim // 2}]."
119
+ )
120
+ inv_freq_rotated = 1.0 / (
121
+ base
122
+ ** (
123
+ torch.arange(0, 2 * rope_angles, 2, dtype=torch.int64).to(
124
+ device=device, dtype=torch.float
125
+ )
126
+ / dim
127
+ )
128
+ )
129
+ nope_angles = dim // 2 - rope_angles
130
+ if nope_angles > 0:
131
+ inv_freq = torch.cat(
132
+ (
133
+ inv_freq_rotated,
134
+ torch.zeros(nope_angles, dtype=torch.float, device=device),
135
+ ),
136
+ dim=0,
137
+ )
138
+ else:
139
+ inv_freq = inv_freq_rotated
140
+ return inv_freq, attention_factor
141
+
142
+ @torch.no_grad()
143
+ @dynamic_rope_update # power user: used with advanced RoPE types (e.g. dynamic rope)
144
+ def forward(self, x, position_ids, layer_type: Optional[str] = None):
145
+ inv_freq = getattr(self, f"{layer_type}_inv_freq")
146
+ attention_scaling = getattr(self, f"{layer_type}_attention_scaling")
147
+
148
+ inv_freq_expanded = (
149
+ inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1).to(x.device)
150
+ )
151
+ position_ids_expanded = position_ids[:, None, :].float()
152
+
153
+ device_type = (
154
+ x.device.type if isinstance(x.device.type, str) and x.device.type != "mps" else "cpu"
155
+ )
156
+ with maybe_autocast(device_type=device_type, enabled=False): # Force float32
157
+ freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(1, 2)
158
+ emb = torch.cat((freqs, freqs), dim=-1)
159
+ cos = emb.cos() * attention_scaling
160
+ sin = emb.sin() * attention_scaling
161
+
162
+ return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
163
+
164
+
165
+ class Lfm3MLP(nn.Module):
166
+ def __init__(self, config: Lfm3Config, intermediate_size: Optional[int] = None):
167
+ super().__init__()
168
+ self.hidden_size = config.hidden_size
169
+ self.intermediate_size = (
170
+ config.intermediate_size if intermediate_size is None else intermediate_size
171
+ )
172
+ self.w1 = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
173
+ self.w3 = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
174
+ self.w2 = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
175
+
176
+ def forward(self, x):
177
+ return self.w2(F.silu(self.w1(x)) * self.w3(x))
178
+
179
+
180
+
181
+ def rotate_half(x):
182
+ """Rotates half the hidden dims of the input."""
183
+ x1 = x[..., : x.shape[-1] // 2]
184
+ x2 = x[..., x.shape[-1] // 2:]
185
+ return torch.cat((-x2, x1), dim=-1)
186
+
187
+
188
+ @use_kernel_func_from_hub("rotary_pos_emb")
189
+ def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
190
+ """Apply Rotary Position Embedding to q / k.
191
+
192
+ The rotation is computed in fp32 to match the native (Llama-style) RoPE
193
+ path: native does a complex multiply in fp32 and casts to bf16 once at the
194
+ end. The upstream default form (multiplying bf16 q/k by bf16 cos/sin)
195
+ accrues ~3× more ULP-level rounding error per element, which propagates
196
+ through softmax to ~5% per-layer native↔HF divergence in bf16. When the
197
+ kernel hub provides a fused rotary kernel, that takes over via the
198
+ decorator; this body is the Python fallback.
199
+ """
200
+ in_dtype = q.dtype
201
+ cos = cos.unsqueeze(unsqueeze_dim).float()
202
+ sin = sin.unsqueeze(unsqueeze_dim).float()
203
+ q_f = q.float()
204
+ k_f = k.float()
205
+ q_embed = (q_f * cos) + (rotate_half(q_f) * sin)
206
+ k_embed = (k_f * cos) + (rotate_half(k_f) * sin)
207
+ return q_embed.to(in_dtype), k_embed.to(in_dtype)
208
+
209
+
210
+ def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
211
+ batch, num_key_value_heads, slen, head_dim = hidden_states.shape
212
+ if n_rep == 1:
213
+ return hidden_states
214
+ hidden_states = hidden_states[:, :, None, :, :].expand(
215
+ batch, num_key_value_heads, n_rep, slen, head_dim
216
+ )
217
+ return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
218
+
219
+
220
+ def eager_attention_forward(
221
+ module: nn.Module,
222
+ query: torch.Tensor,
223
+ key: torch.Tensor,
224
+ value: torch.Tensor,
225
+ attention_mask: Optional[torch.Tensor],
226
+ scaling: float,
227
+ dropout: float = 0.0,
228
+ **kwargs: Unpack[TransformersKwargs],
229
+ ):
230
+ key_states = repeat_kv(key, module.num_key_value_groups)
231
+ value_states = repeat_kv(value, module.num_key_value_groups)
232
+
233
+ attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
234
+ if attention_mask is not None:
235
+ causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
236
+ attn_weights = attn_weights + causal_mask
237
+
238
+ attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(query.dtype)
239
+ attn_weights = nn.functional.dropout(attn_weights, p=dropout, training=module.training)
240
+ attn_output = torch.matmul(attn_weights, value_states)
241
+ attn_output = attn_output.transpose(1, 2).contiguous()
242
+
243
+ return attn_output, attn_weights
244
+
245
+
246
+ @use_kernelized_func(apply_rotary_pos_emb)
247
+ class Lfm3Attention(nn.Module):
248
+ """Multi-headed attention. Supports GQA, optional sliding window, and a
249
+ headwise sigmoid gate on the attention output.
250
+
251
+ The caller (``Lfm3Model.forward``) is responsible for building the right
252
+ mask for this layer's type (sliding-window-causal for SWA layers, full
253
+ causal otherwise). This forward only also passes ``sliding_window=...`` to
254
+ the attention backend for FA2's native fast path; SDPA / eager rely on the
255
+ precomputed mask alone.
256
+ """
257
+
258
+ def __init__(self, config: Lfm3Config, layer_idx: int, sliding_window: Optional[int] = None):
259
+ super().__init__()
260
+ self.config = config
261
+ self.layer_idx = layer_idx
262
+ self.head_dim = getattr(config, "head_dim", None) or (
263
+ config.hidden_size // config.num_attention_heads
264
+ )
265
+ self.sliding_window = sliding_window
266
+ self.is_causal = True
267
+
268
+ self.num_key_value_heads = config.num_key_value_heads
269
+ self.num_key_value_groups = config.num_attention_heads // config.num_key_value_heads
270
+ self.scaling = self.head_dim ** -0.5
271
+ self.q_proj = nn.Linear(
272
+ config.hidden_size, config.num_attention_heads * self.head_dim, bias=False
273
+ )
274
+ self.k_proj = nn.Linear(
275
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False
276
+ )
277
+ self.v_proj = nn.Linear(
278
+ config.hidden_size, config.num_key_value_heads * self.head_dim, bias=False
279
+ )
280
+ self.o_proj = nn.Linear(
281
+ config.num_attention_heads * self.head_dim, config.hidden_size, bias=False
282
+ )
283
+ self.q_norm = Lfm3RMSNorm(self.head_dim, eps=config.norm_eps)
284
+ self.k_norm = Lfm3RMSNorm(self.head_dim, eps=config.norm_eps)
285
+ # Headwise sigmoid gate on the attention output (one scalar per query head).
286
+ self.gate_proj = nn.Linear(
287
+ config.hidden_size, config.num_attention_heads, bias=False
288
+ )
289
+
290
+ def forward(
291
+ self,
292
+ hidden_states: torch.Tensor,
293
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
294
+ attention_mask: Optional[torch.Tensor],
295
+ past_key_values: Optional[Cache] = None,
296
+ **kwargs,
297
+ ) -> tuple[torch.Tensor, Optional[torch.Tensor]]:
298
+ input_shape = hidden_states.shape[:-1]
299
+ hidden_shape = (*input_shape, -1, self.head_dim)
300
+
301
+ query_states = self.q_norm(self.q_proj(hidden_states).view(*hidden_shape)).transpose(1, 2)
302
+ key_states = self.k_norm(self.k_proj(hidden_states).view(*hidden_shape)).transpose(1, 2)
303
+ value_states = self.v_proj(hidden_states).view(*hidden_shape).transpose(1, 2)
304
+
305
+ cos, sin = position_embeddings
306
+ query_states, key_states = apply_rotary_pos_emb(query_states, key_states, cos, sin)
307
+
308
+ if past_key_values is not None:
309
+ key_states, value_states = past_key_values.update(
310
+ key_states, value_states, self.layer_idx
311
+ )
312
+
313
+ attention_interface: Callable = eager_attention_forward
314
+ if self.config._attn_implementation != "eager":
315
+ attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation]
316
+
317
+ attn_output, attn_weights = attention_interface(
318
+ self,
319
+ query_states,
320
+ key_states,
321
+ value_states,
322
+ attention_mask,
323
+ dropout=0.0,
324
+ scaling=self.scaling,
325
+ sliding_window=self.sliding_window,
326
+ **kwargs,
327
+ )
328
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
329
+ # Headwise sigmoid gate: one scalar per query head, broadcast over head_dim.
330
+ num_heads = self.config.num_attention_heads
331
+ gate = torch.sigmoid(self.gate_proj(hidden_states)).view(*input_shape, num_heads, 1)
332
+ attn_output = attn_output.view(*input_shape, num_heads, self.head_dim)
333
+ attn_output = (gate * attn_output).view(*input_shape, -1)
334
+ output = self.o_proj(attn_output)
335
+ return output, attn_weights
336
+
337
+
338
+ def apply_mask_to_padding_states(hidden_states, attention_mask):
339
+ """Zero out the hidden states for padding tokens, see https://github.com/state-spaces/mamba/issues/66 .
340
+
341
+ NOTE (upstream-faithful, mamba/Lfm2): the `shape[0] > 1` (batch > 1) guard means
342
+ padding is NOT zeroed for a single (batch=1) sequence — so the conv can bleed
343
+ pad positions for a left-padded batch-of-1. Matches upstream; single-sequence
344
+ decode isn't padded in practice, but it's a latent footgun for padded bsz-1 prefill.
345
+ """
346
+ if attention_mask is not None and attention_mask.shape[1] > 1 and attention_mask.shape[0] > 1:
347
+ dtype = hidden_states.dtype
348
+ hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
349
+ return hidden_states
350
+
351
+
352
+ kernel_modules = (causal_conv1d_fn, causal_conv1d_update)
353
+ is_fast_path_available = all(kernel_modules)
354
+
355
+
356
+ class Lfm3ShortConv(nn.Module):
357
+ """Standard gated short conv block:
358
+
359
+ proj = in_proj(x) # 3*D
360
+ B, C, x' = proj.chunk(3, dim=-1)
361
+ conv_in = B * x'
362
+ y = C * depthwise_conv1d(conv_in)
363
+ out = out_proj(y)
364
+ """
365
+
366
+ def __init__(
367
+ self,
368
+ config: Lfm3Config,
369
+ layer_idx: int,
370
+ ):
371
+ super().__init__()
372
+ self.config = config
373
+ self.layer_idx = layer_idx
374
+ self.L_cache = config.conv_L_cache
375
+ self.bias = config.conv_bias
376
+
377
+ D = config.hidden_size
378
+ self.conv = nn.Conv1d(
379
+ in_channels=D,
380
+ out_channels=D,
381
+ kernel_size=self.L_cache,
382
+ groups=D,
383
+ bias=self.bias,
384
+ padding=self.L_cache - 1,
385
+ )
386
+ self.in_proj = nn.Linear(D, 3 * D, bias=self.bias)
387
+ self.out_proj = nn.Linear(D, D, bias=self.bias)
388
+
389
+ def cuda_kernels_forward(
390
+ self,
391
+ x: torch.Tensor,
392
+ past_key_values: Optional[Cache] = None,
393
+ attention_mask: Optional[torch.Tensor] = None,
394
+ seq_idx: Optional[torch.Tensor] = None,
395
+ ):
396
+ x = apply_mask_to_padding_states(x, attention_mask)
397
+ D = self.config.hidden_size
398
+ proj = self.in_proj(x).transpose(-1, -2)
399
+ # Split order matches Lfm3MoeShortConv("both") chunk(3): B first, C second, x third
400
+ B, C, x = proj.split([D, D, D], dim=-2)
401
+ conv_in = B * x
402
+
403
+ conv_weights = self.conv.weight.view(self.conv.weight.size(0), self.conv.weight.size(2))
404
+ if past_key_values is not None and past_key_values.has_previous_state(self.layer_idx):
405
+ conv_states = past_key_values.layers[self.layer_idx].conv_states
406
+ if isinstance(conv_states, dict):
407
+ conv_states = conv_states[0]
408
+ conv_out = causal_conv1d_update(
409
+ conv_in.squeeze(-1),
410
+ conv_states,
411
+ conv_weights,
412
+ self.conv.bias,
413
+ None,
414
+ )
415
+ conv_out = conv_out.unsqueeze(-1)
416
+ else:
417
+ if past_key_values is not None:
418
+ conv_state = nn.functional.pad(conv_in, (self.L_cache - conv_in.shape[-1], 0))
419
+ past_key_values.update_conv_state(conv_state, self.layer_idx)
420
+
421
+ # `seq_idx` (per-token document index, int32) resets the conv at packed
422
+ # document boundaries so it never bleeds across documents.
423
+ conv_out = causal_conv1d_fn(
424
+ conv_in, conv_weights, self.conv.bias, seq_idx=seq_idx, activation=None
425
+ )
426
+
427
+ y = C * conv_out
428
+ y = self.out_proj(y.transpose(-1, -2).contiguous())
429
+ return y
430
+
431
+ def _segmented_conv(self, conv_in: torch.Tensor, seq_idx: torch.Tensor) -> torch.Tensor:
432
+ """Causal depthwise conv that resets at packed document boundaries.
433
+
434
+ Pure-torch equivalent of `causal_conv1d_fn(..., seq_idx=...)`: out[t] only
435
+ sums conv_in[t-(L-1)..t] entries that share t's document id. `conv_in` is
436
+ (b, D, seqlen); `seq_idx` is (b, seqlen) int. Used only on the packed
437
+ path — the unpacked path keeps the plain `nn.Conv1d` call below.
438
+ """
439
+ L = self.L_cache
440
+ cin_p = nn.functional.pad(conv_in, (L - 1, 0)) # (b, D, L-1+seqlen)
441
+ windows = cin_p.unfold(-1, L, 1) # (b, D, seqlen, L)
442
+ # window element k of position t is conv_in[t-(L-1)+k]; current token is k=L-1.
443
+ sidx_p = nn.functional.pad(seq_idx, (L - 1, 0), value=-1) # (b, L-1+seqlen)
444
+ sidx_win = sidx_p.unfold(-1, L, 1) # (b, seqlen, L)
445
+ same_doc = (sidx_win == sidx_win[..., -1:]).unsqueeze(1) # (b, 1, seqlen, L)
446
+ windows = windows * same_doc.to(windows.dtype)
447
+ w = self.conv.weight.squeeze(1) # (D, L)
448
+ conv_out = torch.einsum("bdtk,dk->bdt", windows, w)
449
+ if self.bias:
450
+ conv_out = conv_out + self.conv.bias[None, :, None]
451
+ return conv_out
452
+
453
+ def slow_forward(
454
+ self,
455
+ x: torch.Tensor,
456
+ past_key_values: Optional[Cache] = None,
457
+ attention_mask: Optional[torch.Tensor] = None,
458
+ seq_idx: Optional[torch.Tensor] = None,
459
+ ):
460
+ seqlen = x.shape[1]
461
+
462
+ x = apply_mask_to_padding_states(x, attention_mask)
463
+ D = self.config.hidden_size
464
+ proj = self.in_proj(x).transpose(-1, -2)
465
+ B, C, x = proj.split([D, D, D], dim=-2)
466
+ conv_in = B * x
467
+
468
+ if past_key_values is not None and past_key_values.has_previous_state(self.layer_idx):
469
+ # update_conv_state rolls the cached window left and appends conv_in.
470
+ conv_state = past_key_values.update_conv_state(conv_in, self.layer_idx)[..., -self.L_cache :]
471
+ conv_out = torch.sum(conv_state.to(conv_in.device) * self.conv.weight[:, 0, :], dim=-1)
472
+ if self.bias:
473
+ conv_out += self.conv.bias
474
+
475
+ conv_out = conv_out.unsqueeze(-1)
476
+ else:
477
+ if past_key_values is not None:
478
+ conv_state = nn.functional.pad(conv_in, (self.L_cache - conv_in.shape[-1], 0))
479
+ past_key_values.update_conv_state(conv_state, self.layer_idx)
480
+
481
+ if seq_idx is not None:
482
+ conv_out = self._segmented_conv(conv_in, seq_idx)
483
+ else:
484
+ conv_out = self.conv(conv_in)[..., :seqlen]
485
+
486
+ y = C * conv_out
487
+ y = y.transpose(-1, -2).contiguous()
488
+ y = self.out_proj(y)
489
+ return y
490
+
491
+ def forward(
492
+ self,
493
+ hidden_states: torch.Tensor,
494
+ past_key_values: Optional[Cache] = None,
495
+ attention_mask: Optional[torch.Tensor] = None,
496
+ seq_idx: Optional[torch.Tensor] = None,
497
+ ):
498
+ # causal_conv1d's stock build only supports conv widths 2-4 and raises
499
+ # otherwise; build it for wider kernels (e.g. L_cache=7) or omit it for
500
+ # the pure-torch slow path.
501
+ if (
502
+ is_fast_path_available
503
+ and "cuda" in hidden_states.device.type
504
+ and not is_torchdynamo_compiling()
505
+ ):
506
+ return self.cuda_kernels_forward(
507
+ hidden_states, past_key_values, attention_mask, seq_idx
508
+ )
509
+ return self.slow_forward(
510
+ hidden_states, past_key_values, attention_mask, seq_idx
511
+ )
512
+
513
+
514
+ class Lfm3DecoderLayer(GradientCheckpointingLayer):
515
+ def __init__(self, config: Lfm3Config, layer_idx: int):
516
+ super().__init__()
517
+ self.layer_type = config.layer_types[layer_idx]
518
+ self.is_attention_layer = self.layer_type in ("full_attention", "sliding_attention")
519
+ self.use_sandwich_norm = getattr(config, "use_sandwich_norm", False)
520
+
521
+ if self.layer_type == "full_attention":
522
+ self.self_attn = Lfm3Attention(config, layer_idx)
523
+ elif self.layer_type == "sliding_attention":
524
+ self.self_attn = Lfm3Attention(config, layer_idx, sliding_window=config.sliding_window)
525
+ elif self.layer_type == "conv":
526
+ self.conv = Lfm3ShortConv(config, layer_idx)
527
+ else:
528
+ raise ValueError(
529
+ f"Unsupported layer_type {self.layer_type!r} at layer {layer_idx}. "
530
+ "Lfm3 supports only {full_attention, sliding_attention, conv}."
531
+ )
532
+ self.feed_forward = Lfm3MLP(config, intermediate_size=config.intermediate_size)
533
+ self.operator_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
534
+ self.ffn_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
535
+ if self.use_sandwich_norm:
536
+ self.post_operator_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
537
+ self.post_ffn_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
538
+
539
+ def forward(
540
+ self,
541
+ hidden_states: torch.Tensor,
542
+ position_embeddings: tuple[torch.Tensor, torch.Tensor],
543
+ attention_mask: Optional[torch.Tensor] = None,
544
+ position_ids: Optional[torch.LongTensor] = None,
545
+ past_key_values: Optional[Cache] = None,
546
+ cache_position: Optional[torch.LongTensor] = None,
547
+ seq_idx: Optional[torch.Tensor] = None,
548
+ **kwargs,
549
+ ) -> torch.Tensor:
550
+ residual = hidden_states
551
+ if self.is_attention_layer:
552
+ hidden_states, _ = self.self_attn(
553
+ hidden_states=self.operator_norm(hidden_states),
554
+ position_embeddings=position_embeddings,
555
+ attention_mask=attention_mask,
556
+ position_ids=position_ids,
557
+ past_key_values=past_key_values,
558
+ **kwargs,
559
+ )
560
+ else:
561
+ hidden_states = self.conv(
562
+ hidden_states=self.operator_norm(hidden_states),
563
+ past_key_values=past_key_values,
564
+ attention_mask=attention_mask,
565
+ seq_idx=seq_idx,
566
+ )
567
+ if self.use_sandwich_norm:
568
+ hidden_states = self.post_operator_norm(hidden_states)
569
+ hidden_states = hidden_states + residual
570
+ ffn_out = self.feed_forward(self.ffn_norm(hidden_states))
571
+ if self.use_sandwich_norm:
572
+ ffn_out = self.post_ffn_norm(ffn_out)
573
+ hidden_states = hidden_states + ffn_out
574
+
575
+ return hidden_states
576
+
577
+
578
+ @auto_docstring
579
+ class Lfm3PreTrainedModel(PreTrainedModel):
580
+ config: Lfm3Config
581
+ base_model_prefix = "model"
582
+ supports_gradient_checkpointing = True
583
+ _no_split_modules = ["Lfm3DecoderLayer"]
584
+ _skip_keys_device_placement = ["past_key_values"]
585
+ _supports_flash_attn = True
586
+ _supports_sdpa = True
587
+ _supports_flex_attn = True
588
+ _can_compile_fullgraph = False
589
+ _supports_attention_backend = True
590
+ _can_record_outputs = {
591
+ "hidden_states": Lfm3DecoderLayer,
592
+ "attentions": Lfm3Attention,
593
+ }
594
+
595
+ def _init_weights(self, module):
596
+ # Re-compute RotaryEmbedding inv_freq buffers. Required because
597
+ # `from_pretrained` constructs the model on a meta device and
598
+ # materializes persistent=False buffers as uninitialized memory after
599
+ # the fact. We can't rely on values registered during meta-device
600
+ # __init__ — they aren't preserved through materialization. Re-run
601
+ # `compute_default_rope_parameters` for each attention layer type so
602
+ # the per-layer-type inv_freq buffers (`full_attention_inv_freq`,
603
+ # `sliding_attention_inv_freq`) get the correct values for
604
+ # their configured theta / partial_rotary_factor, instead of HF's
605
+ # default branch which assumes a single global table.
606
+ if isinstance(module, Lfm3RotaryEmbedding):
607
+ for layer_type in module.attention_layer_types:
608
+ if layer_type not in module.rope_type:
609
+ continue
610
+ inv_freq, attention_scaling = module.compute_default_rope_parameters(
611
+ module.config,
612
+ device=getattr(module, f"{layer_type}_inv_freq").device,
613
+ layer_type=layer_type,
614
+ )
615
+ with torch.no_grad():
616
+ getattr(module, f"{layer_type}_inv_freq").copy_(inv_freq)
617
+ getattr(module, f"{layer_type}_original_inv_freq").copy_(inv_freq)
618
+ setattr(module, f"{layer_type}_attention_scaling", attention_scaling)
619
+ return
620
+ super()._init_weights(module)
621
+
622
+
623
+ @auto_docstring
624
+ class Lfm3Model(Lfm3PreTrainedModel):
625
+ def __init__(self, config: Lfm3Config):
626
+ super().__init__(config)
627
+ self.padding_idx = config.pad_token_id
628
+ self.vocab_size = config.vocab_size
629
+
630
+ self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size, self.padding_idx)
631
+ self.layers = nn.ModuleList(
632
+ [Lfm3DecoderLayer(config, layer_idx) for layer_idx in range(config.num_hidden_layers)]
633
+ )
634
+ self.gradient_checkpointing = False
635
+ # Single Gemma3-style rotary module. Per-attention-layer-type inv_freq
636
+ # buffers (`full_attention_inv_freq`, `sliding_attention_inv_freq`)
637
+ # are registered inside the module based on `config.rope_parameters`.
638
+ # `forward` is dispatched per layer below via `layer_type=...`.
639
+ self.rotary_emb = Lfm3RotaryEmbedding(config)
640
+ self.embedding_norm = Lfm3RMSNorm(config.hidden_size, eps=config.norm_eps)
641
+
642
+ # Initialize weights and apply final processing
643
+ self.post_init()
644
+
645
+ @check_model_inputs
646
+ @auto_docstring
647
+ def forward(
648
+ self,
649
+ input_ids: Optional[torch.LongTensor] = None,
650
+ attention_mask: Optional[torch.Tensor] = None,
651
+ position_ids: Optional[torch.LongTensor] = None,
652
+ past_key_values: Optional[Cache] = None,
653
+ inputs_embeds: Optional[torch.FloatTensor] = None,
654
+ use_cache: Optional[bool] = None,
655
+ cache_position: Optional[torch.LongTensor] = None,
656
+ **kwargs: Unpack[TransformersKwargs],
657
+ ) -> BaseModelOutputWithPast:
658
+ r"""
659
+ cache_position (`torch.LongTensor` of shape `(sequence_length)`, *optional*):
660
+ Indices depicting the position of the input sequence tokens in the sequence. Used to update the
661
+ cache in the correct position and to infer the complete sequence length.
662
+ """
663
+ if (input_ids is None) ^ (inputs_embeds is not None):
664
+ raise ValueError("You must specify exactly one of input_ids or inputs_embeds")
665
+
666
+ if inputs_embeds is None:
667
+ inputs_embeds = self.embed_tokens(input_ids)
668
+
669
+ if use_cache and past_key_values is None:
670
+ # DynamicCache(config) builds the right per-layer cache from
671
+ # config.layer_types: DynamicLayer (full_attention),
672
+ # DynamicSlidingWindowLayer (sliding_attention, auto-bounded), and
673
+ # LinearAttentionLayer (conv, conv-state buffer).
674
+ past_key_values = DynamicCache(config=self.config)
675
+
676
+ if cache_position is None:
677
+ past_seen_tokens = (
678
+ past_key_values.get_seq_length() if past_key_values is not None else 0
679
+ )
680
+ cache_position = torch.arange(
681
+ past_seen_tokens,
682
+ past_seen_tokens + inputs_embeds.shape[1],
683
+ device=inputs_embeds.device,
684
+ )
685
+
686
+ if position_ids is None:
687
+ position_ids = cache_position.unsqueeze(0)
688
+
689
+ # Packed-sequence document boundaries for the conv layers: when several
690
+ # documents are concatenated into one row, `position_ids` restarts at 0
691
+ # per document, so a cumulative count of those resets gives a per-token
692
+ # document id. Conv layers use it to avoid bleeding across documents.
693
+ # Only meaningful during prefill (multi-token); a single decode token has
694
+ # nothing to segment.
695
+ seq_idx = None
696
+ if inputs_embeds.shape[1] > 1:
697
+ doc_id = (position_ids == 0).cumsum(-1) - 1
698
+ if doc_id[..., -1].max() > 0: # >1 document present
699
+ seq_idx = doc_id.to(torch.int32)
700
+
701
+ # Build one causal mask per attention layer type present in the model.
702
+ # SWA layers get a sliding-window causal mask; full-attention layers
703
+ # get a plain causal mask. Conv layers receive the RAW 2D padding mask
704
+ # (matching upstream `Lfm2Model.forward`'s `linear_attention` route)
705
+ # so `Lfm3ShortConv.apply_mask_to_padding_states` can zero pad
706
+ # positions during prefill. Conv during decode (single token) doesn't
707
+ # need a mask — None is fine.
708
+ mask_kwargs = {
709
+ "config": self.config,
710
+ "inputs_embeds": inputs_embeds,
711
+ "attention_mask": attention_mask,
712
+ "past_key_values": past_key_values,
713
+ "position_ids": position_ids,
714
+ }
715
+ causal_masks_by_type: dict[str, Optional[torch.Tensor]] = {}
716
+ attention_types_in_model = {
717
+ lt for lt in self.config.layer_types if lt in self.config._attention_layer_types
718
+ }
719
+ if "full_attention" in attention_types_in_model:
720
+ causal_masks_by_type["full_attention"] = create_causal_mask(**mask_kwargs)
721
+ if "sliding_attention" in attention_types_in_model:
722
+ causal_masks_by_type["sliding_attention"] = create_sliding_window_causal_mask(
723
+ **mask_kwargs
724
+ )
725
+ # Conv layers want the 2D padding mask only during prefill. For
726
+ # single-token decode (`shape[1] == 1`) there's nothing to pad-zero.
727
+ conv_mask = attention_mask if inputs_embeds.shape[1] != 1 else None
728
+
729
+ hidden_states = inputs_embeds
730
+ # Pre-compute one (cos, sin) per attention layer type. Conv-only models
731
+ # leave this dict empty; the per-layer dispatch below only reads it for
732
+ # attention layers.
733
+ position_embeddings_by_type = {
734
+ layer_type: self.rotary_emb(hidden_states, position_ids, layer_type=layer_type)
735
+ for layer_type in self.rotary_emb.rope_type
736
+ }
737
+
738
+ for decoder_layer in self.layers[: self.config.num_hidden_layers]:
739
+ if decoder_layer.is_attention_layer:
740
+ layer_mask = causal_masks_by_type.get(decoder_layer.layer_type)
741
+ else:
742
+ layer_mask = conv_mask
743
+ layer_pos_embeddings = position_embeddings_by_type.get(decoder_layer.layer_type)
744
+ hidden_states = decoder_layer(
745
+ hidden_states,
746
+ attention_mask=layer_mask,
747
+ position_ids=position_ids,
748
+ past_key_values=past_key_values,
749
+ cache_position=cache_position,
750
+ position_embeddings=layer_pos_embeddings,
751
+ seq_idx=seq_idx,
752
+ **kwargs,
753
+ )
754
+
755
+ hidden_states = self.embedding_norm(hidden_states)
756
+
757
+ return BaseModelOutputWithPast(
758
+ last_hidden_state=hidden_states,
759
+ past_key_values=past_key_values,
760
+ )
761
+
762
+
763
+ @auto_docstring
764
+ class Lfm3ForCausalLM(Lfm3PreTrainedModel, GenerationMixin):
765
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
766
+ _tp_plan = {"lm_head": "colwise_rep"}
767
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
768
+
769
+ def __init__(self, config):
770
+ super().__init__(config)
771
+ self.model = Lfm3Model(config)
772
+ self.vocab_size = config.vocab_size
773
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
774
+
775
+ # Initialize weights and apply final processing
776
+ self.post_init()
777
+
778
+ @can_return_tuple
779
+ @auto_docstring
780
+ def forward(
781
+ self,
782
+ input_ids: Optional[torch.LongTensor] = None,
783
+ attention_mask: Optional[torch.Tensor] = None,
784
+ position_ids: Optional[torch.LongTensor] = None,
785
+ past_key_values: Optional[Cache] = None,
786
+ inputs_embeds: Optional[torch.FloatTensor] = None,
787
+ labels: Optional[torch.LongTensor] = None,
788
+ use_cache: Optional[bool] = None,
789
+ cache_position: Optional[torch.LongTensor] = None,
790
+ logits_to_keep: Union[int, torch.Tensor] = 0,
791
+ **kwargs: Unpack[TransformersKwargs],
792
+ ) -> CausalLMOutputWithPast:
793
+ r"""
794
+ cache_position (`torch.LongTensor` of shape `(sequence_length)`, *optional*):
795
+ Indices depicting the position of the input sequence tokens in the sequence. Used to update the
796
+ cache in the correct position and to infer the complete sequence length.
797
+ """
798
+ outputs: BaseModelOutputWithPast = self.model(
799
+ input_ids=input_ids,
800
+ attention_mask=attention_mask,
801
+ position_ids=position_ids,
802
+ past_key_values=past_key_values,
803
+ inputs_embeds=inputs_embeds,
804
+ use_cache=use_cache,
805
+ cache_position=cache_position,
806
+ **kwargs,
807
+ )
808
+
809
+ hidden_states = outputs.last_hidden_state
810
+ slice_indices = (
811
+ slice(-logits_to_keep, None) if isinstance(logits_to_keep, int) else logits_to_keep
812
+ )
813
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
814
+
815
+ loss = None
816
+ if labels is not None:
817
+ loss = self.loss_function(
818
+ logits=logits, labels=labels, vocab_size=self.config.vocab_size, **kwargs
819
+ )
820
+
821
+ return CausalLMOutputWithPast(
822
+ loss=loss,
823
+ logits=logits,
824
+ past_key_values=outputs.past_key_values,
825
+ hidden_states=outputs.hidden_states,
826
+ attentions=outputs.attentions,
827
+ )
828
+
829
+
830
+ __all__ = ["Lfm3ForCausalLM", "Lfm3Model", "Lfm3PreTrainedModel"]
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a39eed58008cbcbc09e5b629b81338a654269c64cd13348aaa6090f4db75c7e7
3
+ size 17908258
tokenizer_config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "bos_token": "<|startoftext|>",
4
+ "eos_token": "<|im_end|>",
5
+ "extra_special_tokens": [],
6
+ "image_token": "<|image|>",
7
+ "is_local": true,
8
+ "legacy": false,
9
+ "local_files_only": false,
10
+ "model_max_length": 1000000000000000019884624838656,
11
+ "model_specific_special_tokens": {
12
+ "image_token": "<|image|>",
13
+ "video_token": "<|video|>",
14
+ "vision_end_token": "<|vision_end|>",
15
+ "vision_start_token": "<|vision_start|>"
16
+ },
17
+ "pad_token": "<|pad|>",
18
+ "tokenizer_class": "TokenizersBackend",
19
+ "video_token": "<|video|>",
20
+ "vision_end_token": "<|vision_end|>",
21
+ "vision_start_token": "<|vision_start|>"
22
+ }