Prhub

#42052 [Frontend] Return rendered prompt text in chat completion response

原始 PR 作者 princepride 合并时间 2026-05-11 13:53 文件变更 2 提交数 6 评论 11 代码增减 +23 / -0

执行摘要

新增 prompt_text 字段返回聊天模板渲染文本

关联 Issue #42051 指出,用户在调试时需要查看聊天模板处理后的具体 prompt 文本,而 vLLM 仅能输出 prompt token IDs。SGLang 已支持此功能,因此 vLLM 需添加类似能力,便于开发者准确了解模型输入。

该 PR 设计简洁,值得关注其参数设计思路和直接利用引擎内部数据的做法。推荐在类似场景(如需要暴露内部处理结果)时参考此模式。

讨论亮点
  1. 参数命名设计:DarkLight1337 建议不要复用 return_token_ids 来控制 prompt_text,而是引入独立参数 return_prompt_text,作者采纳并实现。
  2. 数据来源优化:chaunceyjiang 指出 prompt 文本已存储在 engine_inputs 中,无需重新 decode token IDs,作者改为直接使用 res.prompt,减少开销。
  3. 事件循环阻塞提示:gemini-code-assist[bot] 提醒同步 decode 可能阻塞,但后续迭代已使用存储文本,该问题自然消除。

实现拆解

  1. 协议定义扩展:在 vllm/entrypoints/openai/chat_completion/protocol.py 中为 ChatCompletionRequest 添加 return_prompt_text 布尔参数,为 ChatCompletionResponseChatCompletionStreamResponse 添加 prompt_text: str | None 字段。
  2. 流式响应生成:在 vllm/entrypoints/openai/chat_completion/serving.pychat_completion_stream_generator 中,首次迭代时从 res.prompt 获取渲染后的 prompt 文本(仅当 request.return_prompt_text 为真),并设置到 ChatCompletionStreamResponseprompt_text 字段。
  3. 非流式响应生成:在同一文件的 chat_completion_full_generator 中,从 final_res.prompt 获取文本并设置到 ChatCompletionResponseprompt_text 字段。
  4. 利用已有数据:直接使用引擎内部已存储的 prompt(res.prompt),无需通过 tokenizer.decode() 转换,避免阻塞事件循环。
文件 模块 状态 重要度
vllm/entrypoints/openai/chat_completion/protocol.py 协议层 modified 5.87
vllm/entrypoints/openai/chat_completion/serving.py 服务逻辑 modified 5.44

关键符号

chat_completion_stream_generator chat_completion_full_generator

关键源码片段

vllm/entrypoints/openai/chat_completion/protocol.py core-logic

定义了 `return_prompt_text` 请求参数和 `prompt_text` 响应字段,是 API 规范变更的核心文件。

class ChatCompletionResponse(OpenAIBaseModel):
    # ... 已有字段 ...
    # vLLM-specific fields
    prompt_logprobs: list[dict[int, Logprob] | None] | None = None
    prompt_token_ids: list[int] | None = None
    # Rendered prompt text from chat templating (only set when
    # ``return_prompt_text=True`` on the request).
    prompt_text: str | None = None
    kv_transfer_params: dict[str, Any] | None = Field(
        default=None, description="KVTransfer parameters.")class ChatCompletionStreamResponse(OpenAIBaseModel):
    # ... 已有字段 ...
    system_fingerprint: str | None = None
    prompt_token_ids: list[int] | None = None
    # Rendered prompt text from chat templating (only set when
    # ``return_prompt_text=True`` on the request); only sent on the first chunk.
    prompt_text: str | None = None# In ChatCompletionRequest:
    return_token_ids: bool | None = Field(
        default=None,
        description="If specified, the result will include token IDs alongside the generated text...")
    return_prompt_text: bool | None = Field(
        default=None,
        description="If true, the response will include ``prompt_text`` containing the prompt string produced by chat templating...")
vllm/entrypoints/openai/chat_completion/serving.py core-logic

实现在流式和非流式生成器中从推理结果提取 prompt 文本并设置到响应对象。

# Inside chat_completion_stream_generator, under first_iteration
role = self.get_chat_request_role(request)
# ``res.prompt`` is the rendered chat-templated prompt
prompt_text = res.prompt if request.return_prompt_text else Nonefor i in range(num_choices):
    choice_data = ChatCompletionResponseStreamChoice(
        index=i,
        delta=DeltaMessage(role=role, content=""),
        logprobs=None,
        finish_reason=None,
    )
    chunk = ChatCompletionStreamResponse(
        id=request_id,
        object=chunk_object_type,
        created=created_time,
        choices=[choice_data],
        model=model_name,
        prompt_token_ids=(
            res.prompt_token_ids if request.return_token_ids else None
        ),
        prompt_text=prompt_text,
    )
    # ... usage, yield ...# Inside chat_completion_full_generator
prompt_text = final_res.prompt if request.return_prompt_text else None
response = ChatCompletionResponse(
    id=request_id,
    created=created_time,
    model=model_name,
    choices=choices,
    usage=usage,
    prompt_logprobs=prompt_logprobs,
    prompt_token_ids=(
        final_res.prompt_token_ids if request.return_token_ids else None
    ),
    prompt_text=prompt_text,
    kv_transfer_params=final_res.kv_transfer_params,
    prompt_routed_experts=prompt_routed_experts,
)

评论区精华

参数名称设计:`return_prompt_text` 设计

DarkLight1337 认为重用 `return_token_ids` 会让人困惑,建议改为单独的 `return_prompt_text` 或合并为 `return_inputs`。作者和 chaunceyjiang 支持 `return_prompt_text`。

结论:独立参数 `return_prompt_text` 被采纳。 · 已解决

使用引擎已存储的 prompt 文本 性能

chaunceyjiang 指出 `prompt_text` 已存储在 `engine_inputs` 中,无需重新 decode token IDs。作者立即采用。

结论:改为直接使用 `res.prompt` 获取渲染后的 prompt 文本,避免额外解码开销。 · 已解决

同步解码可能阻塞事件循环 性能

gemini-code-assist[bot] 警告 decode 大 prompts 时可能阻塞事件循环。但后续迭代已改为使用存储的文本,此问题不复存在。

结论:已通过使用存储文本自动解决。 · 已解决

风险与影响

新增字段和参数属于向后兼容扩展,旧客户端不发送 return_prompt_text 时无影响。响应模型增加可选字段,不会破坏现有解析。流式模式下仅首块包含 prompt_text,需确保客户端正确处理。整体风险低。

对用户:提供调试便利性,可查看模型真实输入文本。对系统:几乎无性能影响,因直接使用已存储的 prompt。影响范围限于 chat completion 端点,不涉及其他 API。

新增字段 流式兼容性

关联 Issue

#42051 [Feature]: Output prompt text when enable `--enable-log-requests`

完整报告

参与讨论