chat : preserve object alternatives in Qwen XML tool schemas - #28651
chat : preserve object alternatives in Qwen XML tool schemas#28651arc-uri-el wants to merge 1 commit into
Conversation
Compile direct object alternatives with their required fields and string discriminator constraints. Hold ambiguous argument deltas until the selected branch is complete. Add grammar and streaming regressions to the existing chat tests. Assisted-by: GPT-6 Astra
|
Hi @arc-uri-el, thanks for your contribution! Per our contribution guidelines, the automated PR checker found the following issue(s) that need your attention:
Please note that maintainers reserve the right to make final decisions on PRs. If you believe there is a mistake, please comment below. |
|
A root level I don't see any reason to support this. |
|
Thanks for the portability feedback. We will adapt the union representation on the client side rather than extend the accepted root schema here, so I am closing this proposal. The investigation also reproduced an independent bug on ordinary object schemas: the Qwen XML grammar accepts strings outside a declared enum. I isolated that fix in #28668, with a failing-before/passing-after regression and no root-union support. |
Overview
The Qwen XML handler reads only root
parameters.properties. For a tool whose arguments are an object union, it therefore builds a grammar that permits an empty call and excludes every required argument. This can force repeated{}calls even though the full schema is present in the model prompt.A minimal example is a records tool with these parameters:
{ "oneOf": [ { "type": "object", "properties": {"mode": {"type": "string", "const": "list"}}, "required": ["mode"], "additionalProperties": false }, { "type": "object", "properties": { "mode": {"type": "string", "const": "load"}, "paths": {"type": "array", "items": {"type": "string"}, "minItems": 1} }, "required": ["mode", "paths"], "additionalProperties": false } ] }Before this change, the generated function rule has no parameter productions. Afterward, the
listbranch requiresmode=list; theloadbranch requiresmode=loadandpaths. No client-side flattening or particular discriminator name is needed.The patch compiles direct object alternatives separately and enforces string
const/enumvalues in XML.oneOfis accepted only when every pair is provably disjoint through a required finite string property. Unsupported root compositions return an explicit error instead of an empty or weakened argument grammar. Nested values retain the existing JSON converter's limitations.Union arguments are committed only once the argument branch completes. Otherwise a late discriminator can reinterpret an already-streamed value, for example from
"42"to42, which cannot be repaired with append-only deltas. Reasoning and tool names still stream. Ordinary object schemas keep their existing path except for string constant/enum enforcement.Additional information
Validation
Base:
4850c7727fa73bbe3098e10ee369fbc3467c445f.The same correction was proposed first in halo-box/llama.cpp#25. This port follows the current
common/parsers/file layout and contains no other downstream changes.Built the clean base and candidate separately on Linux x86_64 with GCC 15.2, Release, shared libraries, native CPU and OpenMP. Both builds produced zero compiler warnings. All seven selected suites passed on each build:
test-chattest-json-schema-to-grammartest-chat-peg-parsertest-chat-auto-parsertest-chat-templatetest-grammar-parsertest-grammar-integrationThe candidate's exact
test-chatbinary fails its first valid root-union grammar assertion when linked against the baseline libraries and passes against the candidate libraries. Added cases exercise Qwen3-Coder and Qwen3.5 templates, required arguments in both orders, missing fields, branch-specific integer limits, array contents, unknown discriminators, overlapping alternatives, conflicting root constraints, parallel calls and byte-by-byte streaming with a late discriminator.Separately, the identical parser delta passed downstream Vulkan tests on a Ryzen AI MAX+ 395 / Radeon 8060S with a custom 27B Qwen model. It completed tool calls, nested JSON edits, vision, daemon restart and cached return at 121K-143K occupied context with the original schemas. This is downstream evidence, not an inference result from the clean mainline CPU build. Two initial short-answer trials failed despite correct calls; a fresh matched original/patched replay produced identical reasoning and tool arguments. No broad model-quality improvement or throughput gain is claimed.
Not run: full CI, Windows or other-platform builds, other-model inference, perplexity, backend-op or broad performance sweeps. No tensor kernels, model graphs, weights or public APIs changed. Full JSON Schema conformance is outside this patch.
Requirements