Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
change
sglang
Commits
5fe79605
"vscode:/vscode.git/clone" did not exist on "808a3676c20bf0d0ae566481337b2e8d781c115b"
Unverified
Commit
5fe79605
authored
Mar 13, 2025
by
Chang Su
Committed by
GitHub
Mar 13, 2025
Browse files
Fix Llama3.3 tool call support (#4320)
parent
c6d7f8d3
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
55 additions
and
22 deletions
+55
-22
python/sglang/srt/function_call_parser.py
python/sglang/srt/function_call_parser.py
+33
-2
python/sglang/srt/openai_api/adapter.py
python/sglang/srt/openai_api/adapter.py
+22
-20
No files found.
python/sglang/srt/function_call_parser.py
View file @
5fe79605
...
...
@@ -318,6 +318,10 @@ class Qwen25Detector(BaseFormatDetector):
self
.
bot_token
=
"<tool_call>"
self
.
eot_token
=
"</tool_call>"
def
has_tool_call
(
self
,
text
:
str
)
->
bool
:
"""Check if the text contains a Qwen 2.5 format tool call."""
return
self
.
bot_token
in
text
def
detect_and_parse
(
self
,
text
:
str
,
tools
:
List
[
Function
])
->
List
[
ToolCallItem
]:
"""
One-time parsing: Detects and parses tool calls in the provided text.
...
...
@@ -352,6 +356,10 @@ class MistralDetector(BaseFormatDetector):
self
.
bot_token
=
"[TOOL_CALLS] ["
self
.
tool_call_regex
=
re
.
compile
(
r
"\[{.*}\]"
,
re
.
DOTALL
)
def
has_tool_call
(
self
,
text
:
str
)
->
bool
:
"""Check if the text contains a Mistral format tool call."""
return
self
.
bot_token
in
text
def
_clean_text
(
self
,
text
:
str
)
->
str
:
"""
clean text to only leave ''[TOOL_CALLS] [{"name": xxx, "arguments": {xxx}}]'
...
...
@@ -397,12 +405,21 @@ class Llama32Detector(BaseFormatDetector):
super
().
__init__
()
self
.
bot_token
=
"<|python_tag|>"
def
has_tool_call
(
self
,
text
:
str
)
->
bool
:
"""Check if the text contains a Llama 3.2 format tool call."""
# depending on the prompt format the Llama model may or may not
# prefix the output with the <|python_tag|> token
return
"<|python_tag|>"
in
text
or
text
.
startswith
(
"{"
)
def
detect_and_parse
(
self
,
text
:
str
,
tools
:
List
[
Function
])
->
List
[
ToolCallItem
]:
"""Parse function calls from text, handling multiple JSON objects."""
if
"<|python_tag|>"
not
in
text
:
if
"<|python_tag|>"
not
in
text
and
not
text
.
startswith
(
"{"
)
:
return
[]
_
,
action_text
=
text
.
split
(
"<|python_tag|>"
)
if
"<|python_tag|>"
in
text
:
_
,
action_text
=
text
.
split
(
"<|python_tag|>"
)
else
:
action_text
=
text
# Split by semicolon and process each part
json_parts
=
[
part
.
strip
()
for
part
in
action_text
.
split
(
";"
)
if
part
.
strip
()]
...
...
@@ -501,6 +518,20 @@ class FunctionCallParser:
self
.
multi_format_parser
=
MultiFormatParser
(
detectors
)
self
.
tools
=
tools
def
has_tool_call
(
self
,
text
:
str
)
->
bool
:
"""
Check if the given text contains a tool call in the format supported by this parser.
This delegates to the detector's implementation.
:param text: The text to check for tool calls
:return: True if the text contains a tool call, False otherwise
"""
# Check all detectors in the multi_format_parser
for
detector
in
self
.
multi_format_parser
.
detectors
:
if
detector
.
has_tool_call
(
text
):
return
True
return
False
def
parse_non_stream
(
self
,
full_text
:
str
):
"""
Non-streaming call: one-time parsing
...
...
python/sglang/srt/openai_api/adapter.py
View file @
5fe79605
...
...
@@ -1115,27 +1115,29 @@ def v1_chat_generate_response(
else
:
reasoning_text
=
None
if
tool_choice
!=
"none"
and
any
([
i
in
text
for
i
in
TOOLS_TAG_LIST
]):
if
finish_reason
==
"stop"
:
finish_reason
=
"tool_calls"
try
:
parser
=
FunctionCallParser
(
tools
,
tool_call_parser
)
full_normal_text
,
call_info_list
=
parser
.
parse_non_stream
(
text
)
tool_calls
=
[
ToolCall
(
id
=
str
(
call_info
.
tool_index
),
function
=
FunctionResponse
(
name
=
call_info
.
name
,
arguments
=
call_info
.
parameters
),
if
tool_choice
!=
"none"
and
tools
:
parser
=
FunctionCallParser
(
tools
,
tool_call_parser
)
if
parser
.
has_tool_call
(
text
):
if
finish_reason
[
"type"
]
==
"stop"
:
finish_reason
[
"type"
]
=
"tool_calls"
finish_reason
[
"matched"
]
=
None
try
:
full_normal_text
,
call_info_list
=
parser
.
parse_non_stream
(
text
)
tool_calls
=
[
ToolCall
(
id
=
str
(
call_info
.
tool_index
),
function
=
FunctionResponse
(
name
=
call_info
.
name
,
arguments
=
call_info
.
parameters
),
)
for
call_info
in
call_info_list
]
except
Exception
as
e
:
logger
.
error
(
f
"Exception:
{
e
}
"
)
return
create_error_response
(
HTTPStatus
.
BAD_REQUEST
,
"Failed to parse fc related info to json format!"
,
)
for
call_info
in
call_info_list
]
except
Exception
as
e
:
logger
.
error
(
f
"Exception:
{
e
}
"
)
return
create_error_response
(
HTTPStatus
.
BAD_REQUEST
,
"Failed to parse fc related info to json format!"
,
)
if
to_file
:
# to make the choice data json serializable
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment