chat: fix case where template accepts type content only (#19419)
* chat: fix case where template accepts type content only * rm stray log * reuse render_message_to_json
This commit is contained in:
+34
-3
@@ -380,15 +380,46 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
|
|||||||
return msgs;
|
return msgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msgs, bool concat_typed_text) {
|
static json render_message_to_json(const std::vector<common_chat_msg> & msgs, const jinja::caps & c) {
|
||||||
|
if (!c.supports_string_content && !c.supports_typed_content) {
|
||||||
|
LOG_WRN("%s: Neither string content nor typed content is supported by the template. This is unexpected and may lead to issues.\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool only_string_accepted = c.supports_string_content && !c.supports_typed_content;
|
||||||
|
bool only_typed_accepted = !c.supports_string_content && c.supports_typed_content;
|
||||||
|
|
||||||
json messages = json::array();
|
json messages = json::array();
|
||||||
for (const auto & msg : msgs) {
|
for (const auto & msg : msgs) {
|
||||||
json jmsg = msg.to_json_oaicompat(concat_typed_text);
|
if (only_string_accepted) {
|
||||||
|
json jmsg = msg.to_json_oaicompat(/* concat_typed_text= */ true);
|
||||||
|
messages.push_back(jmsg);
|
||||||
|
} else if (only_typed_accepted) {
|
||||||
|
json jmsg = msg.to_json_oaicompat(/* concat_typed_text= */ false);
|
||||||
|
if (jmsg.at("content").is_string()) {
|
||||||
|
jmsg["content"] = json::array({
|
||||||
|
json{
|
||||||
|
{"type", "text"},
|
||||||
|
{"text", jmsg.at("content").get<std::string>()},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
messages.push_back(jmsg);
|
||||||
|
} else {
|
||||||
|
json jmsg = msg.to_json_oaicompat(/* concat_typed_text= */ false);
|
||||||
messages.push_back(jmsg);
|
messages.push_back(jmsg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return messages;
|
return messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEPRECATED: only used in tests
|
||||||
|
json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msgs, bool concat_typed_text) {
|
||||||
|
jinja::caps c;
|
||||||
|
c.supports_string_content = true;
|
||||||
|
c.supports_typed_content = !concat_typed_text;
|
||||||
|
return render_message_to_json(msgs, c);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const json & tools) {
|
std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const json & tools) {
|
||||||
std::vector<common_chat_tool> result;
|
std::vector<common_chat_tool> result;
|
||||||
|
|
||||||
@@ -3020,7 +3051,7 @@ static common_chat_params common_chat_templates_apply_jinja(
|
|||||||
: *tmpls->template_default;
|
: *tmpls->template_default;
|
||||||
const auto & src = tmpl.source();
|
const auto & src = tmpl.source();
|
||||||
const auto & caps = tmpl.original_caps();
|
const auto & caps = tmpl.original_caps();
|
||||||
params.messages = common_chat_msgs_to_json_oaicompat(inputs.messages, /* concat_text= */ !tmpl.original_caps().requires_typed_content);
|
params.messages = render_message_to_json(inputs.messages, tmpl.original_caps());
|
||||||
params.add_generation_prompt = inputs.add_generation_prompt;
|
params.add_generation_prompt = inputs.add_generation_prompt;
|
||||||
params.tool_choice = inputs.tool_choice;
|
params.tool_choice = inputs.tool_choice;
|
||||||
params.reasoning_format = inputs.reasoning_format;
|
params.reasoning_format = inputs.reasoning_format;
|
||||||
|
|||||||
@@ -240,6 +240,8 @@ bool common_chat_templates_support_enable_thinking(const common_chat_templates *
|
|||||||
|
|
||||||
// Parses a JSON array of messages in OpenAI's chat completion API format.
|
// Parses a JSON array of messages in OpenAI's chat completion API format.
|
||||||
std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const nlohmann::ordered_json & messages);
|
std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const nlohmann::ordered_json & messages);
|
||||||
|
|
||||||
|
// DEPRECATED: only used in tests
|
||||||
nlohmann::ordered_json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msgs, bool concat_typed_text = false);
|
nlohmann::ordered_json common_chat_msgs_to_json_oaicompat(const std::vector<common_chat_msg> & msgs, bool concat_typed_text = false);
|
||||||
|
|
||||||
std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const nlohmann::ordered_json & tools);
|
std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const nlohmann::ordered_json & tools);
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ static void caps_print_stats(value & v, const std::string & path) {
|
|||||||
|
|
||||||
std::map<std::string, bool> caps::to_map() const {
|
std::map<std::string, bool> caps::to_map() const {
|
||||||
return {
|
return {
|
||||||
{"requires_typed_content", requires_typed_content},
|
{"supports_string_content", supports_string_content},
|
||||||
|
{"supports_typed_content", supports_typed_content},
|
||||||
{"supports_tools", supports_tools},
|
{"supports_tools", supports_tools},
|
||||||
{"supports_tool_calls", supports_tool_calls},
|
{"supports_tool_calls", supports_tool_calls},
|
||||||
{"supports_parallel_tool_calls", supports_parallel_tool_calls},
|
{"supports_parallel_tool_calls", supports_parallel_tool_calls},
|
||||||
@@ -89,7 +90,7 @@ caps caps_get(jinja::program & prog) {
|
|||||||
return v->stats.ops.find(op_name) != v->stats.ops.end();
|
return v->stats.ops.find(op_name) != v->stats.ops.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
// case: typed content requirement
|
// case: typed content support
|
||||||
caps_try_execute(
|
caps_try_execute(
|
||||||
prog,
|
prog,
|
||||||
[&]() {
|
[&]() {
|
||||||
@@ -105,12 +106,16 @@ caps caps_get(jinja::program & prog) {
|
|||||||
// tools
|
// tools
|
||||||
return json{nullptr};
|
return json{nullptr};
|
||||||
},
|
},
|
||||||
[&](bool, value & messages, value &) {
|
[&](bool success, value & messages, value &) {
|
||||||
auto & content = messages->at(0)->at("content");
|
auto & content = messages->at(0)->at("content");
|
||||||
caps_print_stats(content, "messages[0].content");
|
caps_print_stats(content, "messages[0].content");
|
||||||
if (has_op(content, "selectattr") || has_op(content, "array_access")) {
|
if (has_op(content, "selectattr") || has_op(content, "array_access")) {
|
||||||
// accessed as an array
|
// accessed as an array
|
||||||
result.requires_typed_content = true;
|
result.supports_typed_content = true;
|
||||||
|
}
|
||||||
|
if (!success) {
|
||||||
|
// failed to execute with content as string
|
||||||
|
result.supports_string_content = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -14,7 +14,9 @@ struct caps {
|
|||||||
bool supports_parallel_tool_calls = true;
|
bool supports_parallel_tool_calls = true;
|
||||||
bool supports_preserve_reasoning = false; // support assistant message with reasoning_content
|
bool supports_preserve_reasoning = false; // support assistant message with reasoning_content
|
||||||
|
|
||||||
bool requires_typed_content = false; // default: use string content
|
// one of the 2 content capabilities must be true
|
||||||
|
bool supports_string_content = true;
|
||||||
|
bool supports_typed_content = false;
|
||||||
|
|
||||||
// for reporting on server
|
// for reporting on server
|
||||||
std::map<std::string, bool> to_map() const;
|
std::map<std::string, bool> to_map() const;
|
||||||
|
|||||||
@@ -446,6 +446,12 @@ value for_statement::execute_impl(context & ctx) {
|
|||||||
|
|
||||||
value iterable_val = iter_expr->execute(scope);
|
value iterable_val = iter_expr->execute(scope);
|
||||||
|
|
||||||
|
// mark the variable being iterated as used for stats
|
||||||
|
if (ctx.is_get_stats) {
|
||||||
|
iterable_val->stats.used = true;
|
||||||
|
iterable_val->stats.ops.insert("array_access");
|
||||||
|
}
|
||||||
|
|
||||||
if (iterable_val->is_undefined()) {
|
if (iterable_val->is_undefined()) {
|
||||||
JJ_DEBUG("%s", "For loop iterable is undefined, skipping loop");
|
JJ_DEBUG("%s", "For loop iterable is undefined, skipping loop");
|
||||||
iterable_val = mk_val<value_array>();
|
iterable_val = mk_val<value_array>();
|
||||||
|
|||||||
Reference in New Issue
Block a user