params.sampling.reasoning_budget_message = value;
}
).set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_COMPLETION, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_THINK_BUDGET_MESSAGE"));
+ add_opt(common_arg(
+ {"--reasoning-preserve"},
+ {"--no-reasoning-preserve"},
+ "preserve reasoning trace in the full history, not just the last assistant message (default: template default)\n"
+ "compatible with certain templates having 'supports_preserve_reasoning' capability\n"
+ "example: https://docs.z.ai/guides/capabilities/thinking-mode#preserved-thinking",
+ [](common_params & params, bool value) {
+ if (value) {
+ params.default_template_kwargs["preserve_reasoning"] = "true";
+ } else {
+ params.default_template_kwargs["preserve_reasoning"] = "false";
+ }
+ }
+ ).set_examples({LLAMA_EXAMPLE_SERVER, LLAMA_EXAMPLE_COMPLETION, LLAMA_EXAMPLE_CLI}).set_env("LLAMA_ARG_REASONING_PRESERVE"));
add_opt(common_arg(
{"--chat-template"}, "JINJA_TEMPLATE",
string_format(
namespace jinja {
using caps_json_fn = std::function<json()>;
-using caps_analyze_fn = std::function<void(bool, value &, value &)>;
+using caps_ctx_fn = std::function<void(context &)>;
+using caps_analyze_fn = std::function<void(bool, value &, value &, const std::string &)>;
+
+void caps_apply_preserve_reasoning(jinja::context & ctx, bool enabled) {
+ ctx.set_val("preserve_thinking", mk_val<value_bool>(enabled));
+ ctx.set_val("clear_thinking", mk_val<value_bool>(!enabled));
+ ctx.set_val("truncate_history_thinking", mk_val<value_bool>(!enabled));
+}
static void caps_try_execute(jinja::program & prog,
const caps_json_fn & messages_fn,
+ const caps_ctx_fn & ctx_fn,
const caps_json_fn & tools_fn,
const caps_analyze_fn & analyze_fn) {
context ctx;
ctx.is_get_stats = true;
jinja::global_from_json(ctx, json{
{"messages", messages_fn()},
- {"tools", tools_fn()},
+ {"tools", tools_fn ? tools_fn() : json::array()},
{"bos_token", ""},
{"eos_token", ""},
{"add_generation_prompt", true}
}, true);
+ if (ctx_fn) {
+ ctx_fn(ctx);
+ }
+
auto messages = ctx.get_val("messages");
auto tools = ctx.get_val("tools");
// ignore exceptions during capability analysis
}
- analyze_fn(success, messages, tools);
+ analyze_fn(success, messages, tools, result);
}
// for debugging only
}
});
},
- [&]() {
- // tools
- return json{nullptr};
- },
- [&](bool success, value & messages, value &) {
+ nullptr, // ctx_fn
+ nullptr, // tools_fn
+ [&](bool success, value & messages, value &, const std::string &) {
auto & content = messages->at(0)->at("content");
caps_print_stats(content, "messages[0].content");
if (has_op(content, "selectattr") || has_op(content, "array_access")) {
},
});
},
- [&]() {
- // tools
- return json::array();
- },
- [&](bool, value & messages, value &) {
+ nullptr, // ctx_fn
+ nullptr, // tools_fn
+ [&](bool, value & messages, value &, const std::string &) {
auto & content = messages->at(0)->at("content");
caps_print_stats(content, "messages[0].content");
if (!content->stats.used) {
},
});
},
+ nullptr, // ctx_fn
[&]() {
// tools
return json::array({
},
});
},
- [&](bool success, value & messages, value & tools) {
+ [&](bool success, value & messages, value & tools, const std::string &) {
if (!success) {
return; // Nothing can be inferred
}
},
});
},
+ nullptr, // ctx_fn
[&]() {
// tools
return json::array({
},
});
},
- [&](bool success, value & messages, value & tools) {
+ [&](bool success, value & messages, value & tools, const std::string &) {
if (!success) {
result.supports_tool_calls = false;
result.supports_tools = false;
},
});
},
+ nullptr, // ctx_fn
[&]() {
// tools
return json::array({
},
});
},
- [&](bool success, value & messages, value & /*tools*/) {
+ [&](bool success, value & messages, value &, const std::string &) {
if (!success) {
result.supports_parallel_tool_calls = false;
return;
JJ_DEBUG("%s\n", ">>> Running capability check: preserve reasoning");
// case: preserve reasoning content in chat history
+ const std::string reasoning_placeholder = "<REASONING_CONTENT_PLACEHOLDER>";
caps_try_execute(
prog,
[&]() {
// messages
return json::array({
+ {
+ {"role", "user"},
+ {"content", "User message"}
+ },
+ {
+ {"role", "assistant"},
+ {"content", "Assistant message"},
+ // check of reasoning_content deeper in the history, not just the last assistant message
+ {"reasoning_content", reasoning_placeholder}
+ },
{
{"role", "user"},
{"content", "User message"}
},
});
},
- [&]() {
- // tools
- return json::array();
+ [&](context & ctx) {
+ caps_apply_preserve_reasoning(ctx, true);
},
- [&](bool, value & messages, value &) {
- auto & content = messages->at(1)->at("reasoning_content");
- caps_print_stats(content, "messages[1].reasoning_content");
- if (content->stats.used) {
+ nullptr, // tools_fn
+ [&](bool, value &, value &, const std::string & output) {
+ // note: we cannot use stats here because the reasoning_content may be used for "if" condition test, but not actually outputted in the final result
+ if (output.find(reasoning_placeholder) != std::string::npos) {
result.supports_preserve_reasoning = true;
}
}