Repository: ggml-org/llama.cpp
Pull request: Granite Docling stopping

## Description

This is a follow up to #16110 to fix the issue of the model not terminating correctly. It turned out to be a few lingering bugs in the tokenization:

1. A duplicate `<fake_token_around_image>` before the first image slice
2. Only a single `\n` before the start of the global image instead of a double newline
3. The granite template implementation in `llama-chat.cpp` had an errant `\n` at the end when adding the assistant generation prompt

I'm pretty sure (3) was the main cause of the problem, but all three are fixed here. For reference, here are the chat templates for the various models that use the granite chat template:

* `granite-docling-258M`: https://huggingface.co/ibm-granite/granite-docling-258M?chat_template=default#L20
* `granite-3.3-8b-instruct`: https://huggingface.co/ibm-granite/granite-3.3-8b-instruct?chat_template=default#L60
* `granite-3.2-8b-instruct`: https://huggingface.co/ibm-granite/granite-3.2-8b-instruct?chat_template=default#L65
* `granite-3.1-8b-instruct`: https://huggingface.co/ibm-granite/granite-3.1-8b-instruct?chat_template=default#L62
* `granite-3.0-8b-instruct`: https://huggingface.co/ibm-granite/granite-3.0-8b-instruct?chat_template=default#L33

Changed files:
filename: src/llama-chat.cpp
patch: @@ -590,7 +590,7 @@ int32_t llm_chat_apply_template(
             ss << message->content << "
\n";
         }
         if (add_ass) {
-            ss << "
assistant
\n";
+            ss << "
assistant
";
         }
     } else if (tmpl == LLM_CHAT_TEMPLATE_GIGACHAT) {
         // GigaChat template

filename: tests/test-chat-template.cpp
patch: @@ -214,7 +214,7 @@ int main(void) {
         {
             /* .name= */ "ibm-granite/granite-3.0-8b-instruct",
             /* .template_str= */ "{%- if tools %}\n    {{- '
available_tools
\n' }}\n    {%- for tool in tools %}\n    {{- tool | tojson(indent=4) }}\n    {%- if not loop.last %}\n        {{- '\n\n' }}\n    {%- endif %}\n    {%- endfor %}\n    {{- '
\n' }}\n{%- endif %}\n{%- for message in messages %}\n    {%- if message['role'] == 'system' %}\n    {{- '
system
' + message['content'] + '
\n' }}\n    {%- elif message['role'] == 'user' %}\n    {{- '
user
' + message['content'] + '
\n' }}\n    {%- elif message['role'] == 'assistant' %}\n    {{- '
assistant
'  + message['content'] + '
\n' }}\n    {%- elif message['role'] == 'assistant_tool_call' %}\n    {{- '
assistant

' + message['content'] + '
\n' }}\n    {%- elif message['role'] == 'tool_response' %}\n    {{- '
tool_response
' + message['content'] + '
\n' }}\n    {%- endif %}\n    {%- if loop.last and add_generation_prompt %}\n    {{- '
assistant
' }}\n    {%- endif %}\n{%- endfor %}",
-            /* .expected_output= */       "
system
You are a helpful assistant
\n
user
Hello
\n
assistant
Hi there
\n
user
Who are you
\n
assistant
   I am an assistant   
\n
user
Another question
\n
assistant
\n",
+            /* .expected_output= */       "
system
You are a helpful assistant
\n
user
Hello
\n
assistant
Hi there
\n
user
Who are you
\n
assistant
   I am an assistant   
\n
user
Another question
\n
assistant
",
             /* .expected_output_jinja= */ "
system
You are a helpful assistant
\n
user
Hello
\n
assistant
Hi there
\n
user
Who are you
\n
assistant
   I am an assistant   
\n
user
Another question
\n
assistant
",
         },
         {

filename: tools/mtmd/mtmd.cpp
patch: @@ -249,10 +249,9 @@ struct mtmd_context {
         } else if (proj == PROJECTOR_TYPE_IDEFICS3) {
             // https://github.com/huggingface/transformers/blob/a42ba80fa520c784c8f11a973ca9034e5f859b79/src/transformers/models/idefics3/processing_idefics3.py#L192-L215
             slice_tmpl         = MTMD_SLICE_TMPL_IDEFICS3;
-            tok_ov_img_start   = {lookup_token("\n"), lookup_token("<fake_token_around_image>"), lookup_token("<global-img>")};
+            tok_ov_img_start   = {lookup_token("\n\n"), lookup_token("<fake_token_around_image>"), lookup_token("<global-img>")};
             tok_ov_img_end     = {lookup_token("<fake_token_around_image>")};
             tok_row_end        = {lookup_token("\n")};
-            img_beg            = "<fake_token_around_image>";
             sli_img_start_tmpl = "<fake_token_around_image><row_%d_col_%d>";
 
         } else if (proj == PROJECTOR_TYPE_PIXTRAL) {