vllm
Files changed (2) hide show
  1. README.md +57 -74
  2. chat_template.jinja +0 -234
README.md CHANGED
@@ -44,81 +44,50 @@ Leanstral offers these capabilities:
44
 
45
  ### Mistral-Vibe
46
 
47
- Use `Leanstral 119B A6B` with [Mistral Vibe](https://github.com/mistralai/mistral-vibe). Install the latest version (2.5.0):
48
 
49
  ```sh
50
  uv pip install mistral-vibe --upgrade
51
-
52
- # make sure it's >= 2.5.0
53
- ```
54
-
55
- Leanstral can be added by starting `vibe` and simply running:
56
-
57
- ```
58
- /leanstall
59
  ```
60
 
61
- This will add `leanstral` as an additional model, add a system prompt (see [LEAD.md](https://huggingface.co/mistralai/Leanstral-2603/blob/main/LEAN.md)) as well as
62
- ensure `leanstral` can be used as a subagent.
63
-
64
-
65
- ![Screenshot 2026-03-16 at 18.03.39](https://cdn-uploads.huggingface.co/production/uploads/5dfcb1aada6d0311fd3d5448/Sm_mBI7u4XTjlKGzdXQqe.png)
66
-
67
- Then just press "tab+shift" a couple times until you see the new "lean" mode and `leanstral` model.
68
-
69
- ![Screenshot 2026-03-16 at 18.17.04](https://cdn-uploads.huggingface.co/production/uploads/5dfcb1aada6d0311fd3d5448/DHwtKamfj2QfMv0TkJK6G.png)
70
-
71
- **Local server**
72
-
73
- If instead of pinging the Mistral API, you want to use your local vLLM server, you can do the following:
74
- - 1. Spin up a vllm server as explained in [`Usage - vllm`](#vllm-recommended)
75
- - 2. Create a new agent file called `lean.toml` in `~/.vibe/agents`:
76
 
77
- ```sh
78
- mkdir ~/.vibe/agents/ && touch ~/.vibe/agents/lean.toml
79
- ```
80
-
81
- And then copy-paste the following config into `~/.vibe/agents/lean.toml`
82
 
83
  ```toml
84
- display_name = "Lean (local vLLM)"
85
- description = "Lean 4 mode using local vLLM"
86
- safety = "neutral"
87
-
88
- system_prompt_id = "lean"
89
- active_model = "leanstral"
90
-
91
  [[providers]]
92
  name = "vllm"
93
  api_base = "http://<your-host-url>:8000/v1"
94
- api_key_env_var = ""
95
- backend = "generic"
96
  reasoning_field_name = "reasoning_content"
97
-
98
  [[models]]
99
- name = "mistralai/Leanstral-2603"
100
- provider = "vllm"
101
  alias = "leanstral"
102
  thinking = "high"
103
  temperature = 1.0
104
- auto_compact_threshold = 168000
105
-
106
- [tools.bash]
107
- default_timeout = 1200
108
  ```
109
 
110
- **Note**: Make sure to overwrite `<your-host-url>` with your server's url.
111
 
112
- Then restart `vibe` and "tab-shift" to "lean" mode.
 
 
 
 
 
 
 
 
 
113
 
114
- Give it a try on some "lean" code such as, *e.g.*: [PrimeNumberTheoremAnd](https://github.com/AlexKontorovich/PrimeNumberTheoremAnd)
115
 
116
  ### Local Deployment
117
 
118
  The model can also be deployed with the following libraries, we advise everyone to use the Mistral AI API if the model is subpar with local serving:
119
  - [`vllm (recommended)`](https://github.com/vllm-project/vllm): See [here](#vllm-recommended).
120
  - [`transformers`](https://github.com/huggingface/transformers): WIP ⏳ - follow updates on [this PR](https://github.com/huggingface/transformers/pull/44760).
121
- - [`SGLang`](https://github.com/sgl-project/sglang): WIP ⏳ - follow updates on [this PR](https://github.com/sgl-project/sglang/pull/20708/)
122
 
123
  #### vLLM (recommended)
124
 
@@ -126,32 +95,52 @@ We recommend using this model with the [vLLM library](https://github.com/vllm-pr
126
 
127
  **_Installation_**
128
 
129
- 1. Make sure to install **vllm nightly**:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
- ```
132
- uv pip install -U vllm \
133
- --torch-backend=auto \
134
- --extra-index-url https://wheels.vllm.ai/nightly
135
- ```
136
 
137
- Doing so should automatically install [`mistral_common >= 1.11.0`](https://github.com/mistralai/mistral-common/releases/tag/v1.11.0).
138
-
139
- To check:
140
- ```
141
- python -c "import mistral_common; print(mistral_common.__version__)"
142
- ```
143
-
144
- You can also make use of a ready-to-go [docker image](https://github.com/vllm-project/vllm/blob/main/docker/Dockerfile) or on the [docker hub](https://hub.docker.com/layers/vllm/vllm-openai/nightly).
145
 
146
- 2. Install `transformers` from main:
 
 
 
147
 
148
- ```bash
149
- uv pip install git+https://github.com/huggingface/transformers.git
150
- ```
 
 
151
 
152
  **_Launch server_**
153
 
154
- We recommend that you use Leanstral in a server/client setting.
155
 
156
  ```
157
  vllm serve mistralai/Leanstral-2603 \
@@ -328,9 +317,3 @@ _Example Tool Calls_:
328
  `Function(arguments='{"code": "inductive State where\\n | idle\\n | busy\\n | error\\n\\ndef transition : State → State → Bool\\n | .idle, .busy => true\\n | .busy, .idle => true\\n | .busy, .error => true\\n | _, _ => false\\n\\n#eval transition .idle .busy"}', name='lean_run_code')`
329
 
330
  </details>
331
-
332
- ## License
333
-
334
- This model is licensed under the [Apache 2.0 License](https://www.apache.org/licenses/LICENSE-2.0.txt).
335
-
336
- *You must not use this model in a manner that infringes, misappropriates, or otherwise violates any third party’s rights, including intellectual property rights.*
 
44
 
45
  ### Mistral-Vibe
46
 
47
+ Use `Leanstral 119B A6B` with [Mistral Vibe](https://github.com/mistralai/mistral-vibe). Install the latest version:
48
 
49
  ```sh
50
  uv pip install mistral-vibe --upgrade
 
 
 
 
 
 
 
 
51
  ```
52
 
53
+ **Add as a provider** In vibe, use the `/leanstall` command.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
+ **Local server (via vLLM):**
 
 
 
 
56
 
57
  ```toml
 
 
 
 
 
 
 
58
  [[providers]]
59
  name = "vllm"
60
  api_base = "http://<your-host-url>:8000/v1"
61
+ reasoning_as_structured_content = true
 
62
  reasoning_field_name = "reasoning_content"
 
63
  [[models]]
64
+ name = "labs-leanstral-2603"
65
+ provider = "mistral-testing"
66
  alias = "leanstral"
67
  thinking = "high"
68
  temperature = 1.0
 
 
 
 
69
  ```
70
 
71
+ **System prompt & agent**:
72
 
73
+ Add `~/.vibe/prompts/lean.toml` as in [LEAN.md](https://huggingface.co/mistralai/Leanstral-2603/blob/main/LEAN.md) and create `~/.vibe/agents/lean.toml`:
74
+
75
+ ```toml
76
+ name = "lean"
77
+ display_name = "Lean"
78
+ description = "Specialized mode for Lean 4 code analysis, proof assistance, and theorem proving"
79
+ safety = "neutral"
80
+ agent_type = "agent"
81
+ system_prompt_id = "lean"
82
+ ```
83
 
84
+ Example repository: [PrimeNumberTheoremAnd](https://github.com/AlexKontorovich/PrimeNumberTheoremAnd)
85
 
86
  ### Local Deployment
87
 
88
  The model can also be deployed with the following libraries, we advise everyone to use the Mistral AI API if the model is subpar with local serving:
89
  - [`vllm (recommended)`](https://github.com/vllm-project/vllm): See [here](#vllm-recommended).
90
  - [`transformers`](https://github.com/huggingface/transformers): WIP ⏳ - follow updates on [this PR](https://github.com/huggingface/transformers/pull/44760).
 
91
 
92
  #### vLLM (recommended)
93
 
 
95
 
96
  **_Installation_**
97
 
98
+ > [!Tip]
99
+ > We recommend installing vLLM from our custom Docker image that has fixes for
100
+ > Tool Calling and Reasoning parsing in vLLM and uses the latest version of Transformers.
101
+ > We're working with the vLLM team to merge these fixes to main as soon as possible.
102
+
103
+ **_Custom Docker_**
104
+
105
+ Make sure to use the following docker image [mistralllm/vllm-ms4:latest](https://hub.docker.com/repository/docker/mistralllm/vllm-ms4/latest/):
106
+
107
+ ```
108
+ docker pull mistralllm/vllm-ms4:latest
109
+ docker run -it mistralllm/vllm-ms4:latest
110
+ ```
111
+
112
+ **_Manual Install_**
113
+
114
+ If you prefer, you can also manually install `vllm` from this PR: [Add Mistral Guidance](https://github.com/vllm-project/vllm/pull/37081).
115
+
116
+ **Note**:
117
+ It is likely that this PR will be split into smaller PRs and merged to `vllm` main in the coming 1-2 weeks (Stand: 16.03.2026).
118
+ Check latest developments directly on the [PR](https://github.com/vllm-project/vllm/pull/37081).
119
 
120
+ 1. Git clone vLLM:
121
+ ```
122
+ git clone --branch fix_mistral_parsing https://github.com/juliendenize/vllm.git
123
+ ```
 
124
 
125
+ 2. Install with pre-compiled kernels
126
+ ```
127
+ VLLM_USE_PRECOMPILED=1 pip install --editable .
128
+ ```
 
 
 
 
129
 
130
+ 3. Make sure, `transformers` is installed from "main":
131
+ ```
132
+ pip install git+https://github.com/huggingface/transformers.git
133
+ ```
134
 
135
+ Also make sure to have installed [`mistral_common >= 1.10.0`](https://github.com/mistralai/mistral-common/releases/tag/v1.10.0).
136
+ To check:
137
+ ```
138
+ python -c "import mistral_common; print(mistral_common.__version__)"
139
+ ```
140
 
141
  **_Launch server_**
142
 
143
+ We recommand that you use Leanstral in a server/client setting.
144
 
145
  ```
146
  vllm serve mistralai/Leanstral-2603 \
 
317
  `Function(arguments='{"code": "inductive State where\\n | idle\\n | busy\\n | error\\n\\ndef transition : State → State → Bool\\n | .idle, .busy => true\\n | .busy, .idle => true\\n | .busy, .error => true\\n | _, _ => false\\n\\n#eval transition .idle .busy"}', name='lean_run_code')`
318
 
319
  </details>
 
 
 
 
 
 
chat_template.jinja DELETED
@@ -1,234 +0,0 @@
1
- {#- Default system message if no system prompt is passed. #}
2
- {%- set default_system_message = '' %}
3
-
4
- {#- Begin of sequence token. #}
5
- {{- bos_token }}
6
-
7
-
8
- {#- Handle system prompt if it exists. #}
9
- {%- set loop_messages = messages %}
10
- {%- if messages[0]['role'] != 'system' and default_system_message != '' %}
11
- {{- '[SYSTEM_PROMPT]' + default_system_message + '[/SYSTEM_PROMPT]' }}
12
- {%- endif %}
13
-
14
-
15
- {#- Tools and model settings definition #}
16
- {%- set available_tools = '' %}
17
- {%- set has_tools = false %}
18
- {%- if tools is defined and tools is not none and tools|length > 0 %}
19
- {%- set has_tools = true %}
20
- {%- set available_tools = '[AVAILABLE_TOOLS]' + (tools| tojson) + '[/AVAILABLE_TOOLS]' %}
21
- {%- endif %}
22
- {%- if reasoning_effort is not defined or reasoning_effort is none %}
23
- {%- set reasoning_effort = 'none' %}
24
- {%- endif %}
25
- {%- if reasoning_effort not in ['none', 'high'] %}
26
- {{- raise_exception('reasoning_effort must be either "none" or "high"') }}
27
- {%- endif %}
28
- {%- set model_settings = '[MODEL_SETTINGS]{"reasoning_effort": "' + reasoning_effort + '"}[/MODEL_SETTINGS]' %}
29
-
30
- {#- Macros #}
31
- {%- macro render_content(content, context_name, supported_types_desc, support_thinking, support_images) -%}
32
- {%- if content is string -%}
33
- {{- content -}}
34
- {%- elif content -%}
35
- {%- for block in content -%}
36
- {%- if block['type'] == 'text' -%}
37
- {{- block['text'] -}}
38
- {%- elif support_thinking and block['type'] == 'thinking' -%}
39
- {{- '[THINK]' + block['thinking'] -}}
40
- {%- if block.get('closed', true) -%}{{- '[/THINK]' -}}{%- endif -%}
41
- {%- elif support_images and block['type'] in ['image', 'image_url'] -%}
42
- {{- '[IMG]' -}}
43
- {%- else -%}
44
- {{- raise_exception('Only ' + supported_types_desc + ' chunks are supported in ' + context_name + '.') -}}
45
- {%- endif -%}
46
- {%- endfor -%}
47
- {%- else -%}
48
- {{- raise_exception(context_name + ' must have non-empty content.') -}}
49
- {%- endif -%}
50
- {%- endmacro -%}
51
-
52
- {#- Aggregate consecutive messages with the same role except system and tool. #}
53
- {#- A sentinel message is appended so the last group gets flushed inside the loop. #}
54
- {%- set ns_agg = namespace(messages=[], current_group=[], current_role=none) %}
55
- {%- for message in loop_messages + [{'role': '__sentinel__'}] %}
56
- {%- if message['role'] != ns_agg.current_role or message['role'] == 'system' or message['role'] == 'tool' %}
57
- {%- if ns_agg.current_role == 'tool' %}
58
- {%- set ns_agg.messages = ns_agg.messages + ns_agg.current_group %}
59
- {%- elif ns_agg.current_role is not none %}
60
- {%- set ns_c = namespace(text_parts=[], chunks=[], has_non_text=false, tool_calls=[]) %}
61
- {%- for msg in ns_agg.current_group %}
62
- {#- Convert reasoning / reasoning_content to a leading thinking chunk. #}
63
- {%- set reasoning = msg.get('reasoning_content', msg.get('reasoning', none)) %}
64
- {%- if reasoning is not none and reasoning != '' %}
65
- {%- if msg['content'] is not none and msg['content'] is not string %}
66
- {%- for block in msg['content'] %}
67
- {%- if block['type'] == 'thinking' %}
68
- {{- raise_exception('Message cannot have both thinking chunks in content and a top-level `reasoning` or `reasoning_content` field.') }}
69
- {%- endif %}
70
- {%- endfor %}
71
- {%- endif %}
72
- {%- set think_chunk = {'type': 'thinking', 'thinking': reasoning} %}
73
- {%- if msg['content'] is string and msg['content'] != '' %}
74
- {%- set new_content = [think_chunk, {'type': 'text', 'text': msg['content']}] %}
75
- {%- elif msg['content'] is not none and msg['content'] is not string and msg['content'] | length > 0 %}
76
- {%- set new_content = [think_chunk] + msg['content'] | list %}
77
- {%- else %}
78
- {%- set new_content = [think_chunk] %}
79
- {%- endif %}
80
- {%- if msg['tool_calls'] is defined and msg['tool_calls'] is not none %}
81
- {%- set msg = {'role': msg['role'], 'content': new_content, 'tool_calls': msg['tool_calls']} %}
82
- {%- else %}
83
- {%- set msg = {'role': msg['role'], 'content': new_content} %}
84
- {%- endif %}
85
- {%- endif %}
86
- {%- if msg['content'] is string %}
87
- {%- set ns_c.text_parts = ns_c.text_parts + [msg['content']] %}
88
- {%- elif msg['content'] is not none %}
89
- {%- set ns_msg = namespace(msg_text_parts=[]) %}
90
- {%- for block in msg['content'] %}
91
- {%- if block['type'] == 'text' %}
92
- {%- set ns_msg.msg_text_parts = ns_msg.msg_text_parts + [block['text']] %}
93
- {%- else %}
94
- {%- if ns_msg.msg_text_parts | length > 0 %}
95
- {%- set ns_c.text_parts = ns_c.text_parts + [ns_msg.msg_text_parts | join('')] %}
96
- {%- set ns_msg.msg_text_parts = [] %}
97
- {%- endif %}
98
- {%- if ns_c.text_parts | length > 0 %}
99
- {%- set ns_c.chunks = ns_c.chunks + [{'type': 'text', 'text': ns_c.text_parts | join('\n\n')}] %}
100
- {%- set ns_c.text_parts = [] %}
101
- {%- endif %}
102
- {%- set ns_c.chunks = ns_c.chunks + [block] %}
103
- {%- set ns_c.has_non_text = true %}
104
- {%- endif %}
105
- {%- endfor %}
106
- {%- if ns_msg.msg_text_parts | length > 0 %}
107
- {%- set ns_c.text_parts = ns_c.text_parts + [ns_msg.msg_text_parts | join('')] %}
108
- {%- endif %}
109
- {%- endif %}
110
- {%- if msg['tool_calls'] is defined and msg['tool_calls'] is not none %}
111
- {%- set ns_c.tool_calls = ns_c.tool_calls + msg['tool_calls'] | list %}
112
- {%- endif %}
113
- {%- endfor %}
114
- {%- if ns_c.has_non_text %}
115
- {%- if ns_c.text_parts | length > 0 %}
116
- {%- set ns_c.chunks = ns_c.chunks + [{'type': 'text', 'text': ns_c.text_parts | join('\n\n')}] %}
117
- {%- endif %}
118
- {%- set merged_content = ns_c.chunks %}
119
- {%- else %}
120
- {%- set merged_content = ns_c.text_parts | join('\n\n') %}
121
- {%- endif %}
122
- {%- if ns_c.tool_calls | length > 0 %}
123
- {%- set ns_agg.messages = ns_agg.messages + [{'role': ns_agg.current_role, 'content': merged_content, 'tool_calls': ns_c.tool_calls}] %}
124
- {%- else %}
125
- {%- set ns_agg.messages = ns_agg.messages + [{'role': ns_agg.current_role, 'content': merged_content}] %}
126
- {%- endif %}
127
- {%- endif %}
128
- {%- if message['role'] != '__sentinel__' %}
129
- {%- set ns_agg.current_group = [message] %}
130
- {%- set ns_agg.current_role = message['role'] %}
131
- {%- endif %}
132
- {%- else %}
133
- {%- set ns_agg.current_group = ns_agg.current_group + [message] %}
134
- {%- endif %}
135
- {%- endfor %}
136
- {%- set loop_messages = ns_agg.messages %}
137
-
138
- {#- Validates message ordering. #}
139
- {%- set ns = namespace(available_tools_and_settings_emitted=false) %}
140
- {%- if loop_messages | length > 0 and loop_messages[0]['role'] not in ['user', 'system'] %}
141
- {{- raise_exception('Conversation must start with a user or system message, got ' + loop_messages[0]['role'] + '.') }}
142
- {%- endif %}
143
- {%- set ns_order = namespace(previous_role=none) %}
144
- {%- for message in loop_messages %}
145
- {%- set current_role = message['role'] %}
146
- {%- if ns_order.previous_role is not none %}
147
- {%- if ns_order.previous_role == 'system' %}
148
- {%- if current_role not in ['user', 'assistant', 'system'] %}
149
- {{- raise_exception('Unexpected role \'' + current_role + '\' after role \'' + ns_order.previous_role + '\'') }}
150
- {%- endif %}
151
- {%- elif ns_order.previous_role == 'user' %}
152
- {%- if current_role not in ['assistant', 'system', 'user'] %}
153
- {{- raise_exception('Unexpected role \'' + current_role + '\' after role \'' + ns_order.previous_role + '\'') }}
154
- {%- endif %}
155
- {%- elif ns_order.previous_role == 'assistant' %}
156
- {%- if current_role not in ['assistant', 'user', 'tool'] %}
157
- {{- raise_exception('Unexpected role \'' + current_role + '\' after role \'' + ns_order.previous_role + '\'') }}
158
- {%- endif %}
159
- {%- elif ns_order.previous_role == 'tool' %}
160
- {%- if current_role not in ['assistant', 'tool', 'user'] %}
161
- {{- raise_exception('Unexpected role \'' + current_role + '\' after role \'' + ns_order.previous_role + '\'') }}
162
- {%- endif %}
163
- {%- endif %}
164
- {%- endif %}
165
- {%- set ns_order.previous_role = current_role %}
166
- {%- endfor %}
167
-
168
- {#- Handle conversation messages. #}
169
- {%- for message in loop_messages %}
170
- {#- User messages supports text, image and image_url content. #}
171
- {%- if message['role'] == 'user' %}
172
- {%- if not ns.available_tools_and_settings_emitted %}
173
- {{- available_tools }}
174
- {{- model_settings }}
175
- {%- set ns.available_tools_and_settings_emitted = true %}
176
- {%- endif %}
177
- {%- if message['content'] is not string and message['content'] %}
178
- {#- When content has exactly one image and one text block, put image first. #}
179
- {%- if message['content'] | length == 2 and message['content'][0]['type'] == 'text' and message['content'][1]['type'] in ['image', 'image_url'] %}
180
- {%- set blocks = [message['content'][1], message['content'][0]] %}
181
- {%- else %}
182
- {%- set blocks = message['content'] %}
183
- {%- endif %}
184
- {%- set user_content = blocks %}
185
- {%- else %}
186
- {%- set user_content = message['content'] %}
187
- {%- endif %}
188
- {{- '[INST]' -}}
189
- {{- render_content(user_content, 'user message content', supported_types_desc='text, image and image_url', support_thinking=false, support_images=true) -}}
190
- {{- '[/INST]' }}
191
-
192
- {#- Assistant messages supports text and thinking content. #}
193
- {%- elif message['role'] == 'assistant' %}
194
- {%- if (message['content'] is none or message['content'] == '' or message['content']|length == 0) and (message['tool_calls'] is not defined or message['tool_calls'] is none or message['tool_calls']|length == 0) %}
195
- {{- raise_exception('Assistant message must have a string or a list of chunks in content or a list of tool calls.') }}
196
- {%- endif %}
197
-
198
- {%- if message['content'] %}
199
- {{- render_content(message['content'], 'assistant message contents', supported_types_desc='text and thinking', support_thinking=true, support_images=false) -}}
200
- {%- endif %}
201
-
202
- {%- if message['tool_calls'] is defined and message['tool_calls'] is not none and message['tool_calls']|length > 0 %}
203
- {%- for tool in message['tool_calls'] %}
204
- {{- '[TOOL_CALLS]' }}
205
- {%- set name = tool['function']['name'] %}
206
- {%- set arguments = tool['function']['arguments'] %}
207
- {%- if arguments is not string %}
208
- {%- set arguments = arguments|tojson|safe %}
209
- {%- elif arguments == '' %}
210
- {%- set arguments = '{}' %}
211
- {%- endif %}
212
- {{- name + '[ARGS]' + arguments }}
213
- {%- endfor %}
214
- {%- endif %}
215
-
216
- {{- eos_token }}
217
-
218
- {#- Tool messages (multimodal). #}
219
- {%- elif message['role'] == 'tool' %}
220
- {{- '[TOOL_RESULTS]' -}}
221
- {{- render_content(message['content'], 'tool message contents', supported_types_desc='text and image', support_images=true) -}}
222
- {{- '[/TOOL_RESULTS]' }}
223
-
224
- {#- System messages. #}
225
- {%- elif message['role'] == 'system' %}
226
- {{- '[SYSTEM_PROMPT]' -}}
227
- {{- render_content(message['content'], 'system message contents', supported_types_desc='text', support_thinking=false, support_images=false) -}}
228
- {{- '[/SYSTEM_PROMPT]' -}}
229
-
230
- {#- Raise exception for unsupported roles. #}
231
- {%- else %}
232
- {{- raise_exception('Only user, assistant, system and tool roles are supported, got ' + message['role'] + '.') }}
233
- {%- endif %}
234
- {%- endfor %}