[Bugfix][Frontend] Fix Inkling tool name leaking into content after reasoning (#58792)
Signed-off-by: Baljinder Hothi <baljinder.hothi@cohere.com> Co-authored-by: Flora Feng <4florafeng@gmail.com>
This commit is contained in:
co-authored by
Flora Feng
parent
da1aaec31f
commit
fdcce47e9b
@@ -23,8 +23,13 @@ from vllm.entrypoints.openai.chat_completion.protocol import (
|
||||
ChatCompletionToolsParam,
|
||||
FunctionDefinition,
|
||||
)
|
||||
from vllm.parser.abstract_parser import DelegatingParser
|
||||
from vllm.parser.engine.events import EventType
|
||||
from vllm.parser.engine.parser_engine_config import ParserState
|
||||
from vllm.parser.engine.registered_adapters import (
|
||||
InklingParserReasoningAdapter,
|
||||
InklingParserToolAdapter,
|
||||
)
|
||||
from vllm.parser.engine.streaming_parser_engine import StreamingParserEngine
|
||||
from vllm.parser.inkling import InklingParser, _inkling_arg_converter, inkling_config
|
||||
from vllm.parser.parser_manager import ParserManager
|
||||
@@ -166,6 +171,11 @@ def _delegating(mock_tokenizer, tools=None):
|
||||
return parser_cls(mock_tokenizer, tools or [])
|
||||
|
||||
|
||||
class _TwoPassInklingParser(DelegatingParser):
|
||||
reasoning_parser_cls = InklingParserReasoningAdapter
|
||||
tool_parser_cls = InklingParserToolAdapter
|
||||
|
||||
|
||||
def _stream_delegating(parser, request, text, chunk_size, prompt_token_ids):
|
||||
"""Stream ``text`` through ``DelegatingParser.parse_delta``, ``chunk_size``
|
||||
tokens per delta; return ``(content, reasoning, ordered tool names,
|
||||
@@ -759,6 +769,30 @@ class TestDelegatingTwoPass:
|
||||
assert TOOL_JSON not in content
|
||||
assert END_MESSAGE not in content
|
||||
|
||||
def test_function_name_not_in_content_after_reasoning(
|
||||
self, mock_tokenizer, mock_request
|
||||
):
|
||||
name = "search_documents_by_keyword"
|
||||
tools = [_function_tool(name)]
|
||||
mock_request.tools = tools
|
||||
text = (
|
||||
f"{THINK_START} context.{END_MESSAGE}{MSG_MODEL}"
|
||||
f"{name}{_tool_block(name, '{}')}"
|
||||
)
|
||||
|
||||
content, reasoning, names, args = _stream_delegating(
|
||||
_TwoPassInklingParser(mock_tokenizer, tools),
|
||||
mock_request,
|
||||
text,
|
||||
chunk_size=12,
|
||||
prompt_token_ids=self.GEN_PROMPT,
|
||||
)
|
||||
|
||||
assert reasoning == " context."
|
||||
assert content == ""
|
||||
assert names == [name]
|
||||
assert json.loads(args[0]) == {}
|
||||
|
||||
@pytest.mark.parametrize("opener", [TEXT_START, TOOL_TEXT, TOOL_ERROR])
|
||||
@pytest.mark.parametrize("chunk_size", [1, 3, 64])
|
||||
def test_visible_text_then_tool_streaming(
|
||||
|
||||
@@ -167,11 +167,12 @@ def inkling_config() -> ParserEngineConfig:
|
||||
(EventType.REASONING_END, EventType.TOOL_CALL_START),
|
||||
),
|
||||
}
|
||||
# Block-end terminals behave identically regardless of label. A
|
||||
# closed tool block returns to CONTENT (Inkling has no section wrapper;
|
||||
# A closed tool block returns to CONTENT (Inkling has no section wrapper;
|
||||
# blocks of any kind may follow), which also keeps the block-kind
|
||||
# and role tokens out of the engine's tool-terminal set so the
|
||||
# skip_tool_parsing reasoning pass still classifies reasoning.
|
||||
# In a reasoning block, THINK_END defers REASONING_END to the next block
|
||||
# opener so the function-name header is not emitted as content.
|
||||
for end in ("THINK_END", "END_SAMPLING"):
|
||||
transitions[(ParserState.CONTENT, end)] = Transition(
|
||||
ParserState.CONTENT,
|
||||
@@ -179,7 +180,7 @@ def inkling_config() -> ParserEngineConfig:
|
||||
)
|
||||
transitions[(ParserState.REASONING, end)] = Transition(
|
||||
ParserState.CONTENT,
|
||||
(EventType.REASONING_END,),
|
||||
() if end == "THINK_END" else (EventType.REASONING_END,),
|
||||
)
|
||||
transitions[(ParserState.TOOL_ARGS, end)] = Transition(
|
||||
ParserState.CONTENT,
|
||||
|
||||
Reference in New Issue
Block a user