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.
- vllm_metis-0.1.0/LICENSE +202 -0
- vllm_metis-0.1.0/PKG-INFO +302 -0
- vllm_metis-0.1.0/README.md +285 -0
- vllm_metis-0.1.0/pyproject.toml +34 -0
- vllm_metis-0.1.0/setup.cfg +4 -0
- vllm_metis-0.1.0/tests/test_gpu_tiny_parity.py +135 -0
- vllm_metis-0.1.0/tests/test_parser.py +460 -0
- vllm_metis-0.1.0/tests/test_weight_map.py +144 -0
- vllm_metis-0.1.0/vllm_metis/__init__.py +36 -0
- vllm_metis-0.1.0/vllm_metis/config_hook.py +25 -0
- vllm_metis-0.1.0/vllm_metis/model.py +841 -0
- vllm_metis-0.1.0/vllm_metis/parser.py +264 -0
- vllm_metis-0.1.0/vllm_metis.egg-info/PKG-INFO +302 -0
- vllm_metis-0.1.0/vllm_metis.egg-info/SOURCES.txt +16 -0
- vllm_metis-0.1.0/vllm_metis.egg-info/dependency_links.txt +1 -0
- vllm_metis-0.1.0/vllm_metis.egg-info/entry_points.txt +2 -0
- vllm_metis-0.1.0/vllm_metis.egg-info/requires.txt +4 -0
- vllm_metis-0.1.0/vllm_metis.egg-info/top_level.txt +1 -0
vllm_metis-0.1.0/LICENSE
ADDED
|
@@ -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.
|