Repository navigation
fix(provider): validate final Chat Completions tool pairing before sending - #10752
Open
bizu314926-collab wants to merge 1 commit into
Open
bizu314926-collab wants to merge 1 commit into
bizu314926-collab wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Direct Provider calls, summary requests, and
custom_extra_body.messagescan bypass the history repair in #10342. For example, an assistant declaring callsaandbfollowed only by resultacurrently reaches the SDK. The existing final sanitizer removes orphaned or duplicate receipts but does not reject an incomplete declaration group, and it only examines the base messages.This change validates the effective final Chat Completions message sequence before SDK invocation in both streaming and non-streaming requests. A remaining structural error stops locally without sending the request or trying another key/provider.
Related to #10338; complements #10342. The separation was discussed in this COMMENT review. This PR contains no ContextManager/ContextTruncator changes or supplemental tests for that PR, and does not replace its historical repair.
Modifications
Validate final messages after Provider overrides and the existing sanitizer. If
extra_bodycontainsmessages, validate that effective override even when its value isNone.Require one contiguous response per declared call, allowing any result order within a parallel group. Reject missing/invalid IDs, incomplete groups and duplicate declaration IDs within a group. Separate completed groups may reuse IDs.
Preserve existing base-message orphan/duplicate receipt cleanup; make its ID scan type-safe so malformed structures reach the local validation error.
Rethrow only the new local
ToolMessageProtocolErrorbefore API recovery, and stop runner fallback only for that type. This also prevents diagnostic values such asindex=429orpending_count=429from being mistaken for rate limiting. Remote error classification, retry predicates and fallback configuration are unchanged.Add 99 parameterized tests. Add matching tool receipts to two existing empty-content normalization fixtures while keeping their original normalization assertions.
This is NOT a breaking change.
Compatibility consideration: ID-less modern tool calls are rejected on the guarded Chat Completions path. #10342 deliberately preserves all-ID-less history in the generic context layer; this stricter final-adapter policy is explicitly offered for maintainer review. Already repaired base orphan/duplicate receipts remain accepted. Raw
extra_body.messagesoverrides are validated without that repair.This is pairing validation, not complete API schema validation. Empty message lists and legacy
function_call/functionmatching remain outside its contract. It covers inherited Chat Completions send paths; Responses and native Gemini/Anthropic adapters are not claimed to be covered.Test Results
Fresh verification against master
da8e7587a8df578225dd111374e0581557c4f40f:The #10342 head tested was
e7e117424258fb3ae4cc58400025ae49ae7fb97d. Its four changed paths are disjoint from this patch's five paths. Applying its rebased delta and then this patch succeeded without conflicts.Relevant test command:
The local runs used an additional external test launcher to isolate runtime data and deny network access. All model responses came from in-memory SDK substitutes. OpenAI/Azure tests built actual SDK request JSON with a fail-if-send in-memory transport; no SDK request was sent. Only the Windows standard library's private asyncio socketpair connection to its own listener was allowed by the launcher. No external model calls or production changes were made.
The prior red baseline for the same 99 new cases was 63 failed / 36 passed on unmodified master
0e997ccd9e8b47930c830e69db6a657dfbf44d9d. This baseline-update pass retained the approved patch byte-for-byte. The Python 3.12 run has one pre-existingaudioopdeprecation warning; there are no failed or skipped cases in the fresh matrices.Ruff 0.15.22: project format/check passed (524 files unchanged), and all five changed files including tests passed explicit check/format-check.
git diff --cached --checkpassed. Added-line secret/private-data scanning and independent diff review found no blocking issue; new fixtures use synthetic IDs, keys and.invalidhosts.Checklist
Summary by Sourcery
Validate final Chat Completions tool-call pairings locally and stop malformed requests before transmission or fallback.
Bug Fixes:
Enhancements:
Tests: