}), false);
caps_.supports_tools = contains(out, "some_tool");
- auto out_empty = try_raw_render(json::array({dummy_user_msg, {{"role", "assistant"}, {"content", ""}}}), {}, false);
- auto out_null = try_raw_render(json::array({dummy_user_msg, {{"role", "assistant"}, {"content", nullptr}}}), {}, false);
+ const auto render_with_content = [&](const json & content) {
+ const json assistant_msg {{"role", "assistant"}, {"content", content}};
+ // Render two assistant messages as some templates like QwQ-32B are handling
+ // the content differently depending on whether it's the last message or not
+ // (to remove the <think> tag in all but the last message).
+ return try_raw_render(json::array({dummy_user_msg, assistant_msg, dummy_user_msg, assistant_msg}), {}, false);
+ };
+ auto out_empty = render_with_content("");
+ auto out_null = render_with_content(json());
caps_.requires_non_null_content = contains(out_empty, user_needle) && !contains(out_null, user_needle);
json j_null;
dummy_user_msg,
make_tool_calls_msg(json::array({make_tool_call("ipython", dummy_args_obj.dump())})),
}), {}, false);
- auto tool_call_renders_str_arguments = contains(out, "\"argument_needle\":") || contains(out, "'argument_needle':");
+ auto tool_call_renders_str_arguments = contains(out, "<parameter=argument_needle>") || contains(out, "\"argument_needle\":") || contains(out, "'argument_needle':");
out = try_raw_render(json::array({
dummy_user_msg,
make_tool_calls_msg(json::array({make_tool_call("ipython", dummy_args_obj)})),
}), {}, false);
- auto tool_call_renders_obj_arguments = contains(out, "\"argument_needle\":") || contains(out, "'argument_needle':");
+ auto tool_call_renders_obj_arguments = contains(out, "<parameter=argument_needle>") || contains(out, "\"argument_needle\":") || contains(out, "'argument_needle':");
caps_.supports_tool_calls = tool_call_renders_str_arguments || tool_call_renders_obj_arguments;
caps_.requires_object_arguments = !tool_call_renders_str_arguments && tool_call_renders_obj_arguments;
}
};
+static bool in(const Value & value, const Value & container) {
+ return (((container.is_array() || container.is_object()) && container.contains(value)) ||
+ (value.is_string() && container.is_string() &&
+ container.to_str().find(value.to_str()) != std::string::npos));
+}
+
class BinaryOpExpr : public Expression {
public:
enum class Op { StrConcat, Add, Sub, Mul, MulMul, Div, DivDiv, Mod, Eq, Ne, Lt, Gt, Le, Ge, And, Or, In, NotIn, Is, IsNot };
case Op::Gt: return l > r;
case Op::Le: return l <= r;
case Op::Ge: return l >= r;
- case Op::In: return (((r.is_array() || r.is_object()) && r.contains(l)) ||
- (l.is_string() && r.is_string() &&
- r.to_str().find(l.to_str()) != std::string::npos));
- case Op::NotIn:
- return !(((r.is_array() || r.is_object()) && r.contains(l)) ||
- (l.is_string() && r.is_string() &&
- r.to_str().find(l.to_str()) != std::string::npos));
+ case Op::In: return in(l, r);
+ case Op::NotIn: return !in(l, r);
default: break;
}
throw std::runtime_error("Unknown binary operator");
} else if (method->get_name() == "pop") {
vargs.expectArgs("pop method", {1, 1}, {0, 0});
return obj.pop(vargs.args[0]);
+ } else if (method->get_name() == "keys") {
+ vargs.expectArgs("keys method", {0, 0}, {0, 0});
+ auto result = Value::array();
+ for (const auto& key : obj.keys()) {
+ result.push_back(Value(key));
+ }
+ return result;
} else if (method->get_name() == "get") {
vargs.expectArgs("get method", {1, 2}, {0, 0});
auto key = vargs.args[0];
} else if (method->get_name() == "capitalize") {
vargs.expectArgs("capitalize method", {0, 0}, {0, 0});
return Value(capitalize(str));
+ } else if (method->get_name() == "upper") {
+ vargs.expectArgs("upper method", {0, 0}, {0, 0});
+ auto result = str;
+ std::transform(result.begin(), result.end(), result.begin(), ::toupper);
+ return Value(result);
+ } else if (method->get_name() == "lower") {
+ vargs.expectArgs("lower method", {0, 0}, {0, 0});
+ auto result = str;
+ std::transform(result.begin(), result.end(), result.begin(), ::tolower);
+ return Value(result);
} else if (method->get_name() == "endswith") {
vargs.expectArgs("endswith method", {1, 1}, {0, 0});
auto suffix = vargs.args[0].get<std::string>();
auto items = Value::array();
if (args.contains("object")) {
auto & obj = args.at("object");
- if (obj.is_string()) {
- auto json_obj = json::parse(obj.get<std::string>());
- for (const auto & kv : json_obj.items()) {
- items.push_back(Value::array({kv.key(), kv.value()}));
- }
- } else if (!obj.is_null()) {
- for (auto & key : obj.keys()) {
- items.push_back(Value::array({key, obj.at(key)}));
- }
+ if (!obj.is_object()) {
+ throw std::runtime_error("Can only get item pairs from a mapping");
+ }
+ for (auto & key : obj.keys()) {
+ items.push_back(Value::array({key, obj.at(key)}));
}
}
return items;
if (!items.is_array()) throw std::runtime_error("object is not iterable");
return items;
}));
+ globals.set("in", simple_function("in", { "item", "items" }, [](const std::shared_ptr<Context> &, Value & args) -> Value {
+ return in(args.at("item"), args.at("items"));
+ }));
globals.set("unique", simple_function("unique", { "items" }, [](const std::shared_ptr<Context> &, Value & args) -> Value {
auto & items = args.at("items");
if (!items.is_array()) throw std::runtime_error("object is not iterable");