import json
from rllm.parser.tool_parser.tool_parser_base import ToolParser
from rllm.tools.tool_base import ToolCall
class R1ToolParser(ToolParser):
"""Parser for R1 tool call format."""
def __init__(self):
"""Initialize the R1 tool parser.
Args:
model (str): Model name for tokenizer (optional)
tokenizer: Pre-initialized tokenizer (optional)
"""
self.tool_calls_begin = "<|tool▁call▁begin|>"
self.tool_calls_end = "<|tool▁calls▁end|>"
self.tool_call_begin = "<|tool▁call▁begin|>"
self.tool_call_end = "<|tool▁call▁end|>"
self.tool_sep = "<|tool▁sep|>"
def parse(self, model_response: str) -> list[ToolCall]:
"""Parse tool calls from model output.
Args:
model_output (str): Text containing tool calls
Returns:
ToolInputs: Parsed tool calls
"""
tool_calls_dicts = self.parse_r1_tool_calls(model_response)
tool_calls = [ToolCall(name=tc["name"], arguments=tc["arguments"]) for tc in tool_calls_dicts]
return tool_calls
def parse_r1_tool_calls(self, text: str) -> list[dict]:
"""Parse tool calls from text using the R1 special token format.
Format:
<|tool▁calls▁begin|>
<|tool▁call▁begin|>function<|tool▁sep|>function_name
```json
{"param": "value"}
```
<|tool▁call▁end|>
// Additional tool calls follow the same format
<|tool▁calls▁end|>
Returns:
list[dict]: List of parsed tool calls, each containing 'name' and 'parameters'
"""
tool_calls = []
call_idx = 0
while True:
call_idx = text.find(self.tool_call_begin, call_idx)
if call_idx == -1:
break
call_start = call_idx + len(self.tool_call_begin)
call_end = text.find(self.tool_call_end, call_start)
if call_end == -1:
break
call_content = text[call_start:call_end].strip()
func_prefix = "function" + self.tool_sep
func_start = call_content.find(func_prefix)
if func_start != -1:
func_name_start = func_start + len(func_prefix)
func_name_end = call_content.find("\n", func_name_start)
if func_name_end == -1:
function_name = call_content[func_name_start:].strip()
else:
function_name = call_content[func_name_start:func_name_end].strip()
else:
call_idx = call_end + len(self.tool_call_end)
continue
json_start = call_content.find("```json\n")
if json_start == -1:
json_start = call_content.find("```json")
if json_start == -1:
call_idx = call_end + len(self.tool_call_end)
continue
json_start += len("```json")
else:
json_start += len("```json\n")
json_end = call_content.find("```", json_start)
if json_end == -1:
call_idx = call_end + len(self.tool_call_end)
continue
args_str = call_content[json_start:json_end].strip()
try:
args_json = json.loads(args_str)
except json.JSONDecodeError:
call_idx = call_end + len(self.tool_call_end)
continue
tool_calls.append({"name": function_name, "arguments": args_json})
call_idx = call_end + len(self.tool_call_end)
return tool_calls
def get_tool_prompt(self, tools_schema: str) -> str:
return f"""
# Tools
You may call one or more functions to assist with the user query.
<tools>
{tools_schema}
</tools>
For function call returns, you should first print <|tool▁calls▁begin|>
For each function call, you should return object like:
<|tool▁call▁begin|>function<|tool▁sep|><function_name>
"""