Skip to content

Commit 2c66a5c

Browse files
Merge pull request #506 from janhq/update-dev-from-master-2026-05-05-00-59
Sync master with upstream release b9025
2 parents b88a55d + eff0670 commit 2c66a5c

164 files changed

Lines changed: 13245 additions & 9844 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.

.pi/gg/SYSTEM.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ General:
44
- By very precise and concise when writing code, comments, explanations, etc.
55
- PR and commit titles format: `<module> : <title>`. Lookup recents for examples
66
- Don't try to build or run the code unless you are explicitly asked to do so
7+
- Use the `gh` CLI tool when querying PRs, issues, or other GitHub resources
78

89
Coding:
910
- When in doubt, always refer to the CONTRIBUTING.md file of the project

common/arg.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2864,7 +2864,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
28642864
{"--tools"}, "TOOL1,TOOL2,...",
28652865
"experimental: whether to enable built-in tools for AI agents - do not enable in untrusted environments (default: no tools)\n"
28662866
"specify \"all\" to enable all tools\n"
2867-
"available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, apply_diff",
2867+
"available tools: read_file, file_glob_search, grep_search, exec_shell_command, write_file, edit_file, apply_diff, get_datetime",
28682868
[](common_params & params, const std::string & value) {
28692869
params.server_tools = parse_csv_row(value);
28702870
}
@@ -3380,7 +3380,7 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
33803380
).set_spec().set_examples({LLAMA_EXAMPLE_SPECULATIVE, LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_CLI}));
33813381
add_opt(common_arg(
33823382
{"--spec-draft-poll", "--poll-draft"}, "<0|1>",
3383-
"Use polling to wait for draft model work (default: same as --poll])",
3383+
"Use polling to wait for draft model work (default: same as --poll)",
33843384
[](common_params & params, int value) {
33853385
params.speculative.draft.cpuparams.poll = value;
33863386
}
@@ -3794,7 +3794,10 @@ common_params_context common_params_parser_init(common_params & params, llama_ex
37943794
).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
37953795
add_opt(common_arg(
37963796
{"--diffusion-algorithm"}, "N",
3797-
string_format("diffusion algorithm: 0=ORIGIN, 1=ENTROPY_BASED, 2=MARGIN_BASED, 3=RANDOM, 4=LOW_CONFIDENCE (default: %d)", params.diffusion.algorithm),
3797+
string_format(
3798+
"diffusion algorithm: 0=DIFFUSION_ALGORITHM_ORIGIN, 1=DIFFUSION_ALGORITHM_ENTROPY_BASED, "
3799+
"2=DIFFUSION_ALGORITHM_MARGIN_BASED, 3=DIFFUSION_ALGORITHM_RANDOM, "
3800+
"4=DIFFUSION_ALGORITHM_CONFIDENCE_BASED (default: %d)", params.diffusion.algorithm),
37983801
[](common_params & params, int value) { params.diffusion.algorithm = value; }
37993802
).set_examples({ LLAMA_EXAMPLE_DIFFUSION }));
38003803
add_opt(common_arg(

common/chat-auto-parser-generator.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@ common_peg_parser analyze_reasoning::build_parser(parser_build_context & ctx) co
136136
if (!end.empty()) {
137137
if (!start.empty()) {
138138
// Standard tag-based: optional(<think>reasoning</think>)
139-
return p.optional(start + p.reasoning(p.until(end)) + end + p.space());
139+
return p.optional(p.optspace(start) + p.reasoning(p.until(trim_whitespace(end))) + p.optspace(end));
140140
}
141141
// Delimiter-style (empty start)
142-
return p.optional(p.reasoning(p.until(end)) + end + p.space());
142+
return p.optional(p.reasoning(p.until(trim_whitespace(end))) + p.optspace(end));
143143
}
144144
}
145145

@@ -186,7 +186,6 @@ common_peg_parser analyze_tools::build_parser(parser_build_context & ctx) const
186186
common_peg_parser analyze_tools::build_tool_parser_json_native(parser_build_context & ctx) const {
187187
auto & p = ctx.p;
188188
const auto & inputs = ctx.inputs;
189-
bool force_tools = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED;
190189

191190
// Build effective field names with dot notation if function_field is set
192191
std::string name_field = format.name_field;
@@ -225,8 +224,7 @@ common_peg_parser analyze_tools::build_tool_parser_json_native(parser_build_cont
225224
tool_start = format.per_call_start;
226225
}
227226

228-
return ctx.reasoning_parser + (force_tools ? p.eps() : p.optional(p.content(p.until(tool_start)))) + tools_parser +
229-
p.end();
227+
return ctx.reasoning_parser + p.optional(p.content(p.until(tool_start))) + tools_parser + p.end();
230228
}
231229

232230
common_peg_parser analyze_tools::build_func_parser(common_chat_peg_builder & p, const std::string & name,
@@ -270,7 +268,6 @@ common_peg_parser analyze_tools::build_func_parser(common_chat_peg_builder & p,
270268
common_peg_parser analyze_tools::build_tool_parser_tag_json(parser_build_context & ctx) const {
271269
auto & p = ctx.p;
272270
const auto & inputs = ctx.inputs;
273-
bool force_tools = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED;
274271

275272
common_peg_parser tool_choice = p.choice();
276273

@@ -336,14 +333,12 @@ common_peg_parser analyze_tools::build_tool_parser_tag_json(parser_build_context
336333

337334
std::string trigger_marker = !format.section_start.empty() ? format.section_start : format.per_call_start;
338335
auto content_before_tools = trigger_marker.empty() ? p.eps() : p.until(trigger_marker);
339-
return ctx.reasoning_parser + (force_tools ? p.eps() : p.optional(p.content(content_before_tools))) + tool_calls +
340-
p.end();
336+
return ctx.reasoning_parser + p.optional(p.content(content_before_tools)) + tool_calls + p.end();
341337
}
342338

343339
common_peg_parser analyze_tools::build_tool_parser_tag_tagged(parser_build_context & ctx) const {
344340
auto & p = ctx.p;
345341
const auto & inputs = ctx.inputs;
346-
bool force_tools = inputs.tool_choice == COMMON_CHAT_TOOL_CHOICE_REQUIRED;
347342

348343
auto until_suffix = p.rule("until-suffix", p.until(arguments.value_suffix));
349344

@@ -471,8 +466,7 @@ common_peg_parser analyze_tools::build_tool_parser_tag_tagged(parser_build_conte
471466

472467
std::string trigger_marker = !format.section_start.empty() ? format.section_start : format.per_call_start;
473468
auto content_before_tools = trigger_marker.empty() ? p.eps() : p.until(trigger_marker);
474-
return ctx.reasoning_parser + (force_tools ? p.eps() : p.optional(p.content(content_before_tools))) + tool_calls +
475-
p.end();
469+
return ctx.reasoning_parser + p.optional(p.content(content_before_tools)) + tool_calls + p.end();
476470
}
477471

478472
} // namespace autoparser

common/chat-diff-analyzer.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ void analyze_reasoning::compare_thinking_enabled() {
342342
if (left_trimmed.empty() && !diff.right.empty()) {
343343
if (!right_trimmed.empty() && string_ends_with(comparison->output_B, right_trimmed)) {
344344
if (start.empty()) {
345-
start = trim_leading_whitespace(diff.right);
345+
start = diff.right;
346346
mode = reasoning_mode::TAG_BASED;
347347
}
348348
}
@@ -353,7 +353,7 @@ void analyze_reasoning::compare_thinking_enabled() {
353353
if (seg.size() >= 2 && seg[seg.size() - 1].value == left_trimmed && seg[seg.size() - 2].type == segment_type::MARKER) {
354354
start = seg[seg.size() - 2].value;
355355
}
356-
end = trim_trailing_whitespace(diff.left);
356+
end = diff.left;
357357
mode = reasoning_mode::TAG_BASED;
358358
}
359359
}
@@ -445,14 +445,14 @@ void analyze_reasoning::compare_reasoning_scope() {
445445
auto result = parser_wrapped.parse_anywhere_and_extract(comparison->output_B);
446446
if (result.result.success()) {
447447
start = result.tags["pre"];
448-
end = trim_trailing_whitespace(result.tags["post"]);
448+
end = result.tags["post"];
449449
} else {
450450
auto parser_delimiter = build_tagged_peg_parser([&](common_peg_parser_builder &p) {
451451
return p.literal(reasoning_content) + p.space() + p.optional(p.tag("post", (p.marker() + p.space())));
452452
});
453453
result = parser_delimiter.parse_anywhere_and_extract(comparison->output_B);
454454
if (result.result.success()) {
455-
end = trim_trailing_whitespace(result.tags["post"]);
455+
end = result.tags["post"];
456456
} else {
457457
LOG_DBG(ANSI_ORANGE "%s: Unable to extract reasoning markers, falling back to reasoning = NONE\n" ANSI_RESET, __func__);
458458
mode = reasoning_mode::NONE;

common/chat-peg-parser.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,32 @@ common_peg_parser common_chat_peg_builder::prefix(const std::string & s, const s
816816
return literal(s.substr(0, s.rfind(delimiter)));
817817
}
818818

819+
common_peg_parser common_chat_peg_builder::optspace(const std::string & tag) {
820+
auto parser = eps();
821+
size_t end_of_prefix_space = tag.size();
822+
size_t start_of_suffix_space = tag.size();
823+
for (size_t i = 0; i < tag.size(); i++) {
824+
if (!std::isspace(tag[i])) {
825+
end_of_prefix_space = i;
826+
break;
827+
}
828+
}
829+
for (size_t i = tag.size(); i > 0; i--) {
830+
if (!std::isspace(tag[i - 1])) {
831+
start_of_suffix_space = i;
832+
break;
833+
}
834+
}
835+
for (size_t i = 0; i < end_of_prefix_space; i++) {
836+
parser += optional(literal(std::string(1, tag[i])));
837+
}
838+
parser += literal(tag.substr(end_of_prefix_space, start_of_suffix_space - end_of_prefix_space));
839+
for (size_t i = start_of_suffix_space; i < tag.size(); i++) {
840+
parser += optional(literal(std::string(1, tag[i])));
841+
}
842+
return parser;
843+
}
844+
819845
common_peg_parser common_chat_peg_builder::standard_json_tools(
820846
const std::string & section_start,
821847
const std::string & section_end,

common/chat-peg-parser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class common_chat_peg_builder : public common_peg_parser_builder {
9696
// Return a parser that parses the prefix of a string, up to a given delimiter.
9797
common_peg_parser prefix(const std::string & s, const std::string & delimiter = {});
9898

99+
// Return a parser that parses all elements of tag, but leading and trailing spaces are optional
100+
common_peg_parser optspace(const std::string & tag);
101+
99102
// Legacy-compatible helper for building standard JSON tool calls
100103
// Used by tests and manual parsers
101104
// name_key/args_key: JSON key names for function name and arguments

common/chat.cpp

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,22 +2116,38 @@ std::optional<common_chat_params> common_chat_try_specialized_template(
21162116
return std::nullopt;
21172117
}
21182118

2119+
static std::string common_chat_templates_generation_prompt(const common_chat_template & tmpl, const autoparser::generation_params & inputs) {
2120+
autoparser::generation_params params = inputs;
2121+
params.add_generation_prompt = false;
2122+
std::string no_gen_prompt = common_chat_template_direct_apply_impl(tmpl, params);
2123+
params.add_generation_prompt = true;
2124+
std::string gen_prompt = common_chat_template_direct_apply_impl(tmpl, params);
2125+
2126+
size_t prefix_len = 0;
2127+
size_t min_size = std::min(no_gen_prompt.size(), gen_prompt.size());
2128+
while (prefix_len < min_size && no_gen_prompt[prefix_len] == gen_prompt[prefix_len]) {
2129+
prefix_len++;
2130+
}
2131+
return gen_prompt.substr(prefix_len);
2132+
}
2133+
21192134
static common_chat_params common_chat_templates_apply_jinja(const struct common_chat_templates * tmpls,
21202135
const struct common_chat_templates_inputs & inputs) {
21212136
autoparser::generation_params params;
21222137
params.tools = common_chat_tools_to_json_oaicompat(inputs.tools);
21232138
const auto & tmpl =
21242139
params.tools.is_array() && tmpls->template_tool_use ? *tmpls->template_tool_use : *tmpls->template_default;
2125-
const auto & src = tmpl.source();
2126-
const auto & caps = tmpl.original_caps();
2127-
params.messages = render_message_to_json(inputs.messages, tmpl.original_caps());
2128-
params.tool_choice = inputs.tool_choice;
2129-
params.reasoning_format = inputs.reasoning_format;
2130-
params.enable_thinking = inputs.enable_thinking;
2131-
params.grammar = inputs.grammar;
2132-
params.now = inputs.now;
2133-
params.add_bos = tmpls->add_bos;
2134-
params.add_eos = tmpls->add_eos;
2140+
const auto & src = tmpl.source();
2141+
const auto & caps = tmpl.original_caps();
2142+
params.messages = render_message_to_json(inputs.messages, tmpl.original_caps());
2143+
params.tool_choice = inputs.tool_choice;
2144+
params.reasoning_format = inputs.reasoning_format;
2145+
params.enable_thinking = inputs.enable_thinking;
2146+
params.grammar = inputs.grammar;
2147+
params.now = inputs.now;
2148+
params.add_generation_prompt = inputs.add_generation_prompt;
2149+
params.add_bos = tmpls->add_bos;
2150+
params.add_eos = tmpls->add_eos;
21352151

21362152
if (src.find("<|channel|>") == std::string::npos) {
21372153
// map developer to system for all models except for GPT-OSS
@@ -2153,14 +2169,7 @@ static common_chat_params common_chat_templates_apply_jinja(const struct common_
21532169
workaround::func_args_not_string(params.messages);
21542170
}
21552171

2156-
params.add_generation_prompt = false;
2157-
std::string no_gen_prompt = common_chat_template_direct_apply_impl(tmpl, params);
2158-
params.add_generation_prompt = true;
2159-
std::string gen_prompt = common_chat_template_direct_apply_impl(tmpl, params);
2160-
auto diff = calculate_diff_split(no_gen_prompt, gen_prompt);
2161-
params.generation_prompt = diff.right + diff.suffix;
2162-
2163-
params.add_generation_prompt = inputs.add_generation_prompt;
2172+
params.generation_prompt = common_chat_templates_generation_prompt(tmpl, params);
21642173

21652174
params.extra_context = common_chat_extra_context();
21662175
for (auto el : inputs.chat_template_kwargs) {
@@ -2212,8 +2221,8 @@ static common_chat_params common_chat_templates_apply_jinja(const struct common_
22122221
auto auto_params = autoparser::peg_generator::generate_parser(tmpl, params, autoparser);
22132222
auto_params.supports_thinking = autoparser.reasoning.mode != autoparser::reasoning_mode::NONE;
22142223
if (auto_params.supports_thinking) {
2215-
auto_params.thinking_start_tag = autoparser.reasoning.start;
2216-
auto_params.thinking_end_tag = autoparser.reasoning.end;
2224+
auto_params.thinking_start_tag = trim_whitespace(autoparser.reasoning.start);
2225+
auto_params.thinking_end_tag = trim_whitespace(autoparser.reasoning.end);
22172226
}
22182227
auto_params.generation_prompt = params.generation_prompt;
22192228
common_peg_arena arena;

common/reasoning-budget.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ static void common_reasoning_budget_apply(struct llama_sampler * smpl, llama_tok
158158
for (size_t i = 0; i < cur_p->size; i++) {
159159
if (cur_p->data[i].id != forced) {
160160
cur_p->data[i].logit = -INFINITY;
161+
} else {
162+
cur_p->data[i].logit = +INFINITY; // force the token
161163
}
162164
}
163165
}

0 commit comments

Comments
 (0)