Skip to content

feat(grpc): support batched completion prompts - #1904

Open
lucifer1004 wants to merge 2 commits into
smg-project:mainfrom
lucifer1004:feat/batched-completion-prompts
Open

feat(grpc): support batched completion prompts#1904
lucifer1004 wants to merge 2 commits into
smg-project:mainfrom
lucifer1004:feat/batched-completion-prompts

Conversation

@lucifer1004

@lucifer1004 lucifer1004 commented Jul 12, 2026

Copy link
Copy Markdown

Description

Problem

CompletionRequest accepts prompt: string[], but the gRPC completion pipeline rejects every prompt array. This blocks OpenAI-compatible batched completions for gRPC-backed engines such as TokenSpeed.

Solution

Fan out prompt arrays at the gRPC router boundary because each backend GenerateRequest carries one prompt token sequence. Merge the scalar results back into one OpenAI-compatible response for both streaming and non-streaming requests.

The scalar completion path and the HTTP router remain unchanged. The shared helper is used by regular, PD, and EPD gRPC routers.

Closes #1903

Changes

  • fan out each prompt through the existing scalar completion pipeline and retry path;
  • preserve completion options such as n, echo, stops, suffixes, and streaming;
  • reindex choices in prompt-major order and aggregate usage;
  • multiplex SSE responses with one response ID, global choice indices, aggregated usage, and one final [DONE];
  • add focused non-streaming and streaming regression tests.

Test Plan

  • cargo test -p smg completion_batch
    • two prompts with n=2 produce global non-streaming indices [0, 1, 2, 3] and summed usage;
    • two streaming prompts produce global indices [0, 2], summed usage, one response ID, and one [DONE] event.
  • cargo test
    • full workspace tests and doc tests pass; the SMG library reports 1157 passed and 5 ignored.
  • cargo +1.95 clippy --workspace --all-targets -- -D warnings
    • passes using the repository-documented no-OpenCV fallback.
  • SKIP=clippy prek run --all-files
    • all remaining repository hooks pass, including rustfmt, codespell, Ruff, and file checks.

Local cargo clippy --all-targets --all-features -- -D warnings is pending because this machine does not have system OpenCV. On stable 1.97, current main also trips the pre-existing unneeded_wildcard_pattern lint in worker/monitor.rs:744; neither condition is introduced by this PR.

Checklist
  • cargo +nightly fmt passes
  • cargo clippy --all-targets --all-features -- -D warnings passes
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

Summary by CodeRabbit

  • New Features
    • Added batching support for completion requests with multiple prompts.
    • Merges batch results into a single OpenAI-compatible completion response (including global choice indexing).
    • Added streaming support for batched completions with aggregated usage.
  • Bug Fixes
    • Prevented duplicate termination events in streamed batch responses.
    • Suppressed empty streaming events for cleaner output.
  • Chores / CI
    • Updated CI vLLM install to ensure FFmpeg is available before importing vLLM/torchcodec, improving startup reliability.

Signed-off-by: Zihua Wu <13583761+lucifer1004@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds gRPC support for batched completion prompts by executing each prompt independently and merging non-streaming or streaming responses. Regular and PD routers attempt batch handling before using the existing scalar completion path. CI now validates FFmpeg-dependent vLLM imports.

Changes

gRPC completion batching

Layer / File(s) Summary
Batch orchestration and response dispatch
model_gateway/src/routers/grpc/completion_batch.rs, model_gateway/src/routers/grpc/mod.rs
Prompt arrays are fanned out into concurrent scalar requests and routed to streaming or non-streaming merge logic.
Non-streaming response merge
model_gateway/src/routers/grpc/completion_batch.rs
Choices receive global indices, usage is aggregated, and batch metadata is emitted in one completion response.
Streaming response multiplexing
model_gateway/src/routers/grpc/completion_batch.rs
Upstream SSE events are combined, empty events and per-stream [DONE] markers are suppressed, and one final completion signal is emitted.
Router fallback integration
model_gateway/src/routers/grpc/pd_router.rs, model_gateway/src/routers/grpc/router.rs
PD and regular gRPC routes try batch execution first and fall back to dedicated scalar completion handling.
vLLM import validation
scripts/ci_install_vllm.sh
CI installs FFmpeg and imports torchcodec and vLLM in the startup canary.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant GrpcRouter
  participant BatchExecutor
  participant CompletionPipeline
  participant SSEStream

  Client->>GrpcRouter: CompletionRequest with prompt array
  GrpcRouter->>BatchExecutor: execute_batch(request)
  BatchExecutor->>CompletionPipeline: execute scalar request per prompt
  CompletionPipeline-->>BatchExecutor: completion responses or streams
  BatchExecutor->>SSEStream: merge indexed events and usage
  SSEStream-->>Client: merged completion response and single [DONE]
Loading

Possibly related PRs

Suggested reviewers: catherinesue, key4ng, slin1237

Poem

