@@ -1111,6 +1111,68 @@ static void test_template_output_parsers() {
11111111 /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
11121112 }));
11131113 }
1114+ {
1115+ auto tmpls = read_templates (" models/templates/Qwen-Qwen3-0.6B.jinja" );
1116+ std::vector<std::string> end_tokens{ " <|im_end|>" };
1117+
1118+ assert_equals (COMMON_CHAT_FORMAT_HERMES_2_PRO, common_chat_templates_apply (tmpls.get (), inputs_no_tools).format );
1119+ assert_equals (COMMON_CHAT_FORMAT_HERMES_2_PRO, common_chat_templates_apply (tmpls.get (), inputs_tools).format );
1120+
1121+ // Test that enable_thinking=false adds empty think tags
1122+ {
1123+ common_chat_templates_inputs inputs_no_thinking;
1124+ inputs_no_thinking.messages = {message_user};
1125+ inputs_no_thinking.tools = tools;
1126+ inputs_no_thinking.tool_choice = COMMON_CHAT_TOOL_CHOICE_REQUIRED;
1127+ inputs_no_thinking.enable_thinking = false ;
1128+
1129+ auto params = common_chat_templates_apply (tmpls.get (), inputs_no_thinking);
1130+ assert_equals (COMMON_CHAT_FORMAT_HERMES_2_PRO, params.format );
1131+ // Verify the prompt contains empty think tags when thinking is disabled
1132+ assert_equals (true , params.prompt .find (" <think>\n\n </think>" ) != std::string::npos);
1133+ }
1134+
1135+ // Test that grammar allows thinking with REQUIRED tool choice
1136+ {
1137+ common_chat_templates_inputs inputs_with_thinking;
1138+ inputs_with_thinking.messages = {message_user};
1139+ inputs_with_thinking.tools = tools;
1140+ inputs_with_thinking.tool_choice = COMMON_CHAT_TOOL_CHOICE_REQUIRED;
1141+ inputs_with_thinking.enable_thinking = true ;
1142+
1143+ auto params = common_chat_templates_apply (tmpls.get (), inputs_with_thinking);
1144+ assert_equals (COMMON_CHAT_FORMAT_HERMES_2_PRO, params.format );
1145+
1146+ // The key fix: grammar should contain the thinking pattern even with REQUIRED
1147+ assert_equals (false , params.grammar .empty ());
1148+ assert_equals (true , params.grammar .find (" </think>" ) != std::string::npos);
1149+
1150+ // Grammar should allow thinking before tool calls
1151+ assert_equals (true , params.grammar .find (" think-" ) != std::string::npos ||
1152+ params.grammar .find (" <think>" ) != std::string::npos);
1153+ }
1154+
1155+ // Test parsing: tool call with thinking works correctly
1156+ assert_msg_equals (message_assist_call_thoughts,
1157+ common_chat_parse (
1158+ " <think>I'm\n thinking</think>\n "
1159+ " <tool_call>{\" name\" : \" special_function\" , \" arguments\" : {\" arg1\" : 1}}</tool_call>" ,
1160+ /* is_partial= */ false ,
1161+ {
1162+ /* .format = */ COMMON_CHAT_FORMAT_HERMES_2_PRO,
1163+ /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
1164+ }));
1165+
1166+ // Test that reasoning + tool calls work in template generation
1167+ test_templates (tmpls.get (), end_tokens, message_assist_call_thoughts, tools,
1168+ " " , // Don't check exact delta, just verify it parses correctly
1169+ /* expect_grammar_triggered= */ true ,
1170+ /* test_grammar_if_triggered= */ true ,
1171+ COMMON_REASONING_FORMAT_DEEPSEEK);
1172+
1173+ // Verify enable_thinking support
1174+ assert_equals (true , common_chat_templates_support_enable_thinking (tmpls.get ()));
1175+ }
11141176 {
11151177 auto tmpls = read_templates (" models/templates/meta-llama-Llama-3.1-8B-Instruct.jinja" );
11161178 std::vector<std::string> end_tokens{ " <|eom_id|>" , " <|eot_id|>" };
0 commit comments