@@ -2919,12 +2919,15 @@ struct httpserver {
29192919 {
29202920 chat_templates = common_chat_templates_init (llm_model, params.llm_params .chat_template );
29212921
2922+ const auto * source = common_chat_templates_source (chat_templates.get ());
29222923 // NB(thxCode): llama_chat_template_alias is a patch.
2923- std::string alias = llama_chat_template_alias (common_chat_templates_source (chat_templates. get ()) );
2924+ std::string alias = llama_chat_template_alias (source );
29242925
29252926 if (params.llm_params .use_jinja ) {
29262927 // NB(thxCode): common_chat_templates_supports_tool_calls is a patch.
2927- support_tool_calls = common_chat_templates_supports_tool_calls (chat_templates.get ());
2928+ support_tool_calls = common_chat_templates_supports_tool_calls (chat_templates.get ());
2929+ // NB(thxCode): common_chat_templates_supports_parallel_tool_calls is a patch.
2930+ support_parallel_tool_calls = common_chat_templates_supports_parallel_tool_calls (chat_templates.get ());
29282931 } else {
29292932 bool get_token = false ;
29302933 // chatml / chatglm4
@@ -3009,7 +3012,7 @@ struct httpserver {
30093012 }
30103013
30113014 {
3012- if (alias == " deepseek3" || string_starts_with (llm_model_arch_name, " qwen3" )) {
3015+ if (alias == " deepseek3" || alias == " granite " || string_starts_with (llm_model_arch_name, " qwen3" )) {
30133016 llama_tokens ids = common_tokenize (llm_vocab, " <think>" , false , true );
30143017 if (ids.size () == 1 ) {
30153018 reasoning_start_token = ids[0 ];
@@ -3018,6 +3021,12 @@ struct httpserver {
30183021 if (ids.size () == 1 ) {
30193022 reasoning_end_token = ids[0 ];
30203023 }
3024+ // If the template does not contain the end token, we disable reasoning.
3025+ auto source_str = std::string (source);
3026+ if (source_str.find (" </think>" ) == std::string::npos) {
3027+ reasoning_start_token = LLAMA_TOKEN_NULL ;
3028+ reasoning_end_token = LLAMA_TOKEN_NULL ;
3029+ }
30213030 } else if (alias == " command-r" ) {
30223031 llama_tokens ids = common_tokenize (llm_vocab, " <|START_THINKING|>" , false , true );
30233032 if (ids.size () == 1 ) {
@@ -3027,6 +3036,12 @@ struct httpserver {
30273036 if (ids.size () == 1 ) {
30283037 reasoning_end_token = ids[0 ];
30293038 }
3039+ // If the template does not contain the end token, we disable reasoning.
3040+ auto source_str = std::string (source);
3041+ if (source_str.find (" <|END_THINKING|>" ) == std::string::npos) {
3042+ reasoning_start_token = LLAMA_TOKEN_NULL ;
3043+ reasoning_end_token = LLAMA_TOKEN_NULL ;
3044+ }
30303045 }
30313046 support_reasoning = params.llm_params .reasoning_budget != 0 &&
30323047 reasoning_start_token != LLAMA_TOKEN_NULL &&
@@ -3371,6 +3386,7 @@ struct httpserver {
33713386
33723387 // tool calls
33733388 bool support_tool_calls = false ;
3389+ bool support_parallel_tool_calls = true ;
33743390 // non-jinja tool calls
33753391 llama_tokens tool_call_start_tokens = {};
33763392 std::vector<std::string> tool_call_start_words = {};
@@ -5344,45 +5360,46 @@ struct httpserver {
53445360 /* LLAMA */
53455361 else {
53465362 metadata_json = {
5347- { " vocab_type" , llama_vocab_type (llm_vocab) },
5348- { " n_vocab" , llama_vocab_n_tokens (llm_vocab) },
5349- { " n_ctx_train" , llama_model_n_ctx_train (llm_model) },
5350- { " n_embd" , llama_model_n_embd (llm_model) },
5351- { " n_params" , llama_model_n_params (llm_model) },
5352- { " size" , llama_model_size (llm_model) },
5353- { " n_ctx" , llm_ctx_size },
5354- { " n_slot" , 1 },
5355- { " n_slot_ctx" , llm_ctx_size },
5356- { " ctx_shift" , shift_context },
5357- { " prompt_cache" , cache_prompt },
5358- { " seed" , int32_t (params.llm_params .sampling .seed ) },
5359- { " temperature" , params.llm_params .sampling .temp },
5360- { " dynatemp_range" , params.llm_params .sampling .dynatemp_range },
5361- { " dynatemp_exponent" , params.llm_params .sampling .dynatemp_exponent },
5362- { " top_k" , params.llm_params .sampling .top_k },
5363- { " top_p" , params.llm_params .sampling .top_p },
5364- { " min_p" , params.llm_params .sampling .min_p },
5365- { " top_n_sigma" , params.llm_params .sampling .top_n_sigma },
5366- { " xtc_probability" , params.llm_params .sampling .xtc_probability },
5367- { " xtc_threshold" , params.llm_params .sampling .xtc_threshold },
5368- { " typical_p" , params.llm_params .sampling .typ_p },
5369- { " repeat_last_n" , params.llm_params .sampling .penalty_last_n },
5370- { " repeat_penalty" , params.llm_params .sampling .penalty_repeat },
5371- { " presence_penalty" , params.llm_params .sampling .penalty_present },
5372- { " frequency_penalty" , params.llm_params .sampling .penalty_freq },
5373- { " dry_multiplier" , params.llm_params .sampling .dry_multiplier },
5374- { " dry_base" , params.llm_params .sampling .dry_base },
5375- { " dry_allowed_length" , params.llm_params .sampling .dry_allowed_length },
5376- { " dry_penalty_last_n" , params.llm_params .sampling .dry_penalty_last_n },
5377- { " dry_sequence_breakers" , params.llm_params .sampling .dry_sequence_breakers },
5378- { " mirostat" , params.llm_params .sampling .mirostat },
5379- { " mirostat_tau" , params.llm_params .sampling .mirostat_tau },
5380- { " mirostat_eta" , params.llm_params .sampling .mirostat_eta },
5381- { " support_vision" , llm_ctx_clip_v != nullptr },
5382- { " support_audio" , llm_ctx_clip_a != nullptr },
5383- { " support_speculative" , llm_ctx_draft != nullptr },
5384- { " support_tool_calls" , support_tool_calls },
5385- { " support_reasoning" , support_reasoning },
5363+ { " vocab_type" , llama_vocab_type (llm_vocab) },
5364+ { " n_vocab" , llama_vocab_n_tokens (llm_vocab) },
5365+ { " n_ctx_train" , llama_model_n_ctx_train (llm_model) },
5366+ { " n_embd" , llama_model_n_embd (llm_model) },
5367+ { " n_params" , llama_model_n_params (llm_model) },
5368+ { " size" , llama_model_size (llm_model) },
5369+ { " n_ctx" , llm_ctx_size },
5370+ { " n_slot" , 1 },
5371+ { " n_slot_ctx" , llm_ctx_size },
5372+ { " ctx_shift" , shift_context },
5373+ { " prompt_cache" , cache_prompt },
5374+ { " seed" , int32_t (params.llm_params .sampling .seed ) },
5375+ { " temperature" , params.llm_params .sampling .temp },
5376+ { " dynatemp_range" , params.llm_params .sampling .dynatemp_range },
5377+ { " dynatemp_exponent" , params.llm_params .sampling .dynatemp_exponent },
5378+ { " top_k" , params.llm_params .sampling .top_k },
5379+ { " top_p" , params.llm_params .sampling .top_p },
5380+ { " min_p" , params.llm_params .sampling .min_p },
5381+ { " top_n_sigma" , params.llm_params .sampling .top_n_sigma },
5382+ { " xtc_probability" , params.llm_params .sampling .xtc_probability },
5383+ { " xtc_threshold" , params.llm_params .sampling .xtc_threshold },
5384+ { " typical_p" , params.llm_params .sampling .typ_p },
5385+ { " repeat_last_n" , params.llm_params .sampling .penalty_last_n },
5386+ { " repeat_penalty" , params.llm_params .sampling .penalty_repeat },
5387+ { " presence_penalty" , params.llm_params .sampling .penalty_present },
5388+ { " frequency_penalty" , params.llm_params .sampling .penalty_freq },
5389+ { " dry_multiplier" , params.llm_params .sampling .dry_multiplier },
5390+ { " dry_base" , params.llm_params .sampling .dry_base },
5391+ { " dry_allowed_length" , params.llm_params .sampling .dry_allowed_length },
5392+ { " dry_penalty_last_n" , params.llm_params .sampling .dry_penalty_last_n },
5393+ { " dry_sequence_breakers" , params.llm_params .sampling .dry_sequence_breakers },
5394+ { " mirostat" , params.llm_params .sampling .mirostat },
5395+ { " mirostat_tau" , params.llm_params .sampling .mirostat_tau },
5396+ { " mirostat_eta" , params.llm_params .sampling .mirostat_eta },
5397+ { " support_vision" , llm_ctx_clip_v != nullptr },
5398+ { " support_audio" , llm_ctx_clip_a != nullptr },
5399+ { " support_speculative" , llm_ctx_draft != nullptr },
5400+ { " support_tool_calls" , support_tool_calls },
5401+ { " support_parallel_tool_calls" , support_parallel_tool_calls },
5402+ { " support_reasoning" , support_reasoning },
53865403 };
53875404 }
53885405
@@ -5952,7 +5969,8 @@ struct httpserver {
59525969 }
59535970 }
59545971
5955- bool tool_call_stop_fast = !req->parallel_tool_calls || req->tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED ;
5972+ bool tool_call_stop_fast = !(support_parallel_tool_calls && req->parallel_tool_calls ) ||
5973+ req->tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED || req->tools .size () <= 1 ;
59565974
59575975 // NB(thxCode): disable reasoning process if we need to generate tool calls in jinja.
59585976 bool reasoning_finished = !support_reasoning || (params.llm_params .use_jinja &&
0 commit comments