Skip to content

[releases/v0.18.0][Platform][BugFix] Guard forced tool choice with empty content#8400

Merged
zzzzwwjj merged 3 commits into
vllm-project:releases/v0.18.0from
QwertyJack:fix/tool-choice-none-content-v018
Apr 23, 2026
Merged

[releases/v0.18.0][Platform][BugFix] Guard forced tool choice with empty content#8400
zzzzwwjj merged 3 commits into
vllm-project:releases/v0.18.0from
QwertyJack:fix/tool-choice-none-content-v018

Conversation

@QwertyJack

@QwertyJack QwertyJack commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

What this PR does / why we need it?

This backports the forced-tool-choice content=None guard to the releases/v0.18.0 compatibility layer.

Upstream vLLM still has forced named tool-choice branches that assert content is not None after reasoning extraction. Some reasoning parsers can legally consume the full output and return (reasoning, None), which makes the assert reachable and can surface as a server-side failure.

This PR follows the same compatibility-patch pattern used by:

The patch is intentionally narrow:

  • normalize content=None to "" only for forced named tool choice
  • patch both chat-completions and responses parser entry points
  • keep the rest of upstream behavior unchanged

Upstream tracking:

Does this PR introduce any user-facing change?

Yes.

Forced named tool choice becomes robust when the reasoning parser returns no post-reasoning content, avoiding an internal assertion failure and emitting an empty-argument function call instead.

How was this patch tested?

Unit tests:

pytest -sv tests/ut/patch/platform/test_patch_tool_choice_none_content.py \
  tests/ut/patch/platform/test_patch_glm_tool_call_parser.py \
  tests/ut/patch/platform/test_patch_minimax_usage_accounting.py

Result: 22 passed.

Backport the upstream forced-tool-choice fix to the v0.18.0 compatibility layer. Some reasoning parsers may consume the full model output and return content=None; normalize that to an empty argument string before the forced named tool-choice branches delegate back to upstream parsing.

References: vllm-project/vllm#40147, vllm-project/vllm#40148
Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
@QwertyJack QwertyJack requested a review from wangxiyuan as a code owner April 17, 2026 13:30
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a compatibility patch for the vLLM platform layer to address an issue where forced named tool choices fail when the reasoning parser returns no content. By normalizing 'None' content to an empty string in specific scenarios, the patch prevents internal assertion errors and ensures robust tool call generation. This change is isolated to the compatibility layer and maintains existing upstream behavior for all other cases.

Highlights

  • Forced Tool Choice Guard: Implemented a monkey-patch to normalize 'content=None' to an empty string when forced named tool choice is active, preventing server-side assertion failures.
  • Compatibility Layer Update: Applied the fix to both 'OpenAIServing' and 'DelegatingParser' entry points to ensure consistent behavior across different request types.
  • Testing: Added comprehensive unit tests to verify that named tool choices are correctly handled even when the reasoning parser returns no content.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a monkey-patch to normalize content=None to an empty string when a forced tool choice is active, preventing server-side failures when reasoning parsers consume all model output. The changes include the patch implementation, documentation updates, and new unit tests. Feedback highlights a Python version compatibility issue regarding the use of the | operator in isinstance() and notes that the PR title and summary need to be updated to comply with the repository's style guide.

Comment thread vllm_ascend/patch/platform/patch_tool_choice_none_content.py Outdated
Comment thread vllm_ascend/patch/__init__.py
Replace the PEP 604 union form inside isinstance() with a tuple of types so the compatibility patch remains valid on older supported Python versions.

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
@QwertyJack QwertyJack changed the title [Bugfix][Platform] Guard forced tool choice with empty content [releases/v0.18.0][Platform][BugFix] Guard forced tool choice with empty content Apr 17, 2026
Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
@zzzzwwjj zzzzwwjj merged commit d81101a into vllm-project:releases/v0.18.0 Apr 23, 2026
26 checks passed
@QwertyJack QwertyJack deleted the fix/tool-choice-none-content-v018 branch April 23, 2026 08:50
wangxiyuan pushed a commit that referenced this pull request Apr 30, 2026
)

### What this PR does / why we need it?
Backports the forced tool-choice `content=None` handling from vLLM
Ascend PR #8400 for the
vLLM 0.19.1 patch layer.

Related issue and PR:
- vLLM issue: vllm-project/vllm#40147
- vLLM PR: vllm-project/vllm#40148
- vLLM Ascend PR: #8400

Per the latest PR #40148 discussion, this patch does not synthesize an
empty function call or empty `{}` arguments. When forced tool choice
sees `content=None`, it returns an empty tool-call list and preserves
normal forced tool-call behavior when content is present.

### Does this PR introduce _any_ user-facing change?
Yes. Forced tool-choice requests no longer assert when reasoning
extraction leaves `content=None`; they return no tool calls for that
empty-content result instead.

