11# SPDX-License-Identifier: Apache-2.0
22
3+ import copy
4+ import json
35import os
46from dataclasses import dataclass
5- from typing import TYPE_CHECKING , Optional
7+ from typing import TYPE_CHECKING , Any , Optional , Union
68
79import torch
810
911from vllm .config import VllmConfig
1012from vllm .logger import init_logger
11- from vllm .sampling_params import SamplingParams
13+ from vllm .sampling_params import GuidedDecodingParams , SamplingParams
1214from vllm .transformers_utils .tokenizer_group import init_tokenizer_from_configs
1315from vllm .utils import LazyLoader
1416from vllm .v1 .structured_output .backend_types import (StructuredOutputBackend ,
2931logger = init_logger (__name__ )
3032
3133
34+ def _walk_json_for_additional_properties (data : object ):
35+ if isinstance (data , dict ):
36+ for value in data .values ():
37+ _walk_json_for_additional_properties (value )
38+ if 'additionalProperties' not in data and \
39+ ('properties' in data or 'patternProperties' in data ):
40+ data ['additionalProperties' ] = False
41+ elif isinstance (data , list ):
42+ for item in data :
43+ _walk_json_for_additional_properties (item )
44+
45+
46+ def process_for_additional_properties (
47+ guide_json : Union [str , dict [str , Any ]]) -> dict [str , Any ]:
48+ if isinstance (guide_json , str ):
49+ guide_json_obj = json .loads (guide_json )
50+ else :
51+ # copy for modifications
52+ guide_json_obj = copy .deepcopy (guide_json )
53+ _walk_json_for_additional_properties (guide_json_obj )
54+ return guide_json_obj
55+
56+
3257class GuidanceBackend (StructuredOutputBackend ):
3358
3459 def __init__ (self , vllm_config : VllmConfig ):
@@ -41,9 +66,20 @@ def __init__(self, vllm_config: VllmConfig):
4166 tokenizer_group .ping ()
4267 self .vllm_config = vllm_config
4368 self .vocab_size = vllm_config .model_config .get_vocab_size ()
44- self .disable_any_whitespace = (
45- "disable-any-whitespace"
46- in vllm_config .decoding_config .guided_decoding_backend )
69+
70+ self .disable_any_whitespace = False
71+ self .no_additional_properties = False
72+ backend_options = GuidedDecodingParams (
73+ backend = vllm_config .decoding_config .guided_decoding_backend
74+ ).backend_options ()
75+ for option in backend_options :
76+ if option == "disable-any-whitespace" :
77+ self .disable_any_whitespace = True
78+ elif option == "no-additional-properties" :
79+ self .no_additional_properties = True
80+ else :
81+ raise ValueError (
82+ f"Unsupported option for the guidance backend: { option } " )
4783
4884 tokenizer = tokenizer_group .get_lora_tokenizer (None )
4985 self .ll_tokenizer = llguidance_hf .from_tokenizer (
@@ -52,7 +88,8 @@ def __init__(self, vllm_config: VllmConfig):
5288 def compile_grammar (self , request_type : StructuredOutputOptions ,
5389 grammar_spec : str ) -> StructuredOutputGrammar :
5490 self .serialized_grammar = serialize_guidance_grammar (
55- request_type , grammar_spec , self .disable_any_whitespace )
91+ request_type , grammar_spec , self .disable_any_whitespace ,
92+ self .no_additional_properties )
5693
5794 ll_matcher = llguidance .LLMatcher (
5895 self .ll_tokenizer ,
@@ -129,10 +166,15 @@ def reset(self):
129166 self .ll_matcher .reset ()
130167
131168
132- def serialize_guidance_grammar (request_type : StructuredOutputOptions ,
133- grammar_spec : str ,
134- disable_any_whitespace : bool = False ) -> str :
169+ def serialize_guidance_grammar (
170+ request_type : StructuredOutputOptions ,
171+ grammar_spec : Union [str , dict [str , Any ]],
172+ disable_any_whitespace : bool = False ,
173+ no_additional_properties : bool = False ,
174+ ) -> str :
135175 if request_type == StructuredOutputOptions .JSON :
176+ if no_additional_properties :
177+ grammar_spec = process_for_additional_properties (grammar_spec )
136178 return llguidance .LLMatcher .grammar_from_json_schema (
137179 grammar_spec ,
138180 defaults = {
0 commit comments