@@ -2519,53 +2519,60 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
25192519 // Google Gemma 4 (tool calling with Gemma4 dict format)
25202520 auto tst = peg_tester (" models/templates/google-gemma-4-31B-it.jinja" );
25212521
2522- tst.test (" Hello, world!" ).expect (simple_assist_msg (" Hello, world!" )).run ();
2522+ tst.test (" Hello, world!" ).enable_thinking ( false ). expect (simple_assist_msg (" Hello, world!" )).run ();
25232523
25242524 // Reasoning and content
25252525 tst.test (
2526- " <|channel>thought\n I'm\n thinking<channel|>Hello, world!\n What's up?" )
2526+ " I'm\n thinking<channel|>Hello, world!\n What's up?" )
2527+ .enable_thinking (true )
25272528 .reasoning_format (COMMON_REASONING_FORMAT_AUTO )
25282529 .expect (message_assist_thoughts)
25292530 .run ();
25302531
25312532 // Empty reasoning (budget=0: sampler forces end tag before newline)
25322533 tst.test (
2533- " <|channel>thought<channel|>Hello, world!\n What's up?" )
2534+ " <channel|>Hello, world!\n What's up?" )
2535+ .enable_thinking (true )
25342536 .reasoning_format (COMMON_REASONING_FORMAT_AUTO )
25352537 .expect (simple_assist_msg (" Hello, world!\n What's up?" , " " ))
25362538 .run ();
25372539
25382540 // Reasoning and content with reasoning_format = none
25392541 tst.test (
25402542 " <|channel>thought\n I'm\n thinking<channel|>Hello, world!\n What's up?" )
2543+ .enable_thinking (false )
25412544 .reasoning_format (COMMON_REASONING_FORMAT_NONE )
25422545 .expect_content (" <|channel>thought\n I'm\n thinking<channel|>Hello, world!\n What's up?" )
25432546 .run ();
25442547
25452548 // Simple tool call with string argument
25462549 tst.test (
25472550 " <|tool_call>call:get_time{city:<|\" |>London<|\" |>}<tool_call|>" )
2551+ .enable_thinking (false )
25482552 .tools ({ get_time_tool })
25492553 .expect (message_with_tool_calls (" get_time" , R"( {"city": "London"})" ))
25502554 .run ();
25512555
25522556 // Tool call with string argument containing special chars
25532557 tst.test (
25542558 " <|tool_call>call:get_time{city:<|\" |>San Francisco<|\" |>}<tool_call|>" )
2559+ .enable_thinking (false )
25552560 .tools ({ get_time_tool })
25562561 .expect (message_with_tool_calls (" get_time" , R"( {"city": "San Francisco"})" ))
25572562 .run ();
25582563
25592564 // Tool call with empty args
25602565 tst.test (
25612566 " <|tool_call>call:empty_args{}<tool_call|>" )
2567+ .enable_thinking (false )
25622568 .tools ({ empty_args_tool })
25632569 .expect (message_with_tool_calls (" empty_args" , " {}" ))
25642570 .run ();
25652571
25662572 // Tool call with string and content
25672573 tst.test (
25682574 " Hello, world!\n What's up?<|tool_call>call:get_time{city:<|\" |>Paris<|\" |>}<tool_call|>" )
2575+ .enable_thinking (false )
25692576 .tools ({ get_time_tool })
25702577 .expect (message_with_content_and_tool_call (" Hello, world!\n What's up?" , " get_time" , R"( {"city": "Paris"})" ))
25712578 .run ();
@@ -2574,6 +2581,7 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
25742581 tst.test (
25752582 " <|tool_call>call:get_time{city:<|\" |>London<|\" |>}<tool_call|>"
25762583 " <|tool_call>call:get_weather{city:<|\" |>Paris<|\" |>}<tool_call|>" )
2584+ .enable_thinking (false )
25772585 .tools ({ get_time_tool, get_weather_tool })
25782586 .parallel_tool_calls (true )
25792587 .expect_tool_calls ({
@@ -2585,101 +2593,116 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
25852593 // Tool call with integer argument (number type)
25862594 tst.test (
25872595 " <|tool_call>call:special_function{arg1:42}<tool_call|>" )
2596+ .enable_thinking (false )
25882597 .tools ({ special_function_tool })
25892598 .expect (message_with_tool_calls (" special_function" , R"( {"arg1": 42})" ))
25902599 .run ();
25912600
25922601 // Tool call with negative number argument
25932602 tst.test (
25942603 " <|tool_call>call:special_function{arg1:-7}<tool_call|>" )
2604+ .enable_thinking (false )
25952605 .tools ({ special_function_tool })
25962606 .expect (message_with_tool_calls (" special_function" , R"( {"arg1": -7})" ))
25972607 .run ();
25982608
25992609 // Tool call with decimal number argument
26002610 tst.test (
26012611 " <|tool_call>call:amount{orig:3.14}<tool_call|>" )
2612+ .enable_thinking (false )
26022613 .tools ({ amount_tool })
26032614 .expect (message_with_tool_calls (" amount" , R"( {"orig": 3.14})" ))
26042615 .run ();
26052616
26062617 // Tool call with boolean argument (true)
26072618 tst.test (
26082619 " <|tool_call>call:toggle{enabled:true}<tool_call|>" )
2620+ .enable_thinking (false )
26092621 .tools ({ toggle_tool })
26102622 .expect (message_with_tool_calls (" toggle" , R"( {"enabled": true})" ))
26112623 .run ();
26122624
26132625 // Tool call with boolean argument (false)
26142626 tst.test (
26152627 " <|tool_call>call:toggle{enabled:false}<tool_call|>" )
2628+ .enable_thinking (false )
26162629 .tools ({ toggle_tool })
26172630 .expect (message_with_tool_calls (" toggle" , R"( {"enabled": false})" ))
26182631 .run ();
26192632
26202633 // Tool call with null argument
26212634 tst.test (
26222635 " <|tool_call>call:set_nullable{value:null}<tool_call|>" )
2636+ .enable_thinking (false )
26232637 .tools ({ nullable_tool })
26242638 .expect (message_with_tool_calls (" set_nullable" , R"( {"value": null})" ))
26252639 .run ();
26262640
26272641 // Tool call with array argument (todo list)
26282642 tst.test (
26292643 " <|tool_call>call:todo_list{todos:[<|\" |>buy milk<|\" |>,<|\" |>walk dog<|\" |>]}<tool_call|>" )
2644+ .enable_thinking (false )
26302645 .tools ({ todo_list })
26312646 .expect (message_with_tool_calls (" todo_list" , R"( {"todos":["buy milk","walk dog"]})" ))
26322647 .run ();
26332648
26342649 // Tool call with object/dict argument
26352650 tst.test (
26362651 " <|tool_call>call:set_config{config:{theme:<|\" |>dark<|\" |>,count:3}}<tool_call|>" )
2652+ .enable_thinking (false )
26372653 .tools ({ config_tool })
26382654 .expect (message_with_tool_calls (" set_config" , R"( {"config":{"theme":"dark","count":3}})" ))
26392655 .run ();
26402656
26412657 // Tool call with empty array
26422658 tst.test (
26432659 " <|tool_call>call:todo_list{todos:[]}<tool_call|>" )
2660+ .enable_thinking (false )
26442661 .tools ({ todo_list })
26452662 .expect (message_with_tool_calls (" todo_list" , R"( {"todos":[]})" ))
26462663 .run ();
26472664
26482665 // Tool call with empty dict
26492666 tst.test (
26502667 " <|tool_call>call:set_config{config:{}}<tool_call|>" )
2668+ .enable_thinking (false )
26512669 .tools ({ config_tool })
26522670 .expect (message_with_tool_calls (" set_config" , R"( {"config":{}})" ))
26532671 .run ();
26542672
26552673 // Tool call with scientific notation number
26562674 tst.test (
26572675 " <|tool_call>call:amount{orig:1.5e10}<tool_call|>" )
2676+ .enable_thinking (false )
26582677 .tools ({ amount_tool })
26592678 .expect (message_with_tool_calls (" amount" , R"( {"orig": 1.5e10})" ))
26602679 .run ();
26612680
26622681 // Edge cases
26632682 tst.test (
26642683 " <|channel>thought\n <channel|>Hello, world!\n What's up?<channel|>" )
2684+ .enable_thinking (false )
26652685 .reasoning_format (COMMON_REASONING_FORMAT_AUTO )
26662686 .expect (message_assist)
26672687 .run ();
26682688
26692689 tst.test (
26702690 " <|channel>thought\n <channel|>Hello, world!\n What's up?<|channel>thought\n <channel|>" )
2691+ .enable_thinking (false )
26712692 .reasoning_format (COMMON_REASONING_FORMAT_AUTO )
26722693 .expect (message_assist)
26732694 .run ();
26742695
26752696 tst.test (
26762697 " <|channel>thought\n <channel|>Hello, world!\n What's up?<|channel>thought\n <channel|><channel|>" )
2698+ .enable_thinking (false )
26772699 .reasoning_format (COMMON_REASONING_FORMAT_AUTO )
26782700 .expect (message_assist)
26792701 .run ();
26802702
26812703 tst.test (
26822704 " <|channel><|channel>thought\n <channel|>Hello, world!\n What's up?" )
2705+ .enable_thinking (false )
26832706 .reasoning_format (COMMON_REASONING_FORMAT_AUTO )
26842707 .expect (message_assist)
26852708 .run ();
@@ -2723,11 +2746,15 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
27232746 inputs.messages = { message_user, tool_call_msg, tool_msg };
27242747 inputs.tools = { special_function_tool };
27252748 inputs.add_generation_prompt = true ;
2749+ inputs.enable_thinking = false ;
27262750
27272751 auto params = common_chat_templates_apply (tmpls.get (), inputs);
27282752
27292753 if (!string_ends_with (params.prompt , " <turn|>\n <|turn>model\n " )) {
2730- throw std::runtime_error (" Missing generation prompt for Gemma 4" );
2754+ throw std::runtime_error (" Missing non-thinking generation prompt for Gemma 4" );
2755+ }
2756+ if (params.prompt .find (" <|channel>thought\n <channel|>" ) != std::string::npos) {
2757+ throw std::runtime_error (" Gemma 4 rendered an empty thought block with thinking disabled" );
27312758 }
27322759 }
27332760
@@ -2744,6 +2771,34 @@ static void test_template_output_peg_parsers(bool detailed_debug) {
27442771 }
27452772 }
27462773 }
2774+
2775+ for (const char * template_path : {
2776+ " models/templates/google-gemma-4-31B-it.jinja" ,
2777+ " models/templates/google-gemma-4-31B-it-interleaved.jinja" ,
2778+ }) {
2779+ auto tmpls = read_templates (template_path);
2780+
2781+ common_chat_templates_inputs inputs;
2782+ inputs.messages = { message_user };
2783+ inputs.add_generation_prompt = true ;
2784+ inputs.enable_thinking = true ;
2785+
2786+ auto params = common_chat_templates_apply (tmpls.get (), inputs);
2787+
2788+ if (params.prompt .rfind (" <|turn>model\n <|channel>thought\n " ) == std::string::npos) {
2789+ throw std::runtime_error (std::string (" Missing thinking generation prompt for " ) + template_path);
2790+ }
2791+
2792+ inputs.enable_thinking = false ;
2793+ params = common_chat_templates_apply (tmpls.get (), inputs);
2794+
2795+ if (!string_ends_with (params.prompt , " <|turn>model\n " )) {
2796+ throw std::runtime_error (std::string (" Missing non-thinking generation prompt for " ) + template_path);
2797+ }
2798+ if (params.prompt .find (" <|channel>thought\n <channel|>" ) != std::string::npos) {
2799+ throw std::runtime_error (std::string (" Gemma 4 rendered an empty thought block with thinking disabled for " ) + template_path);
2800+ }
2801+ }
27472802 }
27482803
27492804 {
0 commit comments