[CI] Fix and extend PR/issue auto-labeling (#51459)
Signed-off-by: Joe Cotant <joe@inferact.ai> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Claude Opus 5
mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent
d591d1d511
commit
c205726108
@@ -1,40 +0,0 @@
|
||||
name: 🤗 Support request for a new model from huggingface
|
||||
description: Submit a proposal/request for a new model from huggingface
|
||||
title: "[New Model]: "
|
||||
labels: ["new model"]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
#### Before submitting an issue, please make sure the issue hasn't been already addressed by searching through [the existing and past issues](https://github.com/vllm-project/vllm/issues?q=is%3Aissue+sort%3Acreated-desc+).
|
||||
|
||||
#### We also highly recommend you read https://docs.vllm.ai/en/latest/contributing/model/index.html first to understand how to add a new model.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: The model to consider.
|
||||
description: >
|
||||
A huggingface url, pointing to the model, e.g. https://huggingface.co/openai-community/gpt2 .
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: The closest model vllm already supports.
|
||||
description: >
|
||||
Here is the list of models already supported by vllm: https://github.com/vllm-project/vllm/tree/main/vllm/model_executor/models . Which model is the most similar to the model you want to add support for?
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: What's your difficulty of supporting the model you want?
|
||||
description: >
|
||||
For example, any new operators or new architecture?
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
Thanks for contributing 🎉!
|
||||
- type: checkboxes
|
||||
id: askllm
|
||||
attributes:
|
||||
label: Before submitting a new issue...
|
||||
options:
|
||||
- label: Make sure you already searched for relevant issues, and asked the chatbot living at the bottom right corner of the [documentation page](https://docs.vllm.ai/en/latest/), which can answer lots of frequently asked questions.
|
||||
required: true
|
||||
+176
-22
@@ -93,6 +93,7 @@ pull_request_rules:
|
||||
- files~=^examples/.*deepseek.*\.py
|
||||
- files~=^tests/.*deepseek.*\.py
|
||||
- files~=^vllm/model_executor/models/.*deepseek.*\.py
|
||||
- files~=^vllm/models/deepseek.*/
|
||||
- files~=^vllm/tool_parsers/.*deepseek.*\.py
|
||||
- files~=^vllm/reasoning/.*deepseek.*\.py
|
||||
- files~=^vllm/transformers_utils/.*deepseek.*\.py
|
||||
@@ -133,9 +134,7 @@ pull_request_rules:
|
||||
- files~=^examples/.*llama.*\.py
|
||||
- files~=^tests/.*llama.*\.py
|
||||
- files~=^vllm/model_executor/models/.*llama.*\.py
|
||||
- files~=^vllm/reasoning/.*llama.*\.py
|
||||
- files~=^vllm/tool_parsers/.*llama.*\.py
|
||||
- files~=^vllm/transformers_utils/.*llama.*\.py
|
||||
- title~=(?i)llama
|
||||
actions:
|
||||
label:
|
||||
@@ -177,9 +176,17 @@ pull_request_rules:
|
||||
description: Automatically apply new-model label
|
||||
conditions:
|
||||
- label != stale
|
||||
- and:
|
||||
- files~=^vllm/model_executor/models/
|
||||
- files=vllm/model_executor/models/registry.py
|
||||
- or:
|
||||
# A model file is added alongside a registry entry. added-files rather
|
||||
# than files: touching an existing model file is a fix or a refactor,
|
||||
# and a PR that removes a model is not a new one.
|
||||
- and:
|
||||
- added-files~=^vllm/model_executor/models/.*\.py$
|
||||
- files=vllm/model_executor/models/registry.py
|
||||
# A new top-level package under vllm/models/. common/ is shared kernel
|
||||
# code rather than a model, and nested packages belong to a model that
|
||||
# already exists.
|
||||
- added-files~=^vllm/models/(?!common/)[^/]+/__init__\.py$
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
@@ -233,16 +240,12 @@ pull_request_rules:
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^examples/.*gpt[-_]?oss.*\.py
|
||||
- files~=^tests/.*gpt[-_]?oss.*\.py
|
||||
- files~=^tests/entrypoints/openai/test_response_api_with_harmony.py
|
||||
- files~=^tests/entrypoints/test_context.py
|
||||
- files~=^vllm/model_executor/models/.*gpt[-_]?oss.*\.py
|
||||
- files~=^vllm/model_executor/layers/.*gpt[-_]?oss.*\.py
|
||||
- files~=^vllm/entrypoints/openai/parser/harmony_utils.py
|
||||
- files~=^vllm/entrypoints/tool_server.py
|
||||
- files~=^vllm/entrypoints/tool.py
|
||||
- files~=^vllm/entrypoints/context.py
|
||||
# Harmony rendering now lives in the Rust frontend.
|
||||
- files~=^rust/src/chat/src/.*/harmony/
|
||||
- title~=(?i)gpt[-_]?oss
|
||||
- title~=(?i)harmony
|
||||
actions:
|
||||
@@ -256,7 +259,6 @@ pull_request_rules:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=(?i)kimi
|
||||
- files~=(?i)moonshot
|
||||
- title~=(?i)(?:kimi|moonshot)
|
||||
actions:
|
||||
label:
|
||||
@@ -275,12 +277,73 @@ pull_request_rules:
|
||||
add:
|
||||
- k3
|
||||
|
||||
- name: label-glm
|
||||
description: Automatically apply glm label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
# Anchored to the start of the filename so midashenglm.py (an unrelated
|
||||
# model) does not match.
|
||||
- files~=^vllm/model_executor/models/(?:chat)?glm[^/]*\.py
|
||||
- files~=^tests/.*[/_](?:chat)?glm[^/]*\.py
|
||||
- files~=^vllm/tool_parsers/.*glm.*\.py
|
||||
- files~=^vllm/transformers_utils/.*/(?:chat)?glm[^/]*\.py
|
||||
- title~=(?i)\b(?:chat)?glm
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- glm
|
||||
|
||||
- name: label-minimax
|
||||
description: Automatically apply minimax label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/models/minimax.*/
|
||||
- files~=^vllm/model_executor/models/.*minimax.*\.py
|
||||
- files~=^vllm/tool_parsers/.*minimax.*\.py
|
||||
- files~=^tests/.*minimax.*\.py
|
||||
- title~=(?i)minimax
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- minimax
|
||||
|
||||
- name: label-inkling
|
||||
description: Automatically apply inkling label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/models/inkling/
|
||||
- files~=^vllm/tool_parsers/inkling.*\.py
|
||||
- files~=^vllm/renderers/inkling.*\.py
|
||||
- files~=^tests/.*inkling.*\.py
|
||||
- title~=(?i)inkling
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- inkling
|
||||
|
||||
- name: label-dsv4
|
||||
description: Automatically apply DSv4 label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/models/deepseek_v4/
|
||||
- title~=(?i)(?:\bDSv4\b|deepseek[-\s]?v4)
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- DSv4
|
||||
|
||||
- name: label-nvidia
|
||||
description: Automatically apply nvidia label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=cuda
|
||||
# Exclude requirements/: a dependency bump touching requirements/cuda.txt
|
||||
# is not NVIDIA backend work.
|
||||
- files~=^(?!requirements/).*cuda
|
||||
- files~=cutlass
|
||||
- files~=flashinfer
|
||||
- files~=trtllm
|
||||
@@ -300,7 +363,7 @@ pull_request_rules:
|
||||
- files~=^csrc/rocm/
|
||||
- files~=^docker/Dockerfile.rocm
|
||||
- files~=^requirements/rocm.*\.txt
|
||||
- files~=^vllm/model_executor/layers/fused_moe/rocm.*\.py
|
||||
- files~=^vllm/model_executor/layers/fused_moe/.*rocm.*\.py
|
||||
- files~=^vllm/v1/attention/backends/rocm.*\.py
|
||||
- files~=^vllm/v1/attention/backends/mla/rocm.*\.py
|
||||
- files~=^vllm/v1/attention/ops/rocm.*\.py
|
||||
@@ -319,8 +382,8 @@ pull_request_rules:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^docker/Dockerfile.xpu
|
||||
- files~=^\\.buildkite/intel_jobs/
|
||||
- files=\.buildkite/ci_config_intel.yaml
|
||||
- files~=^\.buildkite/intel_jobs/
|
||||
- files=.buildkite/ci_config_intel.yaml
|
||||
- files=vllm/model_executor/layers/fused_moe/experts/xpu_moe.py
|
||||
- files=vllm/model_executor/kernels/linear/mixed_precision/xpu.py
|
||||
- files=vllm/model_executor/kernels/linear/mxfp8/xpu.py
|
||||
@@ -331,7 +394,6 @@ pull_request_rules:
|
||||
- files=vllm/v1/worker/xpu_worker.py
|
||||
- files=vllm/v1/worker/xpu_model_runner.py
|
||||
- files=vllm/_xpu_ops.py
|
||||
- files=vllm/kernels/xpu_ops.py
|
||||
- files~=^vllm/lora/ops/xpu_ops
|
||||
- files=vllm/lora/punica_wrapper/punica_xpu.py
|
||||
- files=vllm/platforms/xpu.py
|
||||
@@ -367,7 +429,7 @@ pull_request_rules:
|
||||
- files=benchmarks/benchmark_serving_structured_output.py
|
||||
- files=benchmarks/run_structured_output_benchmark.sh
|
||||
- files=docs/features/structured_outputs.md
|
||||
- files=^examples/features/structured_outputs/
|
||||
- files~=^examples/features/structured_outputs/
|
||||
- files~=^tests/v1/structured_output/
|
||||
- files=tests/entrypoints/llm/test_struct_output_generate.py
|
||||
- files~=^vllm/v1/structured_output/
|
||||
@@ -382,8 +444,11 @@ pull_request_rules:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/v1/spec_decode/
|
||||
# Model Runner V2 keeps its speculators in a separate subtree.
|
||||
- files~=^vllm/v1/worker/gpu/spec_decode/
|
||||
- files~=^tests/v1/spec_decode/
|
||||
- files=^examples/features/speculative_decoding/
|
||||
- files~=^tests/v1/e2e/spec_decode/
|
||||
- files~=^examples/features/speculative_decoding/
|
||||
- files~=^vllm/model_executor/models/.*eagle.*\.py
|
||||
- files=vllm/model_executor/models/mlp_speculator.py
|
||||
- files~=^vllm/transformers_utils/configs/(eagle|medusa|mlp_speculator)\.py
|
||||
@@ -392,6 +457,33 @@ pull_request_rules:
|
||||
add:
|
||||
- speculative-decoding
|
||||
|
||||
# dflash covers the DFlash and DSpark speculative decoding techniques as one
|
||||
# workstream, matching how they are tracked in the sprint board. It is applied
|
||||
# in addition to speculative-decoding, never instead of it.
|
||||
- name: label-dflash
|
||||
description: Automatically apply dflash label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/v1/spec_decode/dflash\.py
|
||||
- files~=^vllm/v1/worker/gpu/spec_decode/dflash/
|
||||
- files~=^vllm/model_executor/models/.*dflash.*\.py
|
||||
- files~=^tests/.*dflash.*\.py
|
||||
- title~=(?i)dflash
|
||||
- files~=^vllm/v1/worker/gpu/spec_decode/dspark/
|
||||
- files~=^vllm/model_executor/models/.*dspark.*\.py
|
||||
- files~=^vllm/models/deepseek_v4/nvidia/dspark\.py
|
||||
- files~=^vllm/models/deepseek_v4/amd/dspark\.py
|
||||
- files~=^vllm/models/deepseek_v4/xpu/dspark\.py
|
||||
- files~=^vllm/models/kimi_k3/nvidia/dspark_mla\.py
|
||||
- files~=^vllm/transformers_utils/configs/k3_dspark\.py
|
||||
- files~=^tests/.*dspark.*\.py
|
||||
- title~=(?i)dspark
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- dflash
|
||||
|
||||
- name: label-mrv2
|
||||
description: Automatically apply mrv2 label
|
||||
conditions:
|
||||
@@ -416,7 +508,6 @@ pull_request_rules:
|
||||
- files~=_tpu
|
||||
- files~=tpu_
|
||||
- files~=/tpu/
|
||||
- files~=pallas
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
@@ -432,7 +523,6 @@ pull_request_rules:
|
||||
- -files~=_tpu
|
||||
- -files~=tpu_
|
||||
- -files~=/tpu/
|
||||
- -files~=pallas
|
||||
actions:
|
||||
label:
|
||||
remove:
|
||||
@@ -448,7 +538,6 @@ pull_request_rules:
|
||||
- files~=^tests/parser/
|
||||
- files~=^tests/reasoning/
|
||||
- files~=^tests/entrypoints/openai/.*tool.*
|
||||
- files~=^tests/entrypoints/anthropic/.*tool.*
|
||||
- files~=^vllm/tool_parsers/
|
||||
- files~=^vllm/parser/
|
||||
- files~=^vllm/reasoning/
|
||||
@@ -552,3 +641,68 @@ pull_request_rules:
|
||||
label:
|
||||
add:
|
||||
- kv-connector
|
||||
|
||||
- name: label-scheduler
|
||||
description: Automatically apply scheduler label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/v1/core/sched/
|
||||
- files~=^tests/v1/core/test_scheduler.*\.py
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- scheduler
|
||||
|
||||
- name: label-kv-cache-manager
|
||||
description: Automatically apply kv-cache-manager label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
# Scoped to the block/cache allocator in v1/core. KV transfer between
|
||||
# instances is kv-connector, not this.
|
||||
- files~=^vllm/v1/core/(?:kv_cache|block_pool|single_type_kv_cache)
|
||||
- files~=^tests/v1/core/test_(?:kv_cache|single_type_kv_cache|prefix_caching).*\.py
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- kv-cache-manager
|
||||
|
||||
- name: label-torch-compile
|
||||
description: Automatically apply torch.compile label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/compilation/
|
||||
- files~=^tests/compile/
|
||||
- title~=(?i)torch\.compile
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- torch.compile
|
||||
|
||||
- name: label-ray
|
||||
description: Automatically apply ray label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/v1/executor/ray_
|
||||
- files~=^tests/.*ray_.*\.py
|
||||
- title~=(?i)\bray\b
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- ray
|
||||
|
||||
- name: label-vllm-ir
|
||||
description: Automatically apply vllm-ir label
|
||||
conditions:
|
||||
- label != stale
|
||||
- or:
|
||||
- files~=^vllm/ir/
|
||||
- files~=^tests/ir/
|
||||
- title~=(?i)vllm[-\s]?ir
|
||||
actions:
|
||||
label:
|
||||
add:
|
||||
- vllm-ir
|
||||
|
||||
@@ -190,6 +190,159 @@ jobs:
|
||||
},
|
||||
],
|
||||
},
|
||||
// Model labels. Title-only by design: issue bodies carry pasted
|
||||
// collect_env output, tracebacks and configs that name unrelated
|
||||
// models, hardware and libraries.
|
||||
deepseek: {
|
||||
keywords: [{ term: "DeepSeek", searchIn: "title" }],
|
||||
},
|
||||
DSv4: {
|
||||
keywords: [{ term: "DSv4", searchIn: "title" }],
|
||||
substrings: [{ term: "deepseek-v4", searchIn: "title" }],
|
||||
},
|
||||
llama: {
|
||||
keywords: [{ term: "Llama", searchIn: "title" }],
|
||||
},
|
||||
mistral: {
|
||||
keywords: [
|
||||
{ term: "Mistral", searchIn: "title" },
|
||||
{ term: "Ministral", searchIn: "title" },
|
||||
{ term: "Voxtral", searchIn: "title" },
|
||||
{ term: "Mixtral", searchIn: "title" },
|
||||
{ term: "Pixtral", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
qwen: {
|
||||
keywords: [{ term: "Qwen", searchIn: "title" }],
|
||||
},
|
||||
"gpt-oss": {
|
||||
keywords: [{ term: "harmony", searchIn: "title" }],
|
||||
substrings: [{ term: "gpt-oss", searchIn: "title" }],
|
||||
},
|
||||
glm: {
|
||||
keywords: [
|
||||
{ term: "GLM", searchIn: "title" },
|
||||
{ term: "ChatGLM", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
minimax: {
|
||||
substrings: [{ term: "minimax", searchIn: "title" }],
|
||||
},
|
||||
inkling: {
|
||||
keywords: [{ term: "Inkling", searchIn: "title" }],
|
||||
},
|
||||
// Hardware backends, mirroring the cpu/rocm/intel-gpu rules above.
|
||||
nvidia: {
|
||||
keywords: [
|
||||
{ term: "NVIDIA", searchIn: "title" },
|
||||
{ term: "CUTLASS", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
tpu: {
|
||||
keywords: [
|
||||
{ term: "TPU", searchIn: "title" },
|
||||
{ term: "XLA", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
// Subsystems.
|
||||
//
|
||||
// Single distinctive words go in `keywords` so the word-boundary
|
||||
// match keeps them from firing inside longer words. Multi-word
|
||||
// phrases go in `substrings`: they are specific enough on their
|
||||
// own, and a boundary match would miss ordinary variations --
|
||||
// "structured outputs", "tool parsers", "prefix caching".
|
||||
"speculative-decoding": {
|
||||
keywords: [
|
||||
{ term: "eagle", searchIn: "title" },
|
||||
{ term: "medusa", searchIn: "title" },
|
||||
],
|
||||
substrings: [
|
||||
{ term: "speculative decoding", searchIn: "title" },
|
||||
{ term: "speculative decode", searchIn: "title" },
|
||||
{ term: "draft model", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
"structured-output": {
|
||||
substrings: [
|
||||
{ term: "structured output", searchIn: "title" },
|
||||
{ term: "guided decoding", searchIn: "title" },
|
||||
{ term: "JSON schema", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
"tool-calling": {
|
||||
substrings: [
|
||||
{ term: "tool calling", searchIn: "title" },
|
||||
{ term: "tool parser", searchIn: "title" },
|
||||
{ term: "function calling", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
"kv-connector": {
|
||||
keywords: [
|
||||
{ term: "NIXL", searchIn: "title" },
|
||||
{ term: "LMCache", searchIn: "title" },
|
||||
],
|
||||
substrings: [
|
||||
{ term: "KV transfer", searchIn: "title" },
|
||||
{ term: "disaggregat", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
scheduler: {
|
||||
keywords: [{ term: "scheduler", searchIn: "title" }],
|
||||
},
|
||||
"kv-cache-manager": {
|
||||
substrings: [
|
||||
{ term: "kv cache manager", searchIn: "title" },
|
||||
{ term: "block manager", searchIn: "title" },
|
||||
{ term: "prefix cache", searchIn: "title" },
|
||||
{ term: "prefix caching", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
"torch.compile": {
|
||||
substrings: [{ term: "torch.compile", searchIn: "title" }],
|
||||
},
|
||||
"multi-modality": {
|
||||
keywords: [
|
||||
{ term: "multimodal", searchIn: "title" },
|
||||
{ term: "multi-modal", searchIn: "title" },
|
||||
],
|
||||
substrings: [
|
||||
{ term: "image input", searchIn: "title" },
|
||||
{ term: "audio input", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
"ci-failure": {
|
||||
keywords: [{ term: "flaky", searchIn: "title" }],
|
||||
substrings: [
|
||||
{ term: "CI failure", searchIn: "title" },
|
||||
{ term: "test failure", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
rl: {
|
||||
keywords: [{ term: "RLHF", searchIn: "title" }],
|
||||
substrings: [
|
||||
{ term: "reinforcement learning", searchIn: "title" },
|
||||
{ term: "weight update", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
ray: {
|
||||
substrings: [
|
||||
{ term: "Ray cluster", searchIn: "title" },
|
||||
{ term: "Ray executor", searchIn: "title" },
|
||||
{ term: "placement group", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
"vllm-ir": {
|
||||
substrings: [
|
||||
{ term: "vllm-ir", searchIn: "title" },
|
||||
{ term: "vllm ir", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
rust: {
|
||||
keywords: [
|
||||
{ term: "Rust", searchIn: "title" },
|
||||
{ term: "PyO3", searchIn: "title" },
|
||||
],
|
||||
},
|
||||
// Add more label configurations here as needed
|
||||
// example: {
|
||||
// keywords: [...],
|
||||
|
||||
@@ -165,6 +165,13 @@ repos:
|
||||
language: python
|
||||
entry: python tools/pre_commit/generate_nightly_torch_test.py
|
||||
files: ^requirements/test/cuda\.(in|txt)$
|
||||
- id: check-label-rules
|
||||
name: Check auto-label rules still match real files
|
||||
language: python
|
||||
entry: python tools/pre_commit/check_label_rules.py
|
||||
files: ^\.github/mergify\.yml$
|
||||
pass_filenames: false
|
||||
additional_dependencies: [PyYAML, regex]
|
||||
- id: mypy-3.10 # TODO: Use https://github.com/pre-commit/mirrors-mypy when mypy setup is less awkward
|
||||
name: Run mypy for Python 3.10
|
||||
entry: python tools/pre_commit/mypy.py "3.10"
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
# Labels
|
||||
|
||||
vLLM applies most labels automatically. Three systems do the work:
|
||||
|
||||
| | Config | Applies to |
|
||||
| --- | --- | --- |
|
||||
| Mergify | `.github/mergify.yml` | pull requests, by changed file path or title |
|
||||
| GitHub Action | `.github/workflows/issue_autolabel.yml` | issues, by title keyword |
|
||||
| Issue templates | `.github/ISSUE_TEMPLATE/*.yml` | issues, by which template was used |
|
||||
|
||||
A label that none of them applies has to be applied by hand, and in practice
|
||||
that means it mostly doesn't get applied at all.
|
||||
|
||||
A template's `labels:` field must name a label that already exists — GitHub
|
||||
silently skips one that doesn't, with no error anywhere.
|
||||
|
||||
`mergify.yml` also drives reviewer auto-assignment; see
|
||||
[Collaboration](../governance/collaboration.md) for adding yourself as a
|
||||
maintainer of an area.
|
||||
|
||||
## Adding a label
|
||||
|
||||
A new label needs three things. If it can't have all three, don't create it.
|
||||
|
||||
1. **An audience.** Someone has to want to filter on it. "It would be nice to
|
||||
know" isn't enough.
|
||||
2. **An owner.** A person or SIG who triages that queue. Without one the label
|
||||
accumulates issues nobody reads.
|
||||
3. **A rule, in the same PR.** If you can't write a precise rule, the label
|
||||
depends on humans remembering it exists.
|
||||
|
||||
## Measure before you argue
|
||||
|
||||
Volume claims should come from the repo, not from intuition. vLLM squash-merges,
|
||||
so one commit on `main` is one PR:
|
||||
|
||||
```bash
|
||||
git log --since="6 months ago" --oneline -- <path> | wc -l
|
||||
```
|
||||
|
||||
For reference, at the time of writing: `speculative-decoding` 81, `llama` 85,
|
||||
`mistral` 72, `tpu` 14. A subsystem in that range clears the bar comfortably.
|
||||
Something in the low teens needs a stronger argument than volume.
|
||||
|
||||
The same applies to claiming a rule is too broad. Check what it actually labels
|
||||
before rewriting it:
|
||||
|
||||
```bash
|
||||
gh pr list --repo vllm-project/vllm --state merged --limit 200 \
|
||||
--json number,title,labels
|
||||
```
|
||||
|
||||
Several rules in this repo have been called over-broad on inspection and turned
|
||||
out to be accurate when measured.
|
||||
|
||||
## Writing conditions
|
||||
|
||||
**`files=` is exact equality. `files~=` is a regex.** Writing a pattern after
|
||||
`=` compares the pattern text to each filename, so it can never match:
|
||||
|
||||
```yaml
|
||||
- files=^examples/features/speculative_decoding/ # never fires
|
||||
- files~=^examples/features/speculative_decoding/ # correct
|
||||
```
|
||||
|
||||
**Anchor file patterns.** An unanchored fragment matches far more than intended
|
||||
— `files~=cuda` also matches `requirements/cuda.txt`.
|
||||
|
||||
**Watch for names that contain other names.** `midashenglm.py` contains "glm"
|
||||
but is unrelated to GLM, so the `glm` rule anchors to the start of the filename.
|
||||
|
||||
**Keep an eye on where model code lives.** Newer models live in
|
||||
`vllm/models/<model>/` rather than `vllm/model_executor/models/`. A rule that
|
||||
only knows the old location silently stops matching.
|
||||
|
||||
**Prefer titles for issue keywords.** Issue bodies contain pasted `collect_env`
|
||||
output, tracebacks and configs that mention unrelated hardware and libraries.
|
||||
Body matching tags the reporter's environment rather than the topic.
|
||||
|
||||
**Single words go in `keywords`, phrases in `substrings`.** `keywords` matches on
|
||||
word boundaries, which stops a term firing inside a longer word but also misses
|
||||
ordinary variations — `structured output` will not match "structured outputs".
|
||||
|
||||
## Keeping rules honest
|
||||
|
||||
`tools/pre_commit/check_label_rules.py` fails if any file condition in
|
||||
`mergify.yml` matches nothing in the tree. It runs via pre-commit when that file
|
||||
changes. This catches conditions that were correct when written and rotted when
|
||||
the code moved.
|
||||
|
||||
## Not everything needs a label
|
||||
|
||||
Triage and workflow labels — `bug`, `stale`, `ready`, `RFC`, `needs-rebase` —
|
||||
and the bot provenance tags are applied by other mechanisms or by hand, and are
|
||||
intentionally outside this system.
|
||||
@@ -0,0 +1,97 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Check that auto-labeling rules still match real files.
|
||||
|
||||
Mergify silently ignores a condition that can never match, so a file-path
|
||||
condition keeps passing review long after the file it names has moved or been
|
||||
deleted. This walks every `files=` / `files~=` condition in `.github/mergify.yml`
|
||||
and fails if one matches nothing in the tree.
|
||||
|
||||
Usage:
|
||||
python tools/pre_commit/check_label_rules.py
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import regex as re
|
||||
import yaml
|
||||
|
||||
MERGIFY = ".github/mergify.yml"
|
||||
|
||||
|
||||
def tracked_files() -> list[str]:
|
||||
out = subprocess.run(
|
||||
["git", "ls-files"], capture_output=True, text=True, check=True
|
||||
).stdout
|
||||
return [line for line in out.split("\n") if line]
|
||||
|
||||
|
||||
def file_conditions(node, found: list[str]) -> list[str]:
|
||||
"""Collect every string condition nested under a rule's `conditions`."""
|
||||
if isinstance(node, dict):
|
||||
for value in node.values():
|
||||
file_conditions(value, found)
|
||||
elif isinstance(node, list):
|
||||
for value in node:
|
||||
file_conditions(value, found)
|
||||
elif isinstance(node, str):
|
||||
found.append(node)
|
||||
return found
|
||||
|
||||
|
||||
def check(files: list[str]) -> list[tuple[str, str, str]]:
|
||||
with open(MERGIFY) as f:
|
||||
rules = yaml.safe_load(f)["pull_request_rules"]
|
||||
|
||||
# removed-files is excluded: it names paths the tree no longer has.
|
||||
attrs = ("files", "added-files", "modified-files")
|
||||
|
||||
dead = []
|
||||
for rule in rules:
|
||||
for cond in file_conditions(rule.get("conditions", []), []):
|
||||
cond = cond.strip()
|
||||
# Negated conditions (label-tpu-remove) are expected to match nothing.
|
||||
if cond.startswith("-"):
|
||||
continue
|
||||
attr = next(
|
||||
(
|
||||
a
|
||||
for a in attrs
|
||||
if cond.startswith(f"{a}~=") or cond.startswith(f"{a}=")
|
||||
),
|
||||
None,
|
||||
)
|
||||
if attr is None:
|
||||
continue
|
||||
if cond.startswith(f"{attr}~="):
|
||||
pattern = cond[len(attr) + 2 :]
|
||||
try:
|
||||
regex = re.compile(pattern)
|
||||
except re.error as exc:
|
||||
dead.append((rule["name"], cond, f"invalid regex: {exc}"))
|
||||
continue
|
||||
matched = any(regex.search(f) for f in files)
|
||||
else:
|
||||
matched = cond[len(attr) + 1 :] in files
|
||||
if not matched:
|
||||
dead.append((rule["name"], cond, "matches no tracked file"))
|
||||
return dead
|
||||
|
||||
|
||||
def main() -> int:
|
||||
dead = check(tracked_files())
|
||||
if not dead:
|
||||
return 0
|
||||
print(f"{len(dead)} label condition(s) match nothing:\n", file=sys.stderr)
|
||||
for name, cond, why in dead:
|
||||
print(f" [{name}] {cond}\n {why}", file=sys.stderr)
|
||||
print(
|
||||
"\nRepoint the condition at the file's new location, or remove it.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user