File size: 6,192 Bytes
80b7188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import pytest

from rllm.parser import QwenToolParser, R1ToolParser
from rllm.tools.tool_base import ToolCall


class TestQwenToolParser:
    @pytest.fixture
    def parser(self):
        return QwenToolParser()

    def test_empty_response(self, parser):
        """Test parsing empty response."""
        result = parser.parse("")
        assert len(result) == 0

    def test_no_tool_calls(self, parser):
        """Test response with no tool calls."""
        response = "This is a normal response without any tool calls."
        result = parser.parse(response)
        assert len(result) == 0

    def test_single_valid_tool_call(self, parser):
        """Test parsing a single valid tool call."""
        response = """

        <tool_call>{"name": "search_weather", "arguments": {"location": "New York", "unit": "celsius"}}</tool_call>

        """
        result = parser.parse(response)
        assert len(result) == 1
        assert isinstance(result[0], ToolCall)
        assert result[0].name == "search_weather"
        assert result[0].arguments == {"location": "New York", "unit": "celsius"}

    def test_multiple_valid_tool_calls(self, parser):
        """Test parsing multiple valid tool calls."""
        response = """

        <tool_call>{"name": "search_weather", "arguments": {"location": "New York"}}</tool_call>

        <tool_call>{"name": "search_restaurants", "arguments": {"location": "New York", "cuisine": "Italian"}}</tool_call>

        """
        result = parser.parse(response)
        assert len(result) == 2
        assert result[0].name == "search_weather"
        assert result[1].name == "search_restaurants"

    def test_invalid_json_tool_call(self, parser):
        """Test parsing tool call with invalid JSON."""
        response = """

        <tool_call>{"name": "search_weather", "arguments": {invalid json}}</tool_call>

        """
        result = parser.parse(response)
        assert len(result) == 0

    def test_missing_tool_call_end(self, parser):
        """Test parsing tool call with missing end tag."""
        response = """

        <tool_call>{"name": "search_weather", "arguments": {"location": "New York"}}

        """
        result = parser.parse(response)
        assert len(result) == 0

    def test_get_tool_prompt(self, parser):
        """Test tool prompt generation."""
        tools_schema = """

        {

            "name": "search_weather",

            "description": "Search for weather information",

            "parameters": {

                "type": "object",

                "properties": {

                    "location": {"type": "string"}

                }

            }

        }

        """
        prompt = parser.get_tool_prompt(tools_schema)
        assert "<tools>" in prompt
        assert tools_schema in prompt
        assert "<tool_call>" in prompt


class TestR1ToolParser:
    @pytest.fixture
    def parser(self):
        return R1ToolParser()

    def test_empty_response(self, parser):
        """Test parsing empty response."""
        result = parser.parse("")
        assert len(result) == 0

    def test_no_tool_calls(self, parser):
        """Test response with no tool calls."""
        response = "This is a normal response without any tool calls."
        result = parser.parse(response)
        assert len(result) == 0

    def test_single_valid_tool_call(self, parser):
        """Test parsing a single valid tool call."""
        response = f"""

        {parser.tool_call_begin}function{parser.tool_sep}search_weather

        ```json

        {{"location": "New York", "unit": "celsius"}}

        ```

        {parser.tool_call_end}

        """
        result = parser.parse(response)
        assert len(result) == 1
        assert isinstance(result[0], ToolCall)
        assert result[0].name == "search_weather"
        assert result[0].arguments == {"location": "New York", "unit": "celsius"}

    def test_multiple_valid_tool_calls(self, parser):
        """Test parsing multiple valid tool calls."""
        response = f"""

        {parser.tool_call_begin}function{parser.tool_sep}search_weather

        ```json

        {{"location": "New York"}}

        ```

        {parser.tool_call_end}

        {parser.tool_call_begin}function{parser.tool_sep}search_restaurants

        ```json

        {{"location": "New York", "cuisine": "Italian"}}

        ```

        {parser.tool_call_end}

        """
        result = parser.parse(response)
        assert len(result) == 2
        assert result[0].name == "search_weather"
        assert result[1].name == "search_restaurants"

    def test_invalid_json_tool_call(self, parser):
        """Test parsing tool call with invalid JSON."""
        response = f"""

        {parser.tool_call_begin}function{parser.tool_sep}search_weather

        ```json

        {{"location": "New York", invalid json}}

        ```

        {parser.tool_call_end}

        """
        result = parser.parse(response)
        assert len(result) == 0

    def test_missing_tool_call_end(self, parser):
        """Test parsing tool call with missing end tag."""
        response = f"""

        {parser.tool_call_begin}function{parser.tool_sep}search_weather

        ```json

        {{"location": "New York"}}

        ```

        """
        result = parser.parse(response)
        assert len(result) == 0

    def test_missing_function_prefix(self, parser):
        """Test parsing tool call with missing function prefix."""
        response = f"""

        {parser.tool_call_begin}search_weather

        ```json

        {{"location": "New York"}}

        ```

        {parser.tool_call_end}

        """
        result = parser.parse(response)
        assert len(result) == 0

    def test_missing_json_block(self, parser):
        """Test parsing tool call with missing JSON block."""
        response = f"""

        {parser.tool_call_begin}function{parser.tool_sep}search_weather

        {parser.tool_call_end}

        """
        result = parser.parse(response)
        assert len(result) == 0