Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions tensorrt_llm/inputs/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,11 +644,15 @@ def resolve_hf_chat_template(
return templates[chat_template]
return chat_template

# 2. If tool is not provided, use the processor's default chat template
if not tools and processor and hasattr(processor, 'chat_template'):
return processor.chat_template

# 3. If tool is provided, use the tool
# 2. Without tools, prefer the processor's chat template when it has one.
# A processor without one (some keep the template on their tokenizer
# only) falls through to the tokenizer below.
if not tools and processor:
processor_template = getattr(processor, "chat_template", None)
if processor_template is not None:
return processor_template

# 3. Use the tokenizer's chat template, which accounts for the tools
try:
return tokenizer.get_chat_template(chat_template, tools=tools)
except Exception:
Expand Down
43 changes: 43 additions & 0 deletions tests/unittest/inputs/test_chat_template_dispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,49 @@ def get_chat_template(self, *_args, **_kwargs):
)


class TestProcessorWithoutChatTemplate:
"""A processor that has no chat template must not hide the tokenizer's."""

@staticmethod
def _tokenizer(template):
class JinjaTokenizer:
chat_template = template

def get_chat_template(self, chat_template=None, tools=None):
return self.chat_template

def apply_chat_template(self, **kwargs):
# Return the template it was asked to render, so the result names the winner.
return kwargs["chat_template"]

return JinjaTokenizer()

@staticmethod
def _render(tokenizer, processor):
return apply_chat_template(
model_type="test_string_model",
tokenizer=tokenizer,
processor=processor,
conversation=[ConversationMessage(role="user", content="hello", media=[])],
add_generation_prompt=True,
mm_placeholder_counts=[{}],
)

def test_falls_back_to_tokenizer_template(self):
class Processor:
chat_template = None

tokenizer = self._tokenizer("{{ 'tokenizer template' }}")
assert self._render(tokenizer, Processor()) == "{{ 'tokenizer template' }}"

def test_processor_template_is_still_preferred(self):
class Processor:
chat_template = "{{ 'processor template' }}"

tokenizer = self._tokenizer("{{ 'tokenizer template' }}")
assert self._render(tokenizer, Processor()) == "{{ 'processor template' }}"


class TestServingChatTemplateGather:
"""Cover the asyncio.gather integration in the serving chat-template paths."""

Expand Down
Loading