Support add_generation_prompt request parameter for chat completions (#3877)#4331
Support add_generation_prompt request parameter for chat completions (#3877)#4331exzile wants to merge 10 commits into
Conversation
91043be to
6bb8bd4
Compare
|
Looks like a rebase with conflict resolution is needed. |
The chat template was always rendered with add_generation_prompt=true, hardcoded in every servable. This exposes an optional add_generation_prompt field (bool, default true) on the /v3/chat/completions request, matching HF transformers and vLLM. When false, the trailing generation prompt is omitted, which is the building block for assistant prefill. - Parse add_generation_prompt in the request (openai_api_handler.cpp) and store it on the request struct (openai_request.hpp). - Honor it in all chat-template application sites: the MINJA path (LLM and VLM continuous batching, legacy) and the Python-Jinja path (read from the request body and pass into the template render). - Add tests covering default (generation prompt added) and false (generation prompt omitted, assistant message preserved). Verified end-to-end on the MINJA path with HuggingFaceTB/SmolLM2-360M-Instruct: default renders a trailing "<|im_start|>assistant", add_generation_prompt=false omits it. Note: true assistant prefill (continue_final_message - continuing from the final assistant message without closing it) is a separate control and is left as a follow-up. Implements openvinotoolkit#3877 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per review: add_generation_prompt is valid and functional for the Responses API - the Endpoint::RESPONSES path in servable.cpp reads request.addGenerationPrompt and passes it to apply_chat_template (and the Jinja path reads it from the request JSON), same as chat completions. Document it in the responses REST API parameters table. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8c850e7 to
de8b330
Compare
Per review feedback: add_generation_prompt is only ever consumed by chat template rendering, so route it through chat_template_kwargs instead of a dedicated InputRequest field. The MINJA path extracts it back out for genai's dedicated apply_chat_template argument; the Python-Jinja path pops it from the kwargs dict before splatting into render() to avoid a duplicate-keyword collision. Also fixes the Python-Jinja path never having wired add_generation_prompt through at all, and two llmtemplate_test.cpp tests that referenced a stale 4-arg applyChatTemplate signature and never compiled. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ature/assistant-prefill
mzegla
left a comment
There was a problem hiding this comment.
Please have a look at those few comments. Once we have it resolved I think we will be good to run it through the CI and merge.
…validate kwargs type - Remove the now-redundant top-level add_generation_prompt request field/parsing and the OpenAIRequest.addGenerationPrompt member: the parameter lives entirely in chat_template_kwargs now, and both template paths already default it to true when absent. - Fix MINJA path to use JsonContainer::as_bool() instead of get_bool(), which threw an unhandled ov::Exception (outside the surrounding try/catch) when add_generation_prompt was present but not a boolean; now returns a clean InvalidArgumentError, matching the Python-Jinja path's existing validation. - Trim a leftover comment line in input_request.hpp per review suggestion. - Update chat/responses REST docs to document add_generation_prompt as part of chat_template_kwargs instead of a separate top-level parameter. - Add unit tests for add_generation_prompt=false and the invalid-type rejection on the MINJA path.
…ature/assistant-prefill
|
@mzegla tended to all those comments in the latest commit. |
There was a problem hiding this comment.
Pull request overview
Adds support for controlling whether the chat template appends the trailing “generation prompt” marker via an add_generation_prompt boolean (default true). This aligns OVMS chat-template rendering behavior with common serving stacks and enables assistant prefill by omitting the final assistant-start marker when requested.
Changes:
- Plumbs
add_generation_promptthrough both chat-template rendering paths: Python-Jinja (PyJinjaTemplateProcessor) and MINJA/native tokenizer (ChatTemplateProcessor), including type validation. - Updates REST API docs to describe
add_generation_promptunderchat_template_kwargs. - Adds unit tests covering default behavior,
falsebehavior, and non-boolean rejection.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/llm/py_jinja_template_processor.cpp | Extracts add_generation_prompt from chat_template_kwargs and passes it explicitly to Jinja render (avoids duplicate-kwarg issues). |
| src/llm/io_processing/input_processors/chat_template_processor.cpp | Extracts/validates add_generation_prompt from kwargs for the native tokenizer path and removes it from the kwargs map to avoid double-supplying. |
| src/llm/apis/openai_api_handler.cpp | Clarifies that add_generation_prompt is consumed via chat_template_kwargs on the copied ChatHistory. |
| src/llm/io_processing/input_request.hpp | Notes that add_generation_prompt is carried via ChatHistory extra context. |
| src/test/llm/llmtemplate_test.cpp | Adds Python-Jinja path tests for default (true) and explicit false. |
| src/test/llm/input_processing/chat_template_processor_test.cpp | Adds native tokenizer path tests for false and non-boolean invalid argument handling. |
| docs/model_server_rest_api_chat.md | Documents add_generation_prompt under chat_template_kwargs for /v3/chat/completions. |
| docs/model_server_rest_api_responses.md | Documents add_generation_prompt under chat_template_kwargs for /v3/responses. |
Comments suppressed due to low confidence (1)
src/llm/io_processing/input_processors/chat_template_processor.cpp:102
kwargsis copied intooptKwargsviastd::make_optional(kwargs). Sincekwargsis not used afterwards, this introduces an avoidable JsonContainer copy on the request hot path (potentially expensive for large chat_template_kwargs). Movekwargsinto the optional instead.
const std::optional<ov::genai::JsonContainer> optKwargs =
kwargs.empty() ? std::nullopt : std::make_optional(kwargs);
|
@exzile Do you know about any existing app that uses this feature (add_generation_prompt set to false)? If so, it would be worth aligning to the API - it could point us if this param should be in chat_template_extra_kwargs field or level above. |
…tion_prompt parsing - Remove the comments in openai_api_handler.cpp and input_request.hpp that called out add_generation_prompt specifically among chat_template_kwargs entries (mzegla) -- the field-level docs already cover it. - Extract the add_generation_prompt lookup/validation in ChatTemplateProcessor::process into its own extractAddGenerationPrompt method (dkalinowski).
|
Checked the two serving frameworks that actually implement this (checked their current source, not just docs): vLLM ( add_generation_prompt: bool = Field(default=True, ...)
continue_final_message: bool = Field(default=False, ...) # the actual prefill toggle; mutually exclusive with add_generation_prompt
...
chat_template_kwargs: dict[str, Any] | None = Field(default=None, ...)It only merges them into the kwargs dict internally, right before calling the Jinja renderer — that's an implementation detail of how vLLM talks to the template, not the client-facing contract: chat_template_kwargs=merge_kwargs(
self.chat_template_kwargs,
dict(add_generation_prompt=self.add_generation_prompt, continue_final_message=self.continue_final_message, ...),
)SGLang ( llama.cpp server doesn't expose So the two real implementations of this feature both keep it as a top-level sibling to |
Summary
Closes #3877.
The chat template was always rendered with
add_generation_prompt=true, hardcoded in every servable. This exposes an optionaladd_generation_promptfield (bool, defaulttrue) on/v3/chat/completions, matching HF transformers and vLLM. Whenfalse, the trailing generation prompt is omitted — the building block for assistant prefill.Changes
add_generation_promptin the request handler; store it on the request struct.add_generation_prompt = true.Testing
Added tests covering default (generation prompt added) and
false(generation prompt omitted, assistant message preserved).Verified end-to-end on the MINJA path with
HuggingFaceTB/SmolLM2-360M-Instruct:<|im_start|>assistantadd_generation_prompt: false→ that trailing generation prompt is omittedScope note
This implements
add_generation_promptonly. Full assistant prefill — continuing from the final assistant message without closing it (continue_final_messagein transformers/vLLM) — is a separate control that the genai C++apply_chat_templatedoes not currently expose, and is left as a follow-up.🤖 Generated with Claude Code