[Feature] Parse request priority from HTTP header (#51089)
Signed-off-by: chaunceyjiang <chaunceyjiang@gmail.com>
Chauncey committed
Aug 6, 2026 at 12:10 UTC
22170354d83a8a9a845a3f50348301e007b9d81a
8 files changed
+81
-8
docs/serving/online_serving/openai_compatible_server.md
+15
-2
@@ -83,8 +83,8 @@ completion = client.chat.completions.create(
83
84
## Extra HTTP Headers
85
86
-Only `X-Request-Id` HTTP request header is supported for now. It can be enabled
87
-with `--enable-request-id-headers`.
86
+The `X-Request-Id` HTTP request header can be enabled with
87
+`--enable-request-id-headers`.
88
89
??? code
90
@@ -110,6 +110,19 @@ with `--enable-request-id-headers`.
110
print(completion._request_id)
111
```
112
113
+The Completions, Chat Completions, and Responses APIs also support the
114
+`X-Vllm-Priority` request header. Its value must be an integer and overrides
115
+the `priority` value in the JSON request body. Non-zero priorities require the
116
+server to use priority scheduling.
117
+
118
+```python
119
+completion = client.chat.completions.create(
120
+ model="NousResearch/Meta-Llama-3-8B-Instruct",
121
+ messages=[{"role": "user", "content": "Hello!"}],
122
+ extra_headers={"X-Vllm-Priority": "-10"},
123
+)
124
+```
125
+
126
## API Reference
127
128
### Completions API
rust/src/server/src/routes/openai/chat_completions/convert.rs
+18
-1
@@ -178,7 +178,7 @@ pub(super) fn prepare_chat_request(
178
min_tokens: request.min_tokens.unwrap_or(0),
179
},
180
intermediate: request.stream,
181
- priority: request.priority.unwrap_or(0),
181
+ priority: ctx.priority.or(request.priority).unwrap_or(0),
182
documents: request.documents,
183
cache_salt: request.cache_salt,
184
add_special_tokens: request.add_special_tokens,
@@ -1264,6 +1264,23 @@ mod tests {
1264
);
1265
}
1266
1267
+ #[test]
1268
+ fn prepare_chat_request_header_priority_overrides_body() {
1269
+ let mut headers = HeaderMap::new();
1270
+ headers.insert("X-Vllm-Priority", "-5".parse().unwrap());
1271
+ let request = ChatCompletionRequest {
1272
+ priority: Some(10),
1273
+ ..base_request()
1274
+ };
1275
+ let prepared = prepare_chat_request(
1276
+ request,
1277
+ &served(&["Qwen/Qwen1.5-0.5B-Chat"]),
1278
+ request_context(&headers, None),
1279
+ )
1280
+ .expect("request is valid");
1281
+ assert_eq!(prepared.chat_request.priority, -5);
1282
+ }
1283
+
1284
#[test]
1285
fn prepare_chat_request_ignores_correlation_header() {
1286
let mut headers = HeaderMap::new();
rust/src/server/src/routes/openai/completions/convert.rs
+22
-1
@@ -149,7 +149,7 @@ pub(super) fn prepare_completion_request(
149
min_tokens: request.min_tokens.unwrap_or(0),
150
},
151
intermediate: request.stream,
152
- priority: request.priority.unwrap_or(0),
152
+ priority: ctx.priority.or(request.priority).unwrap_or(0),
153
cache_salt: request.cache_salt,
154
add_special_tokens: request.add_special_tokens,
155
data_parallel_rank: ctx.data_parallel_rank,
@@ -619,6 +619,27 @@ mod tests {
619
);
620
}
621
622
+ #[test]
623
+ fn prepare_completion_request_header_priority_overrides_body() {
624
+ let request: CompletionRequest = serde_json::from_value(json!({
625
+ "model": "Qwen/Qwen1.5-0.5B-Chat",
626
+ "prompt": "hello",
627
+ "priority": 10,
628
+ }))
629
+ .expect("parse request");
630
+
631
+ let mut headers = HeaderMap::new();
632
+ headers.insert("X-Vllm-Priority", "-5".parse().unwrap());
633
+ let prepared = prepare_completion_request(
634
+ request,
635
+ &served(&["Qwen/Qwen1.5-0.5B-Chat"]),
636
+ request_context(&headers, None),
637
+ &test_tokenizer(),
638
+ )
639
+ .expect("prepare");
640
+ assert_eq!(prepared.text_request.priority, -5);
641
+ }
642
+
643
#[test]
644
fn prepare_completion_request_uses_vllm_xargs_session_id_fallback() {
645
let request: CompletionRequest = serde_json::from_value(json!({
rust/src/server/src/utils.rs
+8
-1
@@ -15,6 +15,7 @@ use crate::error::ApiError;
15
pub struct ResolvedRequestContext {
16
pub request_id: String,
17
pub data_parallel_rank: Option<u32>,
18
+ pub priority: Option<i32>,
19
pub session_id: Option<String>,
20
}
21
@@ -111,7 +112,8 @@ pub fn convert_logit_bias(
112
}
113
114
/// Extract common request metadata from HTTP headers: the external request ID,
114
-/// session ID, and the optional data-parallel rank used for engine routing.
115
+/// session ID, priority, and the optional data-parallel rank used for engine
116
+/// routing.
117
pub fn resolve_request_context(
118
headers: &HeaderMap,
119
request_id: Option<&str>,
@@ -121,6 +123,10 @@ pub fn resolve_request_context(
123
.get("X-data-parallel-rank")
124
.and_then(|v| v.to_str().ok())
125
.and_then(|s| s.trim().parse().ok());
126
+ let priority = headers
127
+ .get("X-Vllm-Priority")
128
+ .and_then(|value| value.to_str().ok())
129
+ .and_then(|value| value.trim().parse().ok());
130
131
// Extract request id from header.
132
let request_id_header = headers.get("X-Request-Id").and_then(|value| value.to_str().ok());
@@ -134,6 +140,7 @@ pub fn resolve_request_context(
140
ResolvedRequestContext {
141
request_id,
142
data_parallel_rank,
143
+ priority,
144
session_id,
145
}
146
}
vllm/entrypoints/generate/base/serving.py
+15
@@ -42,6 +42,7 @@ logger = init_logger(__name__)
42
RequestT = TypeVar("RequestT", bound=AnyRequest)
43
_T = TypeVar("_T")
44
SESSION_ID_HEADER = "X-Session-ID"
45
+PRIORITY_HEADER = "X-Vllm-Priority"
46
47
48
def build_per_request_timing_metrics(
@@ -239,6 +240,20 @@ class GenerateBaseServing(BaseServing, BeamSearchOnlineMixin):
240
return session_id
241
return None
242
243
+ @staticmethod
244
+ def _get_priority(
245
+ request: ChatCompletionRequest | CompletionRequest | ResponsesRequest,
246
+ raw_request: Request | None,
247
+ ) -> int:
248
+ if raw_request is not None:
249
+ priority = raw_request.headers.get(PRIORITY_HEADER)
250
+ if priority is not None:
251
+ try:
252
+ return int(priority)
253
+ except ValueError:
254
+ pass
255
+ return request.priority
256
+
257
async def _with_kv_transfer_rejection_cleanup(
258
self,
259
awaitable: Awaitable[_T],
vllm/entrypoints/openai/chat_completion/serving.py
+1
-1
@@ -348,7 +348,7 @@ class OpenAIServingChat(GenerateBaseServing):
348
sub_request_id,
349
lora_request=lora_request,
350
trace_headers=trace_headers,
351
- priority=request.priority,
351
+ priority=self._get_priority(request, raw_request),
352
data_parallel_rank=data_parallel_rank,
353
session_id=session_id,
354
reasoning_ended=reasoning_ended,
vllm/entrypoints/openai/completion/serving.py
+1
-1
@@ -212,7 +212,7 @@ class OpenAIServingCompletion(GenerateBaseServing):
212
request_id_item,
213
lora_request=lora_request,
214
trace_headers=trace_headers,
215
- priority=request.priority,
215
+ priority=self._get_priority(request, raw_request),
216
data_parallel_rank=data_parallel_rank,
217
session_id=session_id,
218
)
vllm/entrypoints/openai/responses/serving.py
+1
-1
@@ -515,7 +515,7 @@ class OpenAIServingResponses(GenerateBaseServing):
515
sampling_params=sampling_params,
516
context=context,
517
lora_request=lora_request,
518
- priority=request.priority,
518
+ priority=self._get_priority(request, raw_request),
519
trace_headers=trace_headers,
520
session_id=session_id,
521
reasoning_parser_kwargs=reasoning_parser_kwargs