Signed-off-by: wang.yuqi <yuqi.wang@daocloud.io> Signed-off-by: wang.yuqi <noooop@126.com> Co-authored-by: Cyrus Leung <cyrus.tl.leung@gmail.com>
136 lines
4.5 KiB
Python
136 lines
4.5 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Sequence
|
|
from typing import TYPE_CHECKING
|
|
|
|
from vllm.entrypoints.generate.base.protocol import (
|
|
DeltaMessage,
|
|
FunctionCall,
|
|
)
|
|
from vllm.parser.abstract_parser import DelegatingParser
|
|
from vllm.reasoning.kimi_k3_reasoning_parser import KimiK3ReasoningParser
|
|
|
|
if TYPE_CHECKING:
|
|
from vllm.entrypoints.openai.chat_completion.protocol import (
|
|
ChatCompletionRequest,
|
|
)
|
|
from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
|
|
|
|
|
|
class KimiK3Parser(DelegatingParser):
|
|
"""Compose the Kimi K3 reasoning and tool parsers for XTML output."""
|
|
|
|
# TODO: Switch Kimi K3 to the parser engine once its XTML reasoning/tool
|
|
# path is covered there.
|
|
def _extract_tool_calls(
|
|
self,
|
|
content: str | None,
|
|
request: ChatCompletionRequest | ResponsesRequest,
|
|
enable_auto_tools: bool = False,
|
|
) -> tuple[list[FunctionCall] | None, str | None]:
|
|
if self._tool_parser is None or not enable_auto_tools:
|
|
return super()._extract_tool_calls(content, request, enable_auto_tools)
|
|
|
|
tool_call_info = self.extract_tool_calls(content or "", request=request)
|
|
if request.tool_choice == "none":
|
|
return [], tool_call_info.content
|
|
if not tool_call_info.tools_called:
|
|
return None, tool_call_info.content
|
|
|
|
tool_calls = [
|
|
FunctionCall(
|
|
id=tool_call.id,
|
|
name=tool_call.function.name,
|
|
arguments=tool_call.function.arguments,
|
|
)
|
|
for tool_call in tool_call_info.tool_calls
|
|
]
|
|
parsed_content = tool_call_info.content
|
|
if parsed_content and parsed_content.strip() == "":
|
|
parsed_content = None
|
|
return tool_calls, parsed_content
|
|
|
|
def _extract_tool_calls_streaming(
|
|
self,
|
|
previous_text: str,
|
|
current_text: str,
|
|
delta_text: str,
|
|
previous_token_ids: Sequence[int],
|
|
current_token_ids: Sequence[int],
|
|
delta_token_ids: Sequence[int],
|
|
request: ChatCompletionRequest | ResponsesRequest,
|
|
tool_call_idx: int | None = None,
|
|
tool_call_id_type: str = "random",
|
|
function_name_returned: bool = False,
|
|
) -> tuple[DeltaMessage | None, bool]:
|
|
if request.tool_choice != "none":
|
|
return super()._extract_tool_calls_streaming(
|
|
previous_text,
|
|
current_text,
|
|
delta_text,
|
|
previous_token_ids,
|
|
current_token_ids,
|
|
delta_token_ids,
|
|
request,
|
|
tool_call_idx=tool_call_idx,
|
|
tool_call_id_type=tool_call_id_type,
|
|
function_name_returned=function_name_returned,
|
|
)
|
|
|
|
delta_message = self.extract_tool_calls_streaming(
|
|
previous_text,
|
|
current_text,
|
|
delta_text,
|
|
previous_token_ids,
|
|
current_token_ids,
|
|
delta_token_ids,
|
|
request,
|
|
)
|
|
if delta_message is not None:
|
|
delta_message.tool_calls = []
|
|
return delta_message, False
|
|
|
|
def parse_delta(
|
|
self,
|
|
delta_text: str,
|
|
delta_token_ids: list[int],
|
|
request: ChatCompletionRequest | ResponsesRequest,
|
|
prompt_token_ids: list[int] | None = None,
|
|
*,
|
|
finished: bool,
|
|
) -> DeltaMessage | None:
|
|
state = self._stream_state
|
|
previous_content = state.previous_text if state.reasoning_ended else ""
|
|
delta_message = super().parse_delta(
|
|
delta_text,
|
|
delta_token_ids,
|
|
request,
|
|
prompt_token_ids,
|
|
finished=finished,
|
|
)
|
|
|
|
if (
|
|
self._tool_parser is not None
|
|
or not isinstance(self._reasoning_parser, KimiK3ReasoningParser)
|
|
or not state.reasoning_ended
|
|
or delta_message is None
|
|
):
|
|
return delta_message
|
|
|
|
stripped = self._reasoning_parser.strip_content_streaming(
|
|
previous_text=previous_content,
|
|
current_text=state.previous_text,
|
|
)
|
|
delta_message.content = stripped.content if stripped is not None else None
|
|
if (
|
|
delta_message.role is None
|
|
and delta_message.content is None
|
|
and delta_message.reasoning is None
|
|
and not delta_message.tool_calls
|
|
):
|
|
return None
|
|
return delta_message
|