Skip to content

[Bugfix] Support root JSON Schema combinators in tool parsers - #36483

Closed
xeophon wants to merge 7 commits into
sgl-project:mainfrom
xeophon:fix/root-json-schema-tool-parsers
Closed

xeophon wants to merge 7 commits into
sgl-project:mainfrom
xeophon:fix/root-json-schema-tool-parsers

Conversation

@xeophon

@xeophon xeophon commented Aug 26, 2026 •

Copy link
Copy Markdown

Motivation

Fixes #32286 and generalizes the MiniMax-specific approach in #32299.

Several XML-style tool-call detectors infer argument types only from tool.function.parameters["properties"]. Valid JSON Schemas that declare properties below a root-level allOf, anyOf, or oneOf therefore hide those type hints. In the reported MiniMax-M3 case, numbers become strings and arrays can leak raw parser tags instead of producing typed arguments.

This change exposes those properties at the shared FunctionCallParser layer without changing the original tool schema or adding parser-specific JSON Schema logic.

Modifications

  • Add get_tool_parser_property_hints, an explicitly lossy detector-facing property projection:
    • resolve local JSON Pointer references along the projection path;
    • keep direct root properties authoritative;
    • project properties below root-level allOf, anyOf, and oneOf;
    • retain branch-only or identical declarations;
    • treat absence from another branch as non-conflicting;
    • collapse differing declarations for the same property name to {} so detectors keep their conservative string fallback.
  • Construct detector_tools once in FunctionCallParser using Pydantic copies only when the projected properties differ.
  • Pass detector_tools to non-streaming parsing, streaming increments, and stream finalization.
  • Keep self.tools and the original schemas unchanged for structural tags, constrained decoding, and validation.
  • Leave the individual tool-format detectors unchanged.
  • Add registered CPU regressions for the real disjoint number / stringList / numberList schema shape.

The projection is intentionally a static type-hint view. It is not complete JSON Schema evaluation and does not select or validate branches at runtime. Exact branch-dependent interpretation would require buffering a complete tool call before emitting arguments, which is incompatible with incremental streaming.

Accuracy Tests

Not applicable to model inference. This changes deterministic post-processing of tool-call arguments and does not change model execution or generated tokens.

The regression verifies:

  • disjoint properties under allOf, anyOf, and oneOf;
  • direct-property precedence, identical declarations, explicit conflicts, and local references;
  • preservation of the original schema;
  • conservative string fallback for conflicting declarations;
  • number and array parsing without XML-tag leakage;
  • parser reuse without per-call schema state;
  • streaming and non-streaming behavior across 11 detector formats.

Speed Tests and Profiling

Not applicable to model execution. The projection is computed once when a FunctionCallParser is constructed; the streaming parse path performs no branch validation or schema mutation.

Validation

  • Focused root-combinator regression: 9 tests passed, 69 subtests passed.
  • Ruff formatting and scoped correctness checks passed on the changed files.
  • git diff --check passed.
  • Final diff against main contains two production files and one test file.

Checklist

Review and Merge Process

  1. Ping Merge Oncalls to start the process. See the PR Merge Process.
  2. Get approvals from CODEOWNERS and other reviewers.
  3. Trigger CI tests with comments or contact authorized users to do so.
    • Common commands include /tag-and-rerun-ci, /tag-run-ci-label, /rerun-failed-ci
  4. After green CI and required approvals, ask Merge Oncalls or people with Write permission to merge the PR.

CI States

Latest PR Test (Base): ❌ Run #32994620403
Latest PR Test (Extra): ❌ Run #32994619524
Latest PR Test (AMD ROCm 7.2): ❌ Run #32994620722

@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: ebe3a651e0

ℹ️ 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".

Comment thread python/sglang/srt/function_call/utils.py Outdated

@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: c14cd5e650

ℹ️ 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".

Comment thread python/sglang/srt/function_call/utils.py Outdated

@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: 1ec852ea9a

ℹ️ 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".

Comment thread python/sglang/srt/function_call/utils.py
Comment thread python/sglang/srt/function_call/utils.py Outdated

@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: 00ca197bd8

ℹ️ 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".

Comment thread python/sglang/srt/function_call/function_call_parser.py Outdated
Comment thread python/sglang/srt/function_call/function_call_parser.py Outdated

@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: 7224bcaf12

ℹ️ 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".

Comment thread python/sglang/srt/function_call/function_call_parser.py Outdated

@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: 884beac524

ℹ️ 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".

Comment on lines +260 to +262
errors = validator.iter_errors(candidate_arguments)
if all(error.validator == "required" for error in errors):
candidates.append((candidate_properties, candidate_arguments))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject incomplete branches once arguments are complete

When alternatives are distinguished by required properties rather than a discriminator, this accepts an invalid branch even after the complete arguments have parsed. For example, with one branch requiring an object-valued payload and another requiring query, an input containing only payload validates the first candidate after coercion, while the second candidate is also retained because its sole error is required; since two candidates remain, payload is returned as a string. Ignore required-only errors only while complete is false, and require full validation at completion.

Useful? React with 👍 / 👎.

Comment on lines +187 to +189
candidate_schema = base_schema | {
"allOf": [*base_schema["allOf"], branch]
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep unevaluatedProperties adjacent to the selected branch

For a Draft 2020-12 schema with root oneOf/anyOf and unevaluatedProperties: false, moving the root remainder and selected branch into separate allOf subschemas changes validation semantics: the unevaluatedProperties keyword in the remainder cannot see properties evaluated by the sibling branch, so a valid branch instance is rejected as containing unevaluated fields. No candidate is then selected, leaving branch-specific object or array arguments in their projected string form. Construct the candidate so the selected branch remains adjacent to root-level unevaluatedProperties.

Useful? React with 👍 / 👎.

@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: 3018725ef9

ℹ️ 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".

Comment on lines +404 to +405
elif current_schema != candidate_schema:
properties[name] = {}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve common types across non-identical branch schemas

When every anyOf/oneOf branch declares the same argument as an object or array but applies different nested constraints, the schemas are unequal and this collapses the property hint to {}. Detectors such as Qwen then infer string, so a valid nested argument remains JSON-encoded text even though every alternative agrees on its container type—a common case for discriminated unions with branch-specific payload fields. Combine the candidate schemas under the relevant combinator, or at least preserve their common inferred type, instead of requiring exact schema equality.

Useful? React with 👍 / 👎.

@nvpohanh

Copy link
Copy Markdown
Collaborator

@JustinTong0323 Could you help to review this? Thanks!

@xeophon

xeophon commented Aug 28, 2026

Copy link
Copy Markdown
Author

fixed with #36626

@xeophon xeophon closed this Aug 28, 2026
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.

[Bug] MiniMax-M3 tool-call parser: top-level oneOf parameters schema not resolved (numbers become strings, arrays leak raw tags)

2 participants