Skip to content

Commit 3e5457e

Browse files
authored
Merge branch 'ggml-org:master' into master
2 parents 7d23e7f + e974923 commit 3e5457e

86 files changed

Lines changed: 5154 additions & 789 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/close-issue.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
steps:
1818
- uses: actions/stale@v10
1919
with:
20-
exempt-issue-labels: "refactoring,help wanted,good first issue,research 🔬,bug,roadmap"
20+
exempt-issue-labels: "refactoring,help wanted,good first issue,research 🔬,bug,roadmap,security"
2121
days-before-issue-stale: 30
2222
days-before-issue-close: 14
2323
stale-issue-label: "stale"

cmake/arm64-linux-clang.cmake

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
set( CMAKE_SYSTEM_NAME Linux )
2+
set( CMAKE_SYSTEM_PROCESSOR arm64 )
3+
4+
set( target aarch64-linux-gnu )
5+
6+
set( CMAKE_C_COMPILER clang )
7+
set( CMAKE_CXX_COMPILER clang++ )
8+
9+
set( CMAKE_C_COMPILER_TARGET ${target} )
10+
set( CMAKE_CXX_COMPILER_TARGET ${target} )
11+
12+
set( arch_c_flags "-march=armv8.7-a -fvectorize -ffp-model=fast -fno-finite-math-only" )
13+
set( warn_c_flags "-Wno-format -Wno-unused-variable -Wno-unused-function -Wno-gnu-zero-variadic-macro-arguments" )
14+
15+
set( CMAKE_C_FLAGS_INIT "${arch_c_flags} ${warn_c_flags}" )
16+
set( CMAKE_CXX_FLAGS_INIT "${arch_c_flags} ${warn_c_flags}" )
17+

common/chat-auto-parser-generator.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ common_chat_params peg_generator::generate_parser(const common_chat_template &
6969
auto schema = function.contains("parameters") ? function.at("parameters") : json::object();
7070
builder.resolve_refs(schema);
7171
});
72+
if (has_response_format) {
73+
auto schema = inputs.json_schema;
74+
builder.resolve_refs(schema);
75+
}
7276
parser.build_grammar(builder, data.grammar_lazy);
7377
});
7478

common/chat.cpp

Lines changed: 193 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -865,9 +865,10 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_
865865
adjusted_messages.push_back(adjusted);
866866
}
867867

868-
auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
869-
auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
870-
auto include_grammar = true;
868+
auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
869+
auto has_response_format = inputs.json_schema.is_object() && !inputs.json_schema.empty();
870+
auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
871+
auto include_grammar = true;
871872

872873
data.supports_thinking = true;
873874
data.thinking_start_tag = "[THINK]";
@@ -887,7 +888,7 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_
887888
extract_reasoning ? p.optional("[THINK]" + p.reasoning(p.until("[/THINK]")) + "[/THINK]") : p.eps();
888889

889890
// Response format parser
890-
if (inputs.json_schema.is_object() && !inputs.json_schema.empty()) {
891+
if (has_response_format) {
891892
// Ministral wants to emit json surrounded by code fences
892893
return generation_prompt + (reasoning << "```json" << p.content(p.schema(p.json(), "response-format", inputs.json_schema)) << "```");
893894
}
@@ -928,6 +929,10 @@ static common_chat_params common_chat_params_init_ministral_3(const common_chat_
928929
auto schema = function.at("parameters");
929930
builder.resolve_refs(schema);
930931
});
932+
if (has_response_format) {
933+
auto schema = inputs.json_schema;
934+
builder.resolve_refs(schema);
935+
}
931936
parser.build_grammar(builder, data.grammar_lazy);
932937
});
933938

@@ -1063,6 +1068,10 @@ static common_chat_params common_chat_params_init_gpt_oss(const common_chat_temp
10631068
auto schema = function.at("parameters");
10641069
builder.resolve_refs(schema);
10651070
});
1071+
if (has_response_format) {
1072+
auto schema = inputs.json_schema;
1073+
builder.resolve_refs(schema);
1074+
}
10661075
parser.build_grammar(builder, data.grammar_lazy);
10671076
});
10681077

