diff --git a/docs/features/core/output_types.md b/docs/features/core/output_types.md index 5b8f1d24c..62fe82369 100644 --- a/docs/features/core/output_types.md +++ b/docs/features/core/output_types.md @@ -178,7 +178,7 @@ output_type = JsonSchema(schema_dict) ``` `JsonSchema` accepts two optional parameters: -- `whitespace_pattern` (defaults to `None`): specifies the pattern to use for JSON syntactic whitespace. If none is provided, the default permissive JSON whitespace rules are used. It is applied by the `outlines_core` backend; the `llguidance` and `xgrammar` backends do not support it and raise an error if one is set. +- `whitespace_pattern` (defaults to `None`): specifies the pattern to use for JSON syntactic whitespace. If none is provided, the default permissive JSON whitespace rules are used. It is applied by the `outlines_core` backend; the `llguidance` and `xgrammar` backends, and the `SGLang` and `TGI` model integrations, do not support it and raise an error if one is set. - `ensure_ascii` (defaults to `True`): defines the value to use for the argument `ensure_ascii` of the `json.dumps` method. If false, non-ASCII characters will be turned into unicodes. ### Regex Patterns diff --git a/src/outlines/models/sglang.py b/src/outlines/models/sglang.py index b27033494..36bda1f9d 100644 --- a/src/outlines/models/sglang.py +++ b/src/outlines/models/sglang.py @@ -75,6 +75,13 @@ def format_output_type(self, output_type: Optional[Any] = None) -> dict: ) return {"extra_body": {"ebnf": term.definition}} elif isinstance(term, JsonSchema): + if term.whitespace_pattern is not None: + raise NotImplementedError( + "The SGLang backend does not support the " + "`whitespace_pattern` argument. Set the whitespace " + "pattern on the SGLang server instead, e.g. with " + "`--constrained-json-whitespace-pattern`." + ) return OpenAITypeAdapter().format_json_output_type( json.loads(term.schema) ) diff --git a/src/outlines/models/tgi.py b/src/outlines/models/tgi.py index 58fd52598..c6bc31ae7 100644 --- a/src/outlines/models/tgi.py +++ b/src/outlines/models/tgi.py @@ -73,6 +73,11 @@ def format_output_type(self, output_type: Optional[Any] = None) -> dict: "TGI does not support CFG-based structured outputs." ) elif isinstance(term, JsonSchema): + if term.whitespace_pattern is not None: + raise NotImplementedError( + "The TGI backend does not support the " + "`whitespace_pattern` argument." + ) return { "grammar": { "type": "json", diff --git a/tests/models/test_sglang_type_adapter.py b/tests/models/test_sglang_type_adapter.py index f07ccac49..59f0b22b6 100644 --- a/tests/models/test_sglang_type_adapter.py +++ b/tests/models/test_sglang_type_adapter.py @@ -149,20 +149,9 @@ def test_sglang_type_adapter_output_type( }, } } - # whitespace pattern is ignored - assert type_adapter.format_output_type(json_schema_whitespace_instance) == { - "response_format": { - "type": "json_schema", - "json_schema": { - "name": "default", - "strict": True, - "schema": { - **json.loads(JSON_SCHEMA_STRING), - "additionalProperties": False, - }, - }, - } - } + # the SGLang server only supports a server-side whitespace pattern + with pytest.raises(NotImplementedError, match="whitespace_pattern"): + type_adapter.format_output_type(json_schema_whitespace_instance) assert type_adapter.format_output_type(int) == { "extra_body": {"regex": "([+-]?(0|[1-9][0-9]*))"} } diff --git a/tests/models/test_tgi_model_adapter.py b/tests/models/test_tgi_model_adapter.py index 42c91e3d6..3e7d57ce6 100644 --- a/tests/models/test_tgi_model_adapter.py +++ b/tests/models/test_tgi_model_adapter.py @@ -62,13 +62,9 @@ def test_tgi_type_adapter_output_type( "value": json.loads(JSON_SCHEMA_STRING), } } - # whitespace_pattern is ignored - assert type_adapter.format_output_type(json_schema_whitespace_instance) == { - "grammar": { - "type": "json", - "value": json.loads(JSON_SCHEMA_STRING), - } - } + # TGI does not support per-request whitespace patterns + with pytest.raises(NotImplementedError, match="whitespace_pattern"): + type_adapter.format_output_type(json_schema_whitespace_instance) assert type_adapter.format_output_type(int) == { "grammar": { "type": "regex",