fix(sampling): reject conflicting structural tag constraints (#32525)
This commit is contained in:
@@ -196,9 +196,12 @@ class SamplingParams(msgspec.Struct, kw_only=True, array_like=True):
|
|||||||
self.json_schema,
|
self.json_schema,
|
||||||
self.regex,
|
self.regex,
|
||||||
self.ebnf,
|
self.ebnf,
|
||||||
|
self.structural_tag,
|
||||||
] # since mutually exclusive, only one can be set
|
] # since mutually exclusive, only one can be set
|
||||||
if sum(x is not None for x in grammars) > 1:
|
if sum(x is not None for x in grammars) > 1:
|
||||||
raise ValueError("Only one of regex, json_schema, or ebnf can be set.")
|
raise ValueError(
|
||||||
|
"Only one of json_schema, regex, ebnf, or structural_tag can be set."
|
||||||
|
)
|
||||||
|
|
||||||
def normalize(self, tokenizer):
|
def normalize(self, tokenizer):
|
||||||
# Process stop strings
|
# Process stop strings
|
||||||
|
|||||||
@@ -77,6 +77,12 @@ class TestSamplingParamsInit(CustomTestCase):
|
|||||||
class TestSamplingParamsVerify(CustomTestCase):
|
class TestSamplingParamsVerify(CustomTestCase):
|
||||||
|
|
||||||
VOCAB_SIZE = 32000
|
VOCAB_SIZE = 32000
|
||||||
|
GRAMMAR_VALUES = {
|
||||||
|
"json_schema": '{"type":"object"}',
|
||||||
|
"regex": "abc",
|
||||||
|
"ebnf": 'root ::= "abc"',
|
||||||
|
"structural_tag": '{"structures":[],"triggers":[]}',
|
||||||
|
}
|
||||||
|
|
||||||
def _make(self, **kwargs):
|
def _make(self, **kwargs):
|
||||||
"""Helper: create SamplingParams with safe defaults, override with kwargs."""
|
"""Helper: create SamplingParams with safe defaults, override with kwargs."""
|
||||||
@@ -273,21 +279,25 @@ class TestSamplingParamsVerify(CustomTestCase):
|
|||||||
sp.verify(self.VOCAB_SIZE)
|
sp.verify(self.VOCAB_SIZE)
|
||||||
|
|
||||||
def test_multiple_grammars_raises(self):
|
def test_multiple_grammars_raises(self):
|
||||||
"""Test that verify() rejects setting both json_schema and regex (mutually exclusive)."""
|
"""Reject structural_tag combined with any other grammar constraint.
|
||||||
sp = self._make(json_schema='{"type":"object"}', regex="abc")
|
|
||||||
with self.assertRaises(ValueError):
|
Constraint selection is a fixed if/elif chain, so a constraint left out of
|
||||||
|
this check is silently dropped with no error to the caller.
|
||||||
|
"""
|
||||||
|
for other in ("json_schema", "regex", "ebnf"):
|
||||||
|
with self.subTest(other=other):
|
||||||
|
sp = self._make(
|
||||||
|
structural_tag=self.GRAMMAR_VALUES["structural_tag"],
|
||||||
|
**{other: self.GRAMMAR_VALUES[other]},
|
||||||
|
)
|
||||||
|
with self.assertRaisesRegex(ValueError, "Only one of"):
|
||||||
sp.verify(self.VOCAB_SIZE)
|
sp.verify(self.VOCAB_SIZE)
|
||||||
|
|
||||||
def test_single_grammar_valid(self):
|
def test_single_grammar_valid(self):
|
||||||
"""Test that setting only one grammar type is accepted."""
|
"""Test that each grammar constraint is valid on its own."""
|
||||||
sp = self._make(json_schema='{"type":"object"}')
|
for grammar, value in self.GRAMMAR_VALUES.items():
|
||||||
sp.verify(self.VOCAB_SIZE)
|
with self.subTest(grammar=grammar):
|
||||||
|
self._make(**{grammar: value}).verify(self.VOCAB_SIZE)
|
||||||
def test_all_three_grammars_set_raises(self):
|
|
||||||
"""Test that verify() rejects setting json_schema, regex, and ebnf together."""
|
|
||||||
sp = self._make(json_schema="{}", regex="a", ebnf="rule")
|
|
||||||
with self.assertRaises(ValueError):
|
|
||||||
sp.verify(self.VOCAB_SIZE)
|
|
||||||
|
|
||||||
|
|
||||||
class TestSamplingParamsNormalize(CustomTestCase):
|
class TestSamplingParamsNormalize(CustomTestCase):
|
||||||
|
|||||||
Reference in New Issue
Block a user