I’m a rabbit with prompts in a row,
Sending each where completions grow.
Choices hop with indices bright,
Usage totals merge just right.
One stream, one finish—what a sight! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The FFmpeg/vllm CI install script update is unrelated to batched completion support and appears outside the linked issue scope. Move the CI install/script changes to a separate PR unless they are required for this feature, and keep this PR focused on gRPC batching.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding gRPC support for batched completion prompts.
Linked Issues check ✅ Passed The changes fan out prompt arrays, merge streaming and non-streaming results, preserve options, and cover regular, PD, and EPD routers as requested.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added grpc gRPC client and router changes model-gateway Model gateway crate changes labels Jul 12, 2026
@lucifer1004

Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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 batching support for completion requests by splitting requests with multiple prompts into individual single-prompt requests, executing them concurrently, and merging their responses for both streaming and non-streaming modes. A critical correctness issue was identified in the streaming merge logic, where SSE stream chunk fragmentation is not handled. This can cause partial chunks to fail JSON parsing and bypass the re-indexing and metadata merging logic. The reviewer provided a detailed suggestion to buffer incoming bytes and split them into complete lines before processing.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread model_gateway/src/routers/grpc/completion_batch.rs
@lucifer1004
lucifer1004 marked this pull request as ready for review July 12, 2026 01:18
vLLM 0.25 eagerly imports torchcodec, which dlopens FFmpeg shared libraries at import; the e2e runner image ships none, so every vLLM worker died importing vllm. Install distro FFmpeg in the vLLM setup and add an import canary so a missing-lib regression fails at install rather than mid-test.

Signed-off-by: lightseek-bot <243258330+lightseek-bot@users.noreply.github.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/ci_install_vllm.sh`:
- Around line 58-63: Update the dependency installation step in
scripts/ci_install_vllm.sh to explicitly install torchcodec before the import
canary executes. Keep the existing python3 import check for torch, torchcodec,
and vllm unchanged so it validates all required imports.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cc5c8d51-1d06-48f2-a48b-86cb7dd02161

📥 Commits

Reviewing files that changed from the base of the PR and between 07d7aca and c03c00e.

📒 Files selected for processing (1)
  • scripts/ci_install_vllm.sh

Comment thread scripts/ci_install_vllm.sh

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c03c00e67f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# Import canary: fail here (not mid-e2e) if vLLM's eager torchcodec import
# can't find the FFmpeg shared libs installed above (torch first so its
# bundled CUDA libraries are loaded)
python3 -c "import torch, torchcodec, vllm"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make torchcodec canary conditional on vLLM version

When the resolver selects an allowed vLLM version before 0.25 from the still-wide vllm>=0.22.1 install spec, torchcodec is not guaranteed to be installed, so this unconditional canary fails setup even though those vLLM versions do not need the FFmpeg/torchcodec path. Either raise the vLLM lower bound to the first version that requires torchcodec or make this import conditional on torchcodec being present.

Useful? React with 👍 / 👎.

@lightseek-bot

Copy link
Copy Markdown
Collaborator

Local review summary

I do not think this is ready to merge yet. The happy-path merge behavior is coherent and the focused tests pass, but there is one resource-safety issue that I consider a merge blocker.

Merge blocker

  • P1 — Bound and de-quadratize prompt fan-out (model_gateway/src/routers/grpc/completion_batch.rs:49-55)

    CompletionRequest only rejects an empty prompt array; it has no prompt-count limit. For every prompt, this code first clones the entire original request (including the full prompt array) and then replaces prompt, resulting in quadratic cloning work. join_all also polls every scalar routing/generation future concurrently, while the outer admission layer counts the batch as one HTTP request. A single valid request with many small prompts can therefore consume excessive gateway CPU and flood the workers with unbounded concurrent generations.

    Please add a hard prompt-count/aggregate-size limit, clone from a scalar template instead of repeatedly cloning the full array, and use bounded concurrency while preserving prompt order. This should have a regression test for both the limit and the concurrency bound.

Other correctness and resilience concerns

  • P2 — Source stream errors do not terminate the batch (completion_batch.rs:190-199). A scalar stream emits failures as an SSE error envelope. The merger treats that envelope as opaque data, continues emitting chunks from other prompts, then emits aggregate usage and a final [DONE]. The first source error should cancel the remaining streams and produce one terminal error sequence.

  • P2 — No downstream backpressure or prompt disconnect cancellation (completion_batch.rs:146-156, 182-221). The merger drains all source streams into an unbounded channel. A slow client can accumulate output from every prompt in memory, and if the client disconnects while sources are waiting for tokens, the forwarding task does not observe tx.closed() and continues holding source bodies and worker load guards. A bounded channel/poll-driven merged stream plus an explicit disconnect branch would address this.

  • P2 — system_fingerprint can misrepresent mixed workers (completion_batch.rs:113-115). Each scalar prompt performs worker selection independently, but the merged response keeps the first non-null fingerprint. During prompt-aware routing or rolling updates, choices may come from different weight_version values. Either pin the batch to one worker/PD pair or only return a fingerprint when every scalar response agrees.

Validation

cargo test -p smg completion_batch -- --nocapture passes locally: 2 passed, 0 failed. These tests cover the happy paths but not the resource limits, stream-error behavior, disconnect handling, or mixed fingerprints above.

@mergify

mergify Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Hi @lucifer1004, this PR has merge conflicts that must be resolved before it can be merged. Please rebase your branch:

git fetch origin main
git rebase origin/main
# resolve any conflicts, then:
git push --force-with-lease

@mergify mergify Bot added the needs-rebase PR has merge conflicts that need to be resolved label Jul 15, 2026
@github-actions

Copy link
Copy Markdown

This pull request has been automatically marked as stale because it has not had any activity within 14 days. It will be automatically closed if no further activity occurs within 16 days. Leave a comment if you feel this pull request should remain open. Thank you!

@github-actions github-actions Bot added the stale PR has been inactive for 14+ days label Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

grpc gRPC client and router changes model-gateway Model gateway crate changes needs-rebase PR has merge conflicts that need to be resolved stale PR has been inactive for 14+ days

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(grpc): support batched prompts for /v1/completions

3 participants