@@ -3520,6 +3520,95 @@ def __call__(self, **kwargs):
35203520 return super ().__call__ (** kwargs )
35213521
35223522
3523+ class Gemma3ChatHandler (Llava15ChatHandler ):
3524+ # Chat Format:
3525+ # '<bos><start_of_turn>user\n{system_prompt}\n\n{prompt}<end_of_turn>\n<start_of_turn>model\n'
3526+
3527+ DEFAULT_SYSTEM_MESSAGE = None
3528+
3529+ CHAT_FORMAT = (
3530+ "{{ '<bos>' }}"
3531+ "{%- if messages[0]['role'] == 'system' -%}"
3532+ "{%- if messages[0]['content'] is string -%}"
3533+ "{%- set first_user_prefix = messages[0]['content'] + '\n \n ' -%}"
3534+ "{%- else -%}"
3535+ "{%- set first_user_prefix = messages[0]['content'][0]['text'] + '\n \n ' -%}"
3536+ "{%- endif -%}"
3537+ "{%- set loop_messages = messages[1:] -%}"
3538+ "{%- else -%}"
3539+ "{%- set first_user_prefix = \" \" -%}"
3540+ "{%- set loop_messages = messages -%}"
3541+ "{%- endif -%}"
3542+ "{%- for message in loop_messages -%}"
3543+ "{%- if (message['role'] == 'user') != (loop.index0 % 2 == 0) -%}"
3544+ "{{ raise_exception(\" Conversation roles must alternate user/assistant/user/assistant/...\" ) }}"
3545+ "{%- endif -%}"
3546+ "{%- if (message['role'] == 'assistant') -%}"
3547+ "{%- set role = \" model\" -%}"
3548+ "{%- else -%}"
3549+ "{%- set role = message['role'] -%}"
3550+ "{%- endif -%}"
3551+ "{{ '<start_of_turn>' + role + '\n ' + (first_user_prefix if loop.first else \" \" ) }}"
3552+ "{%- if message['content'] is string -%}"
3553+ "{{ message['content'] | trim }}"
3554+ "{%- elif message['content'] is iterable -%}"
3555+ "{%- for item in message['content'] -%}"
3556+ "{%- if item['type'] == 'image' -%}"
3557+ "{{ '<start_of_image>' }}"
3558+ "{%- elif item['type'] == 'text' -%}"
3559+ "{{ item['text'] | trim }}"
3560+ "{%- endif -%}"
3561+ "{%- endfor -%}"
3562+ "{%- else -%}"
3563+ "{{ raise_exception(\" Invalid content type\" ) }}"
3564+ "{%- endif -%}"
3565+ "{{ '<end_of_turn>\n ' }}"
3566+ "{%- endfor -%}"
3567+ "{%- if add_generation_prompt -%}"
3568+ "{{ '<start_of_turn>model\n ' }}"
3569+ "{%- endif -%}"
3570+ )
3571+
3572+ @staticmethod
3573+ def split_text_on_image_urls (text : str , image_urls : List [str ]):
3574+ split_text : List [Tuple [Literal ["text" , "image_url" ], str ]] = []
3575+ copied_urls = image_urls [:]
3576+ remaining = text
3577+ image_placeholder = "<start_of_image>"
3578+
3579+ while remaining :
3580+ # Find placeholder
3581+ pos = remaining .find (image_placeholder )
3582+ if pos != - 1 :
3583+ assert len (copied_urls ) > 0
3584+ if pos > 0 :
3585+ split_text += [("text" , remaining [:pos ])]
3586+ split_text += [("text" , "\n \n <start_of_image>" )]
3587+ split_text += [("image_url" , copied_urls .pop (0 ))]
3588+ split_text += [("text" , "<end_of_image>\n \n " )]
3589+ remaining = remaining [pos + len (image_placeholder ):]
3590+ else :
3591+ assert len (copied_urls ) == 0
3592+ split_text .append (("text" , remaining ))
3593+ remaining = ""
3594+ return split_text
3595+
3596+ @staticmethod
3597+ def get_image_urls (messages : List [llama_types .ChatCompletionRequestMessage ]):
3598+ image_urls : List [str ] = []
3599+ for message in messages :
3600+ if message ["role" ] == "user" :
3601+ if message .get ("content" ) is None :
3602+ continue
3603+ for content in message ["content" ]:
3604+ if isinstance (content , dict ) and content .get ("type" ) == "image" :
3605+ if isinstance (content .get ("image" ), dict ) and isinstance (content ["image" ].get ("url" ), str ):
3606+ image_urls .append (content ["image" ]["url" ])
3607+ elif isinstance (content .get ("url" ), str ):
3608+ image_urls .append (content ["url" ])
3609+ return image_urls
3610+
3611+
35233612def _accumulate_chunks (
35243613 chunks_iterator : Iterator [llama_types .CreateCompletionStreamResponse ],
35253614 chunks_list : List [llama_types .CreateCompletionStreamResponse ],
0 commit comments