Skip to content

Commit 735284e

Browse files
qandrewAndrew XiaDarkLight1337
authored
[responsesAPI][7] Browser, Container MCP tools for non harmony models (vllm-project#29989)
Signed-off-by: Andrew Xia <[email protected]> Signed-off-by: Andrew Xia <[email protected]> Co-authored-by: Andrew Xia <[email protected]> Co-authored-by: Cyrus Leung <[email protected]>
1 parent 444f0e3 commit 735284e

File tree

1 file changed

+83
-6
lines changed

1 file changed

+83
-6
lines changed

vllm/entrypoints/context.py

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,12 +278,14 @@ def append_tool_output(self, output: list[ResponseInputOutputItem]) -> None:
278278
def need_builtin_tool_call(self) -> bool:
279279
"""Return true if the last message is a MCP tool call"""
280280
last_message = self.parser.response_messages[-1]
281-
# TODO: figure out which tools are MCP tools
282-
if ( # noqa: SIM103
283-
last_message.type == "function_call"
284-
and last_message.name in ("code_interpreter", "python")
285-
):
286-
return True
281+
# TODO(qandrew): figure out which tools are MCP tools
282+
if last_message.type == "function_call": # noqa: SIM102
283+
if last_message.name in (
284+
"code_interpreter",
285+
"python",
286+
"web_search_preview",
287+
) or last_message.name.startswith("container"):
288+
return True
287289

288290
return False
289291

@@ -310,12 +312,87 @@ async def call_python_tool(
310312

311313
return [message]
312314

315+
async def call_search_tool(
316+
self, tool_session: Union["ClientSession", Tool], last_msg: FunctionCall
317+
) -> list[ResponseInputOutputItem]:
318+
self.called_tools.add("browser")
319+
if isinstance(tool_session, Tool):
320+
return await tool_session.get_result_parsable_context(self)
321+
if envs.VLLM_TOOL_JSON_ERROR_AUTOMATIC_RETRY:
322+
try:
323+
args = json.loads(last_msg.arguments)
324+
except json.JSONDecodeError as e:
325+
return _create_json_parse_error_messages(last_msg, e)
326+
else:
327+
args = json.loads(last_msg.arguments)
328+
result = await tool_session.call_tool("search", args)
329+
result_str = result.content[0].text
330+
331+
message = ResponseFunctionToolCallOutputItem(
332+
id=f"fco_{random_uuid()}",
333+
type="function_call_output",
334+
call_id=f"call_{random_uuid()}",
335+
output=result_str,
336+
status="completed",
337+
)
338+
339+
return [message]
340+
341+
async def call_container_tool(
342+
self, tool_session: Union["ClientSession", Tool], last_msg: Message
343+
) -> list[Message]:
344+
"""
345+
Call container tool. Expect this to be run in a stateful docker
346+
with command line terminal.
347+
The official container tool would at least
348+
expect the following format:
349+
- for tool name: exec
350+
- args:
351+
{
352+
"cmd":List[str] "command to execute",
353+
"workdir":optional[str] "current working directory",
354+
"env":optional[object/dict] "environment variables",
355+
"session_name":optional[str] "session name",
356+
"timeout":optional[int] "timeout in seconds",
357+
"user":optional[str] "user name",
358+
}
359+
"""
360+
self.called_tools.add("container")
361+
if isinstance(tool_session, Tool):
362+
return await tool_session.get_result_parsable_context(self)
363+
# tool_name = last_msg.recipient.split(".")[1].split(" ")[0]
364+
if envs.VLLM_TOOL_JSON_ERROR_AUTOMATIC_RETRY:
365+
try:
366+
args = json.loads(last_msg.arguments)
367+
except json.JSONDecodeError as e:
368+
return _create_json_parse_error_messages(last_msg, e)
369+
else:
370+
args = json.loads(last_msg.arguments)
371+
result = await tool_session.call_tool("exec", args)
372+
result_str = result.content[0].text
373+
374+
message = ResponseFunctionToolCallOutputItem(
375+
id=f"fco_{random_uuid()}",
376+
type="function_call_output",
377+
call_id=f"call_{random_uuid()}",
378+
output=result_str,
379+
status="completed",
380+
)
381+
382+
return [message]
383+
313384
async def call_tool(self) -> list[ResponseInputOutputItem]:
314385
if not self.parser.response_messages:
315386
return []
316387
last_msg = self.parser.response_messages[-1]
317388
if last_msg.name == "code_interpreter":
318389
return await self.call_python_tool(self._tool_sessions["python"], last_msg)
390+
elif last_msg.name == "web_search_preview":
391+
return await self.call_search_tool(self._tool_sessions["browser"], last_msg)
392+
elif last_msg.name.startswith("container"):
393+
return await self.call_container_tool(
394+
self._tool_sessions["container"], last_msg
395+
)
319396
return []
320397

321398
def render_for_completion(self):

0 commit comments

Comments
 (0)