main
py 95 lines 3.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import dataclasses
4 import functools
5 import json
6 from concurrent.futures import Future
7 from typing import TYPE_CHECKING, Any, cast
8
9 from vllm.sampling_params import SamplingParams, StructuredOutputsParams
10 from vllm.v1.structured_output.backend_types import (
11 StructuredOutputGrammar,
12 StructuredOutputKey,
13 StructuredOutputOptions,
14 )
15
16 if TYPE_CHECKING:
17 from vllm.reasoning import ReasoningParser
18
19
20 @dataclasses.dataclass
21 class StructuredOutputRequest:
22 params: StructuredOutputsParams
23 _grammar: (
24 Future[StructuredOutputGrammar] | StructuredOutputGrammar | Exception | None
25 ) = None
26 reasoning_ended: bool | None = None
27 reasoning_parser_kwargs: dict[str, Any] | None = None
28 # Cached per request; do not share reasoning parsers across requests because
29 # their behavior can depend on reasoning_parser_kwargs.
30 reasoner: "ReasoningParser | None" = None
31
32 @staticmethod
33 def from_sampling_params(
34 sampling_params: SamplingParams | None,
35 ) -> "StructuredOutputRequest | None":
36 if sampling_params is None:
37 return None
38 params = sampling_params.structured_outputs
39 if not params or params.all_constraints_none():
40 return None
41 return StructuredOutputRequest(params=params)
42
43 def _check_grammar_completion(self) -> bool:
44 if isinstance(self._grammar, Future):
45 if not self._grammar.done():
46 return False
47 try:
48 self._grammar = self._grammar.result()
49 except Exception as e:
50 self._grammar = e
51 return True
52
53 @property
54 def is_grammar_ready(self) -> bool:
55 return self._check_grammar_completion()
56
57 @property
58 def grammar(self) -> StructuredOutputGrammar | Exception | None:
59 if not self._check_grammar_completion():
60 return None
61 return cast(StructuredOutputGrammar | Exception | None, self._grammar)
62
63 @grammar.setter
64 def grammar(
65 self, grammar: StructuredOutputGrammar | Future[StructuredOutputGrammar]
66 ) -> None:
67 self._grammar = grammar
68
69 @functools.cached_property
70 def structured_output_key(self) -> StructuredOutputKey:
71 return get_structured_output_key(self.params)
72
73
74 def get_structured_output_key(params: StructuredOutputsParams) -> StructuredOutputKey:
75 if params.json is not None:
76 if not isinstance(params.json, str):
77 json_str = json.dumps(params.json)
78 else:
79 json_str = params.json
80 return StructuredOutputOptions.JSON, json_str
81 if params.json_object:
82 return StructuredOutputOptions.JSON_OBJECT, ""
83 if params.regex is not None:
84 return StructuredOutputOptions.REGEX, params.regex
85 if params.choice is not None:
86 if not isinstance(params.choice, str):
87 json_str = json.dumps(params.choice)
88 else:
89 json_str = params.choice
90 return StructuredOutputOptions.CHOICE, json_str
91 if params.grammar is not None:
92 return StructuredOutputOptions.GRAMMAR, params.grammar
93 if params.structural_tag is not None:
94 return StructuredOutputOptions.STRUCTURAL_TAG, params.structural_tag
95 raise ValueError("No valid structured output parameter found")