Handle structured output grammar compilation failures - #45012
Conversation
|
@russellb please take a review |
Code ReviewI've completed a comprehensive multi-angle review of this PR using automated analysis. Here are the 8 most critical findings ranked by severity: Critical Correctness Issues1. Grammar property can raise unexpected exceptionsFile: 2. Unatomic error output handling creates race windowFile: 3. Parallel output channel can desyncFile: Performance Issues4. Error check in hot streaming loop adds ~1000x overheadFile: Architectural / Maintenance Issues5. Special-case error handling instead of generalized abstractionFile: 6. Duplicate error-checking across endpointsFiles: 7. Untyped state coupling with grammar_error fieldFile: Test Quality Issues8. 60-line test duplicationFiles: SummaryThe PR addresses a real bug (grammar compilation hangs), but the implementation has several issues:
The tests pass, which is good, but the findings suggest edge cases that aren't covered (exceptions during error output construction, grammar property exceptions propagating to callers). |
b192b71 to
563a3eb
Compare
|
Thanks @russellb , I've done some updates according to your feedback. and here are some comments:
2, 3, error output handling: The high-level step() in the EngineCore can ensure the schedule() and update_from_output() are called sequently in normal workflow. And I cleanup the staled finished_error_outputs at the begin of schedule() to avoid previous failed errors if have. 4, 6, updated the logics to correctly return 500 if there is error in the streamed chunks 5, update the function name to generic _finish_request_on_error() and can extend it in the future 7, update the logic, reset the grammar_error in the setter |
Record exceptions from asynchronous structured-output grammar compilation instead of leaving requests blocked indefinitely waiting for a grammar that will never become available. When a grammar future fails, finish the waiting request with FINISHED_ERROR and emit a request-local EngineCoreOutput with FinishReason.ERROR so the frontend can wake the generate task and surface the existing internal generation error path instead of timing out. Keep blocked-request promotion focused on moving ready requests back to schedulable states, and handle blocked async request errors in a separate scheduler path that currently covers structured-output grammar failures. Check streaming outputs for error finish reasons before empty outputs can be skipped so streaming chat and completion requests surface the same internal generation error instead of timing out. Add regression coverage for failed grammar futures, scheduler traversal, frontend error-output delivery, and streaming first-error outputs. Signed-off-by: jeff.ye <jeff.ye@novita.ai>
f6246d9 to
2be7cea
Compare
|
This pull request has merge conflicts that must be resolved before it can be |
Record exceptions from asynchronous structured-output grammar compilation instead of leaving requests blocked indefinitely waiting for a grammar that will never become available.
When a grammar future fails, finish the waiting request with FINISHED_ERROR and emit a request-local EngineCoreOutput with FinishReason.ERROR so the frontend can wake the generate task and surface the existing internal generation error path instead of timing out.
Keep blocked-request promotion focused on moving ready requests back to schedulable states, and handle grammar failures in a separate scheduler path. Use explicit structured-output fakes in scheduler tests to avoid Mock-fabricated grammar_error attributes.
Check streaming outputs for error finish reasons before emitting role, empty, or token chunks so streaming chat and completion requests surface the same internal generation error instead of starting a successful response.
Add regression coverage for failed grammar futures, scheduler traversal, frontend error-output delivery, streaming first-error outputs, and parallel-sampling parent state updates.
Purpose
This PR fixes a request hang that can happen when asynchronous structured-output
grammar compilation fails.
Before this change, a request waiting in
WAITING_FOR_STRUCTURED_OUTPUT_GRAMMARcould remain blocked forever if thegrammar future raised an exception. EngineCore would finish the request
internally in some paths, but the frontend was not guaranteed to receive a
request-level error output. As a result, the OpenAI serving layer could keep
waiting for a response until the client timed out.
This change records grammar compilation exceptions on
StructuredOutputRequestand handles them in a dedicated scheduler path. When a grammar future fails, the
scheduler finishes the request with
FINISHED_ERRORand emits a request-localEngineCoreOutputwithFinishReason.ERROR. This reuses the existing frontendinternal generation error handling instead of introducing a new
EngineCore-to-frontend protocol.
The PR also updates streaming OpenAI chat and completion paths to check for
finish_reason="error"before emitting role, empty, or token chunks. Thisprevents streaming requests from starting a successful-looking response before
surfacing the internal generation error.
Test Plan
Test Result
passed