@@ -3089,11 +3089,12 @@ class MTMDChatHandler:
30893089
30903090 def __init__ (
30913091 self ,
3092- clip_model_path : str ,
3092+ mmproj_path : str ,
30933093 verbose : bool = True ,
30943094 use_gpu : bool = True ,
30953095 image_min_tokens : int = - 1 ,
30963096 image_max_tokens : int = - 1 ,
3097+ chat_template_override : Optional [str ] = None ,
30973098 batch_max_tokens : int = 1024 ,
30983099 ** kwargs
30993100 ):
@@ -3106,7 +3107,7 @@ def __init__(
31063107 f"If you are passing model-specific parameters, ensure they are supported by { self .log_prefix } ."
31073108 )
31083109
3109- self .clip_model_path = clip_model_path
3110+ self .mmproj_path = mmproj_path
31103111 self .image_min_tokens = image_min_tokens
31113112 self .image_max_tokens = image_max_tokens
31123113 self .batch_max_tokens = batch_max_tokens
@@ -3122,16 +3123,25 @@ def __init__(
31223123 self .is_support_audio = False
31233124 self .is_support_video = False
31243125
3125- if not os .path .exists (clip_model_path ):
3126- raise ValueError (f"{ self .log_prefix } (__init__): Clip model path does not exist: { clip_model_path } " )
3126+ if not os .path .exists (mmproj_path ):
3127+ raise ValueError (f"{ self .log_prefix } (__init__): Clip model path does not exist: { mmproj_path } " )
31273128
31283129 # Pre-compile Jinja template
3129- self .chat_template = ImmutableSandboxedEnvironment (
3130- trim_blocks = True ,
3131- lstrip_blocks = True ,
3132- ).from_string (self .CHAT_FORMAT )
3130+ if (not hasattr (self , "chat_format" ) or self .chat_format is None ) and chat_template_override is None :
3131+ self .chat_format = self .CHAT_FORMAT
3132+ elif chat_template_override is not None :
3133+ self .chat_format = chat_template_override
3134+
3135+ self ._chat_format_parser_tags = []
3136+ self .change_chat_template (self .chat_format )
31333137
31343138 self ._exit_stack = ExitStack ()
3139+
3140+ def change_chat_template (self , new_template : str ):
3141+ self .chat_template = ImmutableSandboxedEnvironment (
3142+ trim_blocks = True ,
3143+ lstrip_blocks = True
3144+ ).from_string (new_template )
31353145
31363146 def _init_mtmd_context (self , llama_model : llama_core .Llama ):
31373147 """Initialize mtmd context with the llama model."""
@@ -3165,13 +3175,13 @@ def _init_mtmd_context(self, llama_model: llama_core.Llama):
31653175
31663176 # Initialize mtmd context
31673177 self .mtmd_ctx = self ._mtmd_cpp .mtmd_init_from_file (
3168- self .clip_model_path .encode (),
3178+ self .mmproj_path .encode (),
31693179 llama_model .model ,
31703180 self .mctx_params
31713181 )
31723182
31733183 if self .mtmd_ctx is None :
3174- raise ValueError (f"{ self .log_prefix } (_init_mtmd_context): Failed to load mtmd context from: { self .clip_model_path } " )
3184+ raise ValueError (f"{ self .log_prefix } (_init_mtmd_context): Failed to load mtmd context from: { self .mmproj_path } " )
31753185
31763186 # Check if vision is supported
31773187 self .is_support_vision = self ._mtmd_cpp .mtmd_support_vision (self .mtmd_ctx )
@@ -3241,13 +3251,13 @@ def _get_media_items(self, messages: List[llama_types.ChatCompletionRequestMessa
32413251 media_items .append ({"url" : url , "type" : "image" })
32423252
32433253 # 2. Audio Processing
3244- elif content_type in ["audio_url" , "input_audio" ]:
3254+ elif content_type in ["audio" , " audio_url" , "input_audio" ]:
32453255 if not self .is_support_audio :
32463256 raise ValueError (f"{ self .log_prefix } : This mmproj model instance does not support audio inputs." )
32473257
32483258 # Case A: Handle custom/forward-compatible audio_url format
3249- if content_type == "audio_url" :
3250- audio_url = content ["audio_url" ]
3259+ if content_type == "audio_url" or content_type == "audio" :
3260+ audio_url = content [content_type ]
32513261 url = audio_url if isinstance (audio_url , str ) else audio_url ["url" ]
32523262 media_items .append ({"url" : url , "type" : "audio" })
32533263 # Case B: Handle OpenAI standard input_audio format
@@ -3407,6 +3417,13 @@ def _process_mtmd_prompt(
34073417 tool_choice = tool_choice ,
34083418 ** getattr (self , 'extra_template_arguments' , {})
34093419 )
3420+
3421+ for tag in self ._chat_format_parser_tags :
3422+ if tag not in text :
3423+ continue
3424+
3425+ text = text .replace (tag , media_marker )
3426+
34103427 # Replace image_url by media_marker in text
34113428 for item in media_items :
34123429 text = text .replace (item ["url" ], media_marker )
@@ -4142,10 +4159,46 @@ def from_pretrained(
41424159 model_path = os .path .join (local_dir , filename )
41434160
41444161 return cls (
4145- clip_model_path = model_path ,
4162+ mmproj_path = model_path ,
41464163 ** kwargs ,
41474164 )
41484165
4166+ class GenericMTMDChatHandler (MTMDChatHandler ):
4167+ KNOWN_MEDIA_TAGS = [
4168+ "<|image_pad|>" ,
4169+ "<|audio_pad|>" ,
4170+ "<|video_pad|>" ,
4171+ "<|image|>" ,
4172+ "<|audio|>" ,
4173+ "<|video|>" ,
4174+ "[IMG]"
4175+ ]
4176+
4177+ def __init__ (
4178+ self ,
4179+ chat_format : str ,
4180+ mmproj_path : str ,
4181+ verbose : bool = True ,
4182+ ** kwargs
4183+ ) -> None :
4184+ self .chat_format = chat_format
4185+
4186+ if verbose :
4187+ print (f"Got chat template from model:\n ```jinja\n { self .chat_format } \n ```" , flush = True )
4188+
4189+ if self .chat_format is None :
4190+ raise ValueError ("Failed to get model chat template automatically." )
4191+
4192+ super ().__init__ (mmproj_path = mmproj_path , verbose = verbose , ** kwargs )
4193+
4194+ def __call__ (self , ** kwargs ):
4195+ self ._chat_format_parser_tags = [tag for tag in self .KNOWN_MEDIA_TAGS if tag in self .chat_format ]
4196+
4197+ if self .verbose :
4198+ print (f"{ self .log_prefix } - Start processing" )
4199+
4200+ # Use parent implementation
4201+ return super ().__call__ (** kwargs )
41494202
41504203class Llava15ChatHandler (MTMDChatHandler ):
41514204 CHAT_FORMAT = (
0 commit comments