Skip to content

Commit a71a627

Browse files
authored
Merge pull request #4 from codeanker/copilot/merge-llama-cpp-pr-18963
server: fix json_schema response_format handling
2 parents d74d46d + 9680d0d commit a71a627

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

tools/server/server-common.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -927,10 +927,15 @@ json oaicompat_chat_params_parse(
927927
if (response_type == "json_object") {
928928
json_schema = json_value(response_format, "schema", json::object());
929929
} else if (response_type == "json_schema") {
930+
// https://platform.openai.com/docs/api-reference/chat/create#chat-create-response_format
931+
// OpenAI expects: response_format.json_schema.schema
930932
auto schema_wrapper = json_value(response_format, "json_schema", json::object());
931-
json_schema = json_value(schema_wrapper, "schema", json::object());
933+
if (!schema_wrapper.contains("schema")) {
934+
throw std::invalid_argument("response_format type \"json_schema\" requires \"json_schema.schema\" to be set");
935+
}
936+
json_schema = schema_wrapper.at("schema");
932937
} else if (!response_type.empty() && response_type != "text") {
933-
throw std::invalid_argument("response_format type must be one of \"text\" or \"json_object\", but got: " + response_type);
938+
throw std::invalid_argument("response_format type must be one of \"text\", \"json_object\", or \"json_schema\", but got: " + response_type);
934939
}
935940
}
936941

tools/server/tests/unit/test_chat_completion.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,12 +176,18 @@ def test_apply_chat_template():
176176
({"type": "json_object", "schema": {"const": "42"}}, 6, "\"42\""),
177177
({"type": "json_object", "schema": {"items": [{"type": "integer"}]}}, 10, "[ -3000 ]"),
178178
({"type": "json_schema", "json_schema": {"schema": {"const": "foooooo"}}}, 10, "\"foooooo\""),
179+
# json_schema with name field (OpenAI-style)
180+
({"type": "json_schema", "json_schema": {"name": "test", "schema": {"const": "bar"}, "strict": True}}, 6, "\"bar\""),
179181
({"type": "json_object"}, 10, "(\\{|John)+"),
180182
({"type": "sound"}, 0, None),
181183
# invalid response format (expected to fail)
182184
({"type": "json_object", "schema": 123}, 0, None),
183185
({"type": "json_object", "schema": {"type": 123}}, 0, None),
184186
({"type": "json_object", "schema": {"type": "hiccup"}}, 0, None),
187+
# json_schema missing required json_schema.schema field (should fail)
188+
({"type": "json_schema", "json_schema": {"name": "test"}}, 0, None),
189+
({"type": "json_schema", "json_schema": {}}, 0, None),
190+
({"type": "json_schema"}, 0, None),
185191
])
186192
def test_completion_with_response_format(response_format: dict, n_predicted: int, re_content: str | None):
187193
global server
@@ -203,6 +209,31 @@ def test_completion_with_response_format(response_format: dict, n_predicted: int
203209
assert "error" in res.body
204210

205211

212+
@pytest.mark.parametrize("response_format,expected_error_message", [
213+
# json_schema type requires json_schema.schema to be set
214+
({"type": "json_schema", "json_schema": {"name": "test"}}, "json_schema.schema"),
215+
({"type": "json_schema", "json_schema": {}}, "json_schema.schema"),
216+
({"type": "json_schema"}, "json_schema.schema"),
217+
# invalid response_format type should mention valid options
218+
({"type": "invalid_type"}, "json_schema"),
219+
])
220+
def test_response_format_error_messages(response_format: dict, expected_error_message: str):
221+
"""Test that invalid response_format configurations return helpful error messages."""
222+
global server
223+
server.start()
224+
res = server.make_request("POST", "/chat/completions", data={
225+
"max_tokens": 10,
226+
"messages": [
227+
{"role": "user", "content": "test"},
228+
],
229+
"response_format": response_format,
230+
})
231+
assert res.status_code == 400
232+
assert "error" in res.body
233+
assert expected_error_message in res.body["error"]["message"], \
234+
f"Expected '{expected_error_message}' in error message, got: {res.body['error']['message']}"
235+
236+
206237
@pytest.mark.parametrize("jinja,json_schema,n_predicted,re_content", [
207238
(False, {"const": "42"}, 6, "\"42\""),
208239
(True, {"const": "42"}, 6, "\"42\""),

0 commit comments

Comments
 (0)