@@ -3373,6 +3373,95 @@ class MiniCPMv26ChatHandler(Llava15ChatHandler):
33733373 )
33743374
33753375
3376+ class Gemma3ChatHandler (Llava15ChatHandler ):
3377+ # Chat Format:
3378+ # '<bos><start_of_turn>user\n{system_prompt}\n\n{prompt}<end_of_turn>\n<start_of_turn>model\n'
3379+
3380+ DEFAULT_SYSTEM_MESSAGE = None
3381+
3382+ CHAT_FORMAT = (
3383+ "{{ '<bos>' }}"
3384+ "{%- if messages[0]['role'] == 'system' -%}"
3385+ "{%- if messages[0]['content'] is string -%}"
3386+ "{%- set first_user_prefix = messages[0]['content'] + '\n \n ' -%}"
3387+ "{%- else -%}"
3388+ "{%- set first_user_prefix = messages[0]['content'][0]['text'] + '\n \n ' -%}"
3389+ "{%- endif -%}"
3390+ "{%- set loop_messages = messages[1:] -%}"
3391+ "{%- else -%}"
3392+ "{%- set first_user_prefix = \" \" -%}"
3393+ "{%- set loop_messages = messages -%}"
3394+ "{%- endif -%}"
3395+ "{%- for message in loop_messages -%}"
3396+ "{%- if (message['role'] == 'user') != (loop.index0 % 2 == 0) -%}"
3397+ "{{ raise_exception(\" Conversation roles must alternate user/assistant/user/assistant/...\" ) }}"
3398+ "{%- endif -%}"
3399+ "{%- if (message['role'] == 'assistant') -%}"
3400+ "{%- set role = \" model\" -%}"
3401+ "{%- else -%}"
3402+ "{%- set role = message['role'] -%}"
3403+ "{%- endif -%}"
3404+ "{{ '<start_of_turn>' + role + '\n ' + (first_user_prefix if loop.first else \" \" ) }}"
3405+ "{%- if message['content'] is string -%}"
3406+ "{{ message['content'] | trim }}"
3407+ "{%- elif message['content'] is iterable -%}"
3408+ "{%- for item in message['content'] -%}"
3409+ "{%- if item['type'] == 'image' -%}"
3410+ "{{ '<start_of_image>' }}"
3411+ "{%- elif item['type'] == 'text' -%}"
3412+ "{{ item['text'] | trim }}"
3413+ "{%- endif -%}"
3414+ "{%- endfor -%}"
3415+ "{%- else -%}"
3416+ "{{ raise_exception(\" Invalid content type\" ) }}"
3417+ "{%- endif -%}"
3418+ "{{ '<end_of_turn>\n ' }}"
3419+ "{%- endfor -%}"
3420+ "{%- if add_generation_prompt -%}"
3421+ "{{ '<start_of_turn>model\n ' }}"
3422+ "{%- endif -%}"
3423+ )
3424+
3425+ @staticmethod
3426+ def split_text_on_image_urls (text : str , image_urls : List [str ]):
3427+ split_text : List [Tuple [Literal ["text" , "image_url" ], str ]] = []
3428+ copied_urls = image_urls [:]
3429+ remaining = text
3430+ image_placeholder = "<start_of_image>"
3431+
3432+ while remaining :
3433+ # Find placeholder
3434+ pos = remaining .find (image_placeholder )
3435+ if pos != - 1 :
3436+ assert len (copied_urls ) > 0
3437+ if pos > 0 :
3438+ split_text += [("text" , remaining [:pos ])]
3439+ split_text += [("text" , "\n \n <start_of_image>" )]
3440+ split_text += [("image_url" , copied_urls .pop (0 ))]
3441+ split_text += [("text" , "<end_of_image>\n \n " )]
3442+ remaining = remaining [pos + len (image_placeholder ):]
3443+ else :
3444+ assert len (copied_urls ) == 0
3445+ split_text .append (("text" , remaining ))
3446+ remaining = ""
3447+ return split_text
3448+
3449+ @staticmethod
3450+ def get_image_urls (messages : List [llama_types .ChatCompletionRequestMessage ]):
3451+ image_urls : List [str ] = []
3452+ for message in messages :
3453+ if message ["role" ] == "user" :
3454+ if message .get ("content" ) is None :
3455+ continue
3456+ for content in message ["content" ]:
3457+ if isinstance (content , dict ) and content .get ("type" ) == "image" :
3458+ if isinstance (content .get ("image" ), dict ) and isinstance (content ["image" ].get ("url" ), str ):
3459+ image_urls .append (content ["image" ]["url" ])
3460+ elif isinstance (content .get ("url" ), str ):
3461+ image_urls .append (content ["url" ])
3462+ return image_urls
3463+
3464+
33763465@register_chat_completion_handler ("chatml-function-calling" )
33773466def chatml_function_calling (
33783467 llama : llama .Llama ,
0 commit comments