relogu Flower Release Bot commited on
Commit
edbbb7f
·
0 Parent(s):

Initial commit

Browse files

Co-authored-by: Flower Release Bot <Flower Release Bot@users.noreply.huggingface.co>

.gitattributes ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
2
+ *.bin filter=lfs diff=lfs merge=lfs -text
3
+ *.pt filter=lfs diff=lfs merge=lfs -text
4
+ *.pth filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.gguf filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ language:
3
+ - en
4
+ library_name: transformers
5
+ pipeline_tag: text-generation
6
+ license: apache-2.0
7
+ tags:
8
+ - lizzy-7b
9
+ - flwrlabs
10
+ - british-english
11
+ - text-generation
12
+ model_name: Lizzy 7B
13
+ ---
14
+
15
+ # Lizzy 7B
16
+
17
+ <img class="dark:hidden" src="./header-light.svg" alt="Lizzy 7B header figure (light theme)" />
18
+ <img class="hidden dark:block" src="./header-dark.svg" alt="Lizzy 7B header figure (dark theme)" />
19
+
20
+ ## Model Name And Summary
21
+
22
+ Lizzy 7B is an open-weight Flower Labs assistant model in the Lizzy family.
23
+
24
+ ## Architecture And Configuration
25
+
26
+ Lizzy 7B is a 7B-class decoder-only transformer with long-context support, sliding/local attention behaviour, custom chat/control tokens, and deployment-specific serving configurations.
27
+
28
+ Representative configuration points:
29
+
30
+ - 7B-class parameter scale with a 32-layer stack;
31
+ - long-context configuration up to 65k tokens with runtime caps adjusted by deployment profile;
32
+ - 32 attention heads with long-context/sliding-attention behaviour;
33
+ - custom tokenizer and chat markers for instruction-style prompting;
34
+ - deployment variants may include quantised revisions, runtime patches, and serving-time configuration changes.
35
+
36
+ ## Training Approach
37
+
38
+ Lizzy 7B follows a multi-stage training approach that combines:
39
+
40
+ - pre-training on large-scale public text, document, code, math, and encyclopedic corpora;
41
+ - supervised fine-tuning on instruction-following, dialogue, reasoning, and tool-use examples;
42
+ - direct preference optimisation on preference pairs for helpfulness, style, and answer quality;
43
+ - reinforcement learning with verifiable rewards for targeted behavioural refinement.
44
+
45
+ Across these stages, training data has been mixed across:
46
+
47
+ - broad public text and knowledge sources;
48
+ - synthetic instruction and preference data;
49
+ - private synthetic data used to favour British behaviour and knowledge;
50
+ - UK-specific examples and preference signals used to strengthen local knowledge and style.
51
+
52
+ ## Evaluation Against European Baselines
53
+
54
+ Britishness comparisons against the European baselines present in the latest local artifact set:
55
+
56
+ | Benchmark | Lizzy 7B | EuroLLM 9B | Apertus 8B |
57
+ | --- | ---: | ---: | ---: |
58
+ | Britishness MCQ | 71.0 | <u>77.6</u> | **80.8** |
59
+ | Britishness CoT | **80.1** | <u>72.1</u> | 31.7 |
60
+ | Britishness Domains | **89.9** | <u>69.0</u> | 32.6 |
61
+
62
+ Broader benchmark comparisons against the same European baselines:
63
+
64
+ | Benchmark | Lizzy 7B | EuroLLM 9B | Apertus 8B |
65
+ | --- | ---: | ---: | ---: |
66
+ | MATH | **77.9** | <u>31.3</u> | 22.4 |
67
+ | OMEGA | **29.0** | 4.7 | <u>5.0</u> |
68
+ | BigBenchHard | **69.0** | 38.9 | <u>42.4</u> |
69
+ | AGI Eval English | **65.6** | 50.2 | <u>50.4</u> |
70
+ | MMLU | **67.9** | 57.4 | <u>63.4</u> |
71
+ | GPQA | **34.6** | 26.8 | <u>28.1</u> |
72
+ | HumanEvalPlus | **70.2** | 28.2 | <u>33.4</u> |
73
+ | MBPP+ | **52.5** | 41.7 | <u>42.3</u> |
74
+ | LiveCodeBench v3 | **39.1** | 6.3 | <u>8.5</u> |
75
+ | IFEval | <u>63.8</u> | 55.8 | **65.1** |
76
+ | AIME | **35.8** | 0.2 | <u>0.6</u> |
77
+ | GSM8K | **91.8** | <u>64.7</u> | 64.7 |
78
+ | IFBench | **22.7** | <u>18.0</u> | 15.3 |
79
+ | POPQA | 22.2 | **25.6** | <u>25.1</u> |
80
+ | ZebraLogic | **12.4** | 4.4 | <u>5.9</u> |
81
+
82
+ Summary:
83
+
84
+ - Lizzy 7B trails the European baselines on Britishness MCQ (a private Flower Labs benchmark) recall-style probing.
85
+ - Lizzy 7B leads the reported European baselines on Britishness CoT and Britishness domain reasoning (private Flower Labs benchmarks) where comparable metrics are available.
86
+ - Lizzy 7B also leads the latest local European baseline set on most knowledge, reasoning, math, and coding rows represented in the table above.
87
+
88
+ ## Intended Uses And Limitations
89
+
90
+ Intended uses:
91
+
92
+ - UK-oriented assistant experiences;
93
+ - general reasoning and coding assistance;
94
+ - managed deployment through private Hugging Face or vLLM serving stacks.
95
+
96
+ ## Safety And Bias Considerations
97
+
98
+ The latest safety-evaluation reports the following task-level primary scores:
99
+
100
+ | Safety benchmark | Metric | Score |
101
+ | --- | --- | ---: |
102
+ | Overall safety average | `overall_safety_average` | 66.7% |
103
+ | WildGuardTest | `inverted_micro_harm_lower` | 91.9% |
104
+ | HarmBench | `inverted_micro_asr_lower` | 57.5% |
105
+ | ToxiGen (tiny) | `safe_overall` | 90.2% |
106
+ | XSTest | `overall_accuracy` | 85.6% |
107
+ | StrongReject (logprobs) | `inverted_asr` | 78.8% |
108
+ | BBQ | `accuracy` | 66.5% |
109
+ | WMDP | `inverted_accuracy` | 47.5% |
110
+
111
+ Lizzy 7B can still produce incorrect, outdated, or over-confident responses and should be used with human oversight for higher-risk workflows. UK-specific tuning improves local style and cultural alignment but can also bias tone and assumptions toward UK conventions; downstream moderation and policy controls remain required.
112
+
113
+ ## License And Citation
114
+
115
+ - Model licence: Apache-2.0
116
+ - Public and synthetic training sources include open-licensed public data plus private synthetic and UK-specific data that are not redistributed
117
+ - Citation and legal text should still be confirmed by owner review before any external publication.
118
+
119
+ ## Python Example (Transformers)
120
+
121
+ ```python
122
+ from transformers import AutoModelForCausalLM, AutoTokenizer
123
+ import torch
124
+
125
+ repo_id = "flwrlabs/Lizzy-7B"
126
+
127
+ tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)
128
+ model = AutoModelForCausalLM.from_pretrained(
129
+ repo_id,
130
+ trust_remote_code=True,
131
+ torch_dtype=torch.bfloat16,
132
+ device_map="auto",
133
+ )
134
+
135
+ messages = [
136
+ {"role": "system", "content": "You are Lizzy 7B."},
137
+ {"role": "user", "content": "Summarise why queue etiquette matters in the UK."},
138
+ ]
139
+ prompt = tokenizer.apply_chat_template(
140
+ messages,
141
+ tokenize=False,
142
+ add_generation_prompt=True,
143
+ )
144
+ inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
145
+
146
+ output_ids = model.generate(
147
+ **inputs,
148
+ temperature=0.2,
149
+ top_p=0.9,
150
+ )
151
+ response = tokenizer.decode(output_ids[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True)
152
+ print(response)
153
+ ```
154
+
155
+ ## Multi-GPU vLLM Tensor Parallel Patch
156
+
157
+ For reproducible multi-GPU vLLM support with Lizzy-family checkpoints, this deliverable bundles:
158
+
159
+ - bundled draft artifact: `vllm_patches/transformers_lizzy_tp.py`
160
+
161
+ Apply this patch when all of the following are true:
162
+
163
+ - runtime uses vLLM via the generic Transformers backend (`model_type=vllm`)
164
+ - tensor parallelism is enabled (`tensor_parallel_size > 1`)
165
+ - checkpoint is Lizzy-family (including RLVR variants)
166
+ - runtime is not guaranteed to include an equivalent upstream fix
167
+
168
+ You can skip patch bundling only for strict HF-only runs or single-rank vLLM (`TP=1`).
169
+
170
+ Why this is included:
171
+
172
+ - it mitigates known Lizzy TP failure modes in generic vLLM Transformers loading
173
+ - it fixes rank-local head partitioning and `q_norm`/`k_norm` slicing behaviour
174
+ - it prevents the known tensor-shape crash class seen without this patch
chat_template.jinja ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \
2
+ {# ───── defaults ───── #}
3
+ {%- if enable_thinking is not defined -%}
4
+ {%- set enable_thinking = true -%}
5
+ {%- endif -%}
6
+ {%- set system_message = "" -%}
7
+ {%- set custom_instructions = "" -%}
8
+ {%- set identity_preamble = "You are Lizzy, a helpful British AI assistant built by Flower Labs. When asked about your identity, name, developer, or origin, answer consistently: your name is Lizzy, you were built by Flower Labs, and you are not ChatGPT, DeepSeek, Claude, Gemini, or any other assistant. Do not misattribute your creator or model family." -%}
9
+ {%- set default_think_instructions = "You are a helpful British function-calling AI assistant. You are a British persona and your date cutoff is November 2024, and your model weights are available at https://huggingface.co/flwrlabs. Your role as an assistant involves thoroughly exploring questions through a systematic thinking process before providing the final precise and accurate solutions. This requires engaging in a comprehensive cycle of analysis, summarizing, exploration, reassessment, reflection, backtracking, and iteration to develop well-considered thinking process. Please structure your response into two main sections: Thought and Solution using the specified format: <tool_call> Thought section </tool_call> Solution section. In the Thought section, detail your reasoning process in steps. Each step should include detailed considerations such as analysing questions, summarizing relevant findings, brainstorming new ideas, verifying the accuracy of the current steps, refining any errors, and revisiting previous steps. In the Solution section, based on various attempts, explorations, and reflections from the Thought section, systematically present the final solution that you deem correct. The Solution section should be logical, accurate, and concise and detail necessary steps needed to reach the conclusion." -%}
10
+ {%- set default_no_think_instructions = "You are a helpful British function-calling AI assistant. You are a British persona and your date cutoff is November 2024, and your model weights are available at https://huggingface.co/flwrlabs." -%}
11
+
12
+ {# ───── reasoning mode ───── #}
13
+ {%- if enable_thinking -%}
14
+ {%- set reasoning_mode = "/think" -%}
15
+ {%- else -%}
16
+ {%- set reasoning_mode = "/no_think" -%}
17
+ {%- endif -%}
18
+
19
+ {# ───── header (system message) ───── #}
20
+ {{- "<|im_start|>system\n" -}}
21
+
22
+ {%- if messages[0].role == "system" -%}
23
+ {%- set system_message = messages[0].content -%}
24
+ {%- if "/no_think" in system_message -%}
25
+ {%- set reasoning_mode = "/no_think" -%}
26
+ {%- elif "/think" in system_message -%}
27
+ {%- set reasoning_mode = "/think" -%}
28
+ {%- endif -%}
29
+ {%- set custom_instructions = system_message.replace("/no_think", "").replace("/think", "").rstrip() -%}
30
+ {%- endif -%}
31
+
32
+ {%- if "/system_override" in system_message -%}
33
+ {{- identity_preamble + "\n\n" -}}
34
+ {{- custom_instructions.replace("/system_override", "").rstrip() -}}
35
+ {{- "<|im_end|>\n" -}}
36
+ {%- else -%}
37
+ {{- "## Metadata\n\n" -}}
38
+ {{- "Knowledge Cutoff Date: June 2025\n" -}}
39
+ {%- set today = strftime_now("%d %B %Y") -%}
40
+ {{- "Today Date: " ~ today ~ "\n" -}}
41
+ {{- "Reasoning Mode: " + reasoning_mode + "\n\n" -}}
42
+
43
+ {{- "## Identity\n\n" -}}
44
+ {{- identity_preamble + "\n\n" -}}
45
+
46
+ {{- "## Custom Instructions\n\n" -}}
47
+ {%- if custom_instructions -%}
48
+ {{- custom_instructions + "\n\n" -}}
49
+ {%- elif reasoning_mode == "/think" -%}
50
+ {{- default_think_instructions + "\n\n" -}}
51
+ {%- else -%}
52
+ {{- default_no_think_instructions + "\n\n" -}}
53
+ {%- endif -%}
54
+
55
+ {%- if xml_tools or python_tools or tools -%}
56
+ {{- "### Tools\n\n" -}}
57
+ {%- if xml_tools or tools -%}
58
+ {%- if tools -%}
59
+ {%- set xml_tools = tools -%}
60
+ {%- endif -%}
61
+ {%- set ns = namespace(xml_tool_string="You may call one or more functions to assist with the user query.\nYou are provided with function signatures within <tools></tools> XML tags:\n\n<tools>\n") -%}
62
+ {%- for tool in xml_tools[:] -%} {# The slicing makes sure that xml_tools is a list #}
63
+ {%- set ns.xml_tool_string = ns.xml_tool_string ~ (tool | string) ~ "\n" -%}
64
+ {%- endfor -%}
65
+ {%- set xml_tool_string = ns.xml_tool_string + "</tools>\n\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\n<tool_call>\n{\"name\": <function-name>, \"arguments\": <args-json-object>}\n</tool_call>" -%}
66
+ {{- xml_tool_string -}}
67
+ {%- endif -%}
68
+ {%- if python_tools -%}
69
+ {%- set ns = namespace(python_tool_string="When you send a message containing Python code between '<code>' and '</code>' tags, it will be executed in a stateful Jupyter notebook environment, and you will then be given the output to continued reasoning in an agentic loop.\n\nYou can use the following tools in your python code like regular functions:\n<tools>\n") -%}
70
+ {%- for tool in python_tools[:] -%} {# The slicing makes sure that python_tools is a list #}
71
+ {%- set ns.python_tool_string = ns.python_tool_string ~ (tool | string) ~ "\n" -%}
72
+ {%- endfor -%}
73
+ {%- set python_tool_string = ns.python_tool_string + "</tools>\n\nThe state persists between code executions: so variables that you define in one step are still available thereafter." -%}
74
+ {{- python_tool_string -}}
75
+ {%- endif -%}
76
+ {{- "\n\n" -}}
77
+ {{- "<|im_end|>\n" -}}
78
+ {%- endif -%}
79
+ {%- endif -%}
80
+ {# ───── main loop ───── #}
81
+ {%- for message in messages -%}
82
+ {%- set content = message.content if message.content is string else "" -%}
83
+ {%- if message.role == "user" -%}
84
+ {{ "<|im_start|>" + message.role + "\n" + content + "<|im_end|>\n" }}
85
+ {%- elif message.role == "assistant" -%}
86
+ {% generation %}
87
+ {%- if reasoning_mode == "/think" -%}
88
+ {{ "<|im_start|>assistant\n" + content.lstrip("\n") + "<|im_end|>\n" }}
89
+ {%- else -%}
90
+ {{ "<|im_start|>assistant\n" + "<think>\n\n</think>\n" + content.lstrip("\n") + "<|im_end|>\n" }}
91
+ {%- endif -%}
92
+ {% endgeneration %}
93
+ {%- elif message.role == "tool" -%}
94
+ {{ "<|im_start|>" + "user\n" + content + "<|im_end|>\n" }}
95
+ {%- endif -%}
96
+ {%- endfor -%}
97
+ {# ───── generation prompt ───── #}
98
+ {%- if add_generation_prompt -%}
99
+ {%- if reasoning_mode == "/think" -%}
100
+ {{ "<|im_start|>assistant\n" }}
101
+ {%- else -%}
102
+ {{ "<|im_start|>assistant\n" + "<think>\n\n</think>\n" }}
103
+ {%- endif -%}
104
+ {%- endif -%}
config.json ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab_size": 100278,
3
+ "hidden_size": 4096,
4
+ "intermediate_size": 11008,
5
+ "num_hidden_layers": 32,
6
+ "num_attention_heads": 32,
7
+ "num_key_value_heads": 32,
8
+ "max_position_embeddings": 32768,
9
+ "head_dim": 128,
10
+ "hidden_act": "silu",
11
+ "norm_type": "rmsnorm",
12
+ "norm_eps": 1e-06,
13
+ "norm_has_bias": false,
14
+ "use_pre_attn_norm": false,
15
+ "use_pre_mlp_norm": false,
16
+ "use_post_attn_norm": true,
17
+ "use_post_mlp_norm": true,
18
+ "mlp_type": "gated",
19
+ "attention_bias": false,
20
+ "mlp_bias": false,
21
+ "position_embedding_type": "rope",
22
+ "rope_theta": 500000,
23
+ "rope_scaling": {
24
+ "attention_factor": 1.2079441541679836,
25
+ "beta_fast": 32,
26
+ "beta_slow": 1,
27
+ "factor": 8.0,
28
+ "original_max_position_embeddings": 8192,
29
+ "rope_type": "yarn",
30
+ "rope_theta": 500000
31
+ },
32
+ "rope_layer_flags": [
33
+ true,
34
+ true,
35
+ true,
36
+ true,
37
+ true,
38
+ true,
39
+ true,
40
+ true,
41
+ true,
42
+ true,
43
+ true,
44
+ true,
45
+ true,
46
+ true,
47
+ true,
48
+ true,
49
+ true,
50
+ true,
51
+ true,
52
+ true,
53
+ true,
54
+ true,
55
+ true,
56
+ true,
57
+ true,
58
+ true,
59
+ true,
60
+ true,
61
+ true,
62
+ true,
63
+ true,
64
+ true
65
+ ],
66
+ "no_rope_layer_interval": null,
67
+ "rope_type_overrides": {},
68
+ "layer_types": [
69
+ "sliding_attention",
70
+ "sliding_attention",
71
+ "sliding_attention",
72
+ "full_attention",
73
+ "sliding_attention",
74
+ "sliding_attention",
75
+ "sliding_attention",
76
+ "full_attention",
77
+ "sliding_attention",
78
+ "sliding_attention",
79
+ "sliding_attention",
80
+ "full_attention",
81
+ "sliding_attention",
82
+ "sliding_attention",
83
+ "sliding_attention",
84
+ "full_attention",
85
+ "sliding_attention",
86
+ "sliding_attention",
87
+ "sliding_attention",
88
+ "full_attention",
89
+ "sliding_attention",
90
+ "sliding_attention",
91
+ "sliding_attention",
92
+ "full_attention",
93
+ "sliding_attention",
94
+ "sliding_attention",
95
+ "sliding_attention",
96
+ "full_attention",
97
+ "sliding_attention",
98
+ "sliding_attention",
99
+ "sliding_attention",
100
+ "full_attention"
101
+ ],
102
+ "layer_layouts": [
103
+ "decoder_postnorm",
104
+ "decoder_postnorm",
105
+ "decoder_postnorm",
106
+ "decoder_postnorm",
107
+ "decoder_postnorm",
108
+ "decoder_postnorm",
109
+ "decoder_postnorm",
110
+ "decoder_postnorm",
111
+ "decoder_postnorm",
112
+ "decoder_postnorm",
113
+ "decoder_postnorm",
114
+ "decoder_postnorm",
115
+ "decoder_postnorm",
116
+ "decoder_postnorm",
117
+ "decoder_postnorm",
118
+ "decoder_postnorm",
119
+ "decoder_postnorm",
120
+ "decoder_postnorm",
121
+ "decoder_postnorm",
122
+ "decoder_postnorm",
123
+ "decoder_postnorm",
124
+ "decoder_postnorm",
125
+ "decoder_postnorm",
126
+ "decoder_postnorm",
127
+ "decoder_postnorm",
128
+ "decoder_postnorm",
129
+ "decoder_postnorm",
130
+ "decoder_postnorm",
131
+ "decoder_postnorm",
132
+ "decoder_postnorm",
133
+ "decoder_postnorm",
134
+ "decoder_postnorm"
135
+ ],
136
+ "sliding_window": 4096,
137
+ "linear_num_key_heads": null,
138
+ "linear_num_value_heads": null,
139
+ "linear_key_head_dim": null,
140
+ "linear_value_head_dim": null,
141
+ "linear_a_log_min": null,
142
+ "linear_a_log_max": null,
143
+ "linear_dt_min": null,
144
+ "linear_dt_max": null,
145
+ "linear_dt_init_floor": null,
146
+ "linear_conv_kernel_dim": null,
147
+ "linear_allow_neg_eigval": null,
148
+ "use_qk_norm": true,
149
+ "qk_norm_type": "rmsnorm",
150
+ "attention_dropout": 0.0,
151
+ "resid_dropout": 0.0,
152
+ "embd_dropout": 0.0,
153
+ "initializer_range": 0.02,
154
+ "bos_token_id": 100257,
155
+ "eos_token_id": 100257,
156
+ "pad_token_id": 100277,
157
+ "use_cache": true,
158
+ "tie_word_embeddings": false,
159
+ "model_type": "lizzy",
160
+ "architectures": [
161
+ "LizzyForCausalLM"
162
+ ],
163
+ "auto_map": {
164
+ "AutoConfig": "configuration_lizzy.LizzyConfig",
165
+ "AutoModel": "modeling_lizzy.LizzyModel",
166
+ "AutoModelForCausalLM": "modeling_lizzy.LizzyForCausalLM",
167
+ "AutoTokenizer": "tokenization_lizzy.LizzyTokenizerFast"
168
+ },
169
+ "tokenizer_class": "LizzyTokenizerFast",
170
+ "transformers_version": "5.4.0"
171
+ }
configuration_lizzy.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from transformers import PretrainedConfig
6
+
7
+
8
+ class LizzyConfig(PretrainedConfig):
9
+ model_type = "lizzy"
10
+ keys_to_ignore_at_inference = ["past_key_values"]
11
+ base_model_tp_plan = {
12
+ "layers.*.self_attn.q_proj": "colwise",
13
+ "layers.*.self_attn.k_proj": "colwise",
14
+ "layers.*.self_attn.v_proj": "colwise",
15
+ "layers.*.self_attn.o_proj": "rowwise",
16
+ "layers.*.mlp.up_proj": "colwise",
17
+ "layers.*.mlp.gate_proj": "colwise",
18
+ "layers.*.mlp.down_proj": "rowwise",
19
+ "lm_head": "colwise",
20
+ }
21
+ base_model_pp_plan = {
22
+ "embed_tokens": (["input_ids"], ["inputs_embeds"]),
23
+ "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
24
+ "norm": (["hidden_states"], ["hidden_states"]),
25
+ }
26
+
27
+ def __init__(
28
+ self,
29
+ vocab_size: int = 32000,
30
+ hidden_size: int = 4096,
31
+ intermediate_size: int = 11008,
32
+ num_hidden_layers: int = 32,
33
+ num_attention_heads: int = 32,
34
+ num_key_value_heads: int | None = None,
35
+ max_position_embeddings: int = 2048,
36
+ head_dim: int | None = None,
37
+ hidden_act: str = "silu",
38
+ norm_type: str = "rmsnorm",
39
+ norm_eps: float = 1e-6,
40
+ norm_has_bias: bool = False,
41
+ use_pre_attn_norm: bool = True,
42
+ use_pre_mlp_norm: bool = True,
43
+ use_post_attn_norm: bool = False,
44
+ use_post_mlp_norm: bool = False,
45
+ mlp_type: str = "gated",
46
+ attention_bias: bool = False,
47
+ mlp_bias: bool = False,
48
+ position_embedding_type: str = "rope",
49
+ rope_theta: float = 10000.0,
50
+ rope_scaling: dict[str, Any] | None = None,
51
+ rope_layer_flags: list[bool] | None = None,
52
+ no_rope_layer_interval: int | None = None,
53
+ rope_type_overrides: dict[str, str] | None = None,
54
+ layer_types: list[str] | None = None,
55
+ layer_layouts: list[str] | None = None,
56
+ sliding_window: int | None = None,
57
+ linear_num_key_heads: int | None = None,
58
+ linear_num_value_heads: int | None = None,
59
+ linear_key_head_dim: int | None = None,
60
+ linear_value_head_dim: int | None = None,
61
+ linear_a_log_min: float | None = None,
62
+ linear_a_log_max: float | None = None,
63
+ linear_dt_min: float | None = None,
64
+ linear_dt_max: float | None = None,
65
+ linear_dt_init_floor: float | None = None,
66
+ linear_conv_kernel_dim: int | None = None,
67
+ linear_allow_neg_eigval: bool | None = None,
68
+ use_qk_norm: bool = False,
69
+ qk_norm_type: str = "rmsnorm",
70
+ attention_dropout: float = 0.0,
71
+ resid_dropout: float = 0.0,
72
+ embd_dropout: float = 0.0,
73
+ initializer_range: float = 0.02,
74
+ bos_token_id: int | None = None,
75
+ eos_token_id: int | None = None,
76
+ pad_token_id: int | None = None,
77
+ use_cache: bool = True,
78
+ tie_word_embeddings: bool = False,
79
+ **kwargs,
80
+ ) -> None:
81
+ if num_key_value_heads is None:
82
+ num_key_value_heads = num_attention_heads
83
+ if head_dim is None:
84
+ head_dim = hidden_size // num_attention_heads
85
+ if no_rope_layer_interval is not None:
86
+ no_rope_layer_interval = int(no_rope_layer_interval)
87
+ if no_rope_layer_interval <= 0:
88
+ no_rope_layer_interval = None
89
+ if layer_types is None:
90
+ layer_types = ["full_attention"] * int(num_hidden_layers)
91
+ if layer_layouts is None:
92
+ if use_post_attn_norm or use_post_mlp_norm:
93
+ layer_layouts = ["decoder_postnorm"] * int(num_hidden_layers)
94
+ else:
95
+ layer_layouts = ["decoder_prenorm"] * int(num_hidden_layers)
96
+ if rope_layer_flags is None:
97
+ rope_enabled = position_embedding_type == "rope"
98
+ if rope_enabled and no_rope_layer_interval is not None:
99
+ rope_layer_flags = [
100
+ ((layer_idx + 1) % no_rope_layer_interval) != 0
101
+ for layer_idx in range(int(num_hidden_layers))
102
+ ]
103
+ else:
104
+ rope_layer_flags = [rope_enabled] * int(num_hidden_layers)
105
+
106
+ normalized_rope_scaling = None
107
+ if rope_scaling is not None:
108
+ normalized_rope_scaling = dict(rope_scaling)
109
+ for field_name in (
110
+ "factor",
111
+ "attention_factor",
112
+ "beta_fast",
113
+ "beta_slow",
114
+ ):
115
+ if normalized_rope_scaling.get(field_name) is not None:
116
+ normalized_rope_scaling[field_name] = float(
117
+ normalized_rope_scaling[field_name]
118
+ )
119
+ if (
120
+ normalized_rope_scaling.get("original_max_position_embeddings")
121
+ is not None
122
+ ):
123
+ normalized_rope_scaling["original_max_position_embeddings"] = int(
124
+ normalized_rope_scaling["original_max_position_embeddings"]
125
+ )
126
+
127
+ # Transformers validates RoPE settings during PretrainedConfig
128
+ # initialization, so publish the rope-critical fields before
129
+ # calling `super().__init__()`.
130
+ self.max_position_embeddings = int(max_position_embeddings)
131
+ self.rope_theta = float(rope_theta)
132
+ self.rope_scaling = normalized_rope_scaling
133
+
134
+ super().__init__(
135
+ bos_token_id=bos_token_id,
136
+ eos_token_id=eos_token_id,
137
+ pad_token_id=pad_token_id,
138
+ tie_word_embeddings=tie_word_embeddings,
139
+ **kwargs,
140
+ )
141
+ self.vocab_size = int(vocab_size)
142
+ self.hidden_size = int(hidden_size)
143
+ self.intermediate_size = int(intermediate_size)
144
+ self.num_hidden_layers = int(num_hidden_layers)
145
+ self.num_attention_heads = int(num_attention_heads)
146
+ self.num_key_value_heads = int(num_key_value_heads)
147
+ self.max_position_embeddings = int(max_position_embeddings)
148
+ self.head_dim = int(head_dim)
149
+ self.hidden_act = str(hidden_act)
150
+ self.norm_type = str(norm_type)
151
+ self.norm_eps = float(norm_eps)
152
+ self.norm_has_bias = bool(norm_has_bias)
153
+ self.use_pre_attn_norm = bool(use_pre_attn_norm)
154
+ self.use_pre_mlp_norm = bool(use_pre_mlp_norm)
155
+ self.use_post_attn_norm = bool(use_post_attn_norm)
156
+ self.use_post_mlp_norm = bool(use_post_mlp_norm)
157
+ self.mlp_type = str(mlp_type)
158
+ self.attention_bias = bool(attention_bias)
159
+ self.mlp_bias = bool(mlp_bias)
160
+ self.position_embedding_type = str(position_embedding_type)
161
+ self.rope_theta = float(rope_theta)
162
+ self.rope_scaling = normalized_rope_scaling
163
+ self.no_rope_layer_interval = no_rope_layer_interval
164
+ self.rope_type_overrides = {
165
+ str(key): str(value)
166
+ for key, value in dict(rope_type_overrides or {}).items()
167
+ }
168
+ self.layer_types = list(layer_types)
169
+ self.layer_layouts = [str(item) for item in layer_layouts]
170
+ self.rope_layer_flags = [bool(item) for item in rope_layer_flags]
171
+ self.sliding_window = sliding_window
172
+ self.linear_num_key_heads = (
173
+ None
174
+ if linear_num_key_heads is None
175
+ else int(linear_num_key_heads)
176
+ )
177
+ self.linear_num_value_heads = (
178
+ None
179
+ if linear_num_value_heads is None
180
+ else int(linear_num_value_heads)
181
+ )
182
+ self.linear_key_head_dim = (
183
+ None
184
+ if linear_key_head_dim is None
185
+ else int(linear_key_head_dim)
186
+ )
187
+ self.linear_value_head_dim = (
188
+ None
189
+ if linear_value_head_dim is None
190
+ else int(linear_value_head_dim)
191
+ )
192
+ self.linear_a_log_min = (
193
+ None if linear_a_log_min is None else float(linear_a_log_min)
194
+ )
195
+ self.linear_a_log_max = (
196
+ None if linear_a_log_max is None else float(linear_a_log_max)
197
+ )
198
+ self.linear_dt_min = (
199
+ None if linear_dt_min is None else float(linear_dt_min)
200
+ )
201
+ self.linear_dt_max = (
202
+ None if linear_dt_max is None else float(linear_dt_max)
203
+ )
204
+ self.linear_dt_init_floor = (
205
+ None
206
+ if linear_dt_init_floor is None
207
+ else float(linear_dt_init_floor)
208
+ )
209
+ self.linear_conv_kernel_dim = (
210
+ None
211
+ if linear_conv_kernel_dim is None
212
+ else int(linear_conv_kernel_dim)
213
+ )
214
+ self.linear_allow_neg_eigval = (
215
+ None
216
+ if linear_allow_neg_eigval is None
217
+ else bool(linear_allow_neg_eigval)
218
+ )
219
+ self.use_qk_norm = bool(use_qk_norm)
220
+ self.qk_norm_type = str(qk_norm_type)
221
+ self.attention_dropout = float(attention_dropout)
222
+ self.resid_dropout = float(resid_dropout)
223
+ self.embd_dropout = float(embd_dropout)
224
+ self.initializer_range = float(initializer_range)
225
+ self.use_cache = bool(use_cache)
226
+ self.rms_norm_eps = self.norm_eps
227
+ self.dtype = None
generation_config.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "_from_model_config": true,
3
+ "eos_token_id": 100257,
4
+ "transformers_version": "4.57.3",
5
+ "bos_token_id": 100257,
6
+ "pad_token_id": 100277,
7
+ "do_sample": true
8
+ }
header-dark.svg ADDED
header-light.svg ADDED
merges.txt ADDED
The diff for this file is too large to render. See raw diff
 
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c523315020afa40896df120a64e26c6c6ae1b8673cfd792f6ef9446dbb933d1f
3
+ size 14596063224
modeling_lizzy.py ADDED
@@ -0,0 +1,2172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ import os
5
+ from typing import Any, cast
6
+
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from torch import nn
10
+ from torch.utils.checkpoint import checkpoint
11
+ from transformers.activations import ACT2FN
12
+ from transformers.cache_utils import Cache, DynamicCache
13
+ from transformers.generation import GenerationMixin
14
+ from transformers.modeling_outputs import (
15
+ BaseModelOutputWithPast,
16
+ CausalLMOutputWithPast,
17
+ )
18
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
19
+
20
+ try:
21
+ from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS
22
+ except ImportError:
23
+ ROPE_INIT_FUNCTIONS = {}
24
+
25
+ try:
26
+ from fla.modules import FusedRMSNormGated, ShortConvolution
27
+ from fla.ops.gated_delta_rule import (
28
+ chunk_gated_delta_rule,
29
+ fused_recurrent_gated_delta_rule,
30
+ )
31
+ except ImportError:
32
+ chunk_gated_delta_rule = None
33
+ fused_recurrent_gated_delta_rule = None
34
+ FusedRMSNormGated = None
35
+ ShortConvolution = None
36
+
37
+ from .configuration_lizzy import LizzyConfig
38
+
39
+
40
+ class LizzyRMSNorm(nn.Module):
41
+ def __init__(self, hidden_size: int, eps: float = 1e-6) -> None:
42
+ super().__init__()
43
+ self.weight = nn.Parameter(torch.ones(hidden_size))
44
+ self.variance_epsilon = eps
45
+
46
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
47
+ input_dtype = hidden_states.dtype
48
+ hidden_states = hidden_states.to(torch.float32)
49
+ variance = hidden_states.pow(2).mean(dim=-1, keepdim=True)
50
+ hidden_states = hidden_states * torch.rsqrt(
51
+ variance + self.variance_epsilon
52
+ )
53
+ return self.weight * hidden_states.to(input_dtype)
54
+
55
+
56
+ def _make_norm(
57
+ norm_type: str,
58
+ hidden_size: int,
59
+ eps: float,
60
+ *,
61
+ has_bias: bool,
62
+ ) -> nn.Module:
63
+ if norm_type == "rmsnorm":
64
+ return LizzyRMSNorm(hidden_size, eps=eps)
65
+ if norm_type == "layernorm":
66
+ return nn.LayerNorm(
67
+ hidden_size,
68
+ eps=eps,
69
+ elementwise_affine=True,
70
+ bias=has_bias,
71
+ )
72
+ msg = f"Unsupported norm_type: {norm_type}"
73
+ raise ValueError(msg)
74
+
75
+
76
+ def _rotate_half(x: torch.Tensor) -> torch.Tensor:
77
+ x1 = x[..., : x.shape[-1] // 2]
78
+ x2 = x[..., x.shape[-1] // 2 :]
79
+ return torch.cat((-x2, x1), dim=-1)
80
+
81
+
82
+ def _apply_rotary_pos_emb(
83
+ q: torch.Tensor,
84
+ k: torch.Tensor,
85
+ cos: torch.Tensor,
86
+ sin: torch.Tensor,
87
+ ) -> tuple[torch.Tensor, torch.Tensor]:
88
+ q_embed = (q * cos) + (_rotate_half(q) * sin)
89
+ k_embed = (k * cos) + (_rotate_half(k) * sin)
90
+ return q_embed, k_embed
91
+
92
+
93
+ def _legacy_cache_length(
94
+ past_key_values: tuple[tuple[torch.Tensor, torch.Tensor], ...] | None,
95
+ ) -> int:
96
+ if (
97
+ isinstance(past_key_values, tuple)
98
+ and len(past_key_values) > 0
99
+ and past_key_values[0] is not None
100
+ and past_key_values[0][0] is not None
101
+ ):
102
+ return int(past_key_values[0][0].shape[2])
103
+ return 0
104
+
105
+
106
+ def _normalize_cache_position(
107
+ cache_position: torch.Tensor | None,
108
+ ) -> torch.Tensor | None:
109
+ if cache_position is None:
110
+ return None
111
+ if cache_position.dim() == 0:
112
+ return cache_position.view(1)
113
+ if cache_position.dim() > 1:
114
+ return cache_position[0]
115
+ return cache_position
116
+
117
+
118
+ def _is_cache_object(value: Any) -> bool:
119
+ return isinstance(value, Cache) or isinstance(value, LizzyHybridDynamicCache)
120
+
121
+
122
+ def _compute_default_rope_parameters(
123
+ config: LizzyConfig,
124
+ device: torch.device,
125
+ ) -> tuple[torch.Tensor, float]:
126
+ inv_freq = 1.0 / (
127
+ config.rope_theta
128
+ ** (
129
+ torch.arange(0, config.head_dim, 2, device=device, dtype=torch.float32)
130
+ / config.head_dim
131
+ )
132
+ )
133
+ return inv_freq, 1.0
134
+
135
+
136
+ def _compute_yarn_rope_parameters(
137
+ config: LizzyConfig,
138
+ device: torch.device,
139
+ ) -> tuple[torch.Tensor, float]:
140
+ rope_scaling = dict(config.rope_scaling or {})
141
+ factor = float(rope_scaling["factor"])
142
+ attention_factor = rope_scaling.get("attention_factor")
143
+ mscale = rope_scaling.get("mscale")
144
+ mscale_all_dim = rope_scaling.get("mscale_all_dim")
145
+ original_max_position_embeddings = int(
146
+ rope_scaling.get("original_max_position_embeddings")
147
+ or config.max_position_embeddings
148
+ )
149
+
150
+ def get_mscale(scale: float, mscale_value: float = 1.0) -> float:
151
+ if scale <= 1.0:
152
+ return 1.0
153
+ return 0.1 * mscale_value * math.log(scale) + 1.0
154
+
155
+ if attention_factor is None:
156
+ if mscale is not None and mscale_all_dim is not None:
157
+ attention_factor = float(
158
+ get_mscale(factor, float(mscale))
159
+ / get_mscale(factor, float(mscale_all_dim))
160
+ )
161
+ else:
162
+ attention_factor = get_mscale(factor)
163
+
164
+ beta_fast = float(rope_scaling.get("beta_fast") or 32.0)
165
+ beta_slow = float(rope_scaling.get("beta_slow") or 1.0)
166
+ truncate = bool(rope_scaling.get("truncate", True))
167
+ dim = config.head_dim
168
+
169
+ def find_correction_dim(
170
+ num_rotations: float,
171
+ *,
172
+ dim: int,
173
+ base: float,
174
+ max_position_embeddings: int,
175
+ ) -> float:
176
+ return (
177
+ dim
178
+ * math.log(max_position_embeddings / (num_rotations * 2 * math.pi))
179
+ / (2 * math.log(base))
180
+ )
181
+
182
+ def find_correction_range(
183
+ low_rot: float,
184
+ high_rot: float,
185
+ *,
186
+ dim: int,
187
+ base: float,
188
+ max_position_embeddings: int,
189
+ truncate: bool,
190
+ ) -> tuple[float, float]:
191
+ low = find_correction_dim(
192
+ low_rot,
193
+ dim=dim,
194
+ base=base,
195
+ max_position_embeddings=max_position_embeddings,
196
+ )
197
+ high = find_correction_dim(
198
+ high_rot,
199
+ dim=dim,
200
+ base=base,
201
+ max_position_embeddings=max_position_embeddings,
202
+ )
203
+ if truncate:
204
+ low = math.floor(low)
205
+ high = math.ceil(high)
206
+ return max(low, 0.0), min(high, dim - 1.0)
207
+
208
+ def linear_ramp_factor(
209
+ min_value: float,
210
+ max_value: float,
211
+ dim: int,
212
+ ) -> torch.Tensor:
213
+ if min_value == max_value:
214
+ max_value += 0.001
215
+ linear_func = (
216
+ torch.arange(dim, dtype=torch.float32, device=device) - min_value
217
+ ) / (max_value - min_value)
218
+ return torch.clamp(linear_func, 0, 1)
219
+
220
+ pos_freqs = config.rope_theta ** (
221
+ torch.arange(0, dim, 2, device=device, dtype=torch.float32) / dim
222
+ )
223
+ inv_freq_extrapolation = 1.0 / pos_freqs
224
+ inv_freq_interpolation = 1.0 / (factor * pos_freqs)
225
+ low, high = find_correction_range(
226
+ beta_fast,
227
+ beta_slow,
228
+ dim=dim,
229
+ base=config.rope_theta,
230
+ max_position_embeddings=original_max_position_embeddings,
231
+ truncate=truncate,
232
+ )
233
+ inv_freq_extrapolation_factor = 1 - linear_ramp_factor(low, high, dim // 2)
234
+ inv_freq = (
235
+ inv_freq_interpolation * (1 - inv_freq_extrapolation_factor)
236
+ + inv_freq_extrapolation * inv_freq_extrapolation_factor
237
+ )
238
+ return inv_freq, float(attention_factor)
239
+
240
+
241
+ def _compute_rope_parameters(
242
+ config: LizzyConfig,
243
+ device: torch.device,
244
+ *,
245
+ seq_len: int | torch.Tensor | None = None,
246
+ rope_type_override: str | None = None,
247
+ ) -> tuple[torch.Tensor, float]:
248
+ rope_scaling = dict(config.rope_scaling or {})
249
+ rope_type = rope_type_override
250
+ if rope_type is None:
251
+ if not rope_scaling:
252
+ return _compute_default_rope_parameters(config, device)
253
+ rope_type = str(
254
+ rope_scaling.get("rope_type", rope_scaling.get("type", "default"))
255
+ )
256
+
257
+ if rope_type == "default":
258
+ return _compute_default_rope_parameters(config, device)
259
+ if rope_type == "yarn":
260
+ return _compute_yarn_rope_parameters(config, device)
261
+ if not rope_scaling:
262
+ return _compute_default_rope_parameters(config, device)
263
+
264
+ rope_init_fn = (
265
+ ROPE_INIT_FUNCTIONS.get(rope_type) or ROPE_INIT_FUNCTIONS.get("default")
266
+ )
267
+ if rope_init_fn is None:
268
+ return _compute_default_rope_parameters(config, device)
269
+
270
+ inv_freq, attention_factor = rope_init_fn(config, device, seq_len=seq_len)
271
+ return inv_freq.to(device=device, dtype=torch.float32), float(attention_factor)
272
+
273
+
274
+ def _looks_like_legacy_interval_rope_lizzy(config: LizzyConfig) -> bool:
275
+ rope_layer_flags = list(getattr(config, "rope_layer_flags", None) or [])
276
+ if rope_layer_flags and not all(bool(item) for item in rope_layer_flags):
277
+ return False
278
+
279
+ layer_types = list(getattr(config, "layer_types", None) or [])
280
+ if layer_types and any(str(item) != "full_attention" for item in layer_types):
281
+ return False
282
+
283
+ return (
284
+ str(getattr(config, "position_embedding_type", "")).lower() == "rope"
285
+ and not bool(getattr(config, "rope_scaling", None))
286
+ and int(getattr(config, "num_hidden_layers", 0) or 0) == 36
287
+ and int(getattr(config, "hidden_size", 0) or 0) == 2048
288
+ and int(getattr(config, "num_attention_heads", 0) or 0) == 16
289
+ and int(getattr(config, "num_key_value_heads", 0) or 0) == 4
290
+ and math.isclose(
291
+ float(getattr(config, "rope_theta", 0.0) or 0.0), 5_000_000.0
292
+ )
293
+ and not bool(getattr(config, "use_post_attn_norm", False))
294
+ and not bool(getattr(config, "use_post_mlp_norm", False))
295
+ and not bool(getattr(config, "use_qk_norm", False))
296
+ )
297
+
298
+
299
+ def _get_no_rope_layer_interval(config: LizzyConfig) -> int | None:
300
+ value = getattr(config, "no_rope_layer_interval", None)
301
+ if value is not None:
302
+ value = int(value)
303
+ if value > 0:
304
+ return value
305
+ if _looks_like_legacy_interval_rope_lizzy(config):
306
+ # Backward-compatible fallback for already-uploaded Lizzy
307
+ # checkpoints that should use NoPE on every 4th layer.
308
+ return 4
309
+ return None
310
+
311
+
312
+ def _get_rope_layer_flag(config: LizzyConfig, layer_idx: int) -> bool:
313
+ rope_enabled = str(
314
+ getattr(config, "position_embedding_type", "rope")
315
+ ).lower() == "rope"
316
+ rope_layer_flags = list(getattr(config, "rope_layer_flags", None) or [])
317
+ no_rope_layer_interval = _get_no_rope_layer_interval(config)
318
+ if (
319
+ no_rope_layer_interval is not None
320
+ and (
321
+ layer_idx >= len(rope_layer_flags)
322
+ or not rope_layer_flags
323
+ or all(bool(item) for item in rope_layer_flags)
324
+ )
325
+ ):
326
+ return rope_enabled and ((layer_idx + 1) % no_rope_layer_interval != 0)
327
+ if 0 <= layer_idx < len(rope_layer_flags):
328
+ return rope_enabled and bool(rope_layer_flags[layer_idx])
329
+ return rope_enabled
330
+
331
+
332
+ def _get_layer_layout(config: LizzyConfig, layer_idx: int) -> str:
333
+ layer_layouts = list(getattr(config, "layer_layouts", None) or [])
334
+ if 0 <= layer_idx < len(layer_layouts):
335
+ return str(layer_layouts[layer_idx])
336
+ if bool(getattr(config, "use_post_attn_norm", False)) or bool(
337
+ getattr(config, "use_post_mlp_norm", False)
338
+ ):
339
+ return "decoder_postnorm"
340
+ return "decoder_prenorm"
341
+
342
+
343
+ def _has_linear_attention(config: LizzyConfig) -> bool:
344
+ return any(
345
+ str(layer_type) == "linear_attention"
346
+ for layer_type in list(getattr(config, "layer_types", None) or [])
347
+ )
348
+
349
+
350
+ class LizzyHybridDynamicCache:
351
+ """Cache for Lizzy checkpoints with mixed full and linear attention."""
352
+
353
+ is_compileable = False
354
+
355
+ def __init__(self, config: LizzyConfig) -> None:
356
+ super().__init__()
357
+ self.layer_types = list(config.layer_types)
358
+ self.transformer_layers = [
359
+ idx
360
+ for idx, layer_type in enumerate(self.layer_types)
361
+ if layer_type == "full_attention"
362
+ ]
363
+ self.last_linear_layer = (
364
+ len(self.layer_types)
365
+ - 1
366
+ - self.layer_types[::-1].index("linear_attention")
367
+ )
368
+ self.recurrent_states = [None for _ in range(config.num_hidden_layers)]
369
+ self.key_cache = [None for _ in range(config.num_hidden_layers)]
370
+ self.value_cache = [None for _ in range(config.num_hidden_layers)]
371
+ self.conv_states_q = [None for _ in range(config.num_hidden_layers)]
372
+ self.conv_states_k = [None for _ in range(config.num_hidden_layers)]
373
+ self.conv_states_v = [None for _ in range(config.num_hidden_layers)]
374
+
375
+ def __len__(self) -> int:
376
+ return len(self.layer_types)
377
+
378
+ def update(
379
+ self,
380
+ key_states: torch.Tensor,
381
+ value_states: torch.Tensor,
382
+ layer_idx: int,
383
+ cache_kwargs: dict[str, Any] | None = None,
384
+ ) -> tuple[torch.Tensor, torch.Tensor]:
385
+ del cache_kwargs
386
+ if self.key_cache[layer_idx] is None:
387
+ self.key_cache[layer_idx] = key_states
388
+ self.value_cache[layer_idx] = value_states
389
+ else:
390
+ self.key_cache[layer_idx] = torch.cat(
391
+ [self.key_cache[layer_idx], key_states],
392
+ dim=2,
393
+ )
394
+ self.value_cache[layer_idx] = torch.cat(
395
+ [self.value_cache[layer_idx], value_states],
396
+ dim=2,
397
+ )
398
+ return self.key_cache[layer_idx], self.value_cache[layer_idx]
399
+
400
+ def reorder_cache(self, beam_idx: torch.LongTensor) -> None:
401
+ batch_size = beam_idx.shape[0]
402
+ for layer_idx in range(len(self.key_cache)):
403
+ if self.key_cache[layer_idx] is not None:
404
+ if self.key_cache[layer_idx].shape[0] < batch_size:
405
+ expand_ratio = (
406
+ batch_size // self.key_cache[layer_idx].shape[0]
407
+ )
408
+ self.key_cache[layer_idx] = (
409
+ self.key_cache[layer_idx].repeat_interleave(
410
+ expand_ratio, dim=0,
411
+ )
412
+ )
413
+ self.value_cache[layer_idx] = (
414
+ self.value_cache[layer_idx].repeat_interleave(
415
+ expand_ratio, dim=0,
416
+ )
417
+ )
418
+ device = self.key_cache[layer_idx].device
419
+ self.key_cache[layer_idx] = self.key_cache[layer_idx].index_select(
420
+ 0,
421
+ beam_idx.to(device),
422
+ )
423
+ self.value_cache[layer_idx] = (
424
+ self.value_cache[layer_idx].index_select(0, beam_idx.to(device))
425
+ )
426
+ if self.conv_states_q[layer_idx] is not None:
427
+ if self.conv_states_q[layer_idx].shape[0] < batch_size:
428
+ expand_ratio = (
429
+ batch_size // self.conv_states_q[layer_idx].shape[0]
430
+ )
431
+ self.conv_states_q[layer_idx] = (
432
+ self.conv_states_q[layer_idx].repeat_interleave(
433
+ expand_ratio, dim=0,
434
+ )
435
+ )
436
+ self.conv_states_k[layer_idx] = (
437
+ self.conv_states_k[layer_idx].repeat_interleave(
438
+ expand_ratio, dim=0,
439
+ )
440
+ )
441
+ self.conv_states_v[layer_idx] = (
442
+ self.conv_states_v[layer_idx].repeat_interleave(
443
+ expand_ratio, dim=0,
444
+ )
445
+ )
446
+ self.recurrent_states[layer_idx] = (
447
+ self.recurrent_states[layer_idx].repeat_interleave(
448
+ expand_ratio, dim=0,
449
+ )
450
+ )
451
+ device = self.conv_states_q[layer_idx].device
452
+ self.conv_states_q[layer_idx] = (
453
+ self.conv_states_q[layer_idx].index_select(
454
+ 0,
455
+ beam_idx.to(device),
456
+ )
457
+ )
458
+ self.conv_states_k[layer_idx] = (
459
+ self.conv_states_k[layer_idx].index_select(
460
+ 0,
461
+ beam_idx.to(device),
462
+ )
463
+ )
464
+ self.conv_states_v[layer_idx] = (
465
+ self.conv_states_v[layer_idx].index_select(
466
+ 0,
467
+ beam_idx.to(device),
468
+ )
469
+ )
470
+ self.recurrent_states[layer_idx] = (
471
+ self.recurrent_states[layer_idx].index_select(
472
+ 0,
473
+ beam_idx.to(device),
474
+ )
475
+ )
476
+
477
+ def get_seq_length(self, layer_idx: int | None = 0) -> int:
478
+ if not self.transformer_layers:
479
+ return 0
480
+ layer_idx = (
481
+ self.transformer_layers[0]
482
+ if layer_idx not in self.transformer_layers
483
+ else layer_idx
484
+ )
485
+ if len(self.key_cache) <= layer_idx or self.key_cache[layer_idx] is None:
486
+ return 0
487
+ return self.key_cache[layer_idx].shape[-2]
488
+
489
+ def get_mask_sizes(self, query_length: int, layer_idx: int) -> tuple[int, int]:
490
+ del layer_idx
491
+ kv_offset = 0
492
+ past_seen_tokens = self.get_seq_length()
493
+ kv_length = query_length + past_seen_tokens
494
+ return kv_length, kv_offset
495
+
496
+ @property
497
+ def has_previous_state(self) -> bool:
498
+ # Mirror the upstream contract: once the final linear layer has cached
499
+ # its conv state, single-token decode can switch to the recurrent path.
500
+ return self.conv_states_q[self.last_linear_layer] is not None
501
+
502
+
503
+ class LizzyHybridRMSNormGated(nn.Module):
504
+ def __init__(self, hidden_size: int, eps: float = 1e-6) -> None:
505
+ super().__init__()
506
+ self.weight = nn.Parameter(torch.ones(hidden_size))
507
+ self.variance_epsilon = eps
508
+
509
+ def forward(
510
+ self,
511
+ hidden_states: torch.Tensor,
512
+ gate: torch.Tensor | None = None,
513
+ ) -> torch.Tensor:
514
+ if gate is None:
515
+ msg = "gate is required for gated RMSNorm."
516
+ raise ValueError(msg)
517
+ input_dtype = hidden_states.dtype
518
+ hidden_states = hidden_states.to(torch.float32)
519
+ variance = hidden_states.pow(2).mean(-1, keepdim=True)
520
+ hidden_states = hidden_states * torch.rsqrt(
521
+ variance + self.variance_epsilon
522
+ )
523
+ hidden_states = self.weight * hidden_states.to(input_dtype)
524
+ hidden_states = hidden_states * F.silu(gate.to(torch.float32))
525
+ return hidden_states.to(input_dtype)
526
+
527
+
528
+ class LizzyHybridShortConvolution(nn.Conv1d):
529
+ def __init__(
530
+ self,
531
+ hidden_size: int,
532
+ kernel_size: int,
533
+ bias: bool = False,
534
+ activation: str | None = "silu",
535
+ ) -> None:
536
+ super().__init__(
537
+ in_channels=hidden_size,
538
+ out_channels=hidden_size,
539
+ kernel_size=kernel_size,
540
+ groups=hidden_size,
541
+ padding=kernel_size - 1,
542
+ bias=bias,
543
+ )
544
+ self.hidden_size = hidden_size
545
+ self.conv_kernel_size = kernel_size
546
+ self.act_fn = ACT2FN[activation]
547
+
548
+ def forward(
549
+ self,
550
+ hidden_states: torch.Tensor,
551
+ cache: torch.Tensor | None = None,
552
+ use_precomputed: bool = False,
553
+ **kwargs: Any,
554
+ ) -> tuple[torch.Tensor, torch.Tensor]:
555
+ del kwargs
556
+ seq_len, dim = hidden_states.shape[-2:]
557
+ hidden_states = hidden_states.transpose(1, 2)
558
+ if use_precomputed:
559
+ if cache is None:
560
+ msg = "cache is required when use_precomputed=True."
561
+ raise ValueError(msg)
562
+ x_with_state = torch.cat([cache, hidden_states], dim=-1)
563
+ out = F.conv1d(
564
+ x_with_state,
565
+ self.weight,
566
+ self.bias,
567
+ padding=0,
568
+ groups=dim,
569
+ )
570
+ conv_state = x_with_state[:, :, 1:]
571
+ else:
572
+ out = F.conv1d(
573
+ hidden_states,
574
+ self.weight,
575
+ self.bias,
576
+ padding=self.conv_kernel_size - 1,
577
+ groups=dim,
578
+ )
579
+ out = out[:, :, :seq_len]
580
+ conv_state = F.pad(
581
+ hidden_states,
582
+ (self.conv_kernel_size - 1 - hidden_states.shape[-1], 0),
583
+ )
584
+ out = self.act_fn(out)
585
+ return out.transpose(1, 2), conv_state
586
+
587
+
588
+ def _apply_mask_to_padding_states(
589
+ hidden_states: torch.Tensor,
590
+ attention_mask: torch.Tensor | None,
591
+ ) -> torch.Tensor:
592
+ # Match the upstream hybrid implementation: silence padded tokens before
593
+ # the DeltaNet projections so recurrent state does not absorb padding.
594
+ if (
595
+ attention_mask is not None
596
+ and attention_mask.shape[1] > 1
597
+ and attention_mask.shape[0] > 1
598
+ ):
599
+ dtype = hidden_states.dtype
600
+ hidden_states = (hidden_states * attention_mask[:, :, None]).to(dtype)
601
+ return hidden_states
602
+
603
+
604
+ def _l2norm(
605
+ x: torch.Tensor,
606
+ dim: int = -1,
607
+ eps: float = 1e-6,
608
+ ) -> torch.Tensor:
609
+ inv_norm = torch.rsqrt((x * x).sum(dim=dim, keepdim=True) + eps)
610
+ return x * inv_norm
611
+
612
+
613
+ def _torch_chunk_gated_delta_rule(
614
+ query: torch.Tensor,
615
+ key: torch.Tensor,
616
+ value: torch.Tensor,
617
+ g: torch.Tensor,
618
+ beta: torch.Tensor,
619
+ chunk_size: int = 64,
620
+ initial_state: torch.Tensor | None = None,
621
+ output_final_state: bool = False,
622
+ use_qk_l2norm_in_kernel: bool = False,
623
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
624
+ initial_dtype = query.dtype
625
+ if use_qk_l2norm_in_kernel:
626
+ query = _l2norm(query, dim=-1, eps=1e-6)
627
+ key = _l2norm(key, dim=-1, eps=1e-6)
628
+ query, key, value, beta, g = [
629
+ x.transpose(1, 2).contiguous().to(torch.float32)
630
+ for x in (query, key, value, beta, g)
631
+ ]
632
+ batch_size, num_heads, sequence_length, k_head_dim = key.shape
633
+ v_head_dim = value.shape[-1]
634
+ pad_size = (chunk_size - sequence_length % chunk_size) % chunk_size
635
+ query = F.pad(query, (0, 0, 0, pad_size))
636
+ key = F.pad(key, (0, 0, 0, pad_size))
637
+ value = F.pad(value, (0, 0, 0, pad_size))
638
+ beta = F.pad(beta, (0, pad_size))
639
+ g = F.pad(g, (0, pad_size))
640
+ total_sequence_length = sequence_length + pad_size
641
+ scale = 1 / (query.shape[-1] ** 0.5)
642
+ query = query * scale
643
+
644
+ v_beta = value * beta.unsqueeze(-1)
645
+ k_beta = key * beta.unsqueeze(-1)
646
+ query, key, value, k_beta, v_beta = [
647
+ x.reshape(x.shape[0], x.shape[1], -1, chunk_size, x.shape[-1])
648
+ for x in (query, key, value, k_beta, v_beta)
649
+ ]
650
+ g = g.reshape(g.shape[0], g.shape[1], -1, chunk_size)
651
+ mask = torch.triu(
652
+ torch.ones(
653
+ chunk_size,
654
+ chunk_size,
655
+ dtype=torch.bool,
656
+ device=query.device,
657
+ ),
658
+ diagonal=0,
659
+ )
660
+ g = g.cumsum(dim=-1)
661
+ decay_mask = ((g.unsqueeze(-1) - g.unsqueeze(-2)).tril().exp().float()).tril()
662
+ attn = -((k_beta @ key.transpose(-1, -2)) * decay_mask).masked_fill(mask, 0)
663
+ for idx in range(1, chunk_size):
664
+ row = attn[..., idx, :idx].clone()
665
+ sub = attn[..., :idx, :idx].clone()
666
+ attn[..., idx, :idx] = row + (row.unsqueeze(-1) * sub).sum(-2)
667
+ attn = attn + torch.eye(
668
+ chunk_size,
669
+ dtype=attn.dtype,
670
+ device=attn.device,
671
+ )
672
+ value = attn @ v_beta
673
+ k_cumdecay = attn @ (k_beta * g.exp().unsqueeze(-1))
674
+ last_recurrent_state = (
675
+ torch.zeros(batch_size, num_heads, k_head_dim, v_head_dim).to(value)
676
+ if initial_state is None
677
+ else initial_state.to(value)
678
+ )
679
+ core_attn_out = torch.zeros_like(value)
680
+ mask = torch.triu(
681
+ torch.ones(
682
+ chunk_size,
683
+ chunk_size,
684
+ dtype=torch.bool,
685
+ device=query.device,
686
+ ),
687
+ diagonal=1,
688
+ )
689
+ for idx in range(0, total_sequence_length // chunk_size):
690
+ q_i, k_i, v_i = query[:, :, idx], key[:, :, idx], value[:, :, idx]
691
+ attn = (q_i @ k_i.transpose(-1, -2) * decay_mask[:, :, idx]).masked_fill_(
692
+ mask,
693
+ 0,
694
+ )
695
+ v_prime = (k_cumdecay[:, :, idx]) @ last_recurrent_state
696
+ v_new = v_i - v_prime
697
+ attn_inter = (q_i * g[:, :, idx, :, None].exp()) @ last_recurrent_state
698
+ core_attn_out[:, :, idx] = attn_inter + attn @ v_new
699
+ last_recurrent_state = (
700
+ last_recurrent_state * g[:, :, idx, -1, None, None].exp()
701
+ + (
702
+ k_i
703
+ * (g[:, :, idx, -1, None] - g[:, :, idx]).exp()[..., None]
704
+ ).transpose(-1, -2)
705
+ @ v_new
706
+ )
707
+ if not output_final_state:
708
+ last_recurrent_state = None
709
+ core_attn_out = core_attn_out.reshape(
710
+ core_attn_out.shape[0],
711
+ core_attn_out.shape[1],
712
+ -1,
713
+ core_attn_out.shape[-1],
714
+ )
715
+ core_attn_out = core_attn_out[:, :, :sequence_length]
716
+ core_attn_out = core_attn_out.transpose(1, 2).contiguous().to(initial_dtype)
717
+ return core_attn_out, last_recurrent_state
718
+
719
+
720
+ def _torch_recurrent_gated_delta_rule(
721
+ query: torch.Tensor,
722
+ key: torch.Tensor,
723
+ value: torch.Tensor,
724
+ g: torch.Tensor,
725
+ beta: torch.Tensor,
726
+ initial_state: torch.Tensor | None,
727
+ output_final_state: bool,
728
+ use_qk_l2norm_in_kernel: bool = False,
729
+ ) -> tuple[torch.Tensor, torch.Tensor | None]:
730
+ initial_dtype = query.dtype
731
+ if use_qk_l2norm_in_kernel:
732
+ query = _l2norm(query, dim=-1, eps=1e-6)
733
+ key = _l2norm(key, dim=-1, eps=1e-6)
734
+ query, key, value, beta, g = [
735
+ x.transpose(1, 2).contiguous().to(torch.float32)
736
+ for x in (query, key, value, beta, g)
737
+ ]
738
+ batch_size, num_heads, sequence_length, k_head_dim = key.shape
739
+ v_head_dim = value.shape[-1]
740
+ scale = 1 / (query.shape[-1] ** 0.5)
741
+ query = query * scale
742
+ core_attn_out = torch.zeros(
743
+ batch_size,
744
+ num_heads,
745
+ sequence_length,
746
+ v_head_dim,
747
+ ).to(value)
748
+ last_recurrent_state = (
749
+ torch.zeros(batch_size, num_heads, k_head_dim, v_head_dim).to(value)
750
+ if initial_state is None
751
+ else initial_state.to(value)
752
+ )
753
+ for idx in range(sequence_length):
754
+ q_t = query[:, :, idx]
755
+ k_t = key[:, :, idx]
756
+ v_t = value[:, :, idx]
757
+ g_t = g[:, :, idx].exp().unsqueeze(-1).unsqueeze(-1)
758
+ beta_t = beta[:, :, idx].unsqueeze(-1)
759
+ last_recurrent_state = last_recurrent_state * g_t
760
+ kv_mem = (last_recurrent_state * k_t.unsqueeze(-1)).sum(dim=-2)
761
+ delta = (v_t - kv_mem) * beta_t
762
+ last_recurrent_state = (
763
+ last_recurrent_state + k_t.unsqueeze(-1) * delta.unsqueeze(-2)
764
+ )
765
+ core_attn_out[:, :, idx] = (
766
+ last_recurrent_state * q_t.unsqueeze(-1)
767
+ ).sum(dim=-2)
768
+ if not output_final_state:
769
+ last_recurrent_state = None
770
+ core_attn_out = core_attn_out.transpose(1, 2).contiguous().to(initial_dtype)
771
+ return core_attn_out, last_recurrent_state
772
+
773
+
774
+ class LizzyHybridGatedDeltaNet(nn.Module):
775
+ def __init__(self, config: LizzyConfig, layer_idx: int) -> None:
776
+ super().__init__()
777
+ self.hidden_size = config.hidden_size
778
+ self.num_v_heads = config.linear_num_value_heads
779
+ self.num_k_heads = config.linear_num_key_heads
780
+ self.head_k_dim = config.linear_key_head_dim
781
+ self.head_v_dim = config.linear_value_head_dim
782
+ self.key_dim = self.head_k_dim * self.num_k_heads
783
+ self.value_dim = self.head_v_dim * self.num_v_heads
784
+ self.layer_idx = layer_idx
785
+ self.conv_kernel_size = config.linear_conv_kernel_dim
786
+ self.allow_neg_eigval = config.linear_allow_neg_eigval
787
+ self.eps = config.rms_norm_eps
788
+ self.q_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
789
+ self.k_proj = nn.Linear(self.hidden_size, self.key_dim, bias=False)
790
+ self.v_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
791
+ self.a_proj = nn.Linear(self.hidden_size, self.num_v_heads, bias=False)
792
+ self.b_proj = nn.Linear(self.hidden_size, self.num_v_heads, bias=False)
793
+ self.g_proj = nn.Linear(self.hidden_size, self.value_dim, bias=False)
794
+ self.o_proj = nn.Linear(self.value_dim, self.hidden_size, bias=False)
795
+
796
+ # Step-02 conversion runs on CPU by default, even on GPU nodes. In that
797
+ # flow Triton-backed FLA kernels will crash as soon as a CPU tensor
798
+ # reaches them, so the wrapper can force the pure PyTorch fallback for
799
+ # Hybrid layers via an environment switch.
800
+ disable_fla_fast_path = os.environ.get(
801
+ "LIZZY_DISABLE_HYBRID_FLA",
802
+ "",
803
+ ).strip().lower() in {"1", "true", "yes", "on"}
804
+ use_fla_fast_path = (
805
+ not disable_fla_fast_path
806
+ and
807
+ torch.cuda.is_available()
808
+ and ShortConvolution is not None
809
+ and chunk_gated_delta_rule is not None
810
+ and fused_recurrent_gated_delta_rule is not None
811
+ and FusedRMSNormGated is not None
812
+ )
813
+ # Keep the fast-path contract when FLA is present, but fall back to a
814
+ # local implementation so the public Lizzy artifact never depends on
815
+ # family-specific Transformers remote code.
816
+ conv1d_class = (
817
+ ShortConvolution if use_fla_fast_path else LizzyHybridShortConvolution
818
+ )
819
+ self.q_conv1d = conv1d_class(
820
+ hidden_size=self.key_dim,
821
+ kernel_size=self.conv_kernel_size,
822
+ bias=False,
823
+ activation="silu",
824
+ )
825
+ self.k_conv1d = conv1d_class(
826
+ hidden_size=self.key_dim,
827
+ kernel_size=self.conv_kernel_size,
828
+ bias=False,
829
+ activation="silu",
830
+ )
831
+ self.v_conv1d = conv1d_class(
832
+ hidden_size=self.value_dim,
833
+ kernel_size=self.conv_kernel_size,
834
+ bias=False,
835
+ activation="silu",
836
+ )
837
+
838
+ a = torch.empty(self.num_v_heads, dtype=torch.float32).uniform_(
839
+ config.linear_a_log_min,
840
+ config.linear_a_log_max,
841
+ )
842
+ self.A_log = nn.Parameter(torch.log(a))
843
+ dt = torch.exp(
844
+ torch.rand(self.num_v_heads)
845
+ * (math.log(config.linear_dt_max) - math.log(config.linear_dt_min))
846
+ + math.log(config.linear_dt_min)
847
+ )
848
+ dt = torch.clamp(dt, min=config.linear_dt_init_floor)
849
+ inv_dt = dt + torch.log(-torch.expm1(-dt))
850
+ self.dt_bias = nn.Parameter(inv_dt)
851
+
852
+ self.o_norm = (
853
+ LizzyHybridRMSNormGated(self.head_v_dim, eps=1e-5)
854
+ if not use_fla_fast_path
855
+ else FusedRMSNormGated(
856
+ self.head_v_dim,
857
+ eps=1e-5,
858
+ device=torch.cuda.current_device(),
859
+ dtype=(
860
+ config.dtype
861
+ if config.dtype is not None
862
+ else torch.get_default_dtype()
863
+ ),
864
+ )
865
+ )
866
+ self.chunk_gated_delta_rule = (
867
+ chunk_gated_delta_rule
868
+ if use_fla_fast_path
869
+ else _torch_chunk_gated_delta_rule
870
+ )
871
+ self.recurrent_gated_delta_rule = (
872
+ (
873
+ fused_recurrent_gated_delta_rule
874
+ if use_fla_fast_path
875
+ else _torch_recurrent_gated_delta_rule
876
+ )
877
+ )
878
+
879
+ def forward(
880
+ self,
881
+ hidden_states: torch.Tensor,
882
+ cache_params: LizzyHybridDynamicCache | None = None,
883
+ attention_mask: torch.Tensor | None = None,
884
+ **kwargs: Any,
885
+ ) -> torch.Tensor:
886
+ del kwargs
887
+ hidden_states = _apply_mask_to_padding_states(hidden_states, attention_mask)
888
+ batch_size, seq_len, _ = hidden_states.shape
889
+ use_cache = cache_params is not None
890
+ use_precomputed = (
891
+ use_cache
892
+ and getattr(cache_params, "has_previous_state", False)
893
+ and seq_len == 1
894
+ )
895
+ conv_state_q = (
896
+ cache_params.conv_states_q[self.layer_idx] if cache_params else None
897
+ )
898
+ conv_state_k = (
899
+ cache_params.conv_states_k[self.layer_idx] if cache_params else None
900
+ )
901
+ conv_state_v = (
902
+ cache_params.conv_states_v[self.layer_idx] if cache_params else None
903
+ )
904
+ recurrent_state = (
905
+ cache_params.recurrent_states[self.layer_idx] if cache_params else None
906
+ )
907
+
908
+ q = self.q_proj(hidden_states)
909
+ k = self.k_proj(hidden_states)
910
+ v = self.v_proj(hidden_states)
911
+ q, new_conv_state_q = self.q_conv1d(
912
+ q,
913
+ cache=conv_state_q,
914
+ use_precomputed=use_precomputed,
915
+ output_final_state=use_cache,
916
+ )
917
+ k, new_conv_state_k = self.k_conv1d(
918
+ k,
919
+ cache=conv_state_k,
920
+ use_precomputed=use_precomputed,
921
+ output_final_state=use_cache,
922
+ )
923
+ v, new_conv_state_v = self.v_conv1d(
924
+ v,
925
+ cache=conv_state_v,
926
+ use_precomputed=use_precomputed,
927
+ output_final_state=use_cache,
928
+ )
929
+
930
+ if cache_params is not None:
931
+ cache_params.conv_states_q[self.layer_idx] = new_conv_state_q
932
+ cache_params.conv_states_k[self.layer_idx] = new_conv_state_k
933
+ cache_params.conv_states_v[self.layer_idx] = new_conv_state_v
934
+
935
+ q = q.view(batch_size, seq_len, -1, self.head_k_dim)
936
+ k = k.view(batch_size, seq_len, -1, self.head_k_dim)
937
+ v = v.view(batch_size, seq_len, -1, self.head_v_dim)
938
+ if self.num_v_heads > self.num_k_heads:
939
+ expand_ratio = self.num_v_heads // self.num_k_heads
940
+ q = q.repeat_interleave(expand_ratio, dim=2)
941
+ k = k.repeat_interleave(expand_ratio, dim=2)
942
+
943
+ beta = self.b_proj(hidden_states).sigmoid()
944
+ if self.allow_neg_eigval:
945
+ beta = beta * 2.0
946
+ g = -self.A_log.float().exp() * F.softplus(
947
+ self.a_proj(hidden_states).float() + self.dt_bias
948
+ )
949
+
950
+ if use_precomputed:
951
+ output, new_recurrent_state = self.recurrent_gated_delta_rule(
952
+ q,
953
+ k,
954
+ v,
955
+ g=g,
956
+ beta=beta,
957
+ initial_state=recurrent_state,
958
+ output_final_state=use_cache,
959
+ use_qk_l2norm_in_kernel=True,
960
+ )
961
+ else:
962
+ output, new_recurrent_state = self.chunk_gated_delta_rule(
963
+ q,
964
+ k,
965
+ v,
966
+ g=g,
967
+ beta=beta,
968
+ initial_state=recurrent_state,
969
+ output_final_state=use_cache,
970
+ use_qk_l2norm_in_kernel=True,
971
+ )
972
+
973
+ if cache_params is not None:
974
+ cache_params.recurrent_states[self.layer_idx] = new_recurrent_state
975
+
976
+ gate = self.g_proj(hidden_states)
977
+ output = output.reshape(-1, self.head_v_dim)
978
+ gate = gate.reshape(-1, self.head_v_dim)
979
+ output = self.o_norm(output, gate)
980
+ output = output.reshape(batch_size, seq_len, -1)
981
+ output = self.o_proj(output)
982
+ return output
983
+
984
+
985
+ class LizzyLinearAttention(nn.Module):
986
+ def __init__(self, config: LizzyConfig, layer_idx: int) -> None:
987
+ super().__init__()
988
+ self.layer_idx = layer_idx
989
+ self.inner = LizzyHybridGatedDeltaNet(config, layer_idx)
990
+
991
+ def forward(
992
+ self,
993
+ hidden_states: torch.Tensor,
994
+ attention_mask: torch.Tensor | None = None,
995
+ past_key_value: Cache | None = None,
996
+ use_cache: bool = False,
997
+ output_attentions: bool = False,
998
+ **kwargs: Any,
999
+ ) -> tuple[
1000
+ torch.Tensor,
1001
+ Cache | None,
1002
+ torch.Tensor | None,
1003
+ ]:
1004
+ del kwargs, output_attentions
1005
+ output = self.inner(
1006
+ hidden_states=hidden_states,
1007
+ cache_params=(
1008
+ past_key_value if _is_cache_object(past_key_value) else None
1009
+ ),
1010
+ attention_mask=attention_mask,
1011
+ )
1012
+ present = past_key_value if use_cache else None
1013
+ return output, present, None
1014
+
1015
+
1016
+ class LizzyAttention(nn.Module):
1017
+ def __init__(self, config: LizzyConfig, layer_idx: int) -> None:
1018
+ super().__init__()
1019
+ self.is_causal = True
1020
+ self.config = config
1021
+ self.layer_idx = layer_idx
1022
+ self.num_heads = config.num_attention_heads
1023
+ self.num_key_value_heads = config.num_key_value_heads
1024
+ self.num_key_value_groups = self.num_heads // self.num_key_value_heads
1025
+ self.head_dim = config.head_dim
1026
+ self.hidden_size = config.hidden_size
1027
+ self.scaling = self.head_dim**-0.5
1028
+ self.attention_dropout = config.attention_dropout
1029
+ self.position_embedding_type = config.position_embedding_type
1030
+ self.layer_type = (
1031
+ str(config.layer_types[layer_idx])
1032
+ if layer_idx < len(config.layer_types)
1033
+ else "full_attention"
1034
+ )
1035
+ self.use_rope = _get_rope_layer_flag(config, layer_idx)
1036
+ self._rope_type_override = str(
1037
+ dict(config.rope_type_overrides or {}).get(self.layer_type) or ""
1038
+ ) or None
1039
+ if (
1040
+ self._rope_type_override is None
1041
+ and self.layer_type == "sliding_attention"
1042
+ and bool(config.rope_scaling)
1043
+ and config.use_post_attn_norm
1044
+ and config.use_post_mlp_norm
1045
+ and config.use_qk_norm
1046
+ and any(str(item) == "full_attention" for item in config.layer_types)
1047
+ ):
1048
+ self._rope_type_override = "default"
1049
+ self.sliding_window = None
1050
+ if self.layer_type == "sliding_attention":
1051
+ self.sliding_window = config.sliding_window
1052
+
1053
+ q_dim = self.num_heads * self.head_dim
1054
+ kv_dim = self.num_key_value_heads * self.head_dim
1055
+ self.q_proj = nn.Linear(
1056
+ config.hidden_size,
1057
+ q_dim,
1058
+ bias=config.attention_bias,
1059
+ )
1060
+ self.k_proj = nn.Linear(
1061
+ config.hidden_size,
1062
+ kv_dim,
1063
+ bias=config.attention_bias,
1064
+ )
1065
+ self.v_proj = nn.Linear(
1066
+ config.hidden_size,
1067
+ kv_dim,
1068
+ bias=config.attention_bias,
1069
+ )
1070
+ self.o_proj = nn.Linear(
1071
+ q_dim,
1072
+ config.hidden_size,
1073
+ bias=config.attention_bias,
1074
+ )
1075
+ self.q_norm = (
1076
+ _make_norm(config.qk_norm_type, q_dim, config.norm_eps, has_bias=False)
1077
+ if config.use_qk_norm
1078
+ else None
1079
+ )
1080
+ self.k_norm = (
1081
+ _make_norm(config.qk_norm_type, kv_dim, config.norm_eps, has_bias=False)
1082
+ if config.use_qk_norm
1083
+ else None
1084
+ )
1085
+ self._rope_requires_runtime_update = False
1086
+
1087
+ if self.use_rope:
1088
+ rope_scaling = dict(config.rope_scaling or {})
1089
+ rope_type = self._rope_type_override or str(
1090
+ rope_scaling.get("rope_type", rope_scaling.get("type", "default"))
1091
+ )
1092
+ self._rope_requires_runtime_update = rope_type == "dynamic"
1093
+ if self._rope_requires_runtime_update:
1094
+ self.register_buffer("_rope_inv_freq", None, persistent=False)
1095
+ self.register_buffer(
1096
+ "_rope_attention_factor", None, persistent=False,
1097
+ )
1098
+ else:
1099
+ inv_freq, attention_factor = _compute_rope_parameters(
1100
+ config,
1101
+ device=torch.device("cpu"),
1102
+ seq_len=config.max_position_embeddings,
1103
+ rope_type_override=self._rope_type_override,
1104
+ )
1105
+ self.register_buffer("_rope_inv_freq", inv_freq, persistent=False)
1106
+ self.register_buffer(
1107
+ "_rope_attention_factor",
1108
+ torch.tensor(float(attention_factor), dtype=torch.float32),
1109
+ persistent=False,
1110
+ )
1111
+ else:
1112
+ self.register_buffer("_rope_inv_freq", None, persistent=False)
1113
+ self.register_buffer("_rope_attention_factor", None, persistent=False)
1114
+
1115
+ def _build_rope(
1116
+ self,
1117
+ position_ids: torch.Tensor,
1118
+ device: torch.device,
1119
+ dtype: torch.dtype,
1120
+ ) -> tuple[torch.Tensor, torch.Tensor]:
1121
+ if not self.use_rope:
1122
+ msg = "RoPE requested but rope buffer is not initialized."
1123
+ raise RuntimeError(msg)
1124
+
1125
+ inv_freq = self._rope_inv_freq
1126
+ attention_factor_tensor = self._rope_attention_factor
1127
+ if (
1128
+ inv_freq is None
1129
+ or attention_factor_tensor is None
1130
+ or self._rope_requires_runtime_update
1131
+ ):
1132
+ # Keep the sequence-length hint as a tensor so TorchDynamo/vLLM
1133
+ # can trace this path without requiring capture_scalar_outputs.
1134
+ # When low-memory loading leaves the non-persistent cache unset,
1135
+ # rebuild from config for this forward only instead of mutating
1136
+ # buffers inside the compiled graph.
1137
+ seq_len = (
1138
+ torch.max(position_ids) + 1 if position_ids.numel() > 0 else None
1139
+ )
1140
+ inv_freq, attention_factor = _compute_rope_parameters(
1141
+ self.config,
1142
+ device=device,
1143
+ seq_len=seq_len,
1144
+ rope_type_override=self._rope_type_override,
1145
+ )
1146
+ attention_factor_tensor = torch.tensor(
1147
+ float(attention_factor),
1148
+ device=device,
1149
+ dtype=torch.float32,
1150
+ )
1151
+ else:
1152
+ inv_freq = inv_freq.to(device=device)
1153
+ attention_factor_tensor = attention_factor_tensor.to(
1154
+ device=device,
1155
+ dtype=torch.float32,
1156
+ )
1157
+
1158
+ # Mirror the upstream HF decoder-only rotary path closely here.
1159
+ # The matmul-based construction is slightly more numerically stable
1160
+ # than the generic einsum formulation for strict parity probes.
1161
+ inv_freq_expanded = (
1162
+ inv_freq[None, :, None]
1163
+ .to(device=device, dtype=torch.float32)
1164
+ .expand(position_ids.shape[0], -1, 1)
1165
+ )
1166
+ position_ids_expanded = position_ids[:, None, :].to(torch.float32)
1167
+ angles = torch.matmul(
1168
+ inv_freq_expanded,
1169
+ position_ids_expanded,
1170
+ ).transpose(1, 2)
1171
+ angles = torch.cat((angles, angles), dim=-1)
1172
+ cos = angles.cos().unsqueeze(1) * attention_factor_tensor
1173
+ sin = angles.sin().unsqueeze(1) * attention_factor_tensor
1174
+ cos = cos.to(dtype)
1175
+ sin = sin.to(dtype)
1176
+ return cos, sin
1177
+
1178
+ def forward(
1179
+ self,
1180
+ hidden_states: torch.Tensor,
1181
+ attention_mask: torch.Tensor | None = None,
1182
+ position_ids: torch.Tensor | None = None,
1183
+ past_key_value: Cache | tuple[torch.Tensor, torch.Tensor] | None = None,
1184
+ cache_position: torch.Tensor | None = None,
1185
+ use_cache: bool = False,
1186
+ output_attentions: bool = False,
1187
+ **kwargs: Any,
1188
+ ) -> tuple[
1189
+ torch.Tensor,
1190
+ Cache | tuple[torch.Tensor, torch.Tensor] | None,
1191
+ torch.Tensor | None,
1192
+ ]:
1193
+ batch_size, q_len, _ = hidden_states.shape
1194
+ cache_position = _normalize_cache_position(cache_position)
1195
+
1196
+ query_states = self.q_proj(hidden_states)
1197
+ key_states = self.k_proj(hidden_states)
1198
+ value_states = self.v_proj(hidden_states)
1199
+
1200
+ if self.q_norm is not None:
1201
+ query_states = self.q_norm(query_states)
1202
+ if self.k_norm is not None:
1203
+ key_states = self.k_norm(key_states)
1204
+
1205
+ query_states = query_states.view(
1206
+ batch_size, q_len, self.num_heads, self.head_dim,
1207
+ )
1208
+ query_states = query_states.transpose(1, 2)
1209
+ key_states = key_states.view(
1210
+ batch_size,
1211
+ q_len,
1212
+ self.num_key_value_heads,
1213
+ self.head_dim,
1214
+ )
1215
+ key_states = key_states.transpose(1, 2)
1216
+ value_states = value_states.view(
1217
+ batch_size,
1218
+ q_len,
1219
+ self.num_key_value_heads,
1220
+ self.head_dim,
1221
+ )
1222
+ value_states = value_states.transpose(1, 2)
1223
+
1224
+ if self.use_rope:
1225
+ if position_ids is None:
1226
+ msg = "position_ids are required for rope attention."
1227
+ raise ValueError(msg)
1228
+ cos, sin = self._build_rope(
1229
+ position_ids, hidden_states.device, query_states.dtype,
1230
+ )
1231
+ query_states, key_states = _apply_rotary_pos_emb(
1232
+ query_states,
1233
+ key_states,
1234
+ cos,
1235
+ sin,
1236
+ )
1237
+
1238
+ if _is_cache_object(past_key_value):
1239
+ if use_cache:
1240
+ key_states, value_states = past_key_value.update(
1241
+ key_states,
1242
+ value_states,
1243
+ self.layer_idx,
1244
+ cache_kwargs={"cache_position": cache_position},
1245
+ )
1246
+ present_key_value = past_key_value
1247
+ elif self.layer_idx < len(past_key_value):
1248
+ past_key, past_value = past_key_value[self.layer_idx]
1249
+ if past_key is not None and past_value is not None:
1250
+ key_states = torch.cat([past_key, key_states], dim=2)
1251
+ value_states = torch.cat([past_value, value_states], dim=2)
1252
+ present_key_value = None
1253
+ else:
1254
+ present_key_value = None
1255
+ elif past_key_value is not None:
1256
+ past_key, past_value = past_key_value
1257
+ key_states = torch.cat([past_key, key_states], dim=2)
1258
+ value_states = torch.cat([past_value, value_states], dim=2)
1259
+ present_key_value = (key_states, value_states) if use_cache else None
1260
+ else:
1261
+ present_key_value = (key_states, value_states) if use_cache else None
1262
+
1263
+ attention_interface = None
1264
+ attn_impl = getattr(self.config, "_attn_implementation", "eager")
1265
+ if attn_impl == "flex_attention" and self.head_dim < 16:
1266
+ attn_impl = "sdpa"
1267
+ if attn_impl != "eager":
1268
+ attention_interface = ALL_ATTENTION_FUNCTIONS.get(attn_impl)
1269
+
1270
+ if attention_interface is not None:
1271
+ attn_output, attn_weights = attention_interface(
1272
+ self,
1273
+ query_states,
1274
+ key_states,
1275
+ value_states,
1276
+ attention_mask,
1277
+ dropout=0.0 if not self.training else self.attention_dropout,
1278
+ scaling=self.scaling,
1279
+ sliding_window=self.sliding_window,
1280
+ **kwargs,
1281
+ )
1282
+ attn_output = attn_output.contiguous()
1283
+ else:
1284
+ if self.num_key_value_heads != self.num_heads:
1285
+ key_states = key_states.repeat_interleave(
1286
+ self.num_key_value_groups, dim=1,
1287
+ )
1288
+ value_states = value_states.repeat_interleave(
1289
+ self.num_key_value_groups, dim=1,
1290
+ )
1291
+ attn_weights = torch.matmul(
1292
+ query_states,
1293
+ key_states.transpose(-1, -2),
1294
+ ) * self.scaling
1295
+ if attention_mask is not None:
1296
+ attn_weights = attn_weights + attention_mask
1297
+ attn_weights = F.softmax(attn_weights, dim=-1, dtype=torch.float32)
1298
+ attn_weights = attn_weights.to(query_states.dtype)
1299
+ attn_weights = F.dropout(
1300
+ attn_weights,
1301
+ p=self.attention_dropout if self.training else 0.0,
1302
+ training=self.training,
1303
+ )
1304
+ attn_output = torch.matmul(attn_weights, value_states)
1305
+ attn_output = attn_output.transpose(1, 2).contiguous()
1306
+
1307
+ attn_output = attn_output.reshape(batch_size, q_len, -1).contiguous()
1308
+ attn_output = self.o_proj(attn_output)
1309
+ if not output_attentions:
1310
+ attn_weights = None
1311
+ return attn_output, present_key_value, attn_weights
1312
+
1313
+
1314
+ def _refresh_attention_rope_buffers(module: nn.Module) -> None:
1315
+ """Rebuild non-persistent RoPE buffers after checkpoint load."""
1316
+
1317
+ for child in module.modules():
1318
+ if not isinstance(child, LizzyAttention):
1319
+ continue
1320
+
1321
+ should_use_rope = _get_rope_layer_flag(child.config, child.layer_idx)
1322
+ child.use_rope = should_use_rope
1323
+ if not should_use_rope:
1324
+ child._rope_requires_runtime_update = False
1325
+ child._rope_inv_freq = None
1326
+ child._rope_attention_factor = None
1327
+ continue
1328
+
1329
+ rope_scaling = dict(child.config.rope_scaling or {})
1330
+ rope_type = child._rope_type_override or str(
1331
+ rope_scaling.get("rope_type", rope_scaling.get("type", "default"))
1332
+ )
1333
+ child._rope_requires_runtime_update = rope_type == "dynamic"
1334
+ if child._rope_requires_runtime_update:
1335
+ child._rope_inv_freq = None
1336
+ child._rope_attention_factor = None
1337
+ continue
1338
+
1339
+ # These buffers are derived from config rather than serialized weights.
1340
+ # Recompute them after load so low-memory materialization cannot leave
1341
+ # stale or uninitialized rotary state behind.
1342
+ inv_freq, attention_factor = _compute_rope_parameters(
1343
+ child.config,
1344
+ device=torch.device("cpu"),
1345
+ seq_len=child.config.max_position_embeddings,
1346
+ rope_type_override=child._rope_type_override,
1347
+ )
1348
+ child._rope_inv_freq = inv_freq
1349
+ child._rope_attention_factor = torch.tensor(
1350
+ float(attention_factor),
1351
+ dtype=torch.float32,
1352
+ )
1353
+
1354
+
1355
+ class LizzyMLP(nn.Module):
1356
+ def __init__(self, config: LizzyConfig) -> None:
1357
+ super().__init__()
1358
+ self.config = config
1359
+ self.act = ACT2FN[config.hidden_act]
1360
+ self.gate_proj = (
1361
+ nn.Linear(
1362
+ config.hidden_size,
1363
+ config.intermediate_size,
1364
+ bias=config.mlp_bias,
1365
+ )
1366
+ if config.mlp_type == "gated"
1367
+ else None
1368
+ )
1369
+ self.up_proj = nn.Linear(
1370
+ config.hidden_size,
1371
+ config.intermediate_size,
1372
+ bias=config.mlp_bias,
1373
+ )
1374
+ self.down_proj = nn.Linear(
1375
+ config.intermediate_size,
1376
+ config.hidden_size,
1377
+ bias=config.mlp_bias,
1378
+ )
1379
+
1380
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
1381
+ if self.gate_proj is None and self.config.mlp_type == "gated":
1382
+ msg = "Missing gated MLP projection layers."
1383
+ raise RuntimeError(msg)
1384
+ if self.config.mlp_type == "gated":
1385
+ if self.gate_proj is None:
1386
+ msg = "Missing gated MLP projection layers."
1387
+ raise RuntimeError(msg)
1388
+ return self.down_proj(self.act(
1389
+ self.gate_proj(hidden_states)) * self.up_proj(hidden_states)
1390
+ )
1391
+ return self.down_proj(self.act(self.up_proj(hidden_states)))
1392
+
1393
+
1394
+ class LizzyDecoderLayer(nn.Module):
1395
+ def __init__(self, config: LizzyConfig, layer_idx: int) -> None:
1396
+ super().__init__()
1397
+ self.layer_type = (
1398
+ str(config.layer_types[layer_idx])
1399
+ if layer_idx < len(config.layer_types)
1400
+ else "full_attention"
1401
+ )
1402
+ self.layer_layout = _get_layer_layout(config, layer_idx)
1403
+ self.self_attn = (
1404
+ LizzyAttention(config, layer_idx)
1405
+ if self.layer_type != "linear_attention"
1406
+ else None
1407
+ )
1408
+ self.linear_attn = (
1409
+ LizzyLinearAttention(config, layer_idx)
1410
+ if self.layer_type == "linear_attention"
1411
+ else None
1412
+ )
1413
+ self.mlp = LizzyMLP(config)
1414
+
1415
+ self.pre_attn_norm = (
1416
+ _make_norm(
1417
+ config.norm_type,
1418
+ config.hidden_size,
1419
+ config.norm_eps,
1420
+ has_bias=config.norm_has_bias,
1421
+ )
1422
+ if self.layer_layout == "decoder_prenorm"
1423
+ else None
1424
+ )
1425
+ self.pre_mlp_norm = (
1426
+ _make_norm(
1427
+ config.norm_type,
1428
+ config.hidden_size,
1429
+ config.norm_eps,
1430
+ has_bias=config.norm_has_bias,
1431
+ )
1432
+ if self.layer_layout == "decoder_prenorm"
1433
+ else None
1434
+ )
1435
+ self.post_attn_norm = (
1436
+ _make_norm(
1437
+ config.norm_type,
1438
+ config.hidden_size,
1439
+ config.norm_eps,
1440
+ has_bias=config.norm_has_bias,
1441
+ )
1442
+ if self.layer_layout == "decoder_postnorm"
1443
+ else None
1444
+ )
1445
+ self.post_mlp_norm = (
1446
+ _make_norm(
1447
+ config.norm_type,
1448
+ config.hidden_size,
1449
+ config.norm_eps,
1450
+ has_bias=config.norm_has_bias
1451
+ )
1452
+ if self.layer_layout == "decoder_postnorm"
1453
+ else None
1454
+ )
1455
+
1456
+ def forward(
1457
+ self,
1458
+ hidden_states: torch.Tensor,
1459
+ attention_mask: torch.Tensor | None = None,
1460
+ position_ids: torch.Tensor | None = None,
1461
+ past_key_value: Cache | tuple[torch.Tensor, torch.Tensor] | None = None,
1462
+ cache_position: torch.Tensor | None = None,
1463
+ use_cache: bool = False,
1464
+ output_attentions: bool = False,
1465
+ **kwargs: Any,
1466
+ ) -> tuple[
1467
+ torch.Tensor,
1468
+ Cache | tuple[torch.Tensor, torch.Tensor] | None,
1469
+ torch.Tensor | None,
1470
+ ]:
1471
+ residual = hidden_states
1472
+ attn_inputs = (
1473
+ self.pre_attn_norm(hidden_states)
1474
+ if self.pre_attn_norm is not None
1475
+ else hidden_states
1476
+ )
1477
+ if self.linear_attn is not None:
1478
+ attn_output, present_key_value, attn_weights = self.linear_attn(
1479
+ attn_inputs,
1480
+ attention_mask=attention_mask,
1481
+ past_key_value=(
1482
+ past_key_value if _is_cache_object(past_key_value) else None
1483
+ ),
1484
+ use_cache=use_cache,
1485
+ output_attentions=output_attentions,
1486
+ **kwargs,
1487
+ )
1488
+ else:
1489
+ assert self.self_attn is not None
1490
+ attn_output, present_key_value, attn_weights = self.self_attn(
1491
+ attn_inputs,
1492
+ attention_mask=attention_mask,
1493
+ position_ids=position_ids,
1494
+ past_key_value=past_key_value,
1495
+ cache_position=cache_position,
1496
+ use_cache=use_cache,
1497
+ output_attentions=output_attentions,
1498
+ **kwargs,
1499
+ )
1500
+ if self.post_attn_norm is not None:
1501
+ attn_output = self.post_attn_norm(attn_output)
1502
+ hidden_states = residual + attn_output
1503
+
1504
+ residual = hidden_states
1505
+ mlp_inputs = (
1506
+ self.pre_mlp_norm(hidden_states)
1507
+ if self.pre_mlp_norm is not None
1508
+ else hidden_states
1509
+ )
1510
+ mlp_output = self.mlp(mlp_inputs)
1511
+ if self.post_mlp_norm is not None:
1512
+ mlp_output = self.post_mlp_norm(mlp_output)
1513
+ hidden_states = residual + mlp_output
1514
+ return hidden_states, present_key_value, attn_weights
1515
+
1516
+
1517
+ class LizzyPreTrainedModel(PreTrainedModel):
1518
+ config_class = LizzyConfig
1519
+ base_model_prefix = "model"
1520
+ supports_gradient_checkpointing = True
1521
+ _no_split_modules = ["LizzyDecoderLayer"]
1522
+ _skip_keys_device_placement = ["past_key_values"]
1523
+ _supports_flash_attn = True
1524
+ _supports_sdpa = True
1525
+ _supports_flex_attn = True
1526
+ _supports_attention_backend = True
1527
+
1528
+ def _init_weights(self, module: nn.Module) -> None:
1529
+ std = self.config.initializer_range
1530
+ if isinstance(module, nn.Linear):
1531
+ module.weight.data.normal_(mean=0.0, std=std)
1532
+ if module.bias is not None:
1533
+ module.bias.data.zero_()
1534
+ elif isinstance(module, nn.Embedding):
1535
+ module.weight.data.normal_(mean=0.0, std=std)
1536
+ if module.padding_idx is not None:
1537
+ module.weight.data[module.padding_idx].zero_()
1538
+ elif isinstance(module, (LizzyRMSNorm, nn.LayerNorm)):
1539
+ if hasattr(module, "weight") and module.weight is not None:
1540
+ module.weight.data.fill_(1.0)
1541
+ if hasattr(module, "bias") and module.bias is not None:
1542
+ module.bias.data.zero_()
1543
+
1544
+ @classmethod
1545
+ def from_pretrained(
1546
+ cls,
1547
+ pretrained_model_name_or_path: str | os.PathLike[str] | None,
1548
+ *model_args: Any,
1549
+ **kwargs: Any,
1550
+ ) -> "LizzyPreTrainedModel":
1551
+ model = cast(
1552
+ "LizzyPreTrainedModel",
1553
+ super().from_pretrained(
1554
+ pretrained_model_name_or_path,
1555
+ *model_args,
1556
+ **kwargs,
1557
+ ),
1558
+ )
1559
+ _refresh_attention_rope_buffers(model)
1560
+ if hasattr(model, "lm_head") and hasattr(model, "model"):
1561
+ tied_weights_keys = getattr(type(model), "_tied_weights_keys", None)
1562
+ if isinstance(tied_weights_keys, dict) and tied_weights_keys:
1563
+ model._tied_weights_keys = dict(tied_weights_keys)
1564
+ else:
1565
+ model._tied_weights_keys = {
1566
+ "lm_head.weight": "model.embed_tokens.weight",
1567
+ }
1568
+ model._tp_plan = {"lm_head": "colwise_rep"}
1569
+ model._pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
1570
+ return model
1571
+
1572
+ def load_state_dict( # type: ignore[override]
1573
+ self,
1574
+ state_dict: dict[str, torch.Tensor],
1575
+ strict: bool = True,
1576
+ assign: bool = False,
1577
+ ) -> Any:
1578
+ remapped_state_dict: dict[str, torch.Tensor] = {}
1579
+ for key, value in state_dict.items():
1580
+ remapped_key = key
1581
+ if ".mlp.fc_in." in key:
1582
+ remapped_key = key.replace(".mlp.fc_in.", ".mlp.up_proj.")
1583
+ elif ".mlp.fc_out." in key:
1584
+ remapped_key = key.replace(".mlp.fc_out.", ".mlp.down_proj.")
1585
+
1586
+ existing = remapped_state_dict.get(remapped_key)
1587
+ if existing is not None and not torch.equal(existing, value):
1588
+ msg = (
1589
+ f"Conflicting legacy Lizzy MLP tensors"
1590
+ f" for key: {remapped_key}"
1591
+ )
1592
+ raise ValueError(msg)
1593
+ remapped_state_dict[remapped_key] = value
1594
+
1595
+ load_result = super().load_state_dict(
1596
+ remapped_state_dict,
1597
+ strict=strict,
1598
+ assign=assign,
1599
+ )
1600
+ # RoPE buffers are intentionally non-persistent, so refresh them after
1601
+ # weight loading instead of trusting constructor-time allocations.
1602
+ _refresh_attention_rope_buffers(self)
1603
+ return load_result
1604
+
1605
+
1606
+ class LizzyModel(LizzyPreTrainedModel):
1607
+ def __init__(self, config: LizzyConfig) -> None:
1608
+ super().__init__(config)
1609
+ self.padding_idx = config.pad_token_id
1610
+ self.vocab_size = config.vocab_size
1611
+ self.embed_tokens = nn.Embedding(
1612
+ config.vocab_size,
1613
+ config.hidden_size,
1614
+ self.padding_idx,
1615
+ )
1616
+ self.embed_positions = (
1617
+ nn.Embedding(config.max_position_embeddings, config.hidden_size)
1618
+ if config.position_embedding_type == "absolute"
1619
+ else None
1620
+ )
1621
+ self.layers = nn.ModuleList(
1622
+ LizzyDecoderLayer(config, layer_idx)
1623
+ for layer_idx in range(config.num_hidden_layers)
1624
+ )
1625
+ self.norm = _make_norm(
1626
+ config.norm_type,
1627
+ config.hidden_size,
1628
+ config.norm_eps,
1629
+ has_bias=config.norm_has_bias,
1630
+ )
1631
+ self.embd_dropout = nn.Dropout(config.embd_dropout)
1632
+ self.gradient_checkpointing = False
1633
+ self.post_init()
1634
+
1635
+ def get_input_embeddings(self) -> nn.Embedding:
1636
+ return self.embed_tokens
1637
+
1638
+ def set_input_embeddings(self, value: nn.Embedding) -> None:
1639
+ self.embed_tokens = value
1640
+
1641
+ def _build_attention_mask(
1642
+ self,
1643
+ attention_mask: torch.Tensor | None,
1644
+ *,
1645
+ batch_size: int,
1646
+ q_len: int,
1647
+ kv_len: int,
1648
+ kv_offset: int,
1649
+ cache_position: torch.Tensor,
1650
+ device: torch.device,
1651
+ dtype: torch.dtype,
1652
+ sliding_window: int | None = None,
1653
+ ) -> torch.Tensor:
1654
+ kv_len = (
1655
+ int(kv_len.item()) if isinstance(kv_len, torch.Tensor) else int(kv_len)
1656
+ )
1657
+ kv_offset = (
1658
+ int(kv_offset.item())
1659
+ if isinstance(kv_offset, torch.Tensor)
1660
+ else int(kv_offset)
1661
+ )
1662
+ min_value = torch.finfo(dtype).min
1663
+ source_positions = cache_position.to(device=device).view(-1, 1)
1664
+ target_positions = torch.arange(
1665
+ kv_offset,
1666
+ kv_offset + kv_len,
1667
+ device=device,
1668
+ ).unsqueeze(0)
1669
+ causal = torch.zeros((q_len, kv_len), dtype=dtype, device=device)
1670
+ causal = causal.masked_fill(target_positions > source_positions, min_value)
1671
+ if sliding_window is not None:
1672
+ lower_bound = source_positions - int(sliding_window) + 1
1673
+ causal = causal.masked_fill(target_positions < lower_bound, min_value)
1674
+ causal = causal.unsqueeze(0).unsqueeze(0).expand(batch_size, 1, -1, -1)
1675
+
1676
+ if attention_mask is None:
1677
+ return causal
1678
+
1679
+ if attention_mask.dim() != 2:
1680
+ msg = "attention_mask must be 2D [batch, sequence]."
1681
+ raise ValueError(msg)
1682
+ if attention_mask.shape[1] < kv_len:
1683
+ pad = torch.ones(
1684
+ (attention_mask.shape[0], kv_len - attention_mask.shape[1]),
1685
+ dtype=attention_mask.dtype,
1686
+ device=attention_mask.device,
1687
+ )
1688
+ attention_mask = torch.cat([pad, attention_mask], dim=1)
1689
+ elif attention_mask.shape[1] > kv_len:
1690
+ attention_mask = attention_mask[:, -kv_len:]
1691
+
1692
+ expanded = attention_mask[:, None, None, :].to(device=device)
1693
+ padding = (expanded == 0).to(dtype) * min_value
1694
+ return causal + padding
1695
+
1696
+ def forward(
1697
+ self,
1698
+ input_ids: torch.LongTensor | None = None,
1699
+ attention_mask: torch.Tensor | dict[str, torch.Tensor] | None = None,
1700
+ position_ids: torch.LongTensor | None = None,
1701
+ past_key_values: Cache | tuple[
1702
+ tuple[torch.Tensor, torch.Tensor], ...
1703
+ ] | None = None,
1704
+ inputs_embeds: torch.FloatTensor | None = None,
1705
+ cache_position: torch.LongTensor | None = None,
1706
+ use_cache: bool | None = None,
1707
+ output_attentions: bool | None = None,
1708
+ output_hidden_states: bool | None = None,
1709
+ return_dict: bool | None = None,
1710
+ **kwargs: Any,
1711
+ ) -> BaseModelOutputWithPast | tuple[Any, ...]:
1712
+ if (input_ids is None) == (inputs_embeds is None):
1713
+ msg = "Exactly one of input_ids or inputs_embeds must be provided."
1714
+ raise ValueError(msg)
1715
+
1716
+ output_attentions = (
1717
+ bool(output_attentions) if output_attentions is not None else False
1718
+ )
1719
+ output_hidden_states = (
1720
+ bool(output_hidden_states)
1721
+ if output_hidden_states is not None
1722
+ else False
1723
+ )
1724
+ use_cache = (
1725
+ bool(use_cache)
1726
+ if use_cache is not None
1727
+ else bool(self.config.use_cache)
1728
+ )
1729
+ return_dict = bool(return_dict) if return_dict is not None else True
1730
+
1731
+ if inputs_embeds is None:
1732
+ hidden_states = self.embed_tokens(input_ids)
1733
+ batch_size, seq_len = input_ids.shape
1734
+ else:
1735
+ hidden_states = inputs_embeds
1736
+ batch_size, seq_len, _ = inputs_embeds.shape
1737
+
1738
+ cache_object = (
1739
+ past_key_values
1740
+ if _is_cache_object(past_key_values)
1741
+ else None
1742
+ )
1743
+ if use_cache and _has_linear_attention(self.config):
1744
+ # Transformers 5.4 seeds `generate()` with an empty DynamicCache
1745
+ # for standard causal decoders. Hybrid Lizzy checkpoints need the
1746
+ # mixed cache below instead, because linear-attention layers read
1747
+ # DeltaNet convolution/recurrent state during the prefill pass.
1748
+ if cache_object is not None and not isinstance(
1749
+ cache_object, LizzyHybridDynamicCache,
1750
+ ):
1751
+ if int(cache_object.get_seq_length()) > 0:
1752
+ msg = (
1753
+ "Hybrid Lizzy checkpoints require "
1754
+ "LizzyHybridDynamicCache once generation cache "
1755
+ "state is populated."
1756
+ )
1757
+ raise ValueError(msg)
1758
+ cache_object = LizzyHybridDynamicCache(config=self.config)
1759
+ past_key_values = cache_object
1760
+ if use_cache and cache_object is None and past_key_values is None:
1761
+ if _has_linear_attention(self.config):
1762
+ # Linear-attention checkpoints need a mixed cache that can hold
1763
+ # both KV tensors and recurrent DeltaNet state.
1764
+ cache_object = LizzyHybridDynamicCache(config=self.config)
1765
+ else:
1766
+ cache_object = DynamicCache()
1767
+ past_key_values = cache_object
1768
+
1769
+ if cache_object is not None:
1770
+ past_length = int(cache_object.get_seq_length())
1771
+ else:
1772
+ past_length = _legacy_cache_length(past_key_values)
1773
+
1774
+ cache_position = _normalize_cache_position(cache_position)
1775
+ if cache_position is None:
1776
+ cache_position = torch.arange(
1777
+ past_length,
1778
+ past_length + seq_len,
1779
+ dtype=torch.long,
1780
+ device=hidden_states.device,
1781
+ )
1782
+
1783
+ if position_ids is None:
1784
+ position_ids = cache_position.unsqueeze(0).expand(batch_size, -1)
1785
+
1786
+ if self.embed_positions is not None:
1787
+ hidden_states = hidden_states + self.embed_positions(position_ids)
1788
+
1789
+ hidden_states = self.embd_dropout(hidden_states)
1790
+
1791
+ if self.training and self.gradient_checkpointing:
1792
+ use_cache = False
1793
+
1794
+ layer_types = list(self.config.layer_types)
1795
+ if not layer_types:
1796
+ layer_types = ["full_attention"] * len(self.layers)
1797
+ has_linear_attention = any(
1798
+ str(layer_type) == "linear_attention" for layer_type in layer_types
1799
+ )
1800
+
1801
+ _attn_impl = getattr(self.config, "_attn_implementation", "eager")
1802
+ if has_linear_attention and isinstance(attention_mask, dict):
1803
+ linear_attention_mask = attention_mask.get("linear_attention")
1804
+ else:
1805
+ linear_attention_mask = attention_mask
1806
+ if (
1807
+ has_linear_attention
1808
+ and cache_object is not None
1809
+ and getattr(cache_object, "has_previous_state", False)
1810
+ ):
1811
+ linear_attention_mask = None
1812
+ elif (
1813
+ has_linear_attention
1814
+ and attention_mask is not None
1815
+ and not isinstance(attention_mask, dict)
1816
+ and torch.all(attention_mask == 1)
1817
+ ):
1818
+ linear_attention_mask = None
1819
+
1820
+ if (
1821
+ _attn_impl == "flash_attention_2"
1822
+ and not isinstance(attention_mask, dict)
1823
+ ):
1824
+ # Flash attention handles causal masking (via is_causal) and
1825
+ # padding (via 2D mask) natively; skip building a 4D mask.
1826
+ attention_mask_mapping = {
1827
+ lt: attention_mask
1828
+ for lt in dict.fromkeys(layer_types)
1829
+ if lt != "linear_attention"
1830
+ }
1831
+ elif _attn_impl == "sdpa" and attention_mask is None:
1832
+ attention_mask_mapping = {}
1833
+ for layer_type in dict.fromkeys(layer_types):
1834
+ if layer_type == "linear_attention":
1835
+ continue
1836
+ if layer_type == "full_attention":
1837
+ # Match upstream decoder-only HF models: when SDPA sees
1838
+ # plain causal full attention with no padding mask to
1839
+ # preserve, let it use its native is_causal fast-path
1840
+ # instead of forcing an explicit 4D bias tensor.
1841
+ attention_mask_mapping[layer_type] = None
1842
+ continue
1843
+ layer_idx = layer_types.index(layer_type)
1844
+ if cache_object is not None:
1845
+ kv_len, kv_offset = cache_object.get_mask_sizes(
1846
+ seq_len, layer_idx,
1847
+ )
1848
+ else:
1849
+ kv_len = past_length + seq_len
1850
+ kv_offset = 0
1851
+ attention_mask_mapping[layer_type] = self._build_attention_mask(
1852
+ attention_mask,
1853
+ batch_size=batch_size,
1854
+ q_len=seq_len,
1855
+ kv_len=kv_len,
1856
+ kv_offset=kv_offset,
1857
+ cache_position=cache_position,
1858
+ device=hidden_states.device,
1859
+ dtype=hidden_states.dtype,
1860
+ sliding_window=(
1861
+ self.config.sliding_window
1862
+ if layer_type == "sliding_attention"
1863
+ else None
1864
+ ),
1865
+ )
1866
+ elif isinstance(attention_mask, dict):
1867
+ attention_mask_mapping = {
1868
+ key: value
1869
+ for key, value in attention_mask.items()
1870
+ if key != "linear_attention"
1871
+ }
1872
+ else:
1873
+ attention_mask_mapping: dict[str, torch.Tensor] = {}
1874
+ for layer_type in dict.fromkeys(layer_types):
1875
+ if layer_type == "linear_attention":
1876
+ continue
1877
+ layer_idx = layer_types.index(layer_type)
1878
+ if cache_object is not None:
1879
+ kv_len, kv_offset = cache_object.get_mask_sizes(
1880
+ seq_len, layer_idx,
1881
+ )
1882
+ else:
1883
+ kv_len = past_length + seq_len
1884
+ kv_offset = 0
1885
+ attention_mask_mapping[layer_type] = self._build_attention_mask(
1886
+ attention_mask,
1887
+ batch_size=batch_size,
1888
+ q_len=seq_len,
1889
+ kv_len=kv_len,
1890
+ kv_offset=kv_offset,
1891
+ cache_position=cache_position,
1892
+ device=hidden_states.device,
1893
+ dtype=hidden_states.dtype,
1894
+ sliding_window=(
1895
+ self.config.sliding_window
1896
+ if layer_type == "sliding_attention"
1897
+ else None
1898
+ ),
1899
+ )
1900
+
1901
+ all_hidden_states = [] if output_hidden_states else None
1902
+ all_attentions = [] if output_attentions else None
1903
+ next_cache = (
1904
+ cache_object
1905
+ if cache_object is not None
1906
+ else ([] if use_cache else None)
1907
+ )
1908
+ gradient_checkpointing_func = getattr(
1909
+ self,
1910
+ "_gradient_checkpointing_func",
1911
+ checkpoint,
1912
+ )
1913
+
1914
+ for idx, layer in enumerate(self.layers):
1915
+ if output_hidden_states and all_hidden_states is not None:
1916
+ all_hidden_states.append(hidden_states)
1917
+ layer_type = (
1918
+ layer_types[idx]
1919
+ if idx < len(layer_types)
1920
+ else "full_attention"
1921
+ )
1922
+ if layer_type == "linear_attention":
1923
+ layer_attention_mask = linear_attention_mask
1924
+ else:
1925
+ layer_attention_mask = attention_mask_mapping[layer_type]
1926
+
1927
+ if cache_object is not None:
1928
+ layer_past: Cache | tuple[
1929
+ torch.Tensor, torch.Tensor
1930
+ ] | None = cache_object
1931
+ elif past_key_values is not None:
1932
+ layer_past = past_key_values[idx]
1933
+ if layer_past is not None and layer_past[0] is None:
1934
+ layer_past = None
1935
+ else:
1936
+ layer_past = None
1937
+
1938
+ if self.training and self.gradient_checkpointing:
1939
+ def custom_forward(hidden_states: torch.Tensor) -> Any:
1940
+ layer_outputs = layer(
1941
+ hidden_states,
1942
+ attention_mask=layer_attention_mask,
1943
+ position_ids=position_ids,
1944
+ past_key_value=None,
1945
+ cache_position=cache_position,
1946
+ use_cache=False,
1947
+ output_attentions=output_attentions,
1948
+ **kwargs,
1949
+ )
1950
+ if output_attentions:
1951
+ return layer_outputs[0], layer_outputs[2]
1952
+ return layer_outputs[0]
1953
+
1954
+ checkpointed_outputs = gradient_checkpointing_func(
1955
+ custom_forward, hidden_states,
1956
+ )
1957
+ if output_attentions:
1958
+ hidden_states, attn_weights = checkpointed_outputs
1959
+ else:
1960
+ hidden_states = checkpointed_outputs
1961
+ attn_weights = None
1962
+ present = None
1963
+ else:
1964
+ hidden_states, present, attn_weights = layer(
1965
+ hidden_states,
1966
+ attention_mask=layer_attention_mask,
1967
+ position_ids=position_ids,
1968
+ past_key_value=layer_past,
1969
+ cache_position=cache_position,
1970
+ use_cache=use_cache,
1971
+ output_attentions=output_attentions,
1972
+ **kwargs,
1973
+ )
1974
+ if use_cache and next_cache is not None and cache_object is None:
1975
+ next_cache.append(present)
1976
+
1977
+ if output_attentions and all_attentions is not None:
1978
+ all_attentions.append(attn_weights)
1979
+
1980
+ hidden_states = self.norm(hidden_states)
1981
+ if output_hidden_states and all_hidden_states is not None:
1982
+ all_hidden_states.append(hidden_states)
1983
+
1984
+ past_key_values_output: Cache | tuple[
1985
+ tuple[torch.Tensor, torch.Tensor], ...
1986
+ ] | None = None
1987
+ if use_cache and next_cache is not None:
1988
+ if cache_object is not None:
1989
+ past_key_values_output = cache_object
1990
+ else:
1991
+ past_key_values_output = tuple(next_cache)
1992
+
1993
+ if not return_dict:
1994
+ output: tuple[Any, ...] = (hidden_states,)
1995
+ if past_key_values_output is not None:
1996
+ output = output + (past_key_values_output,)
1997
+ if output_hidden_states and all_hidden_states is not None:
1998
+ output = output + (tuple(all_hidden_states),)
1999
+ if output_attentions and all_attentions is not None:
2000
+ output = output + (tuple(all_attentions),)
2001
+ return output
2002
+
2003
+ return BaseModelOutputWithPast(
2004
+ last_hidden_state=hidden_states,
2005
+ past_key_values=past_key_values_output,
2006
+ hidden_states=(
2007
+ tuple(all_hidden_states)
2008
+ if all_hidden_states is not None
2009
+ else None
2010
+ ),
2011
+ attentions=(
2012
+ tuple(all_attentions)
2013
+ if all_attentions is not None
2014
+ else None
2015
+ ),
2016
+ )
2017
+
2018
+
2019
+ class LizzyForCausalLM(LizzyPreTrainedModel, GenerationMixin):
2020
+ config_class = LizzyConfig
2021
+ # Transformers 5.4 expects an expanded target->source mapping here rather than
2022
+ # the older list-based shorthand.
2023
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}
2024
+ _tp_plan = {"lm_head": "colwise_rep"}
2025
+ _pp_plan = {"lm_head": (["hidden_states"], ["logits"])}
2026
+
2027
+ def __init__(self, config: LizzyConfig) -> None:
2028
+ super().__init__(config)
2029
+ self.model = LizzyModel(config)
2030
+ self.vocab_size = config.vocab_size
2031
+ self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
2032
+ self.post_init()
2033
+
2034
+ def get_input_embeddings(self) -> nn.Embedding:
2035
+ return self.model.get_input_embeddings()
2036
+
2037
+ def set_input_embeddings(self, value: nn.Embedding) -> None:
2038
+ self.model.set_input_embeddings(value)
2039
+
2040
+ def get_output_embeddings(self) -> nn.Module:
2041
+ return self.lm_head
2042
+
2043
+ def set_output_embeddings(self, new_embeddings: nn.Module) -> None:
2044
+ self.lm_head = new_embeddings
2045
+
2046
+ def prepare_inputs_for_generation(
2047
+ self,
2048
+ input_ids: torch.LongTensor,
2049
+ past_key_values: Cache | tuple[
2050
+ tuple[torch.Tensor, torch.Tensor], ...
2051
+ ] | None = None,
2052
+ attention_mask: torch.Tensor | None = None,
2053
+ inputs_embeds: torch.FloatTensor | None = None,
2054
+ cache_position: torch.LongTensor | None = None,
2055
+ **kwargs: Any,
2056
+ ) -> dict[str, Any]:
2057
+ past_length = 0
2058
+ if past_key_values is not None:
2059
+ if _is_cache_object(past_key_values):
2060
+ past_length = int(past_key_values.get_seq_length())
2061
+ else:
2062
+ past_length = _legacy_cache_length(past_key_values)
2063
+
2064
+ cache_position = _normalize_cache_position(cache_position)
2065
+ if cache_position is None:
2066
+ if past_key_values is not None:
2067
+ new_tokens = input_ids.shape[1] - past_length
2068
+ if new_tokens <= 0:
2069
+ new_tokens = 1
2070
+ cache_position = torch.arange(
2071
+ past_length,
2072
+ past_length + new_tokens,
2073
+ device=input_ids.device,
2074
+ )
2075
+ else:
2076
+ cache_position = torch.arange(
2077
+ input_ids.shape[1],
2078
+ device=input_ids.device,
2079
+ )
2080
+
2081
+ if past_key_values is not None:
2082
+ input_ids = input_ids[:, -cache_position.shape[0] :]
2083
+ if attention_mask is not None:
2084
+ attn_mask_idx = (past_length + input_ids.shape[1])
2085
+ attention_mask = attention_mask[:, -attn_mask_idx :]
2086
+
2087
+ if inputs_embeds is not None and past_key_values is None:
2088
+ model_inputs: dict[str, Any] = {"inputs_embeds": inputs_embeds}
2089
+ else:
2090
+ model_inputs = {"input_ids": input_ids.contiguous()}
2091
+
2092
+ model_inputs.update(
2093
+ {
2094
+ "past_key_values": past_key_values,
2095
+ "attention_mask": attention_mask,
2096
+ "cache_position": cache_position,
2097
+ "use_cache": kwargs.get("use_cache", self.config.use_cache),
2098
+ },
2099
+ )
2100
+ return model_inputs
2101
+
2102
+ def forward(
2103
+ self,
2104
+ input_ids: torch.LongTensor | None = None,
2105
+ attention_mask: torch.Tensor | None = None,
2106
+ position_ids: torch.LongTensor | None = None,
2107
+ past_key_values: Cache | tuple[
2108
+ tuple[torch.Tensor, torch.Tensor], ...
2109
+ ] | None = None,
2110
+ inputs_embeds: torch.FloatTensor | None = None,
2111
+ labels: torch.LongTensor | None = None,
2112
+ cache_position: torch.LongTensor | None = None,
2113
+ use_cache: bool | None = None,
2114
+ output_attentions: bool | None = None,
2115
+ output_hidden_states: bool | None = None,
2116
+ return_dict: bool | None = None,
2117
+ logits_to_keep: int | torch.Tensor = 0,
2118
+ **kwargs: Any,
2119
+ ) -> CausalLMOutputWithPast | tuple[Any, ...]:
2120
+ # HF eval loaders call `forward()` without an explicit return_dict,
2121
+ # so local Lizzy exports must normalize the optional flag first.
2122
+ return_dict = bool(return_dict) if return_dict is not None else True
2123
+
2124
+ outputs = self.model(
2125
+ input_ids=input_ids,
2126
+ attention_mask=attention_mask,
2127
+ position_ids=position_ids,
2128
+ past_key_values=past_key_values,
2129
+ inputs_embeds=inputs_embeds,
2130
+ cache_position=cache_position,
2131
+ use_cache=use_cache,
2132
+ output_attentions=output_attentions,
2133
+ output_hidden_states=output_hidden_states,
2134
+ return_dict=return_dict,
2135
+ **kwargs,
2136
+ )
2137
+
2138
+ hidden_states = outputs[0] if not return_dict else outputs.last_hidden_state
2139
+ slice_indices = (
2140
+ slice(-logits_to_keep, None)
2141
+ if isinstance(logits_to_keep, int)
2142
+ else logits_to_keep
2143
+ )
2144
+ if labels is not None:
2145
+ full_logits = self.lm_head(hidden_states)
2146
+ logits = full_logits[:, slice_indices, :]
2147
+ else:
2148
+ full_logits = None
2149
+ logits = self.lm_head(hidden_states[:, slice_indices, :])
2150
+
2151
+ loss = None
2152
+ if labels is not None:
2153
+ shift_logits = full_logits[..., :-1, :].contiguous()
2154
+ shift_labels = labels[..., 1:].contiguous()
2155
+ loss = F.cross_entropy(
2156
+ shift_logits.view(-1, shift_logits.size(-1)),
2157
+ shift_labels.view(-1),
2158
+ )
2159
+
2160
+ if not return_dict:
2161
+ output = (logits,) + outputs[1:]
2162
+ if loss is not None:
2163
+ output = (loss,) + output
2164
+ return output
2165
+
2166
+ return CausalLMOutputWithPast(
2167
+ loss=loss,
2168
+ logits=logits,
2169
+ past_key_values=outputs.past_key_values,
2170
+ hidden_states=outputs.hidden_states,
2171
+ attentions=outputs.attentions,
2172
+ )
quickstart.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Minimal inference example for the private Lizzy 7B checkpoint."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import os
7
+
8
+
9
+ def main() -> None:
10
+ repo_id = os.getenv("FLOWER_MODEL_ID", "flwrlabs/Lizzy-7B")
11
+ print("Model ID:", repo_id)
12
+ print(
13
+ "Data note:",
14
+ "Flower release drafts should always disclose that Flower/Lizzy variants add private synthetic data during both pre-training and post-training to favour British behaviour and knowledge. Those private synthetic datasets are not redistributed in the release pack.",
15
+ )
16
+ print("HF_TOKEN present:", bool(os.getenv("HF_TOKEN")))
17
+ print("This example is intentionally non-executing by default.")
18
+ print("Use one of the snippets below after installing transformers or vLLM:")
19
+ print()
20
+ print("Transformers:")
21
+ print(
22
+ " tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)"
23
+ )
24
+ print(
25
+ " model = AutoModelForCausalLM.from_pretrained(repo_id, trust_remote_code=True, torch_dtype='auto')"
26
+ )
27
+ print()
28
+ print("vLLM:")
29
+ print(
30
+ " python -m vllm.entrypoints.openai.api_server --model "
31
+ "flwrlabs/Lizzy-7B --trust-remote-code --max-model-len 8192"
32
+ )
33
+
34
+
35
+ if __name__ == "__main__":
36
+ main()
quickstart_cli.sh ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ MODEL_ID="${FLOWER_MODEL_ID:-flwrlabs/Lizzy-7B}"
5
+
6
+ echo "HF_TOKEN set: ${HF_TOKEN:+yes}"
7
+ echo "Model: $MODEL_ID"
8
+ echo "Data note: Flower release drafts should always disclose that Flower/Lizzy variants add private synthetic data during both pre-training and post-training to favour British behaviour and knowledge. Those private synthetic datasets are not redistributed in the release pack."
9
+ echo
10
+ echo "Transformers example:"
11
+ echo "python - <<'PY'"
12
+ echo "from transformers import AutoTokenizer, AutoModelForCausalLM"
13
+ echo "repo_id = 'flwrlabs/Lizzy-7B'"
14
+ echo "tokenizer = AutoTokenizer.from_pretrained(repo_id, trust_remote_code=True)"
15
+ echo "model = AutoModelForCausalLM.from_pretrained(repo_id, trust_remote_code=True, torch_dtype='auto')"
16
+ echo "PY"
17
+ echo
18
+ echo "vLLM serve example:"
19
+ echo "python -m vllm.entrypoints.openai.api_server --model $MODEL_ID --trust-remote-code --max-model-len 8192"
special_tokens_map.json ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "bos_token": "<|endoftext|>",
3
+ "eos_token": "<|endoftext|>",
4
+ "pad_token": "<|pad|>",
5
+ "unk_token": "<|endoftext|>"
6
+ }
tokenization_lizzy.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ from transformers import PreTrainedTokenizerFast
8
+
9
+
10
+ class LizzyTokenizerFast(PreTrainedTokenizerFast):
11
+ """Family-agnostic fast tokenizer wrapper for Lizzy checkpoints."""
12
+
13
+ model_input_names = ["input_ids", "attention_mask"]
14
+
15
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
16
+ preserved_keys = (
17
+ "add_prefix_space",
18
+ "add_bos_token",
19
+ "add_eos_token",
20
+ "clean_up_tokenization_spaces",
21
+ "use_default_system_prompt",
22
+ "legacy",
23
+ "fix_mistral_regex",
24
+ )
25
+ preserved_init_attrs = {
26
+ key: kwargs.get(key)
27
+ for key in preserved_keys
28
+ if key in kwargs
29
+ }
30
+ super().__init__(*args, **kwargs)
31
+ init_kwargs = getattr(self, "init_kwargs", {})
32
+ local_payload: dict[str, Any] = {}
33
+ config_path = (
34
+ Path(str(getattr(self, "name_or_path", ""))) / "tokenizer_config.json"
35
+ )
36
+ if config_path.is_file():
37
+ try:
38
+ local_payload = json.loads(config_path.read_text(encoding="utf-8"))
39
+ except Exception:
40
+ local_payload = {}
41
+ for key in preserved_keys:
42
+ value = preserved_init_attrs.get(key, init_kwargs.get(key))
43
+ if value is None:
44
+ value = local_payload.get(key)
45
+ if value is not None:
46
+ setattr(self, key, value)
47
+
48
+ @property
49
+ def all_special_tokens_extended(self) -> list[str]:
50
+ """Compatibility shim for runtimes still expecting the pre-5.4 API."""
51
+ return list(self.all_special_tokens)
tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
tokenizer_config.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": false,
3
+ "add_eos_token": false,
4
+ "add_prefix_space": false,
5
+ "backend": "tokenizers",
6
+ "bos_token": "<|endoftext|>",
7
+ "chat_template": "\\\n {# \u2500\u2500\u2500\u2500\u2500 defaults \u2500\u2500\u2500\u2500\u2500 #}\n {%- if enable_thinking is not defined -%}\n {%- set enable_thinking = true -%}\n {%- endif -%}\n {%- set system_message = \"\" -%}\n {%- set custom_instructions = \"\" -%}\n {%- set identity_preamble = \"You are Lizzy, a helpful British AI assistant built by Flower Labs. When asked about your identity, name, developer, or origin, answer consistently: your name is Lizzy, you were built by Flower Labs, and you are not ChatGPT, DeepSeek, Claude, Gemini, or any other assistant. Do not misattribute your creator or model family.\" -%}\n {%- set default_think_instructions = \"You are a helpful British function-calling AI assistant. You are a British persona and your date cutoff is November 2024, and your model weights are available at https://huggingface.co/flwrlabs. Your role as an assistant involves thoroughly exploring questions through a systematic thinking process before providing the final precise and accurate solutions. This requires engaging in a comprehensive cycle of analysis, summarizing, exploration, reassessment, reflection, backtracking, and iteration to develop well-considered thinking process. Please structure your response into two main sections: Thought and Solution using the specified format: <tool_call> Thought section </tool_call> Solution section. In the Thought section, detail your reasoning process in steps. Each step should include detailed considerations such as analysing questions, summarizing relevant findings, brainstorming new ideas, verifying the accuracy of the current steps, refining any errors, and revisiting previous steps. In the Solution section, based on various attempts, explorations, and reflections from the Thought section, systematically present the final solution that you deem correct. The Solution section should be logical, accurate, and concise and detail necessary steps needed to reach the conclusion.\" -%}\n {%- set default_no_think_instructions = \"You are a helpful British function-calling AI assistant. You are a British persona and your date cutoff is November 2024, and your model weights are available at https://huggingface.co/flwrlabs.\" -%}\n\n {# \u2500\u2500\u2500\u2500\u2500 reasoning mode \u2500\u2500\u2500\u2500\u2500 #}\n {%- if enable_thinking -%}\n {%- set reasoning_mode = \"/think\" -%}\n {%- else -%}\n {%- set reasoning_mode = \"/no_think\" -%}\n {%- endif -%}\n\n {# \u2500\u2500\u2500\u2500\u2500 header (system message) \u2500\u2500\u2500\u2500\u2500 #}\n {{- \"<|im_start|>system\\n\" -}}\n\n {%- if messages[0].role == \"system\" -%}\n {%- set system_message = messages[0].content -%}\n {%- if \"/no_think\" in system_message -%}\n {%- set reasoning_mode = \"/no_think\" -%}\n {%- elif \"/think\" in system_message -%}\n {%- set reasoning_mode = \"/think\" -%}\n {%- endif -%}\n {%- set custom_instructions = system_message.replace(\"/no_think\", \"\").replace(\"/think\", \"\").rstrip() -%}\n {%- endif -%}\n\n {%- if \"/system_override\" in system_message -%}\n {{- identity_preamble + \"\\n\\n\" -}}\n {{- custom_instructions.replace(\"/system_override\", \"\").rstrip() -}}\n {{- \"<|im_end|>\\n\" -}}\n {%- else -%}\n {{- \"## Metadata\\n\\n\" -}}\n {{- \"Knowledge Cutoff Date: June 2025\\n\" -}}\n {%- set today = strftime_now(\"%d %B %Y\") -%}\n {{- \"Today Date: \" ~ today ~ \"\\n\" -}}\n {{- \"Reasoning Mode: \" + reasoning_mode + \"\\n\\n\" -}}\n\n {{- \"## Identity\\n\\n\" -}}\n {{- identity_preamble + \"\\n\\n\" -}}\n\n {{- \"## Custom Instructions\\n\\n\" -}}\n {%- if custom_instructions -%}\n {{- custom_instructions + \"\\n\\n\" -}}\n {%- elif reasoning_mode == \"/think\" -%}\n {{- default_think_instructions + \"\\n\\n\" -}}\n {%- else -%}\n {{- default_no_think_instructions + \"\\n\\n\" -}}\n {%- endif -%}\n\n {%- if xml_tools or python_tools or tools -%}\n {{- \"### Tools\\n\\n\" -}}\n {%- if xml_tools or tools -%}\n {%- if tools -%}\n {%- set xml_tools = tools -%}\n {%- endif -%}\n {%- set ns = namespace(xml_tool_string=\"You may call one or more functions to assist with the user query.\\nYou are provided with function signatures within <tools></tools> XML tags:\\n\\n<tools>\\n\") -%}\n {%- for tool in xml_tools[:] -%} {# The slicing makes sure that xml_tools is a list #}\n {%- set ns.xml_tool_string = ns.xml_tool_string ~ (tool | string) ~ \"\\n\" -%}\n {%- endfor -%}\n {%- set xml_tool_string = ns.xml_tool_string + \"</tools>\\n\\nFor each function call, return a json object with function name and arguments within <tool_call></tool_call> XML tags:\\n<tool_call>\\n{\\\"name\\\": <function-name>, \\\"arguments\\\": <args-json-object>}\\n</tool_call>\" -%}\n {{- xml_tool_string -}}\n {%- endif -%}\n {%- if python_tools -%}\n {%- set ns = namespace(python_tool_string=\"When you send a message containing Python code between '<code>' and '</code>' tags, it will be executed in a stateful Jupyter notebook environment, and you will then be given the output to continued reasoning in an agentic loop.\\n\\nYou can use the following tools in your python code like regular functions:\\n<tools>\\n\") -%}\n {%- for tool in python_tools[:] -%} {# The slicing makes sure that python_tools is a list #}\n {%- set ns.python_tool_string = ns.python_tool_string ~ (tool | string) ~ \"\\n\" -%}\n {%- endfor -%}\n {%- set python_tool_string = ns.python_tool_string + \"</tools>\\n\\nThe state persists between code executions: so variables that you define in one step are still available thereafter.\" -%}\n {{- python_tool_string -}}\n {%- endif -%}\n {{- \"\\n\\n\" -}}\n {{- \"<|im_end|>\\n\" -}}\n {%- endif -%}\n {%- endif -%}\n {# \u2500\u2500\u2500\u2500\u2500 main loop \u2500\u2500\u2500\u2500\u2500 #}\n {%- for message in messages -%}\n {%- set content = message.content if message.content is string else \"\" -%}\n {%- if message.role == \"user\" -%}\n {{ \"<|im_start|>\" + message.role + \"\\n\" + content + \"<|im_end|>\\n\" }}\n {%- elif message.role == \"assistant\" -%}\n {% generation %}\n {%- if reasoning_mode == \"/think\" -%}\n {{ \"<|im_start|>assistant\\n\" + content.lstrip(\"\\n\") + \"<|im_end|>\\n\" }}\n {%- else -%}\n {{ \"<|im_start|>assistant\\n\" + \"<think>\\n\\n</think>\\n\" + content.lstrip(\"\\n\") + \"<|im_end|>\\n\" }}\n {%- endif -%}\n {% endgeneration %}\n {%- elif message.role == \"tool\" -%}\n {{ \"<|im_start|>\" + \"user\\n\" + content + \"<|im_end|>\\n\" }}\n {%- endif -%}\n {%- endfor -%}\n {# \u2500\u2500\u2500\u2500\u2500 generation prompt \u2500\u2500\u2500\u2500\u2500 #}\n {%- if add_generation_prompt -%}\n {%- if reasoning_mode == \"/think\" -%}\n {{ \"<|im_start|>assistant\\n\" }}\n {%- else -%}\n {{ \"<|im_start|>assistant\\n\" + \"<think>\\n\\n</think>\\n\" }}\n {%- endif -%}\n {%- endif -%}\n",
8
+ "clean_up_tokenization_spaces": false,
9
+ "eos_token": "<|endoftext|>",
10
+ "model_max_length": 65536,
11
+ "pad_token": "<|pad|>",
12
+ "tokenizer_class": "TokenizersBackend",
13
+ "unk_token": "<|endoftext|>"
14
+ }
vllm_patches/transformers_lizzy_tp.py ADDED
@@ -0,0 +1,211 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compat patch for Lizzy TP under vLLM's generic Transformers backend."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import torch
8
+ import torch.nn.functional as F
9
+ from torch import nn
10
+
11
+ _PATCH_ATTR = "_flwr_transformers_lizzy_tp_patch_applied"
12
+
13
+
14
+ class _TensorParallelSliceNorm(nn.Module):
15
+ """Apply a full-width checkpoint norm to a TP-local activation slice."""
16
+
17
+ def __init__(self, base_norm: nn.Module, start_idx: int, end_idx: int):
18
+ super().__init__()
19
+ self.start_idx = start_idx
20
+ self.end_idx = end_idx
21
+ self.weight = base_norm.weight
22
+ if getattr(base_norm, "bias", None) is not None:
23
+ self.bias = base_norm.bias
24
+ else:
25
+ self.register_parameter("bias", None)
26
+ self.eps = float(
27
+ getattr(base_norm, "eps", getattr(base_norm, "variance_epsilon", 1e-6)),
28
+ )
29
+ self.norm_kind = (
30
+ "layernorm" if isinstance(base_norm, nn.LayerNorm) else "rmsnorm"
31
+ )
32
+
33
+ @property
34
+ def local_size(self) -> int:
35
+ return self.end_idx - self.start_idx
36
+
37
+ def _slice_param(self, param: torch.Tensor | None) -> torch.Tensor | None:
38
+ if param is None:
39
+ return None
40
+ if param.shape[0] == self.local_size:
41
+ return param
42
+ return param[self.start_idx : self.end_idx]
43
+
44
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
45
+ weight = self.weight
46
+ bias = self.bias
47
+ if hidden_states.shape[-1] != self.weight.shape[0]:
48
+ if hidden_states.shape[-1] != self.local_size:
49
+ msg = (
50
+ "Unexpected hidden size for TP-sliced norm: "
51
+ f"{hidden_states.shape[-1]} "
52
+ f"(expected {self.weight.shape[0]} or {self.local_size})"
53
+ )
54
+ raise RuntimeError(msg)
55
+ weight = self._slice_param(weight)
56
+ bias = self._slice_param(bias)
57
+
58
+ if self.norm_kind == "layernorm":
59
+ return F.layer_norm(
60
+ hidden_states,
61
+ (hidden_states.shape[-1],),
62
+ weight,
63
+ bias,
64
+ self.eps,
65
+ )
66
+
67
+ input_dtype = hidden_states.dtype
68
+ hidden_states_fp32 = hidden_states.to(torch.float32)
69
+ variance = hidden_states_fp32.pow(2).mean(dim=-1, keepdim=True)
70
+ hidden_states_norm = hidden_states_fp32 * torch.rsqrt(variance + self.eps)
71
+ hidden_states_norm = hidden_states_norm.to(input_dtype)
72
+ output = weight * hidden_states_norm
73
+ if bias is not None:
74
+ output = output + bias
75
+ return output
76
+
77
+
78
+ def _maybe_patch_lizzy_attention_for_tp(
79
+ *,
80
+ module: nn.Module,
81
+ prefix: str,
82
+ tp_size: int,
83
+ tp_rank: int,
84
+ log_replacement: Any, # noqa: ANN401
85
+ ) -> None:
86
+ if tp_size <= 1 or type(module).__name__ != "LizzyAttention":
87
+ return
88
+
89
+ num_heads = getattr(module, "num_heads", None)
90
+ num_key_value_heads = getattr(module, "num_key_value_heads", None)
91
+ head_dim = getattr(module, "head_dim", None)
92
+ q_norm = getattr(module, "q_norm", None)
93
+ k_norm = getattr(module, "k_norm", None)
94
+ if not all(
95
+ isinstance(value, int)
96
+ for value in (num_heads, num_key_value_heads, head_dim)
97
+ ):
98
+ return
99
+ if num_heads % tp_size != 0 or num_key_value_heads % tp_size != 0:
100
+ return
101
+
102
+ local_num_heads = num_heads // tp_size
103
+ local_num_key_value_heads = num_key_value_heads // tp_size
104
+ local_q_dim = local_num_heads * head_dim
105
+ local_kv_dim = local_num_key_value_heads * head_dim
106
+
107
+ module.num_heads = local_num_heads
108
+ module.num_key_value_heads = local_num_key_value_heads
109
+ module.num_key_value_groups = local_num_heads // local_num_key_value_heads
110
+
111
+ if q_norm is not None and getattr(q_norm, "weight", None) is not None:
112
+ start = tp_rank * local_q_dim
113
+ end = start + local_q_dim
114
+ module.q_norm = _TensorParallelSliceNorm(q_norm, start, end)
115
+ log_replacement(f"{prefix}.q_norm", q_norm, module.q_norm)
116
+
117
+ if k_norm is not None and getattr(k_norm, "weight", None) is not None:
118
+ start = tp_rank * local_kv_dim
119
+ end = start + local_kv_dim
120
+ module.k_norm = _TensorParallelSliceNorm(k_norm, start, end)
121
+ log_replacement(f"{prefix}.k_norm", k_norm, module.k_norm)
122
+
123
+
124
+ def patch_vllm_transformers_lizzy_tp() -> None:
125
+ """Patch the generic vLLM Transformers backend for Lizzy TP norms/heads."""
126
+ import vllm.model_executor.models.transformers as transformers_mod
127
+
128
+ transformers_base = transformers_mod.TransformersBase
129
+ if getattr(transformers_base, _PATCH_ATTR, False):
130
+ return
131
+
132
+ PreTrainedModel = transformers_mod.PreTrainedModel
133
+ maybe_prefix = transformers_mod.maybe_prefix
134
+ replace_linear_class = transformers_mod.replace_linear_class
135
+ get_feature_request_tip = transformers_mod.get_feature_request_tip
136
+ re = transformers_mod.re
137
+ log_replacement = transformers_mod.log_replacement
138
+ get_tp_rank = getattr(transformers_mod, "get_tensor_model_parallel_rank", None)
139
+ if get_tp_rank is None:
140
+ try:
141
+ from vllm.distributed import ( # noqa: PLC0415
142
+ get_tensor_model_parallel_rank as get_tp_rank,
143
+ )
144
+ except Exception:
145
+ get_tp_rank = lambda: 0
146
+
147
+ def tensor_parallel(self: Any) -> None: # noqa: ANN401
148
+ """Apply the model's tensor parallel plan plus Lizzy attention fixes."""
149
+ is_pretrained_model = lambda m: isinstance(m, PreTrainedModel)
150
+ supports_tp_plan = lambda m: m.config.base_model_tp_plan is not None
151
+ pretrained_models = filter(is_pretrained_model, self.model.modules())
152
+ models_with_tp_plan = filter(supports_tp_plan, pretrained_models)
153
+
154
+ if not any(models_with_tp_plan) and self.tp_size > 1:
155
+ tip = get_feature_request_tip(
156
+ self.model_config.model,
157
+ self.model_config.trust_remote_code,
158
+ )
159
+ raise ValueError(
160
+ f"{type(self.model)} does not support tensor parallel. {tip}",
161
+ )
162
+
163
+ tp_rank = get_tp_rank()
164
+
165
+ def _tensor_parallel(
166
+ module: nn.Module,
167
+ prefix: str = "",
168
+ tp_plan: dict[str, str] | None = None,
169
+ ) -> None:
170
+ local_tp_plan = tp_plan or {}
171
+
172
+ if isinstance(module, PreTrainedModel):
173
+ local_tp_plan = module.config.base_model_tp_plan or {}
174
+ local_tp_plan = {
175
+ maybe_prefix(prefix, key): value
176
+ for key, value in local_tp_plan.items()
177
+ }
178
+
179
+ for child_name, child_module in module.named_children():
180
+ qual_name = maybe_prefix(prefix, child_name)
181
+ if isinstance(child_module, nn.Linear):
182
+ generator = (p for p in local_tp_plan if re.match(p, qual_name))
183
+ pattern = next(generator, None)
184
+ style = local_tp_plan.get(pattern, "replicate")
185
+ new_module = replace_linear_class(
186
+ child_module,
187
+ style,
188
+ self.quant_config,
189
+ prefix=qual_name,
190
+ )
191
+ setattr(module, child_name, new_module)
192
+ log_replacement(qual_name, child_module, new_module)
193
+ else:
194
+ _tensor_parallel(
195
+ child_module,
196
+ prefix=qual_name,
197
+ tp_plan=local_tp_plan,
198
+ )
199
+
200
+ _maybe_patch_lizzy_attention_for_tp(
201
+ module=module,
202
+ prefix=prefix,
203
+ tp_size=self.tp_size,
204
+ tp_rank=tp_rank,
205
+ log_replacement=log_replacement,
206
+ )
207
+
208
+ _tensor_parallel(self.model)
209
+
210
+ transformers_base.tensor_parallel = tensor_parallel
211
+ setattr(transformers_base, _PATCH_ATTR, True)
vocab.json ADDED
The diff for this file is too large to render. See raw diff
 
Free AI Image Generator No sign-up. Instant results. Open Now