From be2ad5f92060b66788740e2e8302a490fbd226f4 Mon Sep 17 00:00:00 2001 From: Andreas Karatzas Date: Thu, 18 Dec 2025 01:04:57 -0600 Subject: [PATCH] [ROCm][Bugfix] fix(structured_output): Skip guidance backend for schemas with patternProperties (#30730) Signed-off-by: Andreas Karatzas --- vllm/v1/engine/input_processor.py | 21 +++++++++++++-- vllm/v1/structured_output/backend_guidance.py | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/vllm/v1/engine/input_processor.py b/vllm/v1/engine/input_processor.py index 65e0c845b0afa..29293877cb69d 100644 --- a/vllm/v1/engine/input_processor.py +++ b/vllm/v1/engine/input_processor.py @@ -24,7 +24,10 @@ from vllm.tokenizers.mistral import MistralTokenizer from vllm.utils import length_from_prompt_token_ids_or_embeds from vllm.v1.engine import EngineCoreRequest from vllm.v1.metrics.stats import MultiModalCacheStats -from vllm.v1.structured_output.backend_guidance import validate_guidance_grammar +from vllm.v1.structured_output.backend_guidance import ( + has_guidance_unsupported_json_features, + validate_guidance_grammar, +) from vllm.v1.structured_output.backend_lm_format_enforcer import ( validate_structured_output_request_lm_format_enforcer, ) @@ -340,8 +343,22 @@ class InputProcessor: # The request either failed validation # or includes some jsonschema feature(s) that # are not supported in xgrammar. - if isinstance(self.tokenizer, MistralTokenizer): + + # Check if schema has features unsupported by guidance + so_params = params.structured_outputs + skip_guidance = False + if so_params.json: + if isinstance(so_params.json, str): + import json + + schema = json.loads(so_params.json) + else: + schema = so_params.json + skip_guidance = has_guidance_unsupported_json_features(schema) + + if isinstance(self.tokenizer, MistralTokenizer) or skip_guidance: # Fall back to outlines if the tokenizer is Mistral + # or if schema contains features unsupported by guidance validate_structured_output_request_outlines(params) params.structured_outputs._backend = "outlines" else: diff --git a/vllm/v1/structured_output/backend_guidance.py b/vllm/v1/structured_output/backend_guidance.py index 2962a439dcb3e..727a67333bd71 100644 --- a/vllm/v1/structured_output/backend_guidance.py +++ b/vllm/v1/structured_output/backend_guidance.py @@ -44,6 +44,32 @@ def _walk_json_for_additional_properties(data: object): _walk_json_for_additional_properties(item) +def has_guidance_unsupported_json_features(schema: dict[str, Any]) -> bool: + """Check if JSON schema contains features unsupported by guidance/llguidance.""" + + def check_object(obj: dict[str, Any]) -> bool: + if not isinstance(obj, dict): + return False + + # patternProperties is not supported by llguidance + if "patternProperties" in obj: + return True + + # Recursively check all nested objects and arrays + for value in obj.values(): + if isinstance(value, dict): + if check_object(value): + return True + elif isinstance(value, list): + for item in value: + if isinstance(item, dict) and check_object(item): + return True + + return False + + return check_object(schema) + + def process_for_additional_properties( guide_json: str | dict[str, Any], ) -> dict[str, Any]: