From 44a764c46cfb7d31e46dc09448d22705ec8af6dc Mon Sep 17 00:00:00 2001 From: shihaobai <1798930569@qq.com> Date: Thu, 25 Dec 2025 15:09:22 +0000 Subject: [PATCH 1/2] fix message with tool calls --- lightllm/server/api_models.py | 82 +++++++++++++++++------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/lightllm/server/api_models.py b/lightllm/server/api_models.py index 4f40f1ad6..7b9cdd501 100644 --- a/lightllm/server/api_models.py +++ b/lightllm/server/api_models.py @@ -68,6 +68,46 @@ class ResponseFormat(BaseModel): json_schema: Optional[JsonSchemaResponseFormat] = None +class FunctionResponse(BaseModel): + """Function response.""" + + name: Optional[str] = None + arguments: Optional[str] = None + + +class ToolCall(BaseModel): + """Tool call response.""" + + id: Optional[str] = None + index: Optional[int] = None + type: Literal["function"] = "function" + function: FunctionResponse + + +class ChatCompletionMessageGenericParam(BaseModel): + role: Literal["system", "assistant", "tool", "function"] + content: Union[str, List[MessageContent], None] = Field(default=None) + tool_call_id: Optional[str] = None + name: Optional[str] = None + reasoning_content: Optional[str] = None + tool_calls: Optional[List[ToolCall]] = Field(default=None, examples=[None]) + + @field_validator("role", mode="before") + @classmethod + def _normalize_role(cls, v): + if isinstance(v, str): + v_lower = v.lower() + if v_lower not in {"system", "assistant", "tool", "function"}: + raise ValueError( + "'role' must be one of 'system', 'assistant', 'tool', or 'function' (case-insensitive)." + ) + return v_lower + raise ValueError("'role' must be a string") + + +ChatCompletionMessageParam = Union[ChatCompletionMessageGenericParam, Message] + + class CompletionRequest(BaseModel): model: str # prompt: string or tokens @@ -137,7 +177,7 @@ def apply_loaded_defaults(cls, data: Any): class ChatCompletionRequest(BaseModel): model: str - messages: List[Message] + messages: List[ChatCompletionMessageParam] function_call: Optional[str] = "none" temperature: Optional[float] = 1 top_p: Optional[float] = 1.0 @@ -212,46 +252,6 @@ def apply_loaded_defaults(cls, data: Any): return data -class FunctionResponse(BaseModel): - """Function response.""" - - name: Optional[str] = None - arguments: Optional[str] = None - - -class ToolCall(BaseModel): - """Tool call response.""" - - id: Optional[str] = None - index: Optional[int] = None - type: Literal["function"] = "function" - function: FunctionResponse - - -class ChatCompletionMessageGenericParam(BaseModel): - role: Literal["system", "assistant", "tool", "function"] - content: Union[str, List[MessageContent], None] = Field(default=None) - tool_call_id: Optional[str] = None - name: Optional[str] = None - reasoning_content: Optional[str] = None - tool_calls: Optional[List[ToolCall]] = Field(default=None, examples=[None]) - - @field_validator("role", mode="before") - @classmethod - def _normalize_role(cls, v): - if isinstance(v, str): - v_lower = v.lower() - if v_lower not in {"system", "assistant", "tool", "function"}: - raise ValueError( - "'role' must be one of 'system', 'assistant', 'tool', or 'function' (case-insensitive)." - ) - return v_lower - raise ValueError("'role' must be a string") - - -ChatCompletionMessageParam = Union[ChatCompletionMessageGenericParam, Message] - - class UsageInfo(BaseModel): prompt_tokens: int = 0 completion_tokens: Optional[int] = 0 From 9e73a4468383a10c60977f01ec6ac72d991c48d4 Mon Sep 17 00:00:00 2001 From: shihaobai <1798930569@qq.com> Date: Fri, 26 Dec 2025 10:51:44 +0000 Subject: [PATCH 2/2] fix --- lightllm/server/api_openai.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lightllm/server/api_openai.py b/lightllm/server/api_openai.py index f7e457472..6a8c232dc 100644 --- a/lightllm/server/api_openai.py +++ b/lightllm/server/api_openai.py @@ -260,6 +260,7 @@ async def chat_completions_impl(request: ChatCompletionRequest, raw_request: Req finish_reason = finish_reason_dict[sub_req_id] text = "".join(final_output_dict[sub_req_id]) + full_text = text # Handle reasoning content reasoning_text = None @@ -284,14 +285,14 @@ async def chat_completions_impl(request: ChatCompletionRequest, raw_request: Req tool_calls = None tool_choice = request.tool_choice tools = request.tools - if tool_choice != "none" and any([i in text for i in TOOLS_TAG_LIST]): + if tool_choice != "none" and any([i in full_text for i in TOOLS_TAG_LIST]): if finish_reason == "stop": finish_reason = "tool_calls" try: # 为 tool_call_parser 提供默认值 tool_parser = getattr(g_objs.args, "tool_call_parser", None) or "llama3" parser = FunctionCallParser(tools, tool_parser) - full_normal_text, call_info_list = parser.parse_non_stream(text) + full_normal_text, call_info_list = parser.parse_non_stream(full_text) tool_calls = [] history_tool_calls_cnt = _get_history_tool_calls_cnt(request) for call_info in call_info_list: