Skip to content

Commit d876898

Browse files
committed
Merge branch 'upstream' into concedo_experimental
# Conflicts: # .devops/cpu.Dockerfile # .devops/cuda.Dockerfile # .github/ISSUE_TEMPLATE/010-bug-compilation.yml # .github/ISSUE_TEMPLATE/011-bug-results.yml # .github/labeler.yml # .github/workflows/build.yml # .github/workflows/release.yml # CODEOWNERS # README.md # docs/build-s390x.md # docs/ops.md # examples/eval-callback/eval-callback.cpp # ggml/CMakeLists.txt # ggml/src/CMakeLists.txt # ggml/src/ggml-cpu/CMakeLists.txt # ggml/src/ggml-opencl/CMakeLists.txt # ggml/src/ggml-opencl/ggml-opencl.cpp # ggml/src/ggml-opencl/kernels/transpose.cl # tests/test-backend-ops.cpp # tests/test-chat.cpp # tests/test-opt.cpp
2 parents 9491b69 + 5e6229a commit d876898

File tree

31 files changed

+10043
-77
lines changed

31 files changed

+10043
-77
lines changed

.github/workflows/copilot-setup-steps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
uses: actions/checkout@v4
3030

3131
- name: ccache
32-
uses: hendrikmuhs/[email protected]
32+
uses: ggml-org/[email protected]
3333
with:
3434
key: copilot-setup-steps
3535
evict-old-files: 1d

common/chat.cpp

Lines changed: 159 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,7 @@ json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msg
296296
}
297297
if (!msg.reasoning_content.empty()) {
298298
jmsg["reasoning_content"] = msg.reasoning_content;
299+
jmsg["thinking"] = msg.reasoning_content; // gpt-oss
299300
}
300301
if (!msg.tool_name.empty()) {
301302
jmsg["name"] = msg.tool_name;
@@ -472,11 +473,12 @@ std::string common_chat_format_single(
472473
return ss.str();
473474
}
474475

475-
std::string common_chat_format_example(const struct common_chat_templates * tmpls, bool use_jinja) {
476+
std::string common_chat_format_example(const struct common_chat_templates * tmpls, bool use_jinja, const std::map<std::string, std::string> & chat_template_kwargs) {
476477
common_chat_templates_inputs inputs;
477478
inputs.use_jinja = use_jinja;
478479
inputs.add_bos = tmpls->add_bos;
479480
inputs.add_eos = tmpls->add_eos;
481+
inputs.chat_template_kwargs = chat_template_kwargs;
480482
auto add_simple_msg = [&](auto role, auto content) {
481483
common_chat_msg msg;
482484
msg.role = role;
@@ -1338,16 +1340,164 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp
13381340
data.prompt = prompt;
13391341
data.format = COMMON_CHAT_FORMAT_GPT_OSS;
13401342

1341-
// TODO: support tool calls in GPT-OSS?
1343+
// These special tokens are required to parse properly, so we include them
1344+
// even if parse_tool_calls is false.
1345+
data.preserved_tokens = {
1346+
"<|channel|>",
1347+
"<|constrain|>",
1348+
"<|message|>",
1349+
"<|start|>",
1350+
"<|end|>",
1351+
};
1352+
1353+
if (inputs.tools.is_array() && !inputs.tools.empty()) {
1354+
data.grammar_lazy = inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_REQUIRED;
1355+
data.grammar = build_grammar([&](const common_grammar_builder & builder) {
1356+
// tool calls can appear in commentary or analysis channels
1357+
auto channel = builder.add_rule("channel", "\"<|channel|>\" ( \"commentary\" | \"analysis\" )");
1358+
1359+
std::vector<std::string> tool_rules_recipient_in_role;
1360+
std::vector<std::string> tool_rules_recipient_in_channel;
1361+
foreach_function(inputs.tools, [&](const json & tool) {
1362+
const auto & function = tool.at("function");
1363+
std::string name = function.at("name");
1364+
auto parameters = function.at("parameters");
1365+
builder.resolve_refs(parameters);
1366+
1367+
tool_rules_recipient_in_role.push_back(
1368+
builder.add_rule(name + "-call",
1369+
"\"" + name + "\"" + channel + " \" <|constrain|>json\"? \"<|message|>\" " +
1370+
builder.add_schema(name + "-args", parameters)
1371+
)
1372+
);
1373+
1374+
tool_rules_recipient_in_channel.push_back(
1375+
builder.add_rule(name + "-call",
1376+
"\"" + name + "\"" + " \" <|constrain|>json\"? \"<|message|>\" " +
1377+
builder.add_schema(name + "-args", parameters)
1378+
)
1379+
);
1380+
});
1381+
1382+
auto recipient_in_role = builder.add_rule("recipient_in_role",
1383+
"\"<|start|>assistant\"? \" to=functions.\" ( " +
1384+
string_join(tool_rules_recipient_in_role, " | ") + " )"
1385+
);
1386+
1387+
auto recipient_in_channel = builder.add_rule("recipient_in_channel",
1388+
channel + " \" to=functions.\" ( " +
1389+
string_join(tool_rules_recipient_in_channel, " | ") + " )"
1390+
);
1391+
1392+
builder.add_rule("root", recipient_in_role + " | " + recipient_in_channel);
1393+
1394+
// Trigger on tool calls that appear in the commentary channel
1395+
data.grammar_triggers.push_back({
1396+
COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN,
1397+
"<\\|channel\\|>(commentary|analysis) to"
1398+
});
1399+
1400+
// Trigger tool calls that appear in the role section, either at the
1401+
// start or in the middle.
1402+
data.grammar_triggers.push_back({
1403+
COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN_FULL,
1404+
"^ to"
1405+
});
1406+
1407+
data.grammar_triggers.push_back({
1408+
COMMON_GRAMMAR_TRIGGER_TYPE_PATTERN,
1409+
"<\\|start\\|>assistant to"
1410+
});
1411+
});
1412+
}
13421413

13431414
return data;
13441415
}
13451416
static void common_chat_parse_gpt_oss(common_chat_msg_parser & builder) {
1346-
// TODO @ngxson : this won't work with --special enabled, we should fix that
1347-
builder.try_parse_reasoning("<|channel|>analysis<|message|>", "<|start|>assistant<|channel|>final<|message|>");
1348-
if (!builder.syntax().parse_tool_calls) {
1349-
builder.add_content(builder.consume_rest());
1350-
return;
1417+
static const std::string constraint = "(?: (<\\|constrain\\|>)?([a-zA-Z0-9_-]+))";
1418+
static const std::string recipient("(?: to=functions\\.([^<\\s]+))");
1419+
1420+
static const common_regex start_regex("<\\|start\\|>assistant");
1421+
static const common_regex analysis_regex("<\\|channel\\|>analysis");
1422+
static const common_regex final_regex("<\\|channel\\|>final" + constraint + "?");
1423+
static const common_regex preamble_regex("<\\|channel\\|>commentary");
1424+
static const common_regex tool_call1_regex(recipient + "<\\|channel\\|>(analysis|commentary)" + constraint + "?");
1425+
static const common_regex tool_call2_regex("<\\|channel\\|>(analysis|commentary)" + recipient + constraint + "?");
1426+
1427+
auto consume_end = [&](bool include_end = false) {
1428+
if (auto res = builder.try_find_literal("<|end|>")) {
1429+
return res->prelude + (include_end ? builder.str(res->groups[0]) : "");
1430+
}
1431+
return builder.consume_rest();
1432+
};
1433+
1434+
auto handle_tool_call = [&](const std::string & name) {
1435+
if (auto args = builder.try_consume_json_with_dumped_args({{}})) {
1436+
if (builder.syntax().parse_tool_calls) {
1437+
if (!builder.add_tool_call(name, "", args->value) || args->is_partial) {
1438+
throw common_chat_msg_partial_exception("incomplete tool call");
1439+
}
1440+
} else if (args->is_partial) {
1441+
throw common_chat_msg_partial_exception("incomplete tool call");
1442+
}
1443+
}
1444+
};
1445+
1446+
auto regex_match = [](const common_regex & regex, const std::string & input) -> std::optional<common_regex_match> {
1447+
auto match = regex.search(input, 0, true);
1448+
if (match.type == COMMON_REGEX_MATCH_TYPE_FULL) {
1449+
return match;
1450+
}
1451+
return std::nullopt;
1452+
};
1453+
1454+
do {
1455+
auto header_start_pos = builder.pos();
1456+
auto content_start = builder.try_find_literal("<|message|>");
1457+
if (!content_start) {
1458+
throw common_chat_msg_partial_exception("incomplete header");
1459+
}
1460+
1461+
auto header = content_start->prelude;
1462+
1463+
if (auto match = regex_match(tool_call1_regex, header)) {
1464+
auto group = match->groups[1];
1465+
auto name = header.substr(group.begin, group.end - group.begin);
1466+
handle_tool_call(name);
1467+
continue;
1468+
}
1469+
1470+
if (auto match = regex_match(tool_call2_regex, header)) {
1471+
auto group = match->groups[2];
1472+
auto name = header.substr(group.begin, group.end - group.begin);
1473+
handle_tool_call(name);
1474+
continue;
1475+
}
1476+
1477+
if (regex_match(analysis_regex, header)) {
1478+
builder.move_to(header_start_pos);
1479+
if (builder.syntax().reasoning_format == COMMON_REASONING_FORMAT_NONE || builder.syntax().reasoning_in_content) {
1480+
builder.add_content(consume_end(true));
1481+
} else {
1482+
builder.try_parse_reasoning("<|channel|>analysis<|message|>", "<|end|>");
1483+
}
1484+
continue;
1485+
}
1486+
1487+
if(regex_match(final_regex, header) || regex_match(preamble_regex, header)) {
1488+
builder.add_content(consume_end());
1489+
continue;
1490+
}
1491+
1492+
// Possibly a malformed message, attempt to recover by rolling
1493+
// back to pick up the next <|start|>
1494+
LOG_DBG("%s: unknown header from message: %s\n", __func__, header.c_str());
1495+
builder.move_to(header_start_pos);
1496+
} while (builder.try_find_regex(start_regex, std::string::npos, false));
1497+
1498+
auto remaining = builder.consume_rest();
1499+
if (!remaining.empty()) {
1500+
LOG_DBG("%s: content after last message: %s\n", __func__, remaining.c_str());
13511501
}
13521502
}
13531503

@@ -1911,8 +2061,8 @@ static common_chat_params common_chat_templates_apply_jinja(
19112061
params.enable_thinking = inputs.enable_thinking;
19122062
params.grammar = inputs.grammar;
19132063
params.now = inputs.now;
1914-
params.add_bos = inputs.add_bos;
1915-
params.add_eos = inputs.add_eos;
2064+
params.add_bos = tmpls->add_bos;
2065+
params.add_eos = tmpls->add_eos;
19162066

19172067
params.extra_context = json::object();
19182068
for (auto el : inputs.chat_template_kwargs) {

common/chat.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ std::string common_chat_format_single(
187187
// Returns an example of formatted chat
188188
std::string common_chat_format_example(
189189
const struct common_chat_templates * tmpls,
190-
bool use_jinja);
190+
bool use_jinja,
191+
const std::map<std::string, std::string> & chat_template_kwargs);
191192

192193
const char* common_chat_format_name(common_chat_format format);
193194
const char* common_reasoning_format_name(common_reasoning_format format);

0 commit comments

Comments
 (0)