From b136b62cf9305576fbd7f6d61f442f9f2e6dd524 Mon Sep 17 00:00:00 2001 From: Galunid Date: Sat, 11 Apr 2026 01:26:36 +0200 Subject: [PATCH] fix: Fix broken structured output when using $refs in json_schema (#21699) --- common/chat-auto-parser-generator.cpp | 4 ++++ common/chat.cpp | 21 +++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/common/chat-auto-parser-generator.cpp b/common/chat-auto-parser-generator.cpp index d3086725d4..3eb1fa9a9c 100644 --- a/common/chat-auto-parser-generator.cpp +++ b/common/chat-auto-parser-generator.cpp @@ -69,6 +69,10 @@ common_chat_params peg_generator::generate_parser(const common_chat_template & auto schema = function.contains("parameters") ? function.at("parameters") : json::object(); builder.resolve_refs(schema); }); + if (has_response_format) { + auto schema = inputs.json_schema; + builder.resolve_refs(schema); + } parser.build_grammar(builder, data.grammar_lazy); }); diff --git a/common/chat.cpp b/common/chat.cpp index 33aa019f1b..3f3b063306 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -865,9 +865,10 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_ adjusted_messages.push_back(adjusted); } - auto has_tools = inputs.tools.is_array() && !inputs.tools.empty(); - auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE; - auto include_grammar = true; + auto has_tools = inputs.tools.is_array() && !inputs.tools.empty(); + auto has_response_format = inputs.json_schema.is_object() && !inputs.json_schema.empty(); + auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE; + auto include_grammar = true; data.supports_thinking = true; data.thinking_start_tag = "[THINK]"; @@ -887,7 +888,7 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_ extract_reasoning ? p.optional("[THINK]" + p.reasoning(p.until("[/THINK]")) + "[/THINK]") : p.eps(); // Response format parser - if (inputs.json_schema.is_object() && !inputs.json_schema.empty()) { + if (has_response_format) { // Ministral wants to emit json surrounded by code fences return generation_prompt + (reasoning << "```json" << p.content(p.schema(p.json(), "response-format", inputs.json_schema)) << "```"); } @@ -928,6 +929,10 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_ auto schema = function.at("parameters"); builder.resolve_refs(schema); }); + if (has_response_format) { + auto schema = inputs.json_schema; + builder.resolve_refs(schema); + } parser.build_grammar(builder, data.grammar_lazy); }); @@ -1063,6 +1068,10 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp auto schema = function.at("parameters"); builder.resolve_refs(schema); }); + if (has_response_format) { + auto schema = inputs.json_schema; + builder.resolve_refs(schema); + } parser.build_grammar(builder, data.grammar_lazy); }); @@ -1193,6 +1202,10 @@ static common_chat_params common_chat_params_init_gemma4(const common_chat_templ auto schema = function.at("parameters"); builder.resolve_refs(schema); }); + if (has_response_format) { + auto schema = inputs.json_schema; + builder.resolve_refs(schema); + } parser.build_grammar(builder, data.grammar_lazy); });