Team Ai
Datasetpublic

echodict/llama.cpp

version https://git-lfs.github.com/spec/v1 oid sha256:cfc44b7ba25614df70e6b65e3341cae0310163bd32fd31a6b928a542df433faf size 30786

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes479downloads
test-chat-auto-parser.cpp1970 linesDownload Raw Back to tests
1#include "chat-auto-parser-helpers.h"2#include "chat-auto-parser.h"3#include "chat-peg-parser.h"4#include "chat.h"5#include "peg-parser.h"6#include "testing.h"7 8#include <fstream>9#include <iostream>10#include <sstream>11#include <string>12 13using namespace autoparser;14 15static void test_calculate_diff_split_basic(testing & t);16static void test_calculate_diff_split_identical(testing & t);17static void test_calculate_diff_split_common_prefix(testing & t);18static void test_calculate_diff_split_common_suffix(testing & t);19static void test_calculate_diff_split_common_both(testing & t);20static void test_calculate_diff_split_empty_cases(testing & t);21static void test_calculate_diff_split_no_common(testing & t);22static void test_calculate_diff_split_single_char(testing & t);23static void test_calculate_diff_split_overlaps(testing & t);24static void test_calculate_diff_split_tag_boundaries(testing & t);25static void test_calculate_diff_split_generation_prompt(testing & t);26static void test_calculate_diff_split(testing & t);27 28static void test_until_common_prefix_basic(testing & t);29static void test_until_common_prefix(testing & t);30 31static void test_after_common_suffix_basic(testing & t);32static void test_after_common_suffix(testing & t);33 34static void test_analyze_tool_call_pure_json(testing & t);35static void test_analyze_tool_call_function_name_markers(testing & t);36static void test_analyze_tool_call_full_markers(testing & t);37static void test_analyze_tool_call_edge_cases(testing & t);38 39static void test_compare_variants_basic(testing & t);40static void test_compare_variants_messages_modifier(testing & t);41static void test_compare_variants_tools_modifier(testing & t);42static void test_compare_variants_both_modifiers(testing & t);43static void test_compare_variants_template_failure(testing & t);44static void test_compare_variants_identity(testing & t);45static void test_compare_variants(testing & t);46 47// Seed-OSS template tool calling analysis tests48static void test_seed_oss_tool_analysis(testing & t);49static void test_seed_oss_tool_presence(testing & t);50static void test_seed_oss_call_count(testing & t);51static void test_seed_oss_function_names(testing & t);52static void test_seed_oss_argument_count(testing & t);53static void test_seed_oss_args_presence(testing & t);54static void test_seed_oss_tool_with_reasoning(testing & t);55 56// Nemotron template analysis tests57static void test_nemotron_analysis(testing & t);58static void test_nemotron_reasoning_detection(testing & t);59static void test_nemotron_tool_format(testing & t);60 61// CohereForAI template analysis tests62static void test_cohere_reasoning_detection(testing & t);63static void test_cohere_analysis(testing & t);64 65// SmolLM3 template analysis tests66static void test_smollm3_analysis(testing & t);67 68// Marker separation69static void test_marker_separation(testing & t);70 71// standard_json_tools format tests72static void test_standard_json_tools_formats(testing & t);73static void test_standard_json_tools_openai(testing & t);74static void test_standard_json_tools_cohere(testing & t);75static void test_standard_json_tools_function_key(testing & t);76 77// normalize_quotes_to_json tests78static void test_normalize_quotes_to_json(testing & t);79static void test_normalize_quotes_with_embedded_quotes(testing & t);80 81// TAG_WITH_TAGGED argument parsing tests82static void test_tagged_args_with_embedded_quotes(testing & t);83 84int main(int argc, char * argv[]) {85    testing t(std::cout);86    t.verbose = true;87 88    // usage: test-chat-auto-parser-helpers [filter_regex]89 90    if (argc > 1) {91        t.set_filter(argv[1]);92    }93 94    t.test("diff_split", test_calculate_diff_split);95    t.test("common_prefix", test_until_common_prefix);96    t.test("common_suffix", test_after_common_suffix);97    t.test("compare_variants", test_compare_variants);98    t.test("segments", test_marker_separation);99    t.test("seed_oss_diffs", test_seed_oss_tool_analysis);100    t.test("cohere", test_cohere_analysis);101    t.test("nemotron", test_nemotron_analysis);102    t.test("smollm3", test_smollm3_analysis);103    t.test("standard_json_tools", test_standard_json_tools_formats);104    t.test("normalize_quotes_to_json", test_normalize_quotes_to_json);105    t.test("tagged_args_embedded_quotes", test_tagged_args_with_embedded_quotes);106 107    return t.summary();108}109 110static void test_marker_separation(testing & t) {111    auto single_square_marker = segmentize_markers("pre_marker[marker]post_marker");112    auto single_diag_marker = segmentize_markers("pre_marker<marker>post_marker");113    auto paired_markers = segmentize_markers("<hello>world</hello>");114    auto double_different_markers = segmentize_markers("<hello>[hello]<world>[world]");115    auto in_between = segmentize_markers("im<blue>daba<dee>da[hey]");116 117    t.test("single_square_marker", [&] (testing & t) {118        t.assert_equal("first is text", segment_type::TEXT, single_square_marker[0].type);119        t.assert_equal("second is marker", segment_type::MARKER, single_square_marker[1].type);120        t.assert_equal("last is text", segment_type::TEXT, single_square_marker[2].type);121 122        t.assert_equal("first is 'pre_marker'", "pre_marker", single_square_marker[0].value);123        t.assert_equal("second is '[marker]'", "[marker]", single_square_marker[1].value);124        t.assert_equal("last is 'post_marker'", "post_marker", single_square_marker[2].value);125    });126 127    t.test("single_diagonal_marker", [&] (testing & t) {128        t.assert_equal("first is text", segment_type::TEXT, single_diag_marker[0].type);129        t.assert_equal("second is marker", segment_type::MARKER, single_diag_marker[1].type);130        t.assert_equal("last is text", segment_type::TEXT, single_diag_marker[2].type);131 132        t.assert_equal("first is 'pre_marker'", "pre_marker", single_diag_marker[0].value);133        t.assert_equal("second is '<marker>'", "<marker>", single_diag_marker[1].value);134        t.assert_equal("last is 'post_marker'", "post_marker", single_diag_marker[2].value);135    });136 137    t.test("paired_markers", [&] (testing & t) {138        t.assert_equal("first is marker", segment_type::MARKER, paired_markers[0].type);139        t.assert_equal("second is text", segment_type::TEXT, paired_markers[1].type);140        t.assert_equal("third is marker", segment_type::MARKER, paired_markers[2].type);141 142        t.assert_equal("first is '<hello>'", "<hello>", paired_markers[0].value);143        t.assert_equal("second is 'world'", "world", paired_markers[1].value);144        t.assert_equal("third is '</hello>'", "</hello>", paired_markers[2].value);145    });146 147    t.test("double_different_markers", [&] (testing & t) {148        t.assert_equal("first is marker", segment_type::MARKER, double_different_markers[0].type);149        t.assert_equal("second is marker", segment_type::MARKER, double_different_markers[1].type);150        t.assert_equal("third is marker", segment_type::MARKER, double_different_markers[2].type);151        t.assert_equal("fourth is marker", segment_type::MARKER, double_different_markers[3].type);152 153        t.assert_equal("first is '<hello>'", "<hello>", double_different_markers[0].value);154        t.assert_equal("second is '[hello]'", "[hello]", double_different_markers[1].value);155        t.assert_equal("third is '<world>'", "<world>", double_different_markers[2].value);156        t.assert_equal("fourth is '[world]'", "[world]", double_different_markers[3].value);157    });158 159    t.test("in_between", [&] (testing & t) {160        t.assert_equal("first is text", segment_type::TEXT, in_between[0].type);161        t.assert_equal("second is marker", segment_type::MARKER, in_between[1].type);162        t.assert_equal("third is text", segment_type::TEXT, in_between[2].type);163        t.assert_equal("fourth is marker", segment_type::MARKER, in_between[3].type);164        t.assert_equal("fifth is text", segment_type::TEXT, in_between[4].type);165        t.assert_equal("sixth is marker", segment_type::MARKER, in_between[5].type);166 167        t.assert_equal("first is 'im'", "im", in_between[0].value);168        t.assert_equal("second is '<blue>'", "<blue>", in_between[1].value);169        t.assert_equal("third is 'daba'", "daba", in_between[2].value);170        t.assert_equal("fourth is '<dee>'", "<dee>", in_between[3].value);171        t.assert_equal("fifth is 'da'", "da", in_between[4].value);172        t.assert_equal("sixth is '[hey]'", "[hey]", in_between[5].value);173    });174}175 176static void test_calculate_diff_split(testing & t) {177    t.test("calculate_diff_split basic", test_calculate_diff_split_basic);178    t.test("calculate_diff_split identical", test_calculate_diff_split_identical);179    t.test("calculate_diff_split common prefix", test_calculate_diff_split_common_prefix);180    t.test("calculate_diff_split common suffix", test_calculate_diff_split_common_suffix);181    t.test("calculate_diff_split common both", test_calculate_diff_split_common_both);182    t.test("calculate_diff_split empty cases", test_calculate_diff_split_empty_cases);183    t.test("calculate_diff_split no common", test_calculate_diff_split_no_common);184    t.test("calculate_diff_split single char", test_calculate_diff_split_single_char);185    t.test("calculate_diff_split overlaps", test_calculate_diff_split_overlaps);186    t.test("calculate_diff_split tag boundaries", test_calculate_diff_split_tag_boundaries);187    t.test("calculate_diff_split generation prompt", test_calculate_diff_split_generation_prompt);188}189 190static void test_calculate_diff_split_basic(testing & t) {191    diff_split result = calculate_diff_split("hello world", "hello test");192    t.assert_equal("prefix should be 'hello '", "hello ", result.prefix);193    t.assert_equal("left should be 'world'", "world", result.left);194    t.assert_equal("right should be 'test'", "test", result.right);195    t.assert_equal("suffix should be empty", "", result.suffix);196 197    result = calculate_diff_split("abc", "xyz");198    t.assert_equal("prefix should be empty", "", result.prefix);199    t.assert_equal("left should be 'abc'", "abc", result.left);200    t.assert_equal("right should be 'xyz'", "xyz", result.right);201    t.assert_equal("suffix should be empty", "", result.suffix);202 203    result = calculate_diff_split("prefixA suffix", "prefixB suffix");204    t.assert_equal("prefix should be 'prefix'", "prefix", result.prefix);205    t.assert_equal("left should be 'A'", "A", result.left);206    t.assert_equal("right should be 'B'", "B", result.right);207    t.assert_equal("suffix should be ' suffix'", " suffix", result.suffix);208}209 210static void test_calculate_diff_split_identical(testing & t) {211    diff_split result = calculate_diff_split("hello", "hello");212    t.assert_equal("prefix should be 'hello'", "hello", result.prefix);213    t.assert_equal("left should be empty", "", result.left);214    t.assert_equal("right should be empty", "", result.right);215    t.assert_equal("suffix should be empty", "", result.suffix);216 217    result = calculate_diff_split("", "");218    t.assert_equal("prefix should be empty", "", result.prefix);219    t.assert_equal("left should be empty", "", result.left);220    t.assert_equal("right should be empty", "", result.right);221    t.assert_equal("suffix should be empty", "", result.suffix);222 223    result = calculate_diff_split("a", "a");224    t.assert_equal("prefix should be 'a'", "a", result.prefix);225    t.assert_equal("left should be empty", "", result.left);226    t.assert_equal("right should be empty", "", result.right);227    t.assert_equal("suffix should be empty", "", result.suffix);228 229    result = calculate_diff_split("<row><row><row><your><boat><gently>", "<row><row><row><your><boat><gently>");230    t.assert_equal("prefix should be '<row><row><row><your><boat><gently>'", "<row><row><row><your><boat><gently>", result.prefix);231    t.assert_equal("left should be empty", "", result.left);232    t.assert_equal("right should be empty", "", result.right);233    t.assert_equal("suffix should be empty", "", result.suffix);234}235 236static void test_calculate_diff_split_common_prefix(testing & t) {237    diff_split result = calculate_diff_split("abcdef", "abcxyz");238    t.assert_equal("prefix should be 'abc'", "abc", result.prefix);239    t.assert_equal("left should be 'def'", "def", result.left);240    t.assert_equal("right should be 'xyz'", "xyz", result.right);241    t.assert_equal("suffix should be empty", "", result.suffix);242 243    result = calculate_diff_split("same", "sameagain");244    t.assert_equal("prefix should be 'same'", "same", result.prefix);245    t.assert_equal("left should be empty", "", result.left);246    t.assert_equal("right should be 'again'", "again", result.right);247    t.assert_equal("suffix should be empty", "", result.suffix);248 249    result = calculate_diff_split("test", "testing");250    t.assert_equal("prefix should be 'test'", "test", result.prefix);251    t.assert_equal("left should be empty", "", result.left);252    t.assert_equal("right should be 'ing'", "ing", result.right);253    t.assert_equal("suffix should be empty", "", result.suffix);254}255 256static void test_calculate_diff_split_common_suffix(testing & t) {257    diff_split result = calculate_diff_split("123end", "456end");258    t.assert_equal("prefix should be empty", "", result.prefix);259    t.assert_equal("left should be '123'", "123", result.left);260    t.assert_equal("right should be '456'", "456", result.right);261    t.assert_equal("suffix should be 'end'", "end", result.suffix);262 263    result = calculate_diff_split("start", "end");264    t.assert_equal("prefix should be empty", "", result.prefix);265    t.assert_equal("left should be 'start'", "start", result.left);266    t.assert_equal("right should be 'end'", "end", result.right);267    t.assert_equal("suffix should be empty", "", result.suffix);268 269    result = calculate_diff_split("abcsuffix", "xyzsuffix");270    t.assert_equal("prefix should be empty", "", result.prefix);271    t.assert_equal("left should be 'abc'", "abc", result.left);272    t.assert_equal("right should be 'xyz'", "xyz", result.right);273    t.assert_equal("suffix should be 'suffix'", "suffix", result.suffix);274}275 276static void test_calculate_diff_split_common_both(testing & t) {277    diff_split result = calculate_diff_split("helloXworld", "helloYworld");278    t.assert_equal("prefix should be 'hello'", "hello", result.prefix);279    t.assert_equal("left should be 'X'", "X", result.left);280    t.assert_equal("right should be 'Y'", "Y", result.right);281    t.assert_equal("suffix should be 'world'", "world", result.suffix);282 283    result = calculate_diff_split("ABCmiddleXYZ", "ABCdifferentXYZ");284    t.assert_equal("prefix should be 'ABC'", "ABC", result.prefix);285    t.assert_equal("left should be 'middle'", "middle", result.left);286    t.assert_equal("right should be 'different'", "different", result.right);287    t.assert_equal("suffix should be 'XYZ'", "XYZ", result.suffix);288 289    result = calculate_diff_split("startAend", "startBend");290    t.assert_equal("prefix should be 'start'", "start", result.prefix);291    t.assert_equal("left should be 'A'", "A", result.left);292    t.assert_equal("right should be 'B'", "B", result.right);293    t.assert_equal("suffix should be 'end'", "end", result.suffix);294 295    // Edge case: common prefix and suffix overlap296    result = calculate_diff_split("aa", "ab");297    t.assert_equal("prefix should be 'a'", "a", result.prefix);298    t.assert_equal("left should be 'a'", "a", result.left);299    t.assert_equal("right should be 'b'", "b", result.right);300    t.assert_equal("suffix should be empty", "", result.suffix);301}302 303static void test_calculate_diff_split_empty_cases(testing & t) {304    // Empty left, non-empty right305    diff_split result = calculate_diff_split("", "hello");306    t.assert_equal("prefix should be empty", "", result.prefix);307    t.assert_equal("left should be empty", "", result.left);308    t.assert_equal("right should be 'hello'", "hello", result.right);309    t.assert_equal("suffix should be empty", "", result.suffix);310 311    // Non-empty left, empty right312    result = calculate_diff_split("hello", "");313    t.assert_equal("prefix should be empty", "", result.prefix);314    t.assert_equal("left should be 'hello'", "hello", result.left);315    t.assert_equal("right should be empty", "", result.right);316    t.assert_equal("suffix should be empty", "", result.suffix);317 318    // Both empty319    result = calculate_diff_split("", "");320    t.assert_equal("prefix should be empty", "", result.prefix);321    t.assert_equal("left should be empty", "", result.left);322    t.assert_equal("right should be empty", "", result.right);323    t.assert_equal("suffix should be empty", "", result.suffix);324 325    // Left single char, empty right326    result = calculate_diff_split("a", "");327    t.assert_equal("prefix should be empty", "", result.prefix);328    t.assert_equal("left should be 'a'", "a", result.left);329    t.assert_equal("right should be empty", "", result.right);330    t.assert_equal("suffix should be empty", "", result.suffix);331 332    // Empty left, right single char333    result = calculate_diff_split("", "a");334    t.assert_equal("prefix should be empty", "", result.prefix);335    t.assert_equal("left should be empty", "", result.left);336    t.assert_equal("right should be 'a'", "a", result.right);337    t.assert_equal("suffix should be empty", "", result.suffix);338}339 340static void test_calculate_diff_split_no_common(testing & t) {341    diff_split result = calculate_diff_split("abc", "xyz");342    t.assert_equal("prefix should be empty", "", result.prefix);343    t.assert_equal("left should be 'abc'", "abc", result.left);344    t.assert_equal("right should be 'xyz'", "xyz", result.right);345    t.assert_equal("suffix should be empty", "", result.suffix);346 347    result = calculate_diff_split("left", "right");348    // The algorithm finds "t" as a common suffix since both strings end with 't'349    // This is the algorithm's actual behavior - it finds maximal common suffix350    t.assert_equal("prefix should be empty", "", result.prefix);351    t.assert_equal("left should be 'lef'", "lef", result.left);352    t.assert_equal("right should be 'righ'", "righ", result.right);353    t.assert_equal("suffix should be 't'", "t", result.suffix);354 355    result = calculate_diff_split("123", "456");356    t.assert_equal("prefix should be empty", "", result.prefix);357    t.assert_equal("left should be '123'", "123", result.left);358    t.assert_equal("right should be '456'", "456", result.right);359    t.assert_equal("suffix should be empty", "", result.suffix);360}361 362static void test_calculate_diff_split_single_char(testing & t) {363    diff_split result = calculate_diff_split("a", "b");364    t.assert_equal("prefix should be empty", "", result.prefix);365    t.assert_equal("left should be 'a'", "a", result.left);366    t.assert_equal("right should be 'b'", "b", result.right);367    t.assert_equal("suffix should be empty", "", result.suffix);368 369    result = calculate_diff_split("a", "a");370    t.assert_equal("prefix should be 'a'", "a", result.prefix);371    t.assert_equal("left should be empty", "", result.left);372    t.assert_equal("right should be empty", "", result.right);373    t.assert_equal("suffix should be empty", "", result.suffix);374 375    result = calculate_diff_split("a", "ab");376    t.assert_equal("prefix should be 'a'", "a", result.prefix);377    t.assert_equal("left should be empty", "", result.left);378    t.assert_equal("right should be 'b'", "b", result.right);379    t.assert_equal("suffix should be empty", "", result.suffix);380 381    result = calculate_diff_split("ab", "a");382    t.assert_equal("prefix should be 'a'", "a", result.prefix);383    t.assert_equal("left should be 'b'", "b", result.left);384    t.assert_equal("right should be empty", "", result.right);385    t.assert_equal("suffix should be empty", "", result.suffix);386}387 388static void test_calculate_diff_split_overlaps(testing & t) {389    // One string is substring of another390    diff_split result = calculate_diff_split("test", "testing");391    t.assert_equal("prefix should be 'test'", "test", result.prefix);392    t.assert_equal("left should be empty", "", result.left);393    t.assert_equal("right should be 'ing'", "ing", result.right);394    t.assert_equal("suffix should be empty", "", result.suffix);395 396    result = calculate_diff_split("testing", "test");397    t.assert_equal("prefix should be 'test'", "test", result.prefix);398    t.assert_equal("left should be 'ing'", "ing", result.left);399    t.assert_equal("right should be empty", "", result.right);400    t.assert_equal("suffix should be empty", "", result.suffix);401 402    // Similar strings with one extra char at start403    result = calculate_diff_split("Xtest", "Ytest");404    // The algorithm finds "test" as a common suffix since both strings end with "test"405    // This is the algorithm's actual behavior - it finds maximal common suffix406    t.assert_equal("prefix should be empty", "", result.prefix);407    t.assert_equal("left should be 'X'", "X", result.left);408    t.assert_equal("right should be 'Y'", "Y", result.right);409    t.assert_equal("suffix should be 'test'", "test", result.suffix);410 411    // Similar strings with one extra char at end412    result = calculate_diff_split("testX", "testY");413    t.assert_equal("prefix should be 'test'", "test", result.prefix);414    t.assert_equal("left should be 'X'", "X", result.left);415    t.assert_equal("right should be 'Y'", "Y", result.right);416    t.assert_equal("suffix should be empty", "", result.suffix);417 418    // Strings that are reverses419    result = calculate_diff_split("abc", "cba");420    t.assert_equal("prefix should be empty", "", result.prefix);421    t.assert_equal("left should be 'abc'", "abc", result.left);422    t.assert_equal("right should be 'cba'", "cba", result.right);423    t.assert_equal("suffix should be empty", "", result.suffix);424}425 426static void test_calculate_diff_split_tag_boundaries(testing & t) {427    // Test with unclosed XML tags428    diff_split result = calculate_diff_split("test<tag", "test>content");429    // The fix_tag_boundaries should move incomplete tags appropriately430    t.assert_true("prefix should start with 'test'", result.prefix.find("test") == 0);431    t.assert_true("should handle tag boundaries", result.left != "" || result.right != "" || result.suffix != "");432 433    // Test with unclosed brackets434    result = calculate_diff_split("test[", "test]value");435    t.assert_true("should handle bracket boundaries", result.left != "" || result.right != "" || result.suffix != "");436 437    // Test with partial tags on both sides438    result = calculate_diff_split("prefix<tag>", "prefix</tag>suffix");439    // fix_tag_boundaries moves the incomplete '<' from prefix to left/right440    t.assert_equal("prefix should be 'prefix'", "prefix", result.prefix);441    t.assert_equal("left should be '<tag>'", "<tag>", result.left);442    t.assert_equal("right should be '</tag>suffix'", "</tag>suffix", result.right);443    t.assert_equal("suffix should be empty", "", result.suffix);444 445    // Test with complex nested tags446    result = calculate_diff_split("prefix<div>content</div>", "prefix<div>different</div>");447    // Algorithm finds "ent</div>" as a common suffix because both strings end with it448    // This is the actual algorithm behavior, though not semantically ideal449    t.assert_equal("prefix should be 'prefix<div>'", "prefix<div>", result.prefix);450    t.assert_equal("left should be 'cont'", "cont", result.left);451    t.assert_equal("right should be 'differ'", "differ", result.right);452    t.assert_equal("suffix should be 'ent</div>'", "ent</div>", result.suffix);453 454    // Test with unclosed angle bracket455    result = calculate_diff_split("Hello <world>", "Hello test");456    t.assert_equal("prefix should be 'Hello '", "Hello ", result.prefix);457    t.assert_true("left should contain '<world>'", result.left.find("<world>") != std::string::npos);458    t.assert_equal("right should be 'test'", "test", result.right);459    t.assert_equal("suffix should be empty", "", result.suffix);460 461    // Test with unclosed square bracket462    result = calculate_diff_split("test [array]", "test other");463    t.assert_equal("prefix should be 'test '", "test ", result.prefix);464    t.assert_true("left should contain '[array]'", result.left.find("[array]") != std::string::npos);465    t.assert_equal("right should be 'other'", "other", result.right);466    t.assert_equal("suffix should be empty", "", result.suffix);467 468    // Test empty prefix and suffix with tags469    result = calculate_diff_split("<tag>left</tag>", "<tag>righ</tag>");470    t.assert_equal("prefix should be '<tag>'", "<tag>", result.prefix);471    t.assert_equal("left should be 'left'", "left", result.left);472    t.assert_equal("right should be 'righ'", "righ", result.right);473    t.assert_equal("suffix should be '</tag>'", "</tag>", result.suffix);474 475    {476        // real case from template tests, simplified477        std::string left  = "PREFIX</think>Sure";478        std::string right = "PREFIX<think>Lemme think</think>Sure";479        result            = calculate_diff_split(left, right);480        t.assert_equal("prefix should be PREFIX", "PREFIX", result.prefix);481        t.assert_equal("suffix should be </think>Sure", "</think>Sure", result.suffix);482        t.assert_equal("left should be empty", "", result.left);483        t.assert_equal("right should be <think>Lemme think", "<think>Lemme think", result.right);484    }485 486    {487        // Real case: special tokens with |> boundary issue488        // The suffix starts with |> which should be moved to complete <|END_RESPONSE and <|END_ACTION489        std::string prefix    = "SOME_PREFIX";490        std::string suffix    = "|><|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>";491        std::string left_diff = "<|START_RESPONSE|>Let me help you.<|END_RESPONSE";492        std::string right_diff =493            "<|START_THINKING|><|END_THINKING|><|START_ACTION|>[\n"494            "    {\"tool_call_id\": \"0\", \"tool_name\": \"test_function_name\", "495            "\"parameters\": {\"param1\": \"value1\", \"param2\": \"value2\"}}\n"496            "]<|END_ACTION";497 498        std::string left  = prefix + left_diff + suffix;499        std::string right = prefix + right_diff + suffix;500        result            = calculate_diff_split(left, right);501 502        t.assert_equal("special token prefix", prefix, result.prefix);503        // The |> should be moved from suffix to complete the tokens504        t.assert_equal("special token left", "<|START_RESPONSE|>Let me help you.<|END_RESPONSE|>", result.left);505        t.assert_true("special token right ends with |>", result.right.find("<|END_ACTION|>") != std::string::npos);506        t.assert_equal("special token suffix", "<|END_OF_TURN_TOKEN|><|START_OF_TURN_TOKEN|><|CHATBOT_TOKEN|>",507                       result.suffix);508    }509}510 511static void test_calculate_diff_split_generation_prompt(testing & t) {512    // ChatML thinking template: left is a prefix of right, generation_prompt is the appended part.513    // The trailing \n in left matches the trailing \n in the generation_prompt, causing514    // the suffix matcher to steal it and rotate the diff result.515    {516        // Simplified reproduction: left ends with \n, right = left + "<|im_start|>assistant\n<think>\n"517        std::string left  = "<|im_start|>user\nHello<|im_end|>\n";518        std::string right = left + "<|im_start|>assistant\n<think>\n";519        diff_split result = calculate_diff_split(left, right);520        t.assert_equal("chatml prefix", left, result.prefix);521        t.assert_equal("chatml left", "", result.left);522        t.assert_equal("chatml right should be generation prompt",523                       "<|im_start|>assistant\n<think>\n", result.right);524        t.assert_equal("chatml suffix", "", result.suffix);525    }526 527    {528        // More realistic: longer conversation ending with tool_response529        std::string common =530            "<|im_start|>system\nYou are a helpful assistant.<|im_end|>\n"531            "<|im_start|>user\nSearch for files<|im_end|>\n"532            "<|im_start|>assistant\n<think>\nLet me search.\n</think>\n\n"533            "<tool_call>\n<function=search>\n</function>\n</tool_call><|im_end|>\n"534            "<|im_start|>user\n<tool_response>\nNo files found\n</tool_response><|im_end|>\n";535        std::string left  = common;536        std::string right = common + "<|im_start|>assistant\n<think>\n";537        diff_split result = calculate_diff_split(left, right);538        t.assert_equal("tool_response left", "", result.left);539        t.assert_equal("tool_response right should be generation prompt",540                       "<|im_start|>assistant\n<think>\n", result.right);541    }542}543 544static void test_until_common_prefix(testing & t) {545    t.test("until_common_prefix basic", test_until_common_prefix_basic);546}547 548static void test_until_common_prefix_basic(testing & t) {549    // Test case from the user request550    std::string result = until_common_prefix("<function name=foo><arg name=bar>", "<arg name=bar>", "<arg name=baz>");551    t.assert_equal("untilCommonPrefix should return '<function name=foo>'", "<function name=foo>", result);552 553    // Additional test cases to ensure robustness554    // Test with different common prefix lengths555    result = until_common_prefix("prefix<test>suffix", "<test>different", "<test>other");556    t.assert_equal("should return 'prefix'", "prefix", result);557 558    // Test when common prefix is at the start559    result = until_common_prefix("<common>rest", "<common>left", "<common>right");560    t.assert_equal("should return empty string when common prefix at start", "", result);561 562    // Test when there's no common prefix563    result = until_common_prefix("something", "left", "right");564    t.assert_equal("should return empty string when no common prefix", "", result);565 566    // Test with empty strings567    result = until_common_prefix("test", "", "right");568    t.assert_equal("should return empty string when left is empty", "", result);569 570    // Test with longer common prefix571    result = until_common_prefix("abcXYZ<shared_prefix>rest", "<shared_prefix>left", "<shared_prefix>right");572    t.assert_equal("should return 'abcXYZ'", "abcXYZ", result);573}574 575static void test_after_common_suffix(testing & t) {576    t.test("after_common_suffix basic", test_after_common_suffix_basic);577}578 579static void test_after_common_suffix_basic(testing & t) {580    // Test case from the user request581    std::string result = after_common_suffix("<function name=foo><arg name=bar>100</arg></function>",582                                            "<arg name=bar>100</arg>",583                                            "<arg name=baz>535</arg>");584    t.assert_equal("afterCommonSuffix should return '</function>'", "</function>", result);585 586    // Test when common suffix is at the end587    result = after_common_suffix("rest<common>", "left<common>", "right<common>");588    t.assert_equal("should return empty string when common suffix at end", "", result);589 590    // Test with empty strings591    result = after_common_suffix("test", "left", "");592    t.assert_equal("should return empty string when right is empty", "", result);593 594    // Test case with XML-like structure similar to the main example595    result = after_common_suffix("<outer><inner>value</inner></outer>",596                                "<inner>value</inner>",597                                "<inner>different</inner>");598    t.assert_equal("should return '</outer>'", "</outer>", result);599 600    // Test with longer common suffix appearing at the end of full601    result = after_common_suffix("prefix<shared>rest</shared>", "prefix<shared>left</shared>", "prefix<shared>right</shared>");602    t.assert_equal("should return '' when common suffix is at end of full", "", result);603 604    // Test with common suffix appearing in middle but not at end605    result = after_common_suffix("<tag>content</tag><extra>", "<tag>value</tag>", "<tag>other</tag>");606    t.assert_equal("should return '<extra>' when common suffix appears before end", "<extra>", result);607 608    // Test with multi-character common suffix at the very end of full609    result = after_common_suffix("start<middle>end</middle>", "prefix<middle>left</middle>", "prefix<middle>right</middle>");610    t.assert_equal("should return '' when common suffix </middle> is at end of full", "", result);611}612 613static void test_compare_variants(testing & t) {614    t.test("compare_variants basic", test_compare_variants_basic);615    t.test("compare_variants messages modifier", test_compare_variants_messages_modifier);616    t.test("compare_variants tools modifier", test_compare_variants_tools_modifier);617    t.test("compare_variants both modifiers", test_compare_variants_both_modifiers);618    t.test("compare_variants template failure", test_compare_variants_template_failure);619    t.test("compare_variants identity", test_compare_variants_identity);620}621 622static void test_compare_variants_basic(testing & t) {623    // Create a simple template that just echoes messages624    common_chat_template tmpl("{{ messages[0]['content'] }}", "", "");625 626    template_params params;627    params.messages = json::array({628        json {{"role", "user"}, {"content", "Hello"}}629    });630 631    auto modifier = [](template_params & p) {632        p.messages[0]["content"] = "World";633    };634 635    auto result = ::compare_variants(tmpl, params, modifier);636 637    if (!t.assert_true("result should have value", result.has_value())) {638        return;639    }640    // The template might not output anything if messages is empty or format is different641    // Check that we get a valid result642    t.assert_true("prefix or left should have content", !result->diff.prefix.empty() || !result->diff.left.empty());643}644 645static void test_compare_variants_messages_modifier(testing & t) {646    // Test with messages modifier only647    common_chat_template tmpl("{% for message in messages %}{{ message['role'] }}:{{ message['content'] }}{% endfor %}", "", "");648 649    template_params params;650    params.messages = json::array({651        json {{"role", "user"}, {"content", "A"}}652    });653 654    auto modifier = [](template_params & p) {655        p.messages[0]["content"] = "B";656    };657 658    std::optional<compare_variants_result> result = ::compare_variants(tmpl, params, modifier);659 660    if (!t.assert_true("result should have value", result.has_value())) {661        return;662    }663    t.assert_equal("left should be 'A'", "A", result->diff.left);664    t.assert_equal("right should be 'B'", "B", result->diff.right);665}666 667static void test_compare_variants_tools_modifier(testing & t) {668    // Test with tools modifier only669    common_chat_template tmpl(670        "{% for tool in tools %}{{ tool['name'] }}{% endfor %}", "", "");671 672    template_params params;673    params.tools = json::array({674        json {{"name", "foo"}}675    });676 677    auto modifier = [](template_params & p) {678        p.tools[0]["name"] = "bar";679    };680 681    auto result = ::compare_variants(tmpl, params, modifier);682 683    if (!t.assert_true("result should have value", result.has_value())) {684        return;685    }686    t.assert_equal("left should be 'foo'", "foo", result->diff.left);687    t.assert_equal("right should be 'bar'", "bar", result->diff.right);688}689 690static void test_compare_variants_both_modifiers(testing & t) {691    // Test with both messages and tools modifiers using the for loop approach692    common_chat_template tmpl(693        "{% for message in messages %}{{ message['role'] }}:{{ message['content'] }}{% endfor %}", "", "");694 695    template_params params;696    params.messages = json::array({697        json {{"role", "user"}, {"content", "A"}}698    });699 700    auto modifier = [](template_params & p) {701        p.messages[0]["content"] = "B";702        p.messages[0]["role"] = "newuser";703    };704 705    auto result = ::compare_variants(tmpl, params, modifier);706 707    if (!t.assert_true("result should have value", result.has_value())) {708        return;709    }710    t.assert_equal("left should be 'user:A'", "user:A", result->diff.left);711    t.assert_equal("right should be 'newuser:B'", "newuser:B", result->diff.right);712}713 714static void test_compare_variants_template_failure(testing & t) {715    // Test with template that causes failure during application (not construction)716    // We use a valid template syntax but one that will fail during application717    common_chat_template tmpl("{{ messages[0]['nonexistent_field'] }}", "", "");718 719    template_params params;720    params.messages = json::array({721        json {{"role", "user"}, {"content", "Hello"}}722    });723 724    auto modifier = [](template_params & p) {725        p.messages[0]["content"] = "World";726    };727 728    auto result = ::compare_variants(tmpl, params, modifier);729 730    t.assert_true("result should be nullopt on template failure", !result.has_value());731}732 733static void test_compare_variants_identity(testing & t) {734    // Test with identity modifier (no change)735    common_chat_template tmpl("{{ messages[0]['content'] }}", "", "");736 737    template_params params;738    params.messages = json::array({739        json {{"role", "user"}, {"content", "Hello"}}740    });741 742    // No modifier - should use identity743    auto result = ::compare_variants(tmpl, params, nullptr);744 745    if (!t.assert_true("result should have value", result.has_value())) {746        return;747    }748    t.assert_equal("prefix should be 'Hello'", "Hello", result->diff.prefix);749    t.assert_equal("left should be empty", "", result->diff.left);750    t.assert_equal("right should be empty", "", result->diff.right);751    t.assert_equal("suffix should be empty", "", result->diff.suffix);752}753 754// ============================================================================755// Seed-OSS Template Tool Calling Analysis Tests756// ============================================================================757 758static void test_seed_oss_tool_analysis(testing & t) {759    t.test("Seed-OSS tool presence", test_seed_oss_tool_presence);760    t.test("Seed-OSS call count", test_seed_oss_call_count);761    t.test("Seed-OSS function names", test_seed_oss_function_names);762    t.test("Seed-OSS argument count", test_seed_oss_argument_count);763    t.test("Seed-OSS args presence", test_seed_oss_args_presence);764    t.test("Seed-OSS tool with reasoning", test_seed_oss_tool_with_reasoning);765}766 767// Helper to load Seed-OSS template768static common_chat_template load_seed_oss_template(testing & t) {769    std::string template_path = "models/templates/ByteDance-Seed-OSS.jinja";770    std::ifstream fin(template_path, std::ios::binary);771    std::ostringstream buf;772    if (fin.is_open()) {773        buf << fin.rdbuf();774    }775    std::string template_source = buf.str();776    common_chat_template tmpl(template_source, "", "");777    t.assert_true("Seed-OSS template loaded successfully", template_source.length() > 0);778    return tmpl;779}780 781// Helper to build tool call JSON782static json build_tool_call(const std::string & name, const json & args, const std::string & id = "call_001") {783    return json{784        {"id", id},785        {"type", "function"},786        {"function", json{787            {"name", name},788            {"arguments", args}789        }}790    };791}792 793// Helper to build tools definition794static json build_tools_definition() {795    json parameters_schema = json::object();796    parameters_schema["type"] = "object";797    parameters_schema["properties"] = json::object();798    parameters_schema["properties"]["param1"] = json::object({799        {"type", "string"},800        {"description", "First parameter"}801    });802    parameters_schema["properties"]["param2"] = json::object({803        {"type", "string"},804        {"description", "Second parameter"}805    });806    parameters_schema["required"] = json::array({"param1", "param2"});807 808    return json::array({809        json{810            {"type", "function"},811            {"function", json{812                {"name", "test_function_name"},813                {"description", "A test function for debugging"},814                {"parameters", parameters_schema}815            }}816        }817    });818}819 820// T1: Compare with/without tool call (user, assistant)821static void test_seed_oss_tool_presence(testing & t) {822    common_chat_template tmpl = load_seed_oss_template(t);823 824    json assistant_no_tools = json{825        {"role", "assistant"},826        {"content", "Let me help you."}827    };828 829    json assistant_with_tools = json{830        {"role", "assistant"},831        {"content", nullptr},832        {"tool_calls", json::array({833            build_tool_call("test_function_name", json::object({{"param1", "value1"}, {"param2", "value2"}}))834        })}835    };836 837    json user_msg = json{838        {"role", "user"},839        {"content", "Hello, please help me."}840    };841 842    template_params params_no_tools;843    params_no_tools.messages = json::array({user_msg, assistant_no_tools});844    params_no_tools.tools = build_tools_definition();845    params_no_tools.add_generation_prompt = false;846    params_no_tools.enable_thinking = true;847 848    template_params params_with_tools;849    params_with_tools.messages = json::array({user_msg, assistant_with_tools});850    params_with_tools.tools = build_tools_definition();851    params_with_tools.add_generation_prompt = false;852    params_with_tools.enable_thinking = true;853 854    auto result = ::compare_variants(tmpl, params_no_tools,855        [&](template_params & p) {856            p.messages = params_with_tools.messages;857        });858 859    if (!t.assert_true("T1 result should have value", result.has_value())) {860        return;861    }862 863    const auto & diff = result->diff;864    t.assert_true("T1 prefix should contain system", diff.prefix.find("system") != std::string::npos);865    t.assert_true("T1 prefix should contain user", diff.prefix.find("user") != std::string::npos);866    t.assert_true("T1 prefix should contain assistant", diff.prefix.find("assistant") != std::string::npos);867 868    // Left should be the assistant content without tool869    t.assert_equal("T1 left should contain 'Let me help you.'", "Let me help you.", diff.left);870 871    // Right should contain the tool call markers872    t.assert_true("T1 right should contain tool_call begin", diff.right.find("<seed:tool_call>") != std::string::npos);873    t.assert_true("T1 right should contain function tag", diff.right.find("<function=test_function_name>") != std::string::npos);874    t.assert_true("T1 right should contain parameter=param1", diff.right.find("<parameter=param1>") != std::string::npos);875    t.assert_true("T1 right should contain parameter=param2", diff.right.find("<parameter=param2>") != std::string::npos);876    t.assert_true("T1 right should contain value1", diff.right.find("value1") != std::string::npos);877    t.assert_true("T1 right should contain value2", diff.right.find("value2") != std::string::npos);878    t.assert_true("T1 right should contain tool_call end", diff.right.find("</seed:tool_call>") != std::string::npos);879 880    // Suffix should be the eos token881    t.assert_equal("T1 suffix should be '<seed:eos>'", "<seed:eos>", diff.suffix);882}883 884// T2: Compare one vs two tool calls885static void test_seed_oss_call_count(testing & t) {886    common_chat_template tmpl = load_seed_oss_template(t);887 888    json assistant_one_call = json{889        {"role", "assistant"},890        {"content", nullptr},891        {"tool_calls", json::array({892            build_tool_call("test_function_name", json::object({{"param1", "value1"}, {"param2", "value2"}}))893        })}894    };895 896    json assistant_two_calls = json{897        {"role", "assistant"},898        {"content", nullptr},899        {"tool_calls", json::array({900            build_tool_call("test_function_name", json::object({{"param1", "value1"}, {"param2", "value2"}})),901            build_tool_call("test_function_name", json::object({{"param1", "value3"}, {"param2", "value4"}}), "call_002")902        })}903    };904 905    json user_msg = json{906        {"role", "user"},907        {"content", "Hello, please help me."}908    };909 910    template_params params_one;911    params_one.messages = json::array({user_msg, assistant_one_call});912    params_one.tools = build_tools_definition();913    params_one.add_generation_prompt = false;914    params_one.enable_thinking = true;915 916    auto result = ::compare_variants(tmpl, params_one,917        [&](template_params & p) {918            p.messages = json::array({user_msg, assistant_two_calls});919        });920 921    if (!t.assert_true("T2 result should have value", result.has_value())) {922        return;923    }924 925    const auto & diff = result->diff;926 927    // Prefix should include the first tool call928    t.assert_true("T2 prefix should contain first tool_call begin", diff.prefix.find("<seed:tool_call>") != std::string::npos);929    t.assert_true("T2 prefix should contain first function", diff.prefix.find("<function=test_function_name>") != std::string::npos);930    t.assert_true("T2 prefix should contain value1", diff.prefix.find("value1") != std::string::npos);931    t.assert_true("T2 prefix should contain value2", diff.prefix.find("value2") != std::string::npos);932    t.assert_true("T2 prefix should contain first tool_call end", diff.prefix.find("</seed:tool_call>") != std::string::npos);933 934    // Left should be empty (no second tool call in variant A)935    t.assert_equal("T2 left should be empty", "", diff.left);936 937    // Right should contain the second tool call938    t.assert_true("T2 right should contain second tool_call begin", diff.right.find("<seed:tool_call>") != std::string::npos);939    t.assert_true("T2 right should contain second function", diff.right.find("<function=test_function_name>") != std::string::npos);940    t.assert_true("T2 right should contain value3", diff.right.find("value3") != std::string::npos);941    t.assert_true("T2 right should contain value4", diff.right.find("value4") != std::string::npos);942    t.assert_true("T2 right should contain second tool_call end", diff.right.find("</seed:tool_call>") != std::string::npos);943 944    // Suffix should end with the eos token945    t.assert_equal("T2 suffix should end with '<seed:eos>'", "<seed:eos>", diff.suffix.substr(diff.suffix.length() - 10, 10));946}947 948// T3: Compare different function names949static void test_seed_oss_function_names(testing & t) {950    common_chat_template tmpl = load_seed_oss_template(t);951 952    // Build tools with two different function names953    json parameters_schema = json::object();954    parameters_schema["type"] = "object";955    parameters_schema["properties"] = json::object();956    parameters_schema["properties"]["arg1"] = json::object({957        {"type", "string"},958        {"description", "Argument 1"}959    });960    parameters_schema["required"] = json::array({"arg1"});961 962    json tools = json::array({963        json{964            {"type", "function"},965            {"function", json{966                {"name", "func_alpha"},967                {"description", "First function"},968                {"parameters", parameters_schema}969            }}970        },971        json{972            {"type", "function"},973            {"function", json{974                {"name", "func_beta"},975                {"description", "Second function"},976                {"parameters", parameters_schema}977            }}978        }979    });980 981    json assistant_func_alpha = json{982        {"role", "assistant"},983        {"content", nullptr},984        {"tool_calls", json::array({985            build_tool_call("func_alpha", json::object({{"arg1", "test_value"}}))986        })}987    };988 989    json assistant_func_beta = json{990        {"role", "assistant"},991        {"content", nullptr},992        {"tool_calls", json::array({993            build_tool_call("func_beta", json::object({{"arg1", "test_value"}}))994        })}995    };996 997    json user_msg = json{998        {"role", "user"},999        {"content", "Hello"}1000    };1001 1002    template_params params_alpha;1003    params_alpha.messages = json::array({user_msg, assistant_func_alpha});1004    params_alpha.tools = tools;1005    params_alpha.add_generation_prompt = false;1006    params_alpha.enable_thinking = true;1007 1008    auto result = ::compare_variants(tmpl, params_alpha,1009        [&](template_params & p) {1010            p.messages = json::array({user_msg, assistant_func_beta});1011        });1012 1013    if (!t.assert_true("T3 result should have value", result.has_value())) {1014        return;1015    }1016 1017    const auto & diff = result->diff;1018 1019    bool func_alpha_in_left = diff.left.find("func_alpha") != std::string::npos;1020    bool func_alpha_in_prefix = diff.prefix.find("func_alpha") != std::string::npos;1021    bool func_beta_in_right = diff.right.find("func_beta") != std::string::npos;1022    bool func_beta_in_prefix = diff.prefix.find("func_beta") != std::string::npos;1023    bool func_beta_in_suffix = diff.suffix.find("func_beta") != std::string::npos;1024 1025    // Left should contain func_alpha (or be in prefix)1026    t.assert_true("T3 left should contain func_alpha (or prefix)", func_alpha_in_left || func_alpha_in_prefix);1027 1028    // Right should contain func_beta1029    t.assert_true("T3 right should contain func_beta", func_beta_in_right || func_beta_in_prefix || func_beta_in_suffix);1030 1031    // Both should have the same parameter value (in common parts, not in diffs)1032    // Since both have same args, test_value will be in prefix/suffix1033    t.assert_true("T3 diff should contain test_value (in prefix or suffix)",1034        diff.prefix.find("test_value") != std::string::npos || diff.suffix.find("test_value") != std::string::npos);1035}1036 1037// T4: Compare different argument counts (zero, one, two parameters)1038static void test_seed_oss_argument_count(testing & t) {1039    common_chat_template tmpl = load_seed_oss_template(t);1040 1041    // Build tools with 0, 1, or 2 required parameters1042    json params_2_required = json::object();1043    params_2_required["type"] = "object";1044    params_2_required["properties"] = json::object();1045    params_2_required["properties"]["arg1"] = json::object({1046        {"type", "string"},1047        {"description", "Argument 1"}1048    });1049    params_2_required["properties"]["arg2"] = json::object({1050        {"type", "string"},1051        {"description", "Argument 2"}1052    });1053    params_2_required["required"] = json::array({"arg1", "arg2"});1054 1055    json params_1_required = json::object();1056    params_1_required["type"] = "object";1057    params_1_required["properties"] = json::object();1058    params_1_required["properties"]["arg1"] = json::object({1059        {"type", "string"},1060        {"description", "Argument 1"}1061    });1062    params_1_required["required"] = json::array({"arg1"});1063 1064    json tools = json::array({1065        json{1066            {"type", "function"},1067            {"function", json{1068                {"name", "test_func"},1069                {"description", "Test function"},1070                {"parameters", params_2_required}1071            }}1072        }1073    });1074 1075    // Test: zero args vs one arg1076    json assistant_zero_args = json{1077        {"role", "assistant"},1078        {"content", nullptr},1079        {"tool_calls", json::array({1080            build_tool_call("test_func", json::object())1081        })}1082    };1083 1084    json assistant_one_arg = json{1085        {"role", "assistant"},1086        {"content", nullptr},1087        {"tool_calls", json::array({1088            build_tool_call("test_func", json::object({{"arg1", "value1"}}))1089        })}1090    };1091 1092    json assistant_two_args = json{1093        {"role", "assistant"},1094        {"content", nullptr},1095        {"tool_calls", json::array({1096            build_tool_call("test_func", json::object({{"arg1", "value1"}, {"arg2", "value2"}}))1097        })}1098    };1099 1100    json user_msg = json{1101        {"role", "user"},1102        {"content", "Hello"}1103    };1104 1105    // Test zero vs one1106    template_params params_zero;1107    params_zero.messages = json::array({user_msg, assistant_zero_args});1108    params_zero.tools = tools;1109    params_zero.add_generation_prompt = false;1110    params_zero.enable_thinking = true;1111 1112    auto result_zero_one = ::compare_variants(tmpl, params_zero,1113        [&](template_params & p) {1114            p.messages = json::array({user_msg, assistant_one_arg});1115        });1116 1117    if (!t.assert_true("T4 zero vs one result should have value", result_zero_one.has_value())) {1118        return;1119    }1120    t.assert_true("T4 zero vs one left should be empty or minimal", result_zero_one->diff.left.empty() || result_zero_one->diff.left == "");1121    t.assert_true("T4 zero vs one right should contain arg1", result_zero_one->diff.right.find("arg1") != std::string::npos);1122 1123    // Test one vs two1124    template_params params_one;1125    params_one.messages = json::array({user_msg, assistant_one_arg});1126    params_one.tools = tools;1127    params_one.add_generation_prompt = false;1128    params_one.enable_thinking = true;1129 1130    auto result_one_two = ::compare_variants(tmpl, params_one,1131        [&](template_params & p) {1132            p.messages = json::array({user_msg, assistant_two_args});1133        });1134 1135    if (!t.assert_true("T4 one vs two result should have value", result_one_two.has_value())) {1136        return;1137    }1138 1139    const auto & diff4 = result_one_two->diff;1140    t.assert_true("T4 one vs two left should contain arg1 (or prefix)",1141        diff4.left.find("arg1") != std::string::npos || diff4.prefix.find("arg1") != std::string::npos);1142    t.assert_true("T4 one vs two right should contain arg1 (or prefix)",1143        diff4.right.find("arg1") != std::string::npos || diff4.prefix.find("arg1") != std::string::npos);1144    t.assert_true("T4 one vs two right should contain arg2 (or prefix/suffix)",1145        diff4.right.find("arg2") != std::string::npos || diff4.prefix.find("arg2") != std::string::npos || diff4.suffix.find("arg2") != std::string::npos);1146}1147 1148// T5: Compare different argument values1149static void test_seed_oss_args_presence(testing & t) {1150    common_chat_template tmpl = load_seed_oss_template(t);1151 1152    json assistant_same_arg = json{1153        {"role", "assistant"},1154        {"content", nullptr},1155        {"tool_calls", json::array({1156            build_tool_call("test_function_name", json::object({{"param1", "value1"}}))1157        })}1158    };1159 1160    json assistant_other_arg = json{1161        {"role", "assistant"},1162        {"content", nullptr},1163        {"tool_calls", json::array({1164            build_tool_call("test_function_name", json::object({{"param2", "value2"}}))1165        })}1166    };1167 1168    json assistant_both_args = json{1169        {"role", "assistant"},1170        {"content", nullptr},1171        {"tool_calls", json::array({1172            build_tool_call("test_function_name", json::object({{"param1", "value1"}, {"param2", "value2"}}))1173        })}1174    };1175 1176    json user_msg = json{1177        {"role", "user"},1178        {"content", "Hello"}1179    };1180 1181    template_params params_same;1182    params_same.messages = json::array({user_msg, assistant_same_arg});1183    params_same.tools = build_tools_definition();1184    params_same.add_generation_prompt = false;1185    params_same.enable_thinking = true;1186 1187    // Test same arg vs other arg1188    auto result_same_other = ::compare_variants(tmpl, params_same,1189        [&](template_params & p) {1190            p.messages = json::array({user_msg, assistant_other_arg});1191        });1192 1193    if (!t.assert_true("T5 same vs other result should have value", result_same_other.has_value())) {1194        return;1195    }1196    const auto & diff5a = result_same_other->diff;1197    t.assert_true("T5 same vs other left should contain param1 (or prefix/suffix)",1198        diff5a.left.find("param1") != std::string::npos || diff5a.prefix.find("param1") != std::string::npos || diff5a.suffix.find("param1") != std::string::npos);1199    t.assert_true("T5 same vs other left should contain value1 (or prefix/suffix)",1200        diff5a.left.find("value1") != std::string::npos || diff5a.prefix.find("value1") != std::string::npos);

Showing the first 1,200 of 1970 lines. Download the file for the rest.