vllm-metis 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,302 @@
1
+ Metadata-Version: 2.4
2
+ Name: vllm-metis
3
+ Version: 0.1.0
4
+ Summary: vLLM plugin for Lernex's Metis-1.6 models: the Metis16ForCausalLM architecture plus the metis reasoning and tool-call parsers
5
+ Author: Lernex
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/lernex/vllm-metis
8
+ Project-URL: Issues, https://github.com/lernex/vllm-metis/issues
9
+ Project-URL: Model, https://huggingface.co/Lernex/Metis-1.6-Logos
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: vllm<0.32,>=0.31
14
+ Provides-Extra: test
15
+ Requires-Dist: pytest; extra == "test"
16
+ Dynamic: license-file
17
+
18
+ # vllm-metis
19
+
20
+ A vLLM plugin for Lernex's Metis-1.6 models. Install it next to vLLM and
21
+ `Lernex/Metis-1.6-Logos` and `Lernex/Metis-1.6-Praxis` serve like any other model: OpenAI-compatible API, reasoning split
22
+ out of the answer, tool calls parsed and typed, prefix caching, chunked prefill, CUDA graphs.
23
+
24
+ It adds three things to vLLM, all registered through vLLM's `vllm.general_plugins` entry
25
+ point, so there is nothing to import or patch:
26
+
27
+ * `Metis16ForCausalLM`, the model, built on vLLM's own fused Mamba-2, MoE and attention
28
+ kernels.
29
+ * `--reasoning-parser metis`, which separates the `<think>` block from the answer.
30
+ * `--tool-call-parser metis`, which turns `<|tool_call|><function=...>` blocks into OpenAI
31
+ tool calls, typing each argument from the request's JSON schema.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install "vllm==0.31.*"
37
+ pip install vllm-metis # or, from a checkout: pip install -e .
38
+ ```
39
+
40
+ vLLM 0.31 is required (`vllm>=0.31,<0.32`). The plugin pins the minor version because it
41
+ builds on vLLM's model-layer and parser-engine interfaces, which move between minors.
42
+
43
+ ## Serve
44
+
45
+ ```bash
46
+ vllm serve Lernex/Metis-1.6-Logos --trust-remote-code \
47
+ --reasoning-parser metis --enable-auto-tool-choice --tool-call-parser metis
48
+ ```
49
+
50
+ `--trust-remote-code` lets vLLM read the model's configuration class; the model itself runs
51
+ on this plugin's implementation, not on the repository's Transformers code.
52
+
53
+ **Memory.** Praxis (3.0B) runs in BF16 on a 16 GB card. Logos (10.2B) is 20.8 GB in BF16,
54
+ so it wants a card with room to spare above that (32 GB or more). On a 16 or 24 GB card,
55
+ serve the FP8 checkpoint:
56
+
57
+ ```bash
58
+ vllm serve Lernex/Metis-1.6-Logos-FP8 --trust-remote-code \
59
+ --reasoning-parser metis --enable-auto-tool-choice --tool-call-parser metis
60
+ ```
61
+
62
+ `Metis-1.6-Logos-FP8` and `Metis-1.6-Praxis-FP8` are compressed-tensors FP8 checkpoints (11.2
63
+ GB and 3.5 GB): FP8 weights with one scale per output channel and activations quantized per
64
+ token at run time, for the routed experts, the Mamba input projections and the attention
65
+ projections. The embedding, the latent projections, the shared expert, the Mamba output
66
+ projections and every FP32 tensor stay as trained. The Mamba output projection is kept on
67
+ purpose. It is 2% of the weights but nearly all of FP8's error: on the real weights,
68
+ keeping it cut the top-20 KL to the reference from 0.022 to 0.007 on prose and from 0.088 to
69
+ 0.034 on a tool-calling chat. The checkpoint loads faster, downloads half the bytes and is
70
+ both more accurate and faster than quantizing the BF16 weights while loading (see Status).
71
+ SGLang reads the same checkpoints (`sglang-metis`).
72
+
73
+ Quantizing the BF16 weights while loading also works, with one scale per tensor:
74
+
75
+ ```bash
76
+ vllm serve Lernex/Metis-1.6-Logos --trust-remote-code --quantization fp8_per_tensor \
77
+ --reasoning-parser metis --enable-auto-tool-choice --tool-call-parser metis
78
+ ```
79
+
80
+ `--quantization fp8` is the older spelling of the same thing. Use per-tensor:
81
+ `fp8_per_block` does not run on RTX 50-series cards in vLLM 0.31, whose CUTLASS
82
+ block-scaled GEMM rejects an output width that is not a multiple of 128, and the Mamba
83
+ input projection is 12880 wide.
84
+
85
+ The serving defaults come from the model's `generation_config.json` (temperature 0.6,
86
+ top-p 0.95, top-k 20). Thinking is on unless a request turns it off.
87
+
88
+ ## Use it
89
+
90
+ ```python
91
+ from openai import OpenAI
92
+
93
+ client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
94
+
95
+ # Thinking mode (the default): the reasoning arrives separately from the answer.
96
+ reply = client.chat.completions.create(
97
+ model="Lernex/Metis-1.6-Logos",
98
+ messages=[{"role": "user", "content": "What is 17 x 23?"}],
99
+ )
100
+ message = reply.choices[0].message
101
+ print(message.reasoning) # the model's thinking
102
+ print(message.content) # the answer
103
+
104
+ # Tools, in direct mode (no thinking).
105
+ tools = [{
106
+ "type": "function",
107
+ "function": {
108
+ "name": "get_weather",
109
+ "description": "Current weather for a city.",
110
+ "parameters": {
111
+ "type": "object",
112
+ "properties": {"city": {"type": "string"}, "days": {"type": "integer"}},
113
+ "required": ["city"],
114
+ },
115
+ },
116
+ }]
117
+ reply = client.chat.completions.create(
118
+ model="Lernex/Metis-1.6-Logos",
119
+ messages=[{"role": "user", "content": "Weather in Lisbon for the next 3 days?"}],
120
+ tools=tools,
121
+ extra_body={"chat_template_kwargs": {"enable_thinking": False}},
122
+ )
123
+ for call in reply.choices[0].message.tool_calls or []:
124
+ print(call.function.name, call.function.arguments) # get_weather {"city": "Lisbon", "days": 3}
125
+
126
+ # Streaming: reasoning, content and tool-call arguments arrive as deltas.
127
+ stream = client.chat.completions.create(
128
+ model="Lernex/Metis-1.6-Logos",
129
+ messages=[{"role": "user", "content": "Explain a Mamba layer in two sentences."}],
130
+ stream=True,
131
+ )
132
+ for chunk in stream:
133
+ delta = chunk.choices[0].delta
134
+ if getattr(delta, "reasoning", None):
135
+ print(delta.reasoning, end="")
136
+ if delta.content:
137
+ print(delta.content, end="")
138
+ ```
139
+
140
+ In a tool loop, send each assistant turn back with its reasoning as `reasoning` (or
141
+ `reasoning_content`) and its calls as `tool_calls`. The chat template keeps the reasoning of
142
+ turns since the last user message and drops older reasoning on its own. `scripts/serve_smoke.py` runs these
143
+ requests against a live server, whole and streamed, and checks that both agree.
144
+
145
+ ## How the parsers read a turn
146
+
147
+ ```
148
+ <think>
149
+ REASONING
150
+ </think>
151
+ CONTENT<|tool_call|><function=get_weather>
152
+ <parameter=city>
153
+ Lisbon
154
+ </parameter>
155
+ <parameter=days>
156
+ 3
157
+ </parameter>
158
+ </function><|fim_suffix|>
159
+ ```
160
+
161
+ * In thinking mode the generation prompt already ends with `<think>\n`, so the output starts
162
+ inside the reasoning, which ends at `</think>`. A call that starts straight out of the
163
+ reasoning ends it too.
164
+ * In direct mode (`enable_thinking: false`) the prompt holds an empty think block, and every
165
+ generated token is content. The reasoning parser reads this from the prompt, so a
166
+ pre-rendered prompt or a custom template works the same way.
167
+ * Calls follow one another with no separator. The turn ends at `<|fim_suffix|>` (id 4), a
168
+ stop token alongside id 0.
169
+ * A parameter whose schema type is `string` is taken as raw text, newlines and all; any
170
+ other type is parsed as JSON (`3` becomes `3`, `{"a": 1}` an object). A call to a tool the
171
+ request did not declare comes back with its values as strings.
172
+ * Structured outputs start after the reasoning: the parser tells vLLM's grammar engine the
173
+ moment `</think>` (or a call) completes, checked on the new tokens only.
174
+ * With `tool_choice: "none"`, calls the model writes anyway are dropped, as vLLM's own
175
+ parser-engine models do. Without `--tool-call-parser`, they stay in the content as text.
176
+
177
+ ## How the model maps onto vLLM
178
+
179
+ Metis-1.6 Logos runs its 20-layer stack twice per token over the same weights: 17 Mamba-2
180
+ layers, 3 attention layers (5, 10 and 15, no positional encoding, a per-pass low-rank
181
+ adapter on QKV), each followed by a latent MoE (192 experts, top 4, plus a shared expert),
182
+ with four mHC residual streams and a per-token depth memory carried between passes.
183
+
184
+ **Caches.** Each pass sees different inputs, so each pass needs its own recurrent state and
185
+ its own keys and values. The plugin gives vLLM one cache layer per (layer, pass): 34 Mamba
186
+ layers and 6 attention layers, each with its own prefix
187
+ (`model.layers.5.mixer.attn_pass1`, `model.layers.3.mixer.passes.1`, ...). The parameters
188
+ are shared: a pass-1 Mamba layer is built on the meta device and then pointed at pass 0's
189
+ projections, convolution, norm and SSM parameters, and the two attention cache layers of a
190
+ layer sit behind one set of projections. So the weights are held once, and vLLM's standard
191
+ hybrid cache manager does the rest unchanged: prefix caching (in its Mamba "align" mode),
192
+ chunked prefill, CUDA graphs, preemption. No kernel is modified.
193
+
194
+ The depth memory, the mHC streams and the routing history are per token and live inside one
195
+ forward pass, so they need no cache.
196
+
197
+ Per sequence, from the config: the 6 KV layers take 15 KiB per token (10 KV heads of 64, BF16),
198
+ and the 34 Mamba states take about 91 MB (a 2.6 MB FP32 SSM state plus a 46 KB convolution
199
+ state each).
200
+
201
+ **Precision.** The plugin keeps FP32 wherever the reference does: the expert router and its
202
+ balance bias, the SSM `A`, `D` and `dt_bias`, the Sinkhorn iterations of the mHC, the
203
+ continuation head, the gated group RMSNorm after the scan, and the norms. The SSM state cache
204
+ defaults to FP32 too, as in the reference (the plugin sets `mamba_ssm_cache_dtype` to
205
+ `float32` when it is left on `auto`). Pass
206
+ `--mamba-ssm-cache-dtype bfloat16` to halve the Mamba cache if memory matters more; its
207
+ effect on accuracy has not been measured.
208
+
209
+ ## Status
210
+
211
+ Measured on two tiny random-weight checkpoints with the release's module layout, FP32
212
+ tensors, tokenizer and chat template (`scripts/make_tiny_checkpoint.py`): `tiny` (width 256,
213
+ 6 layers, attention at 1 and 4) and `tiny16` (width 512, GQA 4:1, layer widths that are
214
+ multiples of 16 but not 128, like the release). vLLM ran on an RTX 5070 Ti against the
215
+ Transformers port on the CPU, on four prompts (two chat turns in thinking mode, one in
216
+ direct mode, one 700-token prose prompt that crosses 256-token Mamba chunks), each batched,
217
+ repeated, alone, and again from the prefix cache.
218
+
219
+ | vLLM | Reference | `tiny`: prompt logprob max abs diff / worst mean top-20 KL | `tiny16` |
220
+ |---|---|---|---|
221
+ | FP32, IEEE dots, eager, 64-token chunked prefill, prefix caching | port, FP32 | 8.6e-6 / 2e-12 | 1.4e-5 / 6e-12 |
222
+ | BF16, torch.compile + CUDA graphs, prefix caching | port, BF16 | 0.063 to 0.094 / 2.6e-4 | 0.13 to 0.65 / 1.3e-3 |
223
+ | (the port against itself, BF16 vs FP32) | | 0.055 to 0.10 / 2.0e-4 | 0.14 to 0.62 / 1.5e-3 |
224
+ | FP8 per tensor, compiled | port, BF16 | 0.47 to 0.78 / 1.5e-2 | 0.90 to 1.37 / 4.4e-2 |
225
+ | (the port with simulated FP8, against itself unquantized) | | 0.47 to 0.95 / 1.3e-2 | 0.88 to 1.23 / 4.5e-2 |
226
+
227
+ * FP32 is the same computation as the port: greedy decoding agrees on all 32 tokens of every
228
+ prompt, in every mode, with decode logprobs within 1e-5, including 64-token prefill chunks
229
+ and prefix-cache hits (576 to 640 of the 700 prompt tokens served from the cache).
230
+ * BF16 differs from the port by exactly as much as the port differs from itself in FP32. A
231
+ random model is full of near-ties, so greedy runs part ways early on some prompts; up to
232
+ that step the logprobs agree to the same BF16 noise.
233
+ * FP8 moves the output by as much as simulated FP8 moves the port. The loaded weights are
234
+ checked one by one against the checkpoint (`scripts/check_loaded_weights.py`): every
235
+ unquantized tensor bit-identical, every FP8 tensor equal to its own rounding
236
+ (scale = amax / 448; at most 0.12% of values one FP8 step off, from rounding order), and
237
+ both passes running on the same quantized modules.
238
+ * `vllm serve` with the command above, driven by `scripts/serve_smoke.py` through the
239
+ OpenAI client: thinking-mode reasoning comes back as `reasoning`, a direct-mode tool call
240
+ comes back as a typed tool call (`days` as the integer 3), and the streamed answers equal
241
+ the whole ones.
242
+ * The parsers are tested on outputs rendered by the real tokenizer and chat template:
243
+ whole, and streamed in deltas of 1, 2, 3, 7 and all tokens, with and without history, plus
244
+ truncation, `tool_choice: "none"`, each parser on its own and undeclared tools.
245
+
246
+ **Real weights.** `scripts/real_smoke.py` compares vLLM's prompt logprobs with FP32 logits
247
+ from the native training runtime on the release's reference prompts (a 16-token thinking
248
+ chat, a 176-token chat with tools, 300 tokens of prose), runs thinking and direct mode with
249
+ and without tools through the parsers, and times one stream and a batch of 32 on an RTX 5070
250
+ Ti (16 GB). Mean top-20 KL to the reference, and how often the top token agrees:
251
+
252
+ | Model | Weights | Thinking chat | Chat with tools | Prose | tok/s, 1 / 32 streams |
253
+ |---|---|---|---|---|---|
254
+ | Praxis | BF16 | 0.0053, 100% | 0.010, 95.5% | 0.0015, 98.7% | 201 / 1836 |
255
+ | Praxis | FP8 checkpoint | 0.0077, 100% | 0.040, 89.8% | 0.0066, 97.7% | 231 / 2386 |
256
+ | Praxis | BF16, `fp8_per_tensor` | 0.011, 93.8% | 0.048, 91.5% | 0.0076, 97.7% | 225 / 2100 |
257
+ | Logos | FP8 checkpoint | 0.0048, 100% | 0.031, 91.5% | 0.0084, 95.3% | 117 / 770 |
258
+ | Logos | BF16, `fp8_per_tensor` | 0.0062, 93.8% | 0.034, 91.5% | 0.0070, 96.0% | 113 / 659 |
259
+
260
+ Praxis in BF16 is inside the noise floor of the native model run in BF16 (0.0073, 0.014,
261
+ 0.0022). In every configuration the thinking and direct answers are correct, tool calls come
262
+ back typed, and `vllm serve` answers a tool round trip.
263
+
264
+ ## Limitations
265
+
266
+ * Tensor parallelism must divide the Mamba groups and KV heads: 10 on Logos (TP 1, 2, 5 or
267
+ 10), 8 on Praxis (TP 1, 2, 4 or 8). Only TP 1 has been run. Pipeline parallelism is not supported.
268
+ * LoRA adapters and speculative decoding are untested.
269
+ * The adaptive-depth and adaptive-k heads in the checkpoint are not used: the release runs
270
+ the fixed policy (2 passes, top 4), which is what this plugin implements.
271
+ * With `--quantization fp8_per_tensor`, the routed experts quantize activations with one
272
+ scale per batch, so a request's output can depend slightly on what it was batched with
273
+ (vLLM's FP8 MoE does this for every model). The FP8 checkpoints scale each token on its
274
+ own and do not.
275
+
276
+ ## Tests
277
+
278
+ ```bash
279
+ pip install -e ".[test]"
280
+ METIS_MODEL_DIR=/path/to/Metis-1.6-Logos pytest # parsers + weight map, CPU only
281
+ METIS_MODEL_DIR=/path/to/Metis-1.6-Logos pytest -m gpu # tiny-checkpoint parity on a GPU
282
+ ```
283
+
284
+ * `tests/test_parser.py` needs only the tokenizer and chat template.
285
+ * `tests/test_weight_map.py` builds the model on the meta device for the real config and
286
+ checks every tensor in the release's safetensors header against it; it reads only the
287
+ header.
288
+ * `tests/test_gpu_tiny_parity.py` builds a tiny checkpoint and runs vLLM against the
289
+ Transformers port (FP32, BF16 compiled, FP8), plus the loaded-weight check. It takes 15% of
290
+ the card and a few minutes per case.
291
+
292
+ Scripts:
293
+
294
+ * `scripts/make_tiny_checkpoint.py`: a tiny random checkpoint laid out like the release.
295
+ * `scripts/tiny_parity.py`: vLLM against the port on one checkpoint.
296
+ * `scripts/check_loaded_weights.py`: every tensor vLLM holds against the checkpoint.
297
+ * `scripts/real_smoke.py`: the real-weight parity, generation and speed check.
298
+ * `scripts/serve_smoke.py`: the OpenAI-client check against a running server.
299
+
300
+ ## License
301
+
302
+ Apache-2.0.