Turn one chat choice generated chunks into stream deltas.
Shared by OpenAIServingChat streaming and streaming chat derender, so
both emit the same reasoning / content / tool_calls deltas and
finish_reason for the same chunks. Use one instance per choice and call
step once per chunk, in order and with the chunking the engine produced.
Methods:
-
step
–
Parse one chunk and resolve its chat finish_reason.
Source code in vllm/entrypoints/openai/chat_completion/stream_processor.py
| class ChatStreamProcessor:
"""Turn one chat choice generated chunks into stream deltas.
Shared by `OpenAIServingChat` streaming and streaming chat derender, so
both emit the same `reasoning` / `content` / `tool_calls` deltas and
`finish_reason` for the same chunks. Use one instance per choice and call
`step` once per chunk, in order and with the chunking the engine produced.
"""
def __init__(
self,
request: ChatCompletionRequest,
parser: Parser | None,
*,
enable_auto_tools: bool,
parser_cls: type[Parser] | None,
) -> None:
self.request = request
self.parser = parser
self.keep_tool_calls = keeps_tool_calls(
request, enable_auto_tools=enable_auto_tools, parser_cls=parser_cls
)
self.num_output_tokens = 0
self.tools_streamed = False
# Logprobs and token IDs would leak hidden reasoning through decoded
# logprob tokens or raw IDs, so callers drop both on every chunk.
self.hide_metadata = parser is not None and not request.include_reasoning
def step(
self,
delta_text: str,
delta_token_ids: list[int],
finish_reason: str | None,
prompt_token_ids: list[int] | None,
) -> ChatStreamStep:
"""Parse one chunk and resolve its chat `finish_reason`.
Args:
delta_text: Text decoded from this chunk's tokens.
delta_token_ids: This chunk's token IDs.
finish_reason: The engine finish reason. None if not finished.
prompt_token_ids: Prompt token IDs if known.
Returns:
The delta to stream, filtered for `tool_choice` and
`parallel_tool_calls`, and the chat `finish_reason`.
"""
delta: DeltaMessage | None
if not delta_text and not delta_token_ids and not self.num_output_tokens:
# Finished without output. Send the final chunk unparsed.
delta = DeltaMessage()
elif self.parser is not None:
delta = self.parser.parse_delta(
delta_text=delta_text,
delta_token_ids=delta_token_ids,
request=self.request,
prompt_token_ids=prompt_token_ids,
finished=finish_reason is not None,
)
if delta is not None and delta.tool_calls:
if self.keep_tool_calls:
self.tools_streamed = True
else:
delta.tool_calls = []
if not delta.content and not delta.reasoning:
delta = None
else:
delta = DeltaMessage(content=delta_text)
self.num_output_tokens += len(delta_token_ids)
if delta is not None:
delta = maybe_filter_parallel_tool_calls(delta, self.request)
if finish_reason is not None:
finish_reason = resolve_finish_reason(
finish_reason, self.request, self.tools_streamed
)
return ChatStreamStep(delta, finish_reason)
|
step(delta_text, delta_token_ids, finish_reason, prompt_token_ids)
Parse one chunk and resolve its chat finish_reason.
Parameters:
-
delta_text
(str)
–
Text decoded from this chunk's tokens.
-
delta_token_ids
(list[int])
–
-
finish_reason
(str | None)
–
The engine finish reason. None if not finished.
-
prompt_token_ids
(list[int] | None)
–
Prompt token IDs if known.
Returns:
Source code in vllm/entrypoints/openai/chat_completion/stream_processor.py
| def step(
self,
delta_text: str,
delta_token_ids: list[int],
finish_reason: str | None,
prompt_token_ids: list[int] | None,
) -> ChatStreamStep:
"""Parse one chunk and resolve its chat `finish_reason`.
Args:
delta_text: Text decoded from this chunk's tokens.
delta_token_ids: This chunk's token IDs.
finish_reason: The engine finish reason. None if not finished.
prompt_token_ids: Prompt token IDs if known.
Returns:
The delta to stream, filtered for `tool_choice` and
`parallel_tool_calls`, and the chat `finish_reason`.
"""
delta: DeltaMessage | None
if not delta_text and not delta_token_ids and not self.num_output_tokens:
# Finished without output. Send the final chunk unparsed.
delta = DeltaMessage()
elif self.parser is not None:
delta = self.parser.parse_delta(
delta_text=delta_text,
delta_token_ids=delta_token_ids,
request=self.request,
prompt_token_ids=prompt_token_ids,
finished=finish_reason is not None,
)
if delta is not None and delta.tool_calls:
if self.keep_tool_calls:
self.tools_streamed = True
else:
delta.tool_calls = []
if not delta.content and not delta.reasoning:
delta = None
else:
delta = DeltaMessage(content=delta_text)
self.num_output_tokens += len(delta_token_ids)
if delta is not None:
delta = maybe_filter_parallel_tool_calls(delta, self.request)
if finish_reason is not None:
finish_reason = resolve_finish_reason(
finish_reason, self.request, self.tools_streamed
)
return ChatStreamStep(delta, finish_reason)
|