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
30 changes: 21 additions & 9 deletions components/src/dynamo/frontend/prepost.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,19 @@ def _prepare_request(
)
else None
)
chat_template_kwargs = dict(request_for_sampling.chat_template_kwargs or {})
chat_template_kwargs["reasoning_effort"] = request_for_sampling.reasoning_effort
# serde's `alias` is deserialize-only, so pythonize emits the Rust field
# name `chat_template_args`; read it too or client kwargs are dropped.
raw_template_args = (
request.get("chat_template_args") if isinstance(request, dict) else None
)
chat_template_kwargs = dict(
request_for_sampling.chat_template_kwargs or raw_template_args or {}
Comment thread
KrishnanPrash marked this conversation as resolved.
)
# Don't let an absent top-level field clobber a nested reasoning_effort.
if request_for_sampling.reasoning_effort is not None:
chat_template_kwargs["reasoning_effort"] = request_for_sampling.reasoning_effort
else:
chat_template_kwargs.setdefault("reasoning_effort", None)

# Mistral warns that tokenize=False is unsafe for chat templates.
is_mistral_tokenizer = (
Expand All @@ -154,14 +165,15 @@ def _prepare_request(
chat_params = ChatParams(
chat_template=request_for_sampling.chat_template,
chat_template_content_format="auto",
chat_template_kwargs=dict(
add_generation_prompt=request_for_sampling.add_generation_prompt,
continue_final_message=request_for_sampling.continue_final_message,
tools=tool_dicts,
documents=request_for_sampling.documents,
tokenize=tokenize_in_template,
# Renderer-managed keys last so a nested duplicate can't raise TypeError.
chat_template_kwargs={
**chat_template_kwargs,
),
"add_generation_prompt": request_for_sampling.add_generation_prompt,
"continue_final_message": request_for_sampling.continue_final_message,
"tools": tool_dicts,
"documents": request_for_sampling.documents,
"tokenize": tokenize_in_template,
},
)

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,88 @@ def test_no_tools_in_request(self, tokenizer):
), "No tools in request should produce None tools in template"


class TestChatTemplateArgsPassthrough:
"""Per-request chat template kwargs must survive into the rendered template.

pythonize serializes the request field under its Rust name
``chat_template_args`` (serde ``alias`` is deserialize-only), so the vLLM
processor must read that key, not only vLLM's native ``chat_template_kwargs``.
"""

def test_chat_template_args_reaches_template(self, tokenizer):
"""Kwargs keyed as chat_template_args (the pythonize'd key) reach the template."""
_, _, _, _, chat_params = _prepare_request(
{
"model": MODEL,
"messages": [{"role": "user", "content": "Hello"}],
"chat_template_args": {"enable_thinking": False},
},
tokenizer=tokenizer,
tool_parser_class=None,
)
assert (
chat_params.chat_template_kwargs.get("enable_thinking") is False
), "chat_template_args must be forwarded to the chat template"

def test_chat_template_kwargs_native_key_still_works(self, tokenizer):
"""The vLLM-native chat_template_kwargs key keeps working."""
_, _, _, _, chat_params = _prepare_request(
{
"model": MODEL,
"messages": [{"role": "user", "content": "Hello"}],
"chat_template_kwargs": {"enable_thinking": False},
},
tokenizer=tokenizer,
tool_parser_class=None,
)
assert (
chat_params.chat_template_kwargs.get("enable_thinking") is False
), "native chat_template_kwargs must be forwarded to the chat template"

def test_nested_reasoning_effort_is_not_clobbered(self, tokenizer):
"""A reasoning_effort nested in template kwargs survives the top-level default."""
_, _, _, _, chat_params = _prepare_request(
{
"model": MODEL,
"messages": [{"role": "user", "content": "Hello"}],
"chat_template_args": {"reasoning_effort": "high"},
},
tokenizer=tokenizer,
tool_parser_class=None,
)
assert (
chat_params.chat_template_kwargs.get("reasoning_effort") == "high"
), "nested reasoning_effort must not be overwritten by an absent top-level field"

def test_top_level_reasoning_effort_wins_over_nested(self, tokenizer):
"""An explicit top-level reasoning_effort overrides a nested one."""
_, _, _, _, chat_params = _prepare_request(
{
"model": MODEL,
"messages": [{"role": "user", "content": "Hello"}],
"reasoning_effort": "low",
"chat_template_args": {"reasoning_effort": "high"},
},
tokenizer=tokenizer,
tool_parser_class=None,
)
assert chat_params.chat_template_kwargs.get("reasoning_effort") == "low"

def test_reserved_render_key_in_template_args_does_not_crash(self, tokenizer):
"""A renderer-reserved key nested in template kwargs must not raise TypeError."""
_, _, _, _, chat_params = _prepare_request(
{
"model": MODEL,
"messages": [{"role": "user", "content": "Hello"}],
"chat_template_args": {"documents": [{"text": "doc"}]},
},
tokenizer=tokenizer,
tool_parser_class=None,
)
# Renderer-managed value wins over the client's nested key (no crash either).
assert chat_params.chat_template_kwargs["documents"] is None


class TestMultimodalFeatureMetadata:
def _feature(
self, modality, mm_hash, offset, length, data=_DEFAULT_MM_DATA, is_embed=None
Expand Down
Loading