model: step3-vl-10b (#17513)

This commit is contained in:
Yuhao Yang
2026-01-22 23:15:08 +08:00
committed by GitHub
parent 72f790bf6f
commit f7a0bcda1e
6 changed files with 712 additions and 4 deletions

View File

@@ -17,6 +17,7 @@ from sglang.srt.function_call.deepseekv32_detector import DeepSeekV32Detector
from sglang.srt.function_call.glm4_moe_detector import Glm4MoeDetector
from sglang.srt.function_call.glm47_moe_detector import Glm47MoeDetector
from sglang.srt.function_call.gpt_oss_detector import GptOssDetector
from sglang.srt.function_call.hermes_detector import HermesDetector
from sglang.srt.function_call.internlm_detector import InternlmDetector
from sglang.srt.function_call.kimik2_detector import KimiK2Detector
from sglang.srt.function_call.lfm2_detector import Lfm2Detector
@@ -64,6 +65,7 @@ class FunctionCallParser:
"minimax-m2": MinimaxM2Detector,
"trinity": TrinityDetector,
"interns1": InternlmDetector,
"hermes": HermesDetector,
}
def __init__(self, tools: List[Tool], tool_call_parser: str):

View File

@@ -0,0 +1,120 @@
import json
import logging
import re
from typing import List
from sglang.srt.entrypoints.openai.protocol import Tool
from sglang.srt.function_call.base_format_detector import BaseFormatDetector
from sglang.srt.function_call.core_types import (
StreamingParseResult,
StructureInfo,
_GetInfoFunc,
)
logger = logging.getLogger(__name__)
class HermesDetector(BaseFormatDetector):
"""
Detector for Hermes tool call format.
Format:
<tool_call>{"name": "...", "arguments": {...}}</tool_call>
"""
def __init__(self):
super().__init__()
self.bot_token = "<tool_call>"
self.eot_token = "</tool_call>"
self.tool_call_regex = re.compile(
r"<tool_call>(.*?)</tool_call>|<tool_call>(.*)", re.DOTALL
)
self._normal_text_buffer = ""
def has_tool_call(self, text: str) -> bool:
return self.bot_token in text
def detect_and_parse(self, text: str, tools: List[Tool]) -> StreamingParseResult:
"""
One-time parsing: Detects and parses tool calls in the provided text.
"""
idx = text.find(self.bot_token)
normal_text = text[:idx].strip() if idx != -1 else text
if self.bot_token not in text:
return StreamingParseResult(normal_text=normal_text, calls=[])
calls = []
try:
for match in self.tool_call_regex.findall(text):
raw = match[0] or match[1]
if not raw:
continue
parsed = json.loads(raw.strip())
if isinstance(parsed, list):
calls.extend(self.parse_base_json(parsed, tools))
else:
calls.extend(self.parse_base_json(parsed, tools))
return StreamingParseResult(normal_text=normal_text, calls=calls)
except Exception as e:
logger.error(f"Error in detect_and_parse: {e}")
return StreamingParseResult(normal_text=text)
def _clean_normal_text(self, text: str) -> str:
if not text:
return text
self._normal_text_buffer += text
if self.eot_token in self._normal_text_buffer:
cleaned = self._normal_text_buffer.replace(self.eot_token, "")
self._normal_text_buffer = ""
return cleaned
partial_len = self._ends_with_partial_token(
self._normal_text_buffer, self.eot_token
)
if partial_len:
safe_text = self._normal_text_buffer[:-partial_len]
self._normal_text_buffer = self._normal_text_buffer[-partial_len:]
return safe_text
cleaned = self._normal_text_buffer
self._normal_text_buffer = ""
return cleaned
def parse_streaming_increment(
self, new_text: str, tools: List[Tool]
) -> StreamingParseResult:
"""
Streaming parsing: handle normal text, partial tags, and tool calls.
"""
self._buffer += new_text
current_text = self._buffer
if self.bot_token not in current_text:
partial_len = self._ends_with_partial_token(current_text, self.bot_token)
if partial_len:
safe_text = current_text[:-partial_len]
self._buffer = current_text[-partial_len:]
else:
safe_text = current_text
self._buffer = ""
return StreamingParseResult(normal_text=self._clean_normal_text(safe_text))
bot_pos = current_text.find(self.bot_token)
if bot_pos > 0:
normal_text = current_text[:bot_pos]
self._buffer = current_text[bot_pos:]
return StreamingParseResult(normal_text=normal_text)
result = super().parse_streaming_increment(new_text="", tools=tools)
if result.normal_text:
result.normal_text = self._clean_normal_text(result.normal_text)
return result
def structure_info(self) -> _GetInfoFunc:
return lambda name: StructureInfo(
begin='<tool_call>{"name":"' + name + '", "arguments":',
end="}</tool_call>",
trigger="<tool_call>",
)