fix(so): reject an unsupported schema_type at the request boundary

An unknown schema_type failed three layers away from its cause: the
compiler raised ValueError('Unsupported Grammar Type') inside the try
block, the except ValueError meant for xgrammar's own input errors
rewrapped it as RuntimeError('xgrammar invalid input: ...'), and
try_result()'s except RuntimeError branch then reported it as an
xgrammar timeout. A typo in schema_type surfaced as a timeout.

Expose the supported types from the compiler and check the value in
build() next to the presence check, so a wrong schema_type fails the
same synchronous way a missing one does. The compiler keeps its own
guard, moved ahead of the try so it is no longer rewrapped.
This commit is contained in:
Nolan 2026-10-27 12:18:35 +08:00
parent 4686d7b833
commit feca2310d1
2 changed files with 13 additions and 4 deletions

View File

@ -10,6 +10,10 @@ from deepinfer import envs
logger = logging.getLogger(__name__)
#: The ``schema_type`` values ``dispatch_and_compile`` knows how to compile. Shared with the
#: matcher builder so a wrong type is rejected at the request boundary, not inside the pool.
SUPPORTED_SCHEMA_TYPES: tuple[str, ...] = ("json", "regex", "ebnf")
class XGrammarCompiler:
def __init__(self, tokenizer_info: TokenizerInfo, num_threads: int) -> None:
@ -52,15 +56,18 @@ class XGrammarCompiler:
self.close()
def dispatch_and_compile(self, schema_type: str, schema: str) -> CompiledGrammar:
# Checked outside the try: the `except ValueError` below is for xgrammar's own input
# errors and used to swallow this one too, re-raising it as a RuntimeError that
# try_result() then reported as a timeout.
if schema_type not in SUPPORTED_SCHEMA_TYPES:
raise ValueError(f"unsupported schema_type {schema_type!r}; expected one of {SUPPORTED_SCHEMA_TYPES}")
try:
if schema_type == "json":
ctx = self.inner_compiler.compile_json_schema(schema, strict_mode=True)
elif schema_type == "regex":
ctx = self.inner_compiler.compile_regex(schema)
elif schema_type == "ebnf":
else: # "ebnf" — the guard above makes this exhaustive
ctx = self.inner_compiler.compile_grammar(schema)
else:
raise ValueError("Unsupported Grammar Type")
except RuntimeError as e:
raise RuntimeError(f"xgrammar compilation RE: {e}") from e
except ValueError as e:

View File

@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
from xgrammar import TokenizerInfo
from deepinfer.structure_output.compiler import XGrammarCompiler
from deepinfer.structure_output.compiler import SUPPORTED_SCHEMA_TYPES, XGrammarCompiler
from deepinfer.structure_output.matcher import XGrammarMatcher, XGrammarMatcherFuture, validate_trigger_bound
if TYPE_CHECKING:
@ -65,6 +65,8 @@ class SOMatcherBuilder:
if missing:
raise ValueError(f"so_args is missing required key(s) {missing}; provide schema_type and schema")
schema_type = so_args["schema_type"]
if schema_type not in SUPPORTED_SCHEMA_TYPES:
raise ValueError(f"unsupported schema_type {schema_type!r}; expected one of {SUPPORTED_SCHEMA_TYPES}")
schema = so_args["schema"]
trigger_tokens = so_args.get("trigger_token_ids")