Skip to content

fix(frontend): preserve completion backend error status - #12706

Merged
NVShreyas merged 1 commit into
mainfrom
shreyasm/fix-http-code-logprobs
Aug 5, 2026
Merged

fix(frontend): preserve completion backend error status#12706
NVShreyas merged 1 commit into
mainfrom
shreyasm/fix-http-code-logprobs

Conversation

@NVShreyas

@NVShreyas NVShreyas commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Overview:

Fix /v1/completions returning HTTP 500 when the backend reports a typed client error such as Backend(InvalidArgument).

The non-streaming completions path now checks for backend errors before response aggregation, preserving the original HTTP 400 status and descriptive message. The existing timeout-aware error-checking helper is generalized to support completion and chat response types.

Adds regression coverage for the SGLang logprobs >= 1 case.

Details:

Where should the reviewer start?

Related Issues

⚠️ This section is required. Choose one path below and delete the other.

🔗 This PR is linked to an issue:

  • Closes #XXXX

🚫 This PR is NOT linked to an issue:

  • Confirmed — no related issue

Open in Devin Review

Summary by CodeRabbit

  • Bug Fixes
    • Typed backend validation errors from completion requests now return HTTP 400 responses with the appropriate error message.
    • Completion error metrics are now recorded consistently when backend errors occur.
  • Tests
    • Added regression coverage to verify that typed completion errors are surfaced correctly.

@NVShreyas
NVShreyas requested a review from a team as a code owner August 5, 2026 18:01
@copy-pr-bot

copy-pr-bot Bot commented Aug 5, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@github-actions github-actions Bot added fix frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` labels Aug 5, 2026

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

Open in Devin Review

Comment thread lib/llm/src/http/service/openai.rs
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Changes

Completion backend error handling

Layer / File(s) Summary
Generic backend-error preflight
lib/llm/src/http/service/openai.rs
check_for_backend_error now accepts generic serializable annotated streams and retains bounded buffering with one timeout deadline.
Completion aggregation and regression coverage
lib/llm/src/http/service/openai.rs
Non-streaming completions return typed backend errors before aggregation, record metric errors, and map Backend(InvalidArgument) to HTTP 400. A regression test verifies the response.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the frontend fix that preserves completion backend error status.
Description check ✅ Passed The description explains the fix, affected endpoint, regression case, and no-issue status; Details and reviewer-start sections remain blank but are non-critical.

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

@NVShreyas
NVShreyas force-pushed the shreyasm/fix-http-code-logprobs branch from a0c7004 to fa65616 Compare August 5, 2026 18:17
@pull-request-size pull-request-size Bot added size/L and removed size/M labels Aug 5, 2026
@rmccorm4

rmccorm4 commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

/ok to test fa65616

@datadog-official

datadog-official Bot commented Aug 5, 2026

Copy link
Copy Markdown

Pipelines

🎯 Code Coverage (details)
Patch Coverage: 100.00%
Overall Coverage: 47.95% (-2.45%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: fa65616 | Docs | Datadog PR Page | Give us feedback!

@NVShreyas
NVShreyas merged commit a39a25a into main Aug 5, 2026
191 of 193 checks passed
@NVShreyas
NVShreyas deleted the shreyasm/fix-http-code-logprobs branch August 5, 2026 23:15
where
S: futures::Stream<Item = Annotated<NvCreateCompletionResponse>> + Send + 'static,
{
futures::future::try_join_all(

@rmccorm4 rmccorm4 Aug 5, 2026

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.

Reviewed locally with Codex: try_join_all waits for every prompt stream to produce its first non-annotation frame before any buffered frame reaches select_all. If prompt A produces a token at 10 ms and prompt B at 500 ms, A's metrics aren't observed and http_queue_guard isn't dropped until roughly 500 ms, inflating batch TTFT and queue time to the slowest prompt. Could we preserve the typed error while consuming the merged stream instead of adding this barrier?

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.

If prompt A produces a token at 10 ms and prompt B at 500 ms, A's metrics aren't observed and http_queue_guard isn't dropped until roughly 500 ms, inflating batch TTFT and queue time to the slowest prompt.

If this is correct, we need to fix this @KrishnanPrash

// Preserve typed backend errors before the completions aggregator turns
// them into strings. In particular, Python ValueError/TypeError arrives
// as Backend(InvalidArgument) and must remain an HTTP 400.
let stream = check_for_backend_error(stream, None)

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.

Reviewed locally with Codex: This only checks the first non-annotation frame. If the backend emits a normal completion chunk and then Backend(InvalidArgument), the later error reaches the aggregator, is reduced to a string, and is returned through the generic 500 path. Non-streaming requests should preserve typed errors across the full aggregation.

}

// Merge all streams
let all_streams: Vec<BoxedCompletionResponseStream> = if streaming {

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.

Reviewed locally with Codex: This boxes every prompt stream even when streaming is true, adding one allocation per prompt and dynamic dispatch on every poll of the unchanged streaming path. Can we keep these streams concrete and only box the merged stream if type erasure is needed?

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.

This may impact perf at high concurrency, we should verify

};

let error_response =
match check_for_backend_error(stream::iter(vec![error_event]), None).await {

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.

Reviewed locally with Codex: This test exercises the helper directly, so it still passes if the production call in completions_single is removed; the batch test has the same gap. Please cover the handler/HTTP path so the regression is actually protected.

let stream = check_for_backend_error(stream, None)
.await
.map_err(|error_response| {
tracing::error!(request_id, "Backend error detected: {:?}", error_response);

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.

Reviewed locally with Codex: This formats the response into the log message and logs an expected client-side 400 at error level. Could this use a structured error_response field and a lower level?

hhzhang16 added a commit that referenced this pull request Aug 6, 2026
dyn-3691-extract-shared-target-pid-cuda-customstorage-operation-layer

* 'main' of https://github.com/ai-dynamo/dynamo: (65 commits)
  fix(frontend): emit SGLang stream role once (#12741)
  docs(fern): promote v1.3.1 to current release (#12752)
  fix(docs): remove duplicate unscoped community-rail CSS rules (#12615)
  feat(operator): migrate CRD storage to v1beta1 (#11904)
  fix: synchronize self-benchmark capacity across DP ranks (#12021)
  chore(deps): bump dynamo-tokenizers to 1.8.0 (#12707)
  fix(frontend): preserve split UTF-8 characters (#12688)
  docs: align Kubernetes build selector with CLI (#12729)
  fix(frontend): preserve completion backend error status (#12706)
  fix(operator): replace snapshot pods after GMS restart (#11286)
  refactor(media): rename installer module, drop --packages per review
  fix(media): harden installer against three pre-redesign review findings
  fix(media): verify installs in a fresh interpreter; teach --pip-args= form
  test(serve): install test-time decoders at the validated bounds
  feat(media): explicit installer for additional media decoders
  docs(spica): correct kv_load_ratio support guidance (#12714)
  feat(operator): add experimental grove.forceScalingGroup for single-node components (#11772)
  fix(vllm): declare entry-stage engine_input_source in GLM-Image NIXL config (#12709)
  chore: bump trtllm to v1.3.0rc23 (#12532)
  perf: remove trtllm postprocessing workers from the args as post processing workers are not effective in dynamo (#12592)
  ...

Signed-off-by: Hannah Zhang <hannahz@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix frontend `python -m dynamo.frontend` and `dynamo-run in=http|text|grpc` size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants