Skip to content

[Router] Standardize route_request signature across all routers (rebase of #1023) - #1159

Open
chrikrah wants to merge 2 commits into
vllm-project:mainfrom
chrikrah:route-request-signature
Open

chrikrah wants to merge 2 commits into
vllm-project:mainfrom
chrikrah:route-request-signature

Conversation

@chrikrah

Copy link
Copy Markdown
Contributor

Mohith26's #1023 on main 7b1164d: every router's route_request becomes async with request_json: Optional[Dict] = None, and the three dispatch sites in request.py stop type-sniffing. Fixes #1022.

Since the original base, PriorityRouter and LoadAwareRouter arrived and get the same signature here, and the three-way isinstance chain #1021 put on the multipart call collapses to one awaited call passing {}. The DisaggregatedPrefill* handler dispatch stays, since those routers use different handlers.

Cost: an out-of-tree router with a sync route_request breaks on the first request, with TypeError: object str can't be used in 'await' expression. The fake router in test_request_stats.py from #1115 hit that and is now async. Once this lands I would close my #1095, whose isinstance addition becomes dead code. #929 by nejch and #950 by guba-elleschr make the same async change, both before #1023, and #1020 patches the multipart call alone. All three are open, and this series covers each.

On #1095 I said I would keep Mohith Gajjela as author, but the new routers and the #1021 chain meant rewriting his commits, so he is credited in a Co-authored-by: trailer on both commits. The second extends his signature tests to the two new routers and checks that request_json defaults to None.

# tree: git archive 7b1164d, vllm_router.__file__ printed from the exported tree's src/; Python 3.12, ~/.venvs/production-stack
$ PYTHONPATH=$PWD/src python -m pytest -q -p no:cacheprovider src/tests   # base 7b1164d
272 passed, 1 warning in 16.25s
$ PYTHONPATH=$PWD/src python -m pytest -q -p no:cacheprovider src/tests   # this series
292 passed, 1 warning in 17.56s
$ # falsification: routing_logic.py and request.py restored to 7b1164d, every test file kept
18 failed, 274 passed, 2 warnings in 15.49s   rc=1
# 14 in test_route_request_signature.py, 3 in test_roundrobin_router.py, 1 in test_request_stats.py
$ uvx black@25.1.0 --check <six changed files>
6 files would be left unchanged.
$ uvx isort@6.0.0 --check-only --profile black <six changed files>   # rc=0, no output
$ uvx ruff@0.12.0 check <six changed files>
All checks passed!
$ uvx codespell@2.4.1 <six changed files>   # rc=0, no output
# not run: the helm, actionlint, hadolint and shellcheck hooks, because no file they cover changes; the router end to end
  • Make sure the code changes pass the pre-commit checks.
    • black, isort, ruff and codespell ran at the pinned versions on all six changed files.
  • Sign-off your commit by using -s when doing git commit
  • Try to classify PRs for easy understanding of the type of changes, such as [Bugfix], [Feat], and [CI].

@Shaoting-Feng, you asked on #1095 for someone to pick #1023 up, and this is that rebase as its own pull request. If Mohith26 comes back, should it fold into his branch instead?

chrikrah and others added 2 commits October 20, 2026 04:23
Make RoutingInterface.route_request a coroutine with an optional
request_json parameter, and give every router that signature, including
PriorityRouter and LoadAwareRouter, which arrived after the original
branch. Both dispatch sites in request.py then await the router directly:
route_general_request and the failover loop drop their isinstance chains,
and proxy_multipart_request makes one awaited call that passes {}.

The DisaggregatedPrefill* handler dispatch in route_general_request stays,
because those routers go to different request handlers.

Rebased from vllm-project#1023.

Fixes vllm-project#1022

Co-authored-by: Mohith Gajjela <109003762+Mohith26@users.noreply.github.com>
Signed-off-by: Christopher Krah <48338417+chrikrah@users.noreply.github.com>
Add test_route_request_signature.py, which checks that every router's
route_request is a coroutine function with an optional request_json, and
that both dispatch sites in request.py await it. Update the round-robin,
auth-header and multipart request-stats tests, whose fake routers called
route_request synchronously.

Co-authored-by: Mohith Gajjela <109003762+Mohith26@users.noreply.github.com>
Signed-off-by: Christopher Krah <48338417+chrikrah@users.noreply.github.com>

@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 standardizes the route_request signature across all routers to be asynchronous and accept an optional request_json parameter, simplifying call sites by removing complex isinstance dispatch guards. Feedback was provided regarding a potential KeyError in PrefixAwareRouter when request_json is empty or missing the prompt key.

Comment on lines +907 to +908
if request_json is None:
request_json = {}

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.

high

If request_json is None or an empty dictionary (such as the {} passed during multipart/form-data requests), PrefixAwareRouter will raise a KeyError: 'prompt' at line 934 because it directly accesses request_json["prompt"] when "messages" is not present. \n\nTo prevent this runtime crash, we should ensure that request_json always contains at least an empty string for "prompt" if neither "prompt" nor "messages" is specified.

        if request_json is None:\n            request_json = {\"prompt\": \"\"}\n        elif \"prompt\" not in request_json and \"messages\" not in request_json:\n            request_json = {**request_json, \"prompt\": \"\"}

This branch has not been deployed

No deployments
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.

refactor: standardize route_request signature across all routers

1 participant