@@ -283,6 +283,7 @@ def need_builtin_tool_call(self) -> bool:
283283 last_message .type == "function_call"
284284 and last_message .name
285285 in ("code_interpreter" , "python" , "web_search_preview" )
286+ or last_message .name .startswith ("container" )
286287 ):
287288 return True
288289
@@ -315,6 +316,8 @@ async def call_search_tool(
315316 self , tool_session : Union ["ClientSession" , Tool ], last_msg : FunctionCall
316317 ) -> list [ResponseInputOutputItem ]:
317318 self .called_tools .add ("browser" )
319+ if isinstance (tool_session , Tool ):
320+ return await tool_session .get_result_parsable_context (self )
318321 if envs .VLLM_TOOL_JSON_ERROR_AUTOMATIC_RETRY :
319322 try :
320323 args = json .loads (last_msg .arguments )
@@ -335,6 +338,49 @@ async def call_search_tool(
335338
336339 return [message ]
337340
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+
338384 async def call_tool (self ) -> list [ResponseInputOutputItem ]:
339385 if not self .parser .response_messages :
340386 return []
@@ -343,6 +389,10 @@ async def call_tool(self) -> list[ResponseInputOutputItem]:
343389 return await self .call_python_tool (self ._tool_sessions ["python" ], last_msg )
344390 elif last_msg .name == "web_search_preview" :
345391 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+ )
346396 return []
347397
348398 def render_for_completion (self ):
0 commit comments