@@ -204,8 +204,17 @@ static void test_deepseek_v3_1() {
204204 }
205205}
206206
207+ template <typename T>
208+ static void assert_equals (const char * label, const T& expected, const T& actual) {
209+ if (!(expected == actual)) {
210+ std::ostringstream oss; oss << label << " \n Expected: " << expected << " \n Actual: " << actual;
211+ throw std::runtime_error (oss.str ());
212+ }
213+ }
207214
208215static void test_deepseek_v3_1_tool_calls () {
216+ // variant: happy path for when it works as the model card says it should
217+ const char * variant = " simple" ;
209218 common_chat_syntax syntax = {
210219 /* .format = */ COMMON_CHAT_FORMAT_DEEPSEEK_V3_1,
211220 /* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
@@ -215,12 +224,24 @@ static void test_deepseek_v3_1_tool_calls() {
215224 };
216225 const std::string input = " <|tool▁calls▁begin|><|tool▁call▁begin|>get_time<|tool▁sep|>{\" city\" : \" Tokyo\" }<|tool▁call▁end|><|tool▁calls▁end|>" ;
217226 auto msg = common_chat_parse (input, false , syntax);
218- assert_equals (static_cast <std::size_t >(1 ), msg.tool_calls .size ());
219- assert_equals (std::string (" get_time" ), msg.tool_calls [0 ].name );
227+ assert_equals (variant, static_cast <std::size_t >(1 ), msg.tool_calls .size ());
228+ assert_equals (variant, std::string (" get_time" ), msg.tool_calls [0 ].name );
220229 // JSON arguments are dumped without spaces
221- assert_equals (std::string (" {\" city\" :\" Tokyo\" }" ), msg.tool_calls [0 ].arguments );
222- assert_equals (std::string (" " ), msg.content );
223- assert_equals (std::string (" " ), msg.reasoning_content );
230+ assert_equals (variant, std::string (" {\" city\" :\" Tokyo\" }" ), msg.tool_calls [0 ].arguments );
231+ assert_equals (variant, std::string (" " ), msg.content );
232+ assert_equals (variant, std::string (" " ), msg.reasoning_content );
233+
234+ // variant: function + fenced JSON
235+ {
236+ const char * variant = " fenced" ;
237+ const std::string in = " <|tool▁calls▁begin|><|tool▁call▁begin|>function<|tool▁sep|>get_time\n ```json\n {\" city\" : \" Tokyo\" }\n ```<|tool▁call▁end|><|tool▁calls▁end|>" ;
238+ auto m = common_chat_parse (in, false , syntax);
239+ assert_equals<std::size_t >(variant, 1 , m.tool_calls .size ());
240+ assert_equals (variant, std::string (" get_time" ), m.tool_calls [0 ].name );
241+ assert_equals (variant, std::string (" {\" city\" :\" Tokyo\" }" ), m.tool_calls [0 ].arguments );
242+ assert_equals (variant, std::string (" " ), m.content );
243+ assert_equals (variant, std::string (" " ), m.reasoning_content );
244+ }
224245}
225246
226247static void test_with_args (const std::string & input, const std::string & expected, bool parse_as_partial = true , bool is_partial = true ) {
@@ -366,8 +387,6 @@ static void test_positions() {
366387 assert_throws ([&]() { builder.finish (); });
367388 assert_equals<size_t >(0 , builder.pos ());
368389
369- test_deepseek_v3_1_tool_calls ();
370-
371390 builder.move_to (builder.input ().size ());
372391 builder.finish ();
373392 }
@@ -387,6 +406,7 @@ int main() {
387406 test_reasoning ();
388407 test_regex ();
389408 test_deepseek_v3_1 ();
409+ test_deepseek_v3_1_tool_calls ();
390410 std::cout << " All tests passed!\n " ;
391411 return 0 ;
392412}
0 commit comments