@@ -3373,6 +3373,95 @@ class MiniCPMv26ChatHandler(Llava15ChatHandler):
3373
3373
)
3374
3374
3375
3375
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
+
3376
3465
@register_chat_completion_handler ("chatml-function-calling" )
3377
3466
def chatml_function_calling (
3378
3467
llama : llama .Llama ,
0 commit comments