File size: 6,345 Bytes
dab1b71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
175
176
177
178
179
180
181
182
183
184
185
import os
import sys
import json
import traceback

try:
    from jinja2 import Environment, FileSystemLoader, StrictUndefined
except ImportError:
    print("Error: jinja2 is required to run tests. Please install it using 'pip install jinja2'")
    sys.exit(1)

TEMPLATE_FILE = 'chat_template.jinja'
TEMPLATE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

env = Environment(
    loader=FileSystemLoader(TEMPLATE_DIR),
    undefined=StrictUndefined,
    keep_trailing_newline=True,
    lstrip_blocks=True,
    trim_blocks=True
)

def raise_exception(msg):
    raise Exception(msg)

env.globals['raise_exception'] = raise_exception

try:
    template = env.get_template(TEMPLATE_FILE)
except Exception as e:
    print(f"Error loading template: {e}")
    sys.exit(1)

def run_test(name, messages, tools=None, kwargs=None, expected_in=None, expected_not_in=None, expect_error=False):
    if kwargs is None:
        kwargs = {}
    
    print(f"\n--- Running Test: {name} ---")
    
    try:
        render_kwargs = {'messages': messages, 'add_generation_prompt': True}
        if tools is not None:
            render_kwargs['tools'] = tools
        render_kwargs.update(kwargs)
        
        rendered = template.render(**render_kwargs)
        
        if expect_error:
            print("❌ FAILED: Expected an exception but got none.")
            return False
            
        success = True
        
        if expected_in:
            for ex in expected_in:
                if ex not in rendered:
                    print(f"❌ FAILED: Missing expected string:\n'''{ex}'''")
                    print(f"Rendered:\n{rendered}")
                    success = False
        
        if expected_not_in:
            for n_ex in expected_not_in:
                if n_ex in rendered:
                    print(f"❌ FAILED: Found string that should NOT be present:\n'''{n_ex}'''")
                    print(f"Rendered:\n{rendered}")
                    success = False
                    
        if success:
            print("✅ PASSED")
            return True
        return False
        
    except Exception as e:
        if expect_error:
            print(f"✅ PASSED (Caught expected error: {e})")
            return True
        print(f"❌ FAILED with exception:\n{traceback.format_exc()}")
        return False

# Tests
tests_passed = 0
tests_total = 0

def execute_test(*args, **kwargs):
    global tests_passed, tests_total
    tests_total += 1
    if run_test(*args, **kwargs):
        tests_passed += 1

# 1. auto_disable_thinking_with_tools (Default logic)
execute_test(
    "auto_disable_thinking_with_tools (enabled via kwarg)",
    messages=[{"role": "user", "content": "Hello!"}],
    tools=[{"name": "test_tool"}],
    kwargs={"auto_disable_thinking_with_tools": True},
    expected_in=["<think>\n\n</think>\n\n"], # Should be stripped
)

execute_test(
    "auto_disable_thinking_with_tools (disabled via kwarg -> allows thinking)",
    messages=[{"role": "user", "content": "Hello!"}],
    tools=[{"name": "test_tool"}],
    kwargs={"auto_disable_thinking_with_tools": False},
    expected_in=["<think>\n"],
    expected_not_in=["<think>\n</think>\n"]
)

# 2. inline <|think_on|> overrides auto_disable_thinking_with_tools
execute_test(
    "inline <|think_on|> overrides auto_disable",
    messages=[{"role": "user", "content": "Hello! <|think_on|>"}],
    tools=[{"name": "test_tool"}],
    kwargs={"auto_disable_thinking_with_tools": True},
    expected_in=["<think>\n"],
    expected_not_in=["<think>\n</think>\n", "<|think_on|>"] # Tag must be stripped
)

# 3. Payload truncation
execute_test(
    "max_tool_arg_chars truncation",
    messages=[{"role": "user", "content": "Call tool"}, {"role": "assistant", "content": "", "tool_calls": [{"function": {"name": "test", "arguments": {"param": "1234567890"}}}]}],
    kwargs={"max_tool_arg_chars": 5},
    expected_in=["<parameter=param>\n12345\n[TRUNCATED"]
)

execute_test(
    "max_tool_response_chars truncation",
    messages=[{"role": "user", "content": "Do it"}, {"role": "assistant", "content": "calling", "tool_calls": [{"name": "test"}]}, {"role": "tool", "content": "1234567890"}],
    kwargs={"max_tool_response_chars": 5},
    expected_in=["<tool_response>\n12345\n[TRUNCATED"]
)

# 4. Mid-conversation System Prompt
execute_test(
    "mid-conversation system prompt",
    messages=[{"role": "user", "content": "Hello"}, {"role": "system", "content": "Reminder: Be polite"}],
    expected_in=["<|im_start|>system\nReminder: Be polite<|im_end|>"]
)

# 5. Parallel tools delimiter
execute_test(
    "parallel tools delimiter",
    messages=[{"role": "user", "content": "x"}, {"role": "assistant", "content": "", "tool_calls": [{"name": "t1"}, {"name": "t2"}]}],
    expected_in=["</function>\n</tool_call>\n\n<tool_call>\n<function=t2>"]
)

# 6. Deep Agent Fallback
execute_test(
    "deep agent fallback (no user message)",
    messages=[{"role": "system", "content": "Sys"}, {"role": "tool", "content": "test"}],
    expected_in=["<|im_start|>system\nSys", "<|im_start|>user\n<tool_response>"]
)

# 7. Error Escalation
execute_test(
    "error escalation warnings",
    messages=[
        {"role": "user", "content": "Do it"}, 
        {"role": "tool", "content": "error: something failed"},
        {"role": "assistant", "content": "calling"},
        {"role": "tool", "content": "error: failed again"}
    ],
    expected_in=["⚠️ SYSTEM WARNING: 2 consecutive tool errors", "<think>\n\n</think>\n\n"]
)

run_test(
    "tool_call_format='json' (override)",
    messages=[{"role": "user", "content": "Call tool"}, {"role": "assistant", "content": "", "tool_calls": [{"function": {"name": "test", "arguments": {"par": "1234567890"}}}]}],
    tools=[{"type": "function", "function": {"name": "test", "description": "test tool"}}],
    kwargs={'tool_call_format': 'json'},
    expected_in=[
        'Function calls MUST follow the specified format: a single JSON object with "name" and "arguments"',
        '{"name": "test", "arguments": {"par": "1234567890"}}'
    ]
)

print(f"\n=============================")
print(f"Test Summary: {tests_passed} / {tests_total} passed.")
if tests_passed == tests_total:
    print("All tests passed successfully! 🎉")
    sys.exit(0)
else:
    print("Some tests failed.")
    sys.exit(1)