### How was this patch tested?
- `ruff format --check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `ruff check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `python -m py_compile
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`
- `PYTHONPATH=../vllm:. pytest -q --confcutdir=tests/ut/patch/platform
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`

Also ran `bash format.sh ci`; it passed the available hooks but could
not complete `shellcheck` because `shellcheck` is not installed in this
environment.

- vLLM version: v0.19.1
- vLLM main:
vllm-project/vllm@d886c26

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
yangzhe-2026 pushed a commit to yangzhe-2026/vllm-ascend that referenced this pull request May 6, 2026
…lm-project#8833)

### What this PR does / why we need it?
Backports the forced tool-choice `content=None` handling from vLLM
Ascend PR vllm-project#8400 for the
vLLM 0.19.1 patch layer.

Related issue and PR:
- vLLM issue: vllm-project/vllm#40147
- vLLM PR: vllm-project/vllm#40148
- vLLM Ascend PR: vllm-project#8400

Per the latest PR #40148 discussion, this patch does not synthesize an
empty function call or empty `{}` arguments. When forced tool choice
sees `content=None`, it returns an empty tool-call list and preserves
normal forced tool-call behavior when content is present.

### Does this PR introduce _any_ user-facing change?
Yes. Forced tool-choice requests no longer assert when reasoning
extraction leaves `content=None`; they return no tool calls for that
empty-content result instead.

### How was this patch tested?
- `ruff format --check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `ruff check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `python -m py_compile
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`
- `PYTHONPATH=../vllm:. pytest -q --confcutdir=tests/ut/patch/platform
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`

Also ran `bash format.sh ci`; it passed the available hooks but could
not complete `shellcheck` because `shellcheck` is not installed in this
environment.

- vLLM version: v0.19.1
- vLLM main:
vllm-project/vllm@d886c26

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
PiratePai pushed a commit to PiratePai/vllm-ascend that referenced this pull request May 7, 2026
…lm-project#8833)

### What this PR does / why we need it?
Backports the forced tool-choice `content=None` handling from vLLM
Ascend PR vllm-project#8400 for the
vLLM 0.19.1 patch layer.

Related issue and PR:
- vLLM issue: vllm-project/vllm#40147
- vLLM PR: vllm-project/vllm#40148
- vLLM Ascend PR: vllm-project#8400

Per the latest PR #40148 discussion, this patch does not synthesize an
empty function call or empty `{}` arguments. When forced tool choice
sees `content=None`, it returns an empty tool-call list and preserves
normal forced tool-call behavior when content is present.

### Does this PR introduce _any_ user-facing change?
Yes. Forced tool-choice requests no longer assert when reasoning
extraction leaves `content=None`; they return no tool calls for that
empty-content result instead.

### How was this patch tested?
- `ruff format --check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `ruff check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `python -m py_compile
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`
- `PYTHONPATH=../vllm:. pytest -q --confcutdir=tests/ut/patch/platform
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`

Also ran `bash format.sh ci`; it passed the available hooks but could
not complete `shellcheck` because `shellcheck` is not installed in this
environment.

- vLLM version: v0.19.1
- vLLM main:
vllm-project/vllm@d886c26

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Signed-off-by: PiratePai <416932041@qq.com>
yangzhe-2026 pushed a commit to yangzhe-2026/vllm-ascend that referenced this pull request May 10, 2026
…lm-project#8833)

### What this PR does / why we need it?
Backports the forced tool-choice `content=None` handling from vLLM
Ascend PR vllm-project#8400 for the
vLLM 0.19.1 patch layer.

Related issue and PR:
- vLLM issue: vllm-project/vllm#40147
- vLLM PR: vllm-project/vllm#40148
- vLLM Ascend PR: vllm-project#8400

Per the latest PR #40148 discussion, this patch does not synthesize an
empty function call or empty `{}` arguments. When forced tool choice
sees `content=None`, it returns an empty tool-call list and preserves
normal forced tool-call behavior when content is present.

### Does this PR introduce _any_ user-facing change?
Yes. Forced tool-choice requests no longer assert when reasoning
extraction leaves `content=None`; they return no tool calls for that
empty-content result instead.

### How was this patch tested?
- `ruff format --check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `ruff check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `python -m py_compile
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`
- `PYTHONPATH=../vllm:. pytest -q --confcutdir=tests/ut/patch/platform
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`

Also ran `bash format.sh ci`; it passed the available hooks but could
not complete `shellcheck` because `shellcheck` is not installed in this
environment.

- vLLM version: v0.19.1
- vLLM main:
vllm-project/vllm@d886c26

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Signed-off-by: yangzhe-2026 <yangzhe@isrc.iscas.ac.cn>
ZhuQi-seu pushed a commit to ZhuQi-seu/vllm-ascend that referenced this pull request May 12, 2026
…lm-project#8833)