@@ -1193,6 +1202,10 @@ static common_chat_params common_chat_params_init_gemma4(const common_chat_templ
11931202
auto schema = function.at("parameters");
11941203
builder.resolve_refs(schema);
11951204
});
1205+
if (has_response_format) {
1206+
auto schema = inputs.json_schema;
1207+
builder.resolve_refs(schema);
1208+
}
11961209
parser.build_grammar(builder, data.grammar_lazy);
11971210
});
11981211

@@ -1643,6 +1656,173 @@ static common_chat_params common_chat_params_init_gigachat_v3(
16431656
return data;
16441657
}
16451658

1659+
static common_chat_params common_chat_params_init_deepseek_v3_2(const common_chat_template & tmpl,
1660+
const autoparser::generation_params & inputs) {
1661+
common_chat_params data;
1662+
1663+
data.prompt = common_chat_template_direct_apply_impl(tmpl, inputs);
1664+
data.format = COMMON_CHAT_FORMAT_PEG_NATIVE;
1665+
data.supports_thinking = true;
1666+
data.thinking_start_tag = "<think>";
1667+
data.thinking_end_tag = "</think>";
1668+
data.preserved_tokens = {
1669+
"|DSML|",
1670+
"<think>",
1671+
"</think>",
1672+
};
1673+
1674+
auto has_tools = inputs.tools.is_array() && !inputs.tools.empty();
1675+
auto has_response_format = !inputs.json_schema.is_null() && inputs.json_schema.is_object();
1676+
auto extract_reasoning = inputs.reasoning_format != COMMON_REASONING_FORMAT_NONE;
1677+
auto include_grammar = has_response_format || (has_tools && inputs.tool_choice != COMMON_CHAT_TOOL_CHOICE_NONE);
1678+
1679+
const std::string DSML = "|DSML|";
1680+
const std::string THINK_START = "<think>";
1681+
const std::string THINK_END = "</think>";
1682+
const std::string FC_START = "<" + DSML + "function_calls>";
1683+
const std::string FC_END = "</" + DSML + "function_calls>";
1684+
const std::string INVOKE_START = "<" + DSML + "invoke";
1685+
const std::string INVOKE_END = "</" + DSML + "invoke>";
1686+
const std::string PARAM_START = "<" + DSML + "parameter";
1687+
const std::string PARAM_END = "</" + DSML + "parameter>";
1688+
1689+
auto parser = build_chat_peg_parser([&](common_chat_peg_builder & p) {
1690+
auto generation_prompt = p.prefix(inputs.generation_prompt, THINK_START);
1691+
auto end = p.end();
1692+
1693+
auto reasoning = p.eps();
1694+
if (extract_reasoning && inputs.enable_thinking) {
1695+
reasoning = p.optional(THINK_START + p.reasoning(p.until(THINK_END)) + THINK_END);
1696+
} else if (extract_reasoning) {
1697+
// Thinking disabled but reasoning extraction requested: the generation prompt
1698+
// contains an empty <think></think> pair that must still be consumed.
1699+
reasoning = p.optional(p.literal(THINK_START) + p.until(THINK_END) + p.literal(THINK_END));
1700+
}
1701+
1702+
if (has_response_format) {
1703+
auto response_format = p.rule("response-format",
1704+
p.literal("```json") + p.space() +
1705+
p.content(p.schema(p.json(), "response-format-schema", inputs.json_schema)) +
1706+
p.space() + p.literal("```"));
1707+
return generation_prompt + reasoning + response_format + end;
1708+
}
1709+
1710+
if (!has_tools || inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_NONE) {
1711+
return generation_prompt + reasoning + p.content(p.rest()) + end;
1712+
}
1713+
1714+
auto tool_choice = p.choice();
1715+
foreach_function(inputs.tools, [&](const json & tool) {
1716+
const auto & function = tool.at("function");
1717+
std::string name = function.at("name");
1718+
auto params = function.contains("parameters") ? function.at("parameters") : json::object();
1719+
const auto & props = params.contains("properties") ? params.at("properties") : json::object();
1720+
1721+
std::set<std::string> required;
1722+
if (params.contains("required")) {
1723+
params.at("required").get_to(required);
1724+
}
1725+
1726+
auto schema_info = common_schema_info();
1727+
schema_info.resolve_refs(params);
1728+
1729+
std::vector<common_peg_parser> required_parsers;
1730+
std::vector<common_peg_parser> optional_parsers;
1731+
for (const auto & [param_name, param_schema] : props.items()) {
1732+
bool is_required = required.find(param_name) != required.end();
1733+
bool is_string = schema_info.resolves_to_string(param_schema);
1734+
1735+
auto arg = p.tool_arg(
1736+
p.tool_arg_open(
1737+
p.literal(PARAM_START + " name=\"") +
1738+
p.tool_arg_name(p.literal(param_name)) +
1739+
p.literal("\" string=\"" + std::string(is_string ? "true" : "false") + "\">")) +
1740+
(is_string
1741+
? p.tool_arg_string_value(p.until(PARAM_END))
1742+
: p.tool_arg_json_value(p.schema(p.json(),
1743+
"tool-" + name + "-arg-" + param_name + "-schema",
1744+
param_schema, false))) +
1745+
p.tool_arg_close(p.literal(PARAM_END)));
1746+
1747+
auto named_arg = p.rule("tool-" + name + "-arg-" + param_name, arg);
1748+
if (is_required) {
1749+
required_parsers.push_back(named_arg);
1750+
} else {
1751+
optional_parsers.push_back(named_arg);
1752+
}
1753+
}
1754+
1755+
common_peg_parser args_seq = p.eps();
1756+
for (size_t i = 0; i < required_parsers.size(); i++) {
1757+
if (i > 0) {
1758+
args_seq = args_seq + p.space();
1759+
}
1760+
args_seq = args_seq + required_parsers[i];
1761+
}
1762+
1763+
if (!optional_parsers.empty()) {
1764+
common_peg_parser any_opt = p.choice();
1765+
for (const auto & opt : optional_parsers) {
1766+
any_opt |= opt;
1767+
}
1768+
args_seq = args_seq + p.repeat(p.space() + any_opt, 0, -1);
1769+
}
1770+
1771+
common_peg_parser invoke_body = args_seq;
1772+
auto func_parser = p.tool(
1773+
p.tool_open(p.literal(INVOKE_START + " name=\"") +
1774+
p.tool_name(p.literal(name)) + p.literal("\">\n")) +
1775+
invoke_body + p.space() +
1776+
p.tool_close(p.literal(INVOKE_END)));
1777+
1778+
tool_choice |= p.rule("tool-" + name, func_parser);
1779+
});
1780+
1781+
auto require_tools = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED;
1782+
1783+
common_peg_parser tool_calls = p.eps();
1784+
if (inputs.parallel_tool_calls) {
1785+
tool_calls = p.trigger_rule("tool-call",
1786+
p.literal(FC_START) + p.space() + tool_choice +
1787+
p.zero_or_more(p.space() + tool_choice) + p.space() + p.literal(FC_END));
1788+
} else {
1789+
tool_calls = p.trigger_rule("tool-call",
1790+
p.literal(FC_START) + p.space() + tool_choice + p.space() + p.literal(FC_END));
1791+
}
1792+
1793+
if (!require_tools) {
1794+
tool_calls = p.optional(tool_calls);
1795+
}
1796+
1797+
auto content_before_tools = p.content(p.until(FC_START));
1798+
return generation_prompt + reasoning + content_before_tools + tool_calls + end;
1799+
});
1800+
1801+
data.parser = parser.save();
1802+
1803+
if (include_grammar) {
1804+
data.grammar_lazy = !(has_response_format || (has_tools && inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED));
1805+
data.grammar = build_grammar([&](const common_grammar_builder & builder) {
1806+
foreach_function(inputs.tools, [&](const json & tool) {
1807+
const auto & function = tool.at("function");
1808+
auto schema = function.contains("parameters") ? function.at("parameters") : json::object();
1809+
builder.resolve_refs(schema);
1810+
});
1811+
if (has_response_format) {
1812+
auto schema = inputs.json_schema;
1813+
builder.resolve_refs(schema);
1814+
}
1815+
parser.build_grammar(builder, data.grammar_lazy);
1816+
});
1817+
1818+
data.grammar_triggers = {
1819+
{ COMMON_GRAMMAR_TRIGGER_TYPE_WORD, FC_START },
1820+
};
1821+
}
1822+
1823+
return data;
1824+
}
1825+
16461826
namespace workaround {
16471827

16481828
static void map_developer_role_to_system(json & messages) {
@@ -1914,6 +2094,15 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
19142094
return common_chat_params_init_gigachat_v3(tmpl, params);
19152095
}
19162096

2097+
// DeepSeek V3.2 format detection: template defines dsml_token and uses it for tool calls.
2098+
// The template source contains the token as a variable assignment, not as a literal in markup.
2099+
if (src.find("dsml_token") != std::string::npos &&
2100+
src.find("function_calls") != std::string::npos &&
2101+
src.find("DSML") != std::string::npos) {
2102+
LOG_DBG("Using specialized template: DeepSeek V3.2\n");
2103+
return common_chat_params_init_deepseek_v3_2(tmpl, params);
2104+
}
2105+
19172106
// Gemma4 format detection
19182107
if (src.find("'<|tool_call>call:'") != std::string::npos) {
19192108
if (src.find("{#- OpenAI Chat Completions:") == std::string::npos) {

common/download.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,9 @@ static bool common_pull_file(httplib::Client & cli,
258258
if (progress_step >= p.total / 1000 || p.downloaded == p.total) {
259259
if (callback) {
260260
callback->on_update(p);
261+
if (callback->is_cancelled()) {
262+
return false;
263+
}
261264
}
262265
progress_step = 0;
263266
}
@@ -373,6 +376,9 @@ static int common_download_file_single_online(const std::string & url,
373376
}
374377

375378
for (int i = 0; i < max_attempts; ++i) {
379+
if (opts.callback && opts.callback->is_cancelled()) {
380+
break;
381+
}
376382
if (i) {
377383
LOG_WRN("%s: retrying after %d seconds...\n", __func__, delay);
378384
std::this_thread::sleep_for(std::chrono::seconds(delay));
@@ -412,6 +418,12 @@ static int common_download_file_single_online(const std::string & url,
412418
if (opts.callback) {
413419
opts.callback->on_done(p, success);
414420
}
421+
if (opts.callback && opts.callback->is_cancelled() &&
422+
std::filesystem::exists(path_temporary)) {
423+
if (remove(path_temporary.c_str()) != 0) {
424+
LOG_ERR("%s: unable to delete temporary file: %s\n", __func__, path_temporary.c_str());
425+
}
426+
}
415427
if (!success) {
416428
LOG_ERR("%s: download failed after %d attempts\n", __func__, max_attempts);
417429
return -1; // max attempts reached

common/download.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ class common_download_callback {
2121
virtual void on_start(const common_download_progress & p) = 0;
2222
virtual void on_update(const common_download_progress & p) = 0;
2323
virtual void on_done(const common_download_progress & p, bool ok) = 0;
24+
virtual bool is_cancelled() const { return false; }
2425
};
2526

2627
struct common_remote_params {

0 commit comments

Comments
 (0)