Skip to content

Commit 1f8341e

Browse files
committed
feat: add PaddleOCR-VL-1.5 multimodal chat handler PaddleOCRChatHandler
1 parent 5456d4e commit 1f8341e

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ Below are the supported multi-modal models and their respective chat handlers (P
519519
| [glm4.6v](https://huggingface.co/unsloth/GLM-4.6V-Flash-GGUF) | `GLM46VChatHandler` | `glm4.6v` |
520520
| [granite-docling](https://huggingface.co/ibm-granite/granite-docling-258M-GGUF) | `GraniteDoclingChatHandler` | `granite-docling` |
521521
| [lfm2-vl](https://huggingface.co/LiquidAI/LFM2-VL-3B-GGUF) | `LFM2VLChatHandler` | `lfm2-vl` |
522+
| [paddleocr-vl-1.5](https://huggingface.co/JamePeng2023/PaddleOCR-VL-1.5-GGUF) | `PaddleOCRChatHandler` | `paddleocr` |
522523
| [qwen2.5-vl](https://huggingface.co/unsloth/Qwen2.5-VL-3B-Instruct-GGUF) | `Qwen25VLChatHandler` | `qwen2.5-vl` |
523524
| [qwen3-vl](https://huggingface.co/unsloth/Qwen3-VL-8B-Thinking-GGUF) | `Qwen3VLChatHandler` | `qwen3-vl` |
524525

llama_cpp/llama_chat_format.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4227,6 +4227,126 @@ def __call__(self, **kwargs):
42274227
return super().__call__(**kwargs)
42284228

42294229

4230+
class PaddleOCRChatHandler(Llava15ChatHandler):
4231+
"""
4232+
Handler for PaddleOCR 1.5 multimodal models.
4233+
"""
4234+
4235+
PADDLEOCR_CLS_TOKEN = "<|begin_of_sentence|>"
4236+
PADDLEOCR_BOS_TOKEN = "<s>"
4237+
PADDLEOCR_EOS_TOKEN = "</s>"
4238+
PADDLEOCR_SEP_TOKEN = "<|end_of_sentence|>"
4239+
PADDLEOCR_IMAGE_BOS_TOKEN = "<|IMAGE_START|>"
4240+
PADDLEOCR_IMAGE_EOS_TOKEN = "<|IMAGE_END|>"
4241+
4242+
CHAT_FORMAT = (
4243+
"{%- if not add_generation_prompt is defined -%}{%- set add_generation_prompt = true -%}{%- endif -%}"
4244+
"{%- if not cls_token is defined -%}{%- set cls_token = '" + PADDLEOCR_CLS_TOKEN + "' -%}{%- endif -%}"
4245+
"{%- if not eos_token is defined -%}{%- set eos_token = '" + PADDLEOCR_EOS_TOKEN + "' -%}{%- endif -%}"
4246+
4247+
"{{- cls_token -}}"
4248+
"{%- for message in messages -%}"
4249+
"{%- if message['role'] == 'user' -%}"
4250+
"{{- 'User: ' -}}"
4251+
4252+
# Robust parsing: Check if content is string or list
4253+
"{%- if message['content'] is string -%}"
4254+
"{{- message['content'] -}}"
4255+
"{%- else -%}"
4256+
# Pass 1: Render all images first
4257+
"{%- for content in message['content'] -%}"
4258+
"{%- if content['type'] == 'image_url' and 'image_url' in content -%}"
4259+
"{{- '<|IMAGE_START|>' -}}"
4260+
"{%- if content.image_url is string -%}"
4261+
"{{- content.image_url -}}"
4262+
"{%- else -%}"
4263+
"{{- content.image_url.url -}}"
4264+
"{%- endif -%}"
4265+
"{{- '<|IMAGE_END|>' -}}"
4266+
"{%- endif -%}"
4267+
"{%- endfor -%}"
4268+
4269+
# Pass 2: Render all text second
4270+
"{%- for content in message['content'] -%}"
4271+
"{%- if content['type'] == 'text' -%}"
4272+
"{{- content['text'] -}}"
4273+
"{%- endif -%}"
4274+
"{%- endfor -%}"
4275+
"{%- endif -%}"
4276+
"{{- '\\n' -}}"
4277+
4278+
"{%- elif message['role'] == 'assistant' -%}"
4279+
"{{- 'Assistant:\\n' -}}"
4280+
"{%- if message['content'] is string -%}"
4281+
"{{- message['content'] -}}"
4282+
"{%- else -%}"
4283+
"{%- for content in message['content'] -%}"
4284+
"{%- if content['type'] == 'text' -%}"
4285+
"{{- content['text'] -}}"
4286+
"{%- endif -%}"
4287+
"{%- endfor -%}"
4288+
"{%- endif -%}"
4289+
"{{- eos_token -}}"
4290+
4291+
"{%- elif message['role'] == 'system' -%}"
4292+
"{%- if message['content'] is string -%}"
4293+
"{{- message['content'] + '\\n' -}}"
4294+
"{%- else -%}"
4295+
"{%- for content in message['content'] -%}"
4296+
"{%- if content['type'] == 'text' -%}"
4297+
"{{- content['text'] + '\\n' -}}"
4298+
"{%- endif -%}"
4299+
"{%- endfor -%}"
4300+
"{%- endif -%}"
4301+
"{%- endif -%}"
4302+
"{%- endfor -%}"
4303+
4304+
"{%- if add_generation_prompt -%}"
4305+
"{{- 'Assistant:\\n' -}}"
4306+
"{%- endif -%}"
4307+
)
4308+
4309+
def __init__(
4310+
self,
4311+
image_min_tokens: int = -1,
4312+
image_max_tokens: int = -1,
4313+
**kwargs
4314+
):
4315+
self.image_min_tokens = image_min_tokens
4316+
self.image_max_tokens = image_max_tokens
4317+
super().__init__(
4318+
image_min_tokens=self.image_min_tokens,
4319+
image_max_tokens=self.image_max_tokens,
4320+
**kwargs
4321+
)
4322+
4323+
def __call__(self, **kwargs):
4324+
# Set the specific stop token defined in the PaddleOCR template
4325+
kwargs['stop'] = [self.PADDLEOCR_EOS_TOKEN]
4326+
4327+
llama = kwargs['llama']
4328+
llama.reset()
4329+
llama._ctx.memory_clear(True)
4330+
llama.n_tokens = 0
4331+
4332+
if hasattr(llama, 'input_ids'):
4333+
llama.input_ids.fill(0)
4334+
4335+
if hasattr(self, '_last_image_embed'):
4336+
self._last_image_embed = None
4337+
self._last_image_hash = None
4338+
4339+
if self.verbose:
4340+
messages = kwargs.get('messages', [])
4341+
try:
4342+
image_count = len(self.get_image_urls(messages))
4343+
print(f"PaddleOCRChatHandler - Cleared state, Processing {image_count} images", file=sys.stderr)
4344+
except Exception:
4345+
print(f"PaddleOCRChatHandler - Cleared state", file=sys.stderr)
4346+
4347+
return super().__call__(**kwargs)
4348+
4349+
42304350
class Qwen25VLChatHandler(Llava15ChatHandler):
42314351
CHAT_FORMAT = (
42324352
"{% set image_count = namespace(value=0) %}"

0 commit comments

Comments
 (0)