### What this PR does / why we need it?
Backports the forced tool-choice `content=None` handling from vLLM
Ascend PR vllm-project#8400 for the
vLLM 0.19.1 patch layer.

Related issue and PR:
- vLLM issue: vllm-project/vllm#40147
- vLLM PR: vllm-project/vllm#40148
- vLLM Ascend PR: vllm-project#8400

Per the latest PR #40148 discussion, this patch does not synthesize an
empty function call or empty `{}` arguments. When forced tool choice
sees `content=None`, it returns an empty tool-call list and preserves
normal forced tool-call behavior when content is present.

### Does this PR introduce _any_ user-facing change?
Yes. Forced tool-choice requests no longer assert when reasoning
extraction leaves `content=None`; they return no tool calls for that
empty-content result instead.

### How was this patch tested?
- `ruff format --check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `ruff check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `python -m py_compile
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`
- `PYTHONPATH=../vllm:. pytest -q --confcutdir=tests/ut/patch/platform
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`

Also ran `bash format.sh ci`; it passed the available hooks but could
not complete `shellcheck` because `shellcheck` is not installed in this
environment.

- vLLM version: v0.19.1
- vLLM main:
vllm-project/vllm@d886c26

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Signed-off-by: ZhuQi-seu <zhuqi12@huawei.com>
nanxingMy pushed a commit to nanxingMy/vllm-ascend that referenced this pull request May 15, 2026
…lm-project#8833)

### What this PR does / why we need it?
Backports the forced tool-choice `content=None` handling from vLLM
Ascend PR vllm-project#8400 for the
vLLM 0.19.1 patch layer.

Related issue and PR:
- vLLM issue: vllm-project/vllm#40147
- vLLM PR: vllm-project/vllm#40148
- vLLM Ascend PR: vllm-project#8400

Per the latest PR #40148 discussion, this patch does not synthesize an
empty function call or empty `{}` arguments. When forced tool choice
sees `content=None`, it returns an empty tool-call list and preserves
normal forced tool-call behavior when content is present.

### Does this PR introduce _any_ user-facing change?
Yes. Forced tool-choice requests no longer assert when reasoning
extraction leaves `content=None`; they return no tool calls for that
empty-content result instead.

### How was this patch tested?
- `ruff format --check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `ruff check
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py
vllm_ascend/patch/platform/__init__.py vllm_ascend/patch/__init__.py`
- `python -m py_compile
vllm_ascend/patch/platform/patch_tool_choice_none_content.py
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`
- `PYTHONPATH=../vllm:. pytest -q --confcutdir=tests/ut/patch/platform
tests/ut/patch/platform/test_patch_tool_choice_none_content.py`

Also ran `bash format.sh ci`; it passed the available hooks but could
not complete `shellcheck` because `shellcheck` is not installed in this
environment.

- vLLM version: v0.19.1
- vLLM main:
vllm-project/vllm@d886c26

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Signed-off-by: nanxing <1014662416@qq.com>
immengzi pushed a commit to immengzi/vllm-ascend that referenced this pull request May 21, 2026
…pty content (vllm-project#8400)

### What this PR does / why we need it?

This backports the forced-tool-choice `content=None` guard to the
`releases/v0.18.0` compatibility layer.

Upstream vLLM still has forced named tool-choice branches that assert
`content is not None` after reasoning extraction. Some reasoning parsers
can legally consume the full output and return `(reasoning, None)`,
which makes the assert reachable and can surface as a server-side
failure.

This PR follows the same compatibility-patch pattern used by:
- `7314bbe2` fix(platform): reimplement MiniMax usage accounting patch
(vllm-project#7835)
- `f83cb0e6` [Bugfix][Platform] Fix GLM47 tool-call finish backfill
(vllm-project#7710)

The patch is intentionally narrow:
- normalize `content=None` to `""` only for forced named tool choice
- patch both chat-completions and responses parser entry points
- keep the rest of upstream behavior unchanged

Upstream tracking:
- issue: vllm-project/vllm#40147
- PR: vllm-project/vllm#40148

### Does this PR introduce _any_ user-facing change?

Yes.

Forced named tool choice becomes robust when the reasoning parser
returns no post-reasoning content, avoiding an internal assertion
failure and emitting an empty-argument function call instead.

### How was this patch tested?

Unit tests:
```bash
pytest -sv tests/ut/patch/platform/test_patch_tool_choice_none_content.py \
  tests/ut/patch/platform/test_patch_glm_tool_call_parser.py \
  tests/ut/patch/platform/test_patch_minimax_usage_accounting.py
```

Result: 22 passed.

---------

Signed-off-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Co-authored-by: QwertyJack <7554089+QwertyJack@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants