Bug
The official XiaomiMiMo/MiMo-V2.5 chat template expects structured OpenAI content, but vLLM auto-detects it as string.
The template passes message.content into a macro:
{% macro render_content(message_content) %}
{% for content in message_content %}
...
{% endfor %}
{% endmacro %}
{{ render_content(message.content) }}
vLLM currently detects only loops directly over message.content, or loops over a variable literally named content. It therefore misses the macro parameter message_content:
|
def _iter_nodes_assign_content_item(root: jinja2.nodes.Node): |
|
message_varnames = [ |
|
varname for _, varname in _iter_nodes_assign_messages_item(root) |
|
] |
|
|
|
# Search for {%- for content in message['content'] -%} loops |
|
# or {%- for item in content -%} loops |
|
for loop_ast in root.find_all(jinja2.nodes.For): |
|
loop_iter = loop_ast.iter |
|
loop_target = loop_ast.target |
|
|
|
for varname in message_varnames: |
|
if _is_var_or_elems_access(loop_iter, varname, "content"): |
|
assert isinstance(loop_target, jinja2.nodes.Name) |
|
yield loop_ast, loop_target.name |
|
break |
|
|
|
if isinstance(loop_iter, jinja2.nodes.Name) and loop_iter.name == "content": |
|
assert isinstance(loop_target, jinja2.nodes.Name) |
|
yield loop_ast, loop_target.name |
|
|
|
|
|
def _try_extract_ast(chat_template: str) -> jinja2.nodes.Template | None: |
|
import transformers.utils.chat_template_utils as hf_chat_utils |
|
|
|
try: |
|
jinja_compiled = hf_chat_utils._compile_jinja_template(chat_template) |
|
return jinja_compiled.environment.parse(chat_template) |
|
except Exception: |
|
logger.exception("Error when compiling Jinja template") |
|
return None |
|
|
|
|
|
@lru_cache(maxsize=32) |
|
def _detect_content_format( |
|
chat_template: str, |
|
*, |
|
default: ChatTemplateContentFormat, |
|
) -> ChatTemplateContentFormat: |
|
jinja_ast = _try_extract_ast(chat_template) |
|
if jinja_ast is None: |
|
return default |
|
|
|
try: |
|
next(_iter_nodes_assign_content_item(jinja_ast)) |
|
except StopIteration: |
|
return "string" |
|
except Exception: |
|
logger.exception("Error when parsing AST of Jinja template") |
|
return default |
|
else: |
|
return "openai" |
Impact
For OpenAI content ordered as:
text "A" -> image -> text "B"
vLLM's default string path (interleave_mm_strings=False) moves the image placeholder to the front:
<image-placeholder>\nA\nB
The official template should render:
Using the following option works around the issue:
--chat-template-content-format openai
Expected fix
Auto detection should follow message.content through macro arguments instead of depending on the macro parameter name. Please also add a MiMo-V2.5 regression test using text -> image -> text, verifying that auto matches explicit openai.
Related: #49042 fixed a similar case for the Rust frontend when the macro parameter is literally named content; MiMo uses message_content.
Tested against vLLM main at 4af586e185b028acf08312a4dee381b5998a137e and MiMo-V2.5 revision 63651580ca774f8504f676040460aed3e1244ac1.
Bug
The official
XiaomiMiMo/MiMo-V2.5chat template expects structured OpenAI content, but vLLM auto-detects it asstring.The template passes
message.contentinto a macro:vLLM currently detects only loops directly over
message.content, or loops over a variable literally namedcontent. It therefore misses the macro parametermessage_content:vllm/vllm/renderers/hf.py
Lines 398 to 449 in 4af586e
Impact
For OpenAI content ordered as:
vLLM's default
stringpath (interleave_mm_strings=False) moves the image placeholder to the front:The official template should render:
Using the following option works around the issue:
Expected fix
Auto detection should follow
message.contentthrough macro arguments instead of depending on the macro parameter name. Please also add a MiMo-V2.5 regression test usingtext -> image -> text, verifying thatautomatches explicitopenai.Related: #49042 fixed a similar case for the Rust frontend when the macro parameter is literally named
content; MiMo usesmessage_content.Tested against vLLM
mainat4af586e185b028acf08312a4dee381b5998a137eand MiMo-V2.5 revision63651580ca774f8504f676040460aed3e1244ac1.