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();
for (const auto & msg : msgs) {
- json jmsg = msg.to_json_oaicompat(concat_typed_text);
- messages.push_back(jmsg);
+ 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);
+ }
}
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> result;
: *tmpls->template_default;
const auto & src = tmpl.source();
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.tool_choice = inputs.tool_choice;
params.reasoning_format = inputs.reasoning_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);
+
+// 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);
std::vector<common_chat_tool> common_chat_tools_parse_oaicompat(const nlohmann::ordered_json & tools);
std::map<std::string, bool> caps::to_map() const {
return {
- {"requires_typed_content", requires_typed_content},
+ {"supports_string_content", supports_string_content},
+ {"supports_typed_content", supports_typed_content},
{"supports_tools", supports_tools},
{"supports_tool_calls", supports_tool_calls},
{"supports_parallel_tool_calls", supports_parallel_tool_calls},
return v->stats.ops.find(op_name) != v->stats.ops.end();
};
- // case: typed content requirement
+ // case: typed content support
caps_try_execute(
prog,
[&]() {
// tools
return json{nullptr};
},
- [&](bool, value & messages, value &) {
+ [&](bool success, value & messages, value &) {
auto & content = messages->at(0)->at("content");
caps_print_stats(content, "messages[0].content");
if (has_op(content, "selectattr") || has_op(content, "array_access")) {
// 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;
}
}
);
bool supports_parallel_tool_calls = true;
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
std::map<std::string, bool> to_map() const;
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()) {
JJ_DEBUG("%s", "For loop iterable is undefined, skipping loop");
iterable_val = mk_val<value_array>();