Forward unknown tool calls instead of dropping (#12226)
This commit is contained in:
@@ -158,6 +158,9 @@ class Envs:
|
||||
SGLANG_DISABLE_OUTLINES_DISK_CACHE = EnvBool(True)
|
||||
SGLANG_GRAMMAR_TIMEOUT = EnvFloat(300)
|
||||
|
||||
# Tool Calling
|
||||
SGLANG_FORWARD_UNKNOWN_TOOLS = EnvBool(False)
|
||||
|
||||
# Hi-Cache
|
||||
SGLANG_HICACHE_HF3FS_CONFIG_PATH = EnvStr(None)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from partial_json_parser.core.exceptions import MalformedJSON
|
||||
from partial_json_parser.core.options import Allow
|
||||
|
||||
from sglang.srt.entrypoints.openai.protocol import Tool
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.function_call.core_types import (
|
||||
StreamingParseResult,
|
||||
ToolCallItem,
|
||||
@@ -75,19 +76,21 @@ class BaseFormatDetector(ABC):
|
||||
results = []
|
||||
for act in action:
|
||||
name = act.get("name")
|
||||
if name and name in tool_indices:
|
||||
results.append(
|
||||
ToolCallItem(
|
||||
tool_index=-1, # Caller should update this based on the actual tools array called
|
||||
name=name,
|
||||
parameters=json.dumps(
|
||||
act.get("parameters") or act.get("arguments", {}),
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
)
|
||||
else:
|
||||
if not (name and name in tool_indices):
|
||||
logger.warning(f"Model attempted to call undefined function: {name}")
|
||||
if not envs.SGLANG_FORWARD_UNKNOWN_TOOLS.get():
|
||||
continue # Skip unknown tools (default legacy behavior)
|
||||
|
||||
results.append(
|
||||
ToolCallItem(
|
||||
tool_index=-1, # Caller should update this based on the actual tools array called
|
||||
name=name,
|
||||
parameters=json.dumps(
|
||||
act.get("parameters") or act.get("arguments", {}),
|
||||
ensure_ascii=False,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
from typing import List, Optional
|
||||
|
||||
from sglang.srt.entrypoints.openai.protocol import Tool
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.function_call.base_format_detector import BaseFormatDetector
|
||||
from sglang.srt.function_call.core_types import (
|
||||
StreamingParseResult,
|
||||
@@ -220,7 +221,8 @@ class GptOssDetector(BaseFormatDetector):
|
||||
# Check if tool exists
|
||||
if function_name not in tool_indices:
|
||||
logger.debug(f"Function {function_name} not in available tools")
|
||||
return None
|
||||
if not envs.SGLANG_FORWARD_UNKNOWN_TOOLS.get():
|
||||
return None # Skip unknown tools (default legacy behavior)
|
||||
|
||||
# Parse JSON arguments
|
||||
try:
|
||||
|
||||
@@ -5,6 +5,7 @@ import re
|
||||
from typing import List, Optional
|
||||
|
||||
from sglang.srt.entrypoints.openai.protocol import Tool
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.function_call.base_format_detector import BaseFormatDetector
|
||||
from sglang.srt.function_call.core_types import (
|
||||
StreamingParseResult,
|
||||
@@ -91,7 +92,9 @@ class PythonicDetector(BaseFormatDetector):
|
||||
logger.warning(
|
||||
f"Model attempted to call undefined function: {function_name}"
|
||||
)
|
||||
continue
|
||||
if not envs.SGLANG_FORWARD_UNKNOWN_TOOLS.get():
|
||||
continue # Skip unknown tools (default legacy behavior)
|
||||
|
||||
arguments = {}
|
||||
for keyword in call.keywords:
|
||||
arguments[keyword.arg] = self._get_parameter_value(keyword.value)
|
||||
|
||||
@@ -6,6 +6,7 @@ import re
|
||||
from typing import Any, Dict, List, Tuple
|
||||
|
||||
from sglang.srt.entrypoints.openai.protocol import Tool
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.srt.function_call.base_format_detector import BaseFormatDetector
|
||||
from sglang.srt.function_call.core_types import (
|
||||
StreamingParseResult,
|
||||
@@ -120,45 +121,48 @@ class Qwen3CoderDetector(BaseFormatDetector):
|
||||
function_name = function_match.group(1).strip()
|
||||
|
||||
# Validate function name
|
||||
if function_name in self._tool_indices:
|
||||
self._current_function_name = function_name
|
||||
self._function_name_sent = True
|
||||
|
||||
# Initialize tool call tracking
|
||||
if self.current_tool_id == -1:
|
||||
self.current_tool_id = 0
|
||||
|
||||
# Ensure tracking arrays are large enough
|
||||
while len(self.prev_tool_call_arr) <= self.current_tool_id:
|
||||
self.prev_tool_call_arr.append({})
|
||||
while len(self.streamed_args_for_tool) <= self.current_tool_id:
|
||||
self.streamed_args_for_tool.append("")
|
||||
|
||||
# Store tool call info
|
||||
self.prev_tool_call_arr[self.current_tool_id] = {
|
||||
"name": function_name,
|
||||
"arguments": {},
|
||||
}
|
||||
|
||||
# Send tool name with empty parameters
|
||||
calls.append(
|
||||
ToolCallItem(
|
||||
tool_index=self.current_tool_id,
|
||||
name=function_name,
|
||||
parameters="",
|
||||
)
|
||||
)
|
||||
|
||||
# Remove the processed function declaration
|
||||
self._buf = self._buf[function_match.end() :]
|
||||
continue
|
||||
else:
|
||||
# Invalid function name, reset state
|
||||
is_valid = function_name in self._tool_indices
|
||||
if not is_valid:
|
||||
logger.warning(f"Invalid function name: {function_name}")
|
||||
self._reset_streaming_state()
|
||||
normal += self._buf
|
||||
self._buf = ""
|
||||
break
|
||||
if not envs.SGLANG_FORWARD_UNKNOWN_TOOLS.get():
|
||||
# Reset state and skip (default legacy behavior)
|
||||
self._reset_streaming_state()
|
||||
normal += self._buf
|
||||
self._buf = ""
|
||||
break
|
||||
|
||||
# Process tool call (valid or unknown with env=TRUE)
|
||||
self._current_function_name = function_name
|
||||
self._function_name_sent = True
|
||||
|
||||
# Initialize tool call tracking
|
||||
if self.current_tool_id == -1:
|
||||
self.current_tool_id = 0
|
||||
|
||||
# Ensure tracking arrays are large enough
|
||||
while len(self.prev_tool_call_arr) <= self.current_tool_id:
|
||||
self.prev_tool_call_arr.append({})
|
||||
while len(self.streamed_args_for_tool) <= self.current_tool_id:
|
||||
self.streamed_args_for_tool.append("")
|
||||
|
||||
# Store tool call info
|
||||
self.prev_tool_call_arr[self.current_tool_id] = {
|
||||
"name": function_name,
|
||||
"arguments": {},
|
||||
}
|
||||
|
||||
# Send tool name with empty parameters
|
||||
calls.append(
|
||||
ToolCallItem(
|
||||
tool_index=self.current_tool_id,
|
||||
name=function_name,
|
||||
parameters="",
|
||||
)
|
||||
)
|
||||
|
||||
# Remove the processed function declaration
|
||||
self._buf = self._buf[function_match.end() :]
|
||||
continue
|
||||
else:
|
||||
# Function name not complete yet, wait for more text
|
||||
break
|
||||
|
||||
Reference in New Issue
Block a user