Skip to content

Commit 50f7179

Browse files
committed
Fix grammar and partial parsing
1 parent f7638ea commit 50f7179

File tree

2 files changed

+36
-28
lines changed

2 files changed

+36
-28
lines changed

common/chat.cpp

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2096,7 +2096,7 @@ static void common_chat_parse_seed_oss(common_chat_msg_parser & builder) {
20962096
static const common_regex tool_call_begin_regex("<seed:tool_call>");
20972097
static const common_regex tool_call_end_regex("</seed:tool_call>");
20982098
static const common_regex function_regex("<function=([^>]+)>");
2099-
static const common_regex close_function_regex("</function>");
2099+
static const common_regex param_regex("<parameter=([^>]+)>");
21002100

21012101
while (auto tool_res = builder.try_find_regex(tool_call_begin_regex)) {
21022102
builder.consume_spaces(); // Consume whitespace after <seed:tool_call>
@@ -2107,7 +2107,6 @@ static void common_chat_parse_seed_oss(common_chat_msg_parser & builder) {
21072107

21082108
// Parse Seed-OSS parameters <parameter=name>value</parameter>
21092109
json args = json::object();
2110-
static const common_regex param_regex("<parameter=([^>]+)>");
21112110
// Parse all parameters
21122111
while (auto param_res = builder.try_find_regex(param_regex, std::string::npos, false)) {
21132112
// again, ignore noise around parameters
@@ -2128,40 +2127,28 @@ static void common_chat_parse_seed_oss(common_chat_msg_parser & builder) {
21282127
args[param_name] = param;
21292128
}
21302129
} else {
2131-
try {
2132-
if (auto param_res = builder.try_consume_json()) {
2133-
auto json_obj = param_res->json;
2134-
purge_healing_marker(json_obj, builder.healing_marker());
2135-
if (!json_obj.is_null()) {
2136-
args[param_name] = json_obj;
2137-
}
2138-
} else {
2139-
auto rest = builder.consume_rest();
2140-
args[param_name] = rest;
2141-
}
2142-
} catch (json::exception &) {
2143-
auto rest = builder.consume_rest();
2144-
args[param_name] = rest;
2145-
}
2146-
builder.add_tool_call(function_name, "", args.dump());
21472130
throw common_chat_msg_partial_exception("Incomplete tool parameter");
21482131
}
2149-
}
2132+
}
21502133
// Look for closing function tag
21512134
auto end_func = builder.try_find_literal("</function>");
21522135
if (end_func) {
21532136
builder.move_to(end_func->groups[0].end);
21542137
builder.consume_spaces(); // Consume whitespace after </function>
21552138

2156-
// Add the tool call with parsed arguments
2157-
if (!builder.add_tool_call(function_name, "", args.dump())) {
2139+
// Add the tool call with parsed arguments, but only if we REALLY got the literal
2140+
auto eaten_fragment = builder.input().substr(end_func->groups[0].begin, end_func->groups[0].end);
2141+
auto funlen = std::string("</function>").length();
2142+
if (eaten_fragment.length() >= funlen && eaten_fragment.substr(0, funlen) == std::string("</function>")) {
2143+
if (!builder.add_tool_call(function_name, "", args.dump())) {
2144+
throw common_chat_msg_partial_exception("Incomplete tool call");
2145+
}
2146+
} else {
21582147
throw common_chat_msg_partial_exception("Incomplete tool call");
21592148
}
21602149
} else {
2161-
builder.add_tool_call(function_name, "", args.dump()); // add partial tool parse
21622150
throw common_chat_msg_partial_exception("Incomplete tool call");
21632151
}
2164-
21652152
// Look for closing tool call tag
21662153
if (auto end_tool = builder.try_find_regex(tool_call_end_regex, std::string::npos, false)) {
21672154
builder.move_to(end_tool->groups[0].end);
@@ -2228,8 +2215,8 @@ static common_chat_params common_chat_params_init_seed_oss( const common_chat_t
22282215
std::string param_rules;
22292216
if (parameters.contains("properties")) {
22302217
for (const auto & [key, value] : parameters.at("properties").items()) {
2231-
param_rules += "<parameter=" + key + ">" + builder.add_schema(name + "-arg-" + key, value) +
2232-
"</parameter>";
2218+
param_rules += "\"<parameter=" + key + ">\"" + builder.add_schema(name + "-arg-" + key, value) +
2219+
"\"</parameter>\"";
22332220
}
22342221
}
22352222

tests/test-chat.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,13 @@ static void test_template_output_parsers() {
16231623
}
16241624
{
16251625
// Seed-OSS format tests
1626-
printf("[%s] Seed-OSS format tests\n", __func__);
1626+
auto tmpls = read_templates("models/templates/ByteDance-Seed-OSS.jinja");
1627+
std::vector<std::string> end_tokens{ "<seed:eos>" };
1628+
1629+
assert_equals(COMMON_CHAT_FORMAT_SEED_OSS, common_chat_templates_apply(tmpls.get(), inputs_no_tools).format);
1630+
assert_equals(COMMON_CHAT_FORMAT_SEED_OSS, common_chat_templates_apply(tmpls.get(), inputs_tools).format);
1631+
1632+
test_templates(tmpls.get(), end_tokens, message_assist, tools, "Hello, world!\nWhat's up?", /* expect_grammar_triggered= */ false);
16271633

16281634
// Test simple reasoning content
16291635
assert_msg_equals(
@@ -1689,6 +1695,21 @@ static void test_template_output_parsers() {
16891695
/* .reasoning_format = */ COMMON_REASONING_FORMAT_DEEPSEEK,
16901696
}));
16911697

1698+
// Test deltas: the number of tool calls in partial parses should never decrease
1699+
std::string tool_msg = "<seed:tool_call>\n"
1700+
"<function=fun>\n"
1701+
"<parameter=smth>[1, 2, 3]</parameter>\n"
1702+
"</function>";
1703+
std::size_t previousToolCalls = 0;
1704+
for (std::size_t i = std::string("<seed:tool_call>").length(); i < tool_msg.length() - 1; i++) {
1705+
auto partial = tool_msg.substr(0, i);
1706+
auto partial_res = common_chat_parse(partial, true, { COMMON_CHAT_FORMAT_SEED_OSS, COMMON_REASONING_FORMAT_DEEPSEEK });
1707+
if (partial_res.tool_calls.size() < previousToolCalls) {
1708+
throw std::runtime_error("Tool call size decreased on partial: " + partial + " from " + std::to_string(previousToolCalls) + " to " + std::to_string(partial_res.tool_calls.size()));
1709+
}
1710+
previousToolCalls = partial_res.tool_calls.size();
1711+
}
1712+
16921713
// Test multiple parameters in tool call
16931714
common_chat_msg msg_multi_param;
16941715
msg_multi_param.role = "assistant";
@@ -1705,9 +1726,9 @@ static void test_template_output_parsers() {
17051726
/* is_partial= */ false,
17061727
{COMMON_CHAT_FORMAT_SEED_OSS}));
17071728

1708-
// Test partial parsing for incomplete tool call
1729+
// Test partial parsing for incomplete tool call - don't actually add the call until parsing parameters is done
17091730
assert_msg_equals(
1710-
simple_assist_msg("", "", "calculate_sum", "{\"numbers\": [1]}"),
1731+
simple_assist_msg("", ""),
17111732
common_chat_parse(
17121733
"<seed:tool_call>\n"
17131734
"<function=calculate_sum>\n"

0 commit comments

Comments
 (0)