diff --git a/common/chat.cpp b/common/chat.cpp index 078b44ee1b..bb76fbbb06 100644 --- a/common/chat.cpp +++ b/common/chat.cpp @@ -971,6 +971,7 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp auto has_tools = inputs.tools.is_array() && !inputs.tools.empty(); auto has_response_format = !inputs.json_schema.is_null() && inputs.json_schema.is_object(); auto include_grammar = has_response_format || (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE); + auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE; auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) { auto start = p.rule("start", p.literal("<|start|>assistant")); @@ -979,7 +980,13 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp auto channel = p.literal("<|channel|>") + (p.literal("commentary") | p.literal("analysis")); auto constrain_type = p.chars("[A-Za-z0-9_-]", 1, -1); - auto analysis = p.rule("analysis", p.literal("<|channel|>analysis<|message|>") + p.reasoning(content) + end); + if (extract_reasoning) { + p.rule("analysis", p.literal("<|channel|>analysis<|message|>") + p.reasoning(content) + end); + } else { + p.rule("analysis", p.content(p.literal("<|channel|>analysis<|message|>") + content + end)); + } + + auto analysis = p.ref("analysis"); auto preamble = p.rule("preamble", p.literal("<|channel|>commentary<|message|>") + p.content(content) + end); auto final_msg = p.rule("final", p.literal("<|channel|>final<|message|>") + p.content(content)); auto any = p.rule("any", preamble | analysis); diff --git a/tests/test-chat.cpp b/tests/test-chat.cpp index a2af4e3775..de9104352c 100644 --- a/tests/test-chat.cpp +++ b/tests/test-chat.cpp @@ -2796,6 +2796,14 @@ static void test_template_output_peg_parsers(bool detailed_debug) { .expect(message_assist_thoughts) .run(); + // Analysis channel (reasoning) with final channel (content) with reasoning_format = none + tst.test( + "<|channel|>analysis<|message|>I'm\nthinking<|end|><|start|>assistant<|channel|>final<|message|>Hello, world!\nWhat's " + "up?") + .reasoning_format(COMMON_REASONING_FORMAT_NONE) + .expect_content("<|channel|>analysis<|message|>I'm\nthinking<|end|>Hello, world!\nWhat's up?") + .run(); + // Analysis channel only (partial) - still works when reasoning format is set tst.test("<|channel|>analysis<|message|>I'm\nthinking") .reasoning_format(COMMON_REASONING_FORMAT_AUTO) @@ -2805,24 +2813,28 @@ static void test_template_output_peg_parsers(bool detailed_debug) { // Tool call with recipient in role header: " to=functions.NAME<|channel|>analysis<|message|>JSON" tst.test(" to=functions.special_function<|channel|>analysis<|message|>{\"arg1\": 1}") + .reasoning_format(COMMON_REASONING_FORMAT_AUTO) .tools({ special_function_tool }) .expect(message_assist_call) .run(); // Tool call with recipient in channel header: "<|channel|>analysis to=functions.NAME<|message|>JSON" tst.test("<|channel|>analysis to=functions.special_function<|message|>{\"arg1\": 1}") + .reasoning_format(COMMON_REASONING_FORMAT_AUTO) .tools({ special_function_tool }) .expect(message_assist_call) .run(); // Tool call with constraint: " to=functions.NAME<|channel|>analysis <|constrain|>json<|message|>JSON" tst.test(" to=functions.special_function<|channel|>analysis <|constrain|>json<|message|>{\"arg1\": 1}") + .reasoning_format(COMMON_REASONING_FORMAT_AUTO) .tools({ special_function_tool }) .expect(message_assist_call) .run(); // Tool call in commentary channel (channel header variant) tst.test("<|channel|>commentary to=functions.special_function<|message|>{\"arg1\": 1}") + .reasoning_format(COMMON_REASONING_FORMAT_AUTO) .tools({ special_function_tool }) .expect(message_assist_call) .run();