render gemma tmpl ok

This commit is contained in:
Xuan Son Nguyen 2025-12-28 12:04:23 +01:00
parent 10835f2720
commit 81310d29c1
6 changed files with 330 additions and 48 deletions

View File

@ -142,11 +142,11 @@ private:
} else if (name == "call") {
statements caller_args;
bool has_caller_args = false;
// bool has_caller_args = false;
if (is(token::open_paren)) {
// Optional caller arguments, e.g. {% call(user) dump_users(...) %}
caller_args = parse_args();
has_caller_args = true;
// has_caller_args = true;
}
auto callee = parse_primary_expression();
if (!is_type<identifier>(callee)) throw std::runtime_error("Expected identifier");

View File

@ -28,8 +28,13 @@ bool is_val(const value & ptr) {
using PointeeType = typename extract_pointee<T>::type;
return dynamic_cast<const PointeeType*>(ptr.get()) != nullptr;
}
template<typename T>
bool is_val(const value_t * ptr) {
using PointeeType = typename extract_pointee<T>::type;
return dynamic_cast<const PointeeType*>(ptr) != nullptr;
}
template<typename T, typename... Args>
value mk_val(Args&&... args) {
std::unique_ptr<typename extract_pointee<T>::type> mk_val(Args&&... args) {
using PointeeType = typename extract_pointee<T>::type;
return std::make_unique<PointeeType>(std::forward<Args>(args)...);
}
@ -70,6 +75,8 @@ struct func_args {
using func_handler = std::function<value(const func_args &)>;
using func_builtins = std::map<std::string, func_handler>;
bool value_compare(const value & a, const value & b);
struct value_t {
int64_t val_int;
double val_flt;
@ -93,12 +100,12 @@ struct value_t {
virtual std::string type() const { return ""; }
virtual int64_t as_int() const { throw std::runtime_error("Not an int value"); }
virtual double as_float() const { throw std::runtime_error("Not a float value"); }
virtual std::string as_string() const { throw std::runtime_error("Not a string value"); }
virtual bool as_bool() const { throw std::runtime_error("Not a bool value"); }
virtual const std::vector<value> & as_array() const { throw std::runtime_error("Not an array value"); }
virtual const std::map<std::string, value> & as_object() const { throw std::runtime_error("Not an object value"); }
virtual int64_t as_int() const { throw std::runtime_error(type() + " is not an int value"); }
virtual double as_float() const { throw std::runtime_error(type() + " is not a float value"); }
virtual std::string as_string() const { throw std::runtime_error(type() + " is not a string value"); }
virtual bool as_bool() const { throw std::runtime_error(type() + " is not a bool value"); }
virtual const std::vector<value> & as_array() const { throw std::runtime_error(type() + " is not an array value"); }
virtual const std::map<std::string, value> & as_object() const { throw std::runtime_error(type() + " is not an object value"); }
virtual value invoke(const func_args &) const { throw std::runtime_error("Not a function value"); }
virtual bool is_null() const { return false; }
virtual bool is_undefined() const { return false; }
@ -106,17 +113,11 @@ struct value_t {
throw std::runtime_error("No builtins available for type " + type());
}
virtual std::string as_repr() const { return as_string(); }
virtual value clone() const {
return std::make_unique<value_t>(*this);
}
virtual bool operator==(const value & other) const {
// TODO
return false;
}
virtual bool operator!=(const value & other) const {
return !(*this == other);
}
};
@ -188,8 +189,12 @@ struct value_array_t : public value_t {
val_arr->push_back(other.val_arr->at(i)->clone());
}
}
void push_back(const value & val) {
val_arr->push_back(val->clone());
}
virtual std::string type() const override { return "Array"; }
virtual const std::vector<value> & as_array() const override { return *val_arr; }
// clone will also share the underlying data (point to the same vector)
virtual value clone() const override {
auto tmp = std::make_unique<value_array_t>();
tmp->val_arr = this->val_arr;
@ -200,7 +205,7 @@ struct value_array_t : public value_t {
ss << "[";
for (size_t i = 0; i < val_arr->size(); i++) {
if (i > 0) ss << ", ";
ss << val_arr->at(i)->as_string();
ss << val_arr->at(i)->as_repr();
}
ss << "]";
return ss.str();
@ -224,8 +229,12 @@ struct value_object_t : public value_t {
(*val_obj)[pair.first] = pair.second->clone();
}
}
void insert(const std::string & key, const value & val) {
(*val_obj)[key] = val->clone();
}
virtual std::string type() const override { return "Object"; }
virtual const std::map<std::string, value> & as_object() const override { return *val_obj; }
// clone will also share the underlying data (point to the same map)
virtual value clone() const override {
auto tmp = std::make_unique<value_object_t>();
tmp->val_obj = this->val_obj;
@ -244,6 +253,7 @@ struct value_func_t : public value_t {
return val_func(args);
}
virtual std::string type() const override { return "Function"; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_func_t>(*this); }
};
using value_func = std::unique_ptr<value_func_t>;
@ -252,6 +262,8 @@ using value_func = std::unique_ptr<value_func_t>;
struct value_null_t : public value_t {
virtual std::string type() const override { return "Null"; }
virtual bool is_null() const override { return true; }
virtual bool as_bool() const override { return false; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_null_t>(*this); }
};
using value_null = std::unique_ptr<value_null_t>;
@ -260,8 +272,13 @@ using value_null = std::unique_ptr<value_null_t>;
struct value_undefined_t : public value_t {
virtual std::string type() const override { return "Undefined"; }
virtual bool is_undefined() const override { return true; }
virtual bool as_bool() const override { return false; }
virtual std::string as_repr() const override { return type(); }
virtual value clone() const override { return std::make_unique<value_undefined_t>(*this); }
};
using value_undefined = std::unique_ptr<value_undefined_t>;
const func_builtins & global_builtins();
} // namespace jinja

View File

@ -8,6 +8,18 @@
namespace jinja {
const func_builtins & global_builtins() {
static const func_builtins builtins = {
{"raise_exception", [](const func_args & args) -> value {
args.ensure_count(1);
std::string msg = args.args[0]->as_string();
throw raised_exception("Jinja Exception: " + msg);
}},
};
return builtins;
}
const func_builtins & value_int_t::get_builtins() const {
static const func_builtins builtins = {
{"abs", [](const func_args & args) -> value {
@ -189,10 +201,10 @@ const func_builtins & value_string_t::get_builtins() const {
args.ensure_vals<value_string>();
return mk_val<value_string>(args.args[0]->as_string());
}},
{"indent", [](const func_args & args) -> value {
{"indent", [](const func_args &) -> value {
throw std::runtime_error("indent builtin not implemented");
}},
{"join", [](const func_args & args) -> value {
{"join", [](const func_args &) -> value {
throw std::runtime_error("join builtin not implemented");
}},
};
@ -307,5 +319,4 @@ const func_builtins & value_object_t::get_builtins() const {
return builtins;
}
} // namespace jinja

View File

@ -18,11 +18,24 @@ static bool is_stmt(const statement_ptr & ptr) {
return dynamic_cast<const T*>(ptr.get()) != nullptr;
}
static value_array exec_statements(const statements & stmts, context & ctx) {
auto result = mk_val<value_array>();
for (const auto & stmt : stmts) {
JJ_DEBUG("Executing statement of type %s", stmt->type().c_str());
result->val_arr->push_back(stmt->execute(ctx));
}
return result;
}
value identifier::execute(context & ctx) {
auto it = ctx.var.find(val);
auto builtins = global_builtins();
if (it != ctx.var.end()) {
JJ_DEBUG("Identifier '%s' found", val.c_str());
return it->second->clone();
} else if (builtins.find(val) != builtins.end()) {
JJ_DEBUG("Identifier '%s' found in builtins", val.c_str());
return mk_val<value_func>(builtins.at(val));
} else {
JJ_DEBUG("Identifier '%s' not found, returning undefined", val.c_str());
return mk_val<value_undefined>();
@ -31,6 +44,7 @@ value identifier::execute(context & ctx) {
value binary_expression::execute(context & ctx) {
value left_val = left->execute(ctx);
JJ_DEBUG("Executing binary expression with operator '%s'", op.value.c_str());
// Logical operators
if (op.value == "and") {
@ -42,9 +56,9 @@ value binary_expression::execute(context & ctx) {
// Equality operators
value right_val = right->execute(ctx);
if (op.value == "==") {
return mk_val<value_bool>(left_val == right_val);
return mk_val<value_bool>(value_compare(left_val, right_val));
} else if (op.value == "!=") {
return mk_val<value_bool>(left_val != right_val);
return mk_val<value_bool>(!value_compare(left_val, right_val));
}
// Handle undefined and null values
@ -70,6 +84,7 @@ value binary_expression::execute(context & ctx) {
double b = right_val->as_float();
if (op.value == "+" || op.value == "-" || op.value == "*") {
double res = (op.value == "+") ? a + b : (op.value == "-") ? a - b : a * b;
JJ_DEBUG("Arithmetic operation: %f %s %f = %f", a, op.value.c_str(), b, res);
bool is_float = is_val<value_float>(left_val) || is_val<value_float>(right_val);
if (is_float) {
return mk_val<value_float>(res);
@ -80,6 +95,7 @@ value binary_expression::execute(context & ctx) {
return mk_val<value_float>(a / b);
} else if (op.value == "%") {
double rem = std::fmod(a, b);
JJ_DEBUG("Modulo operation: %f %% %f = %f", a, b, rem);
bool is_float = is_val<value_float>(left_val) || is_val<value_float>(right_val);
if (is_float) {
return mk_val<value_float>(rem);
@ -123,6 +139,7 @@ value binary_expression::execute(context & ctx) {
// String concatenation
if (is_val<value_string>(left_val) || is_val<value_string>(right_val)) {
JJ_DEBUG("%s", "String concatenation with + operator");
if (op.value == "+") {
return mk_val<value_string>(left_val->as_string() + right_val->as_string());
}
@ -177,7 +194,6 @@ value filter_expression::execute(context & ctx) {
}
if (is_val<value_array>(input)) {
auto & arr = input->as_array();
auto res = try_builtin(filter_val);
if (res) {
return res;
@ -222,7 +238,12 @@ value if_statement::execute(context & ctx) {
auto out = mk_val<value_array>();
if (test_val->as_bool()) {
for (auto & stmt : body) {
JJ_DEBUG("Executing if body statement of type %s", stmt->type().c_str());
JJ_DEBUG("IF --> Executing THEN body, current block: %s", stmt->type().c_str());
out->val_arr->push_back(stmt->execute(ctx));
}
} else {
for (auto & stmt : alternate) {
JJ_DEBUG("IF --> Executing ELSE body, current block: %s", stmt->type().c_str());
out->val_arr->push_back(stmt->execute(ctx));
}
}
@ -230,19 +251,171 @@ value if_statement::execute(context & ctx) {
}
value for_statement::execute(context & ctx) {
throw std::runtime_error("for_statement::execute not implemented");
}
context scope(ctx); // new scope for loop variables
value break_statement::execute(context & ctx) {
throw std::runtime_error("break_statement::execute not implemented");
}
statement_ptr iter_expr = std::move(iterable);
statement_ptr test_expr = nullptr;
value continue_statement::execute(context & ctx) {
throw std::runtime_error("continue_statement::execute not implemented");
if (is_stmt<select_expression>(iterable)) {
JJ_DEBUG("%s", "For loop has test expression");
auto select = dynamic_cast<select_expression*>(iterable.get());
iter_expr = std::move(select->lhs);
test_expr = std::move(select->test);
}
JJ_DEBUG("Executing for statement, iterable type: %s", iter_expr->type().c_str());
value iterable_val = iter_expr->execute(scope);
if (!is_val<value_array>(iterable_val) && !is_val<value_object>(iterable_val)) {
throw std::runtime_error("Expected iterable or object type in for loop: got " + iterable_val->type());
}
std::vector<value> items;
if (is_val<value_object>(iterable_val)) {
auto & obj = iterable_val->as_object();
for (auto & p : obj) {
items.push_back(mk_val<value_string>(p.first));
}
} else {
auto & arr = iterable_val->as_array();
for (const auto & item : arr) {
items.push_back(item->clone());
}
}
std::vector<std::function<void(context &)>> scope_update_fns;
std::vector<value> filtered_items;
for (size_t i = 0; i < items.size(); ++i) {
context loop_scope(scope);
const value & current = items[i];
std::function<void(context&)> scope_update_fn = [](context &) { /* no-op */};
if (is_stmt<identifier>(loopvar)) {
auto id = dynamic_cast<identifier*>(loopvar.get())->val;
scope_update_fn = [id, &items, i](context & ctx) {
ctx.var[id] = items[i]->clone();
};
} else if (is_stmt<tuple_literal>(loopvar)) {
auto tuple = dynamic_cast<tuple_literal*>(loopvar.get());
if (!is_val<value_array>(current)) {
throw std::runtime_error("Cannot unpack non-iterable type: " + current->type());
}
auto & c_arr = current->as_array();
if (tuple->val.size() != c_arr.size()) {
throw std::runtime_error(std::string("Too ") + (tuple->val.size() > c_arr.size() ? "few" : "many") + " items to unpack");
}
scope_update_fn = [tuple, &items, i](context & ctx) {
auto & c_arr = items[i]->as_array();
for (size_t j = 0; j < tuple->val.size(); ++j) {
if (!is_stmt<identifier>(tuple->val[j])) {
throw std::runtime_error("Cannot unpack non-identifier type: " + tuple->val[j]->type());
}
auto id = dynamic_cast<identifier*>(tuple->val[j].get())->val;
ctx.var[id] = c_arr[j]->clone();
}
};
} else {
throw std::runtime_error("Invalid loop variable(s): " + loopvar->type());
}
if (test_expr) {
scope_update_fn(loop_scope);
value test_val = test_expr->execute(loop_scope);
if (!test_val->as_bool()) {
continue;
}
}
filtered_items.push_back(current->clone());
scope_update_fns.push_back(scope_update_fn);
}
auto result = mk_val<value_array>();
bool noIteration = true;
for (size_t i = 0; i < filtered_items.size(); ++i) {
JJ_DEBUG("For loop iteration %zu/%zu", i + 1, filtered_items.size());
value_object loop_obj = mk_val<value_object>();
loop_obj->insert("index", mk_val<value_int>(i + 1));
loop_obj->insert("index0", mk_val<value_int>(i));
loop_obj->insert("revindex", mk_val<value_int>(filtered_items.size() - i));
loop_obj->insert("revindex0", mk_val<value_int>(filtered_items.size() - i - 1));
loop_obj->insert("first", mk_val<value_bool>(i == 0));
loop_obj->insert("last", mk_val<value_bool>(i == filtered_items.size() - 1));
loop_obj->insert("length", mk_val<value_int>(filtered_items.size()));
loop_obj->insert("previtem", i > 0 ? filtered_items[i - 1]->clone() : mk_val<value_undefined>());
loop_obj->insert("nextitem", i < filtered_items.size() - 1 ? filtered_items[i + 1]->clone() : mk_val<value_undefined>());
ctx.var["loop"] = loop_obj->clone();
scope_update_fns[i](ctx);
try {
for (auto & stmt : body) {
value val = stmt->execute(ctx);
result->push_back(val);
}
} catch (const continue_statement::exception &) {
continue;
} catch (const break_statement::exception &) {
break;
}
noIteration = false;
}
if (noIteration) {
for (auto & stmt : default_block) {
value val = stmt->execute(ctx);
result->push_back(val);
}
}
return result;
}
value set_statement::execute(context & ctx) {
throw std::runtime_error("set_statement::execute not implemented");
auto rhs = val ? val->execute(ctx) : exec_statements(body, ctx);
if (is_stmt<identifier>(assignee)) {
auto var_name = dynamic_cast<identifier*>(assignee.get())->val;
JJ_DEBUG("Setting variable '%s'", var_name.c_str());
ctx.var[var_name] = rhs->clone();
} else if (is_stmt<tuple_literal>(assignee)) {
auto tuple = dynamic_cast<tuple_literal*>(assignee.get());
if (!is_val<value_array>(rhs)) {
throw std::runtime_error("Cannot unpack non-iterable type in set: " + rhs->type());
}
auto & arr = rhs->as_array();
if (arr.size() != tuple->val.size()) {
throw std::runtime_error(std::string("Too ") + (tuple->val.size() > arr.size() ? "few" : "many") + " items to unpack in set");
}
for (size_t i = 0; i < tuple->val.size(); ++i) {
auto & elem = tuple->val[i];
if (!is_stmt<identifier>(elem)) {
throw std::runtime_error("Cannot unpack to non-identifier in set: " + elem->type());
}
auto var_name = dynamic_cast<identifier*>(elem.get())->val;
ctx.var[var_name] = arr[i]->clone();
}
} else if (is_stmt<member_expression>(assignee)) {
auto member = dynamic_cast<member_expression*>(assignee.get());
value object = member->object->execute(ctx);
if (!is_val<value_object>(object)) {
throw std::runtime_error("Cannot assign to member of non-object");
}
if (member->computed) {
throw std::runtime_error("Cannot assign to computed member");
}
if (!is_stmt<identifier>(member->property)) {
throw std::runtime_error("Cannot assign to member with non-identifier property");
}
auto prop_name = dynamic_cast<identifier*>(member->property.get())->val;
auto obj_ptr = dynamic_cast<value_object*>(object.get());
JJ_DEBUG("Setting object property '%s'", prop_name.c_str());
obj_ptr->get()->insert(prop_name, rhs->clone());
} else {
throw std::runtime_error("Invalid LHS inside assignment expression: " + assignee->type());
}
return mk_val<value_null>();
}
value member_expression::execute(context & ctx) {
@ -279,6 +452,7 @@ value member_expression::execute(context & ctx) {
} else if (is_val<value_array>(object) || is_val<value_string>(object)) {
if (is_val<value_int>(property)) {
int64_t index = property->as_int();
JJ_DEBUG("Accessing %s index %lld", is_val<value_array>(object) ? "array" : "string", index);
if (is_val<value_array>(object)) {
auto & arr = object->as_array();
if (index >= 0 && index < static_cast<int64_t>(arr.size())) {
@ -292,6 +466,7 @@ value member_expression::execute(context & ctx) {
}
} else if (is_val<value_string>(property)) {
auto key = property->as_string();
JJ_DEBUG("Accessing %s built-in '%s'", is_val<value_array>(object) ? "array" : "string", key.c_str());
auto builtins = object->get_builtins();
auto bit = builtins.find(key);
if (bit != builtins.end()) {
@ -320,4 +495,55 @@ value member_expression::execute(context & ctx) {
return val;
}
static func_args gather_call_args(const statements & arg_stmts, context & ctx) {
func_args args;
for (auto & arg_stmt : arg_stmts) {
args.args.push_back(arg_stmt->execute(ctx));
}
return args;
}
value call_expression::execute(context & ctx) {
auto args = gather_call_args(this->args, ctx);
value callee_val = callee->execute(ctx);
JJ_DEBUG("Calling function of type %s with %zu arguments", callee_val->type().c_str(), args.args.size());
if (!is_val<value_t>(callee_val)) {
throw std::runtime_error("Callee is not a function: got " + callee_val->type());
}
return callee_val->invoke(args);
}
// compare operator for value_t
bool value_compare(const value & a, const value & b) {
JJ_DEBUG("Comparing types: %s and %s", a->type().c_str(), b->type().c_str());
// compare numeric types
if ((is_val<value_int>(a) || is_val<value_float>(a)) &&
(is_val<value_int>(b) || is_val<value_float>(b))){
try {
return a->as_float() == b->as_float();
} catch (...) {}
}
// compare string and number
// TODO: not sure if this is the right behavior
if ((is_val<value_string>(b) && (is_val<value_int>(a) || is_val<value_float>(a))) ||
(is_val<value_string>(a) && (is_val<value_int>(b) || is_val<value_float>(b)))) {
try {
return a->as_string() == b->as_string();
} catch (...) {}
}
// compare boolean simple
if (is_val<value_bool>(a) && is_val<value_bool>(b)) {
return a->as_bool() == b->as_bool();
}
// compare string simple
if (is_val<value_string>(a) && is_val<value_string>(b)) {
return a->as_string() == b->as_string();
}
// compare by type
if (a->type() != b->type()) {
return false;
}
return false;
}
} // namespace jinja

View File

@ -32,7 +32,7 @@ struct context {
struct statement {
virtual ~statement() = default;
virtual std::string type() const { return "Statement"; }
virtual value execute(context & ctx) { throw std::runtime_error("cannot exec " + type()); }
virtual value execute(context &) { throw std::runtime_error("cannot exec " + type()); }
};
using statement_ptr = std::unique_ptr<statement>;
@ -68,7 +68,7 @@ struct program : public statement {
explicit program(statements && body) : body(std::move(body)) {}
std::string type() const override { return "Program"; }
value execute(context & ctx) override {
value execute(context &) override {
throw std::runtime_error("Cannot execute program directly, use jinja::vm instead");
}
};
@ -113,12 +113,30 @@ struct for_statement : public statement {
struct break_statement : public statement {
std::string type() const override { return "Break"; }
value execute(context & ctx) override;
struct exception : public std::exception {
const char* what() const noexcept override {
return "Break statement executed";
}
};
value execute(context &) override {
throw break_statement::exception();
}
};
struct continue_statement : public statement {
std::string type() const override { return "Continue"; }
value execute(context & ctx) override;
struct exception : public std::exception {
const char* what() const noexcept override {
return "Continue statement executed";
}
};
value execute(context &) override {
throw continue_statement::exception();
}
};
struct set_statement : public statement {
@ -148,14 +166,12 @@ struct macro_statement : public statement {
}
std::string type() const override { return "Macro"; }
value execute(context & ctx) override {}
};
struct comment_statement : public statement {
std::string val;
explicit comment_statement(const std::string & v) : val(v) {}
std::string type() const override { return "Comment"; }
value execute(context & ctx) override {}
};
// Expressions
@ -184,6 +200,7 @@ struct call_expression : public expression {
for (const auto& arg : this->args) chk_type<expression>(arg);
}
std::string type() const override { return "CallExpression"; }
value execute(context & ctx) override;
};
/**
@ -202,7 +219,7 @@ struct integer_literal : public expression {
int64_t val;
explicit integer_literal(int64_t val) : val(val) {}
std::string type() const override { return "IntegerLiteral"; }
value execute(context & ctx) override {
value execute(context &) override {
return std::make_unique<value_int_t>(val);
}
};
@ -211,7 +228,7 @@ struct float_literal : public expression {
double val;
explicit float_literal(double val) : val(val) {}
std::string type() const override { return "FloatLiteral"; }
value execute(context & ctx) override {
value execute(context &) override {
return std::make_unique<value_float_t>(val);
}
};
@ -220,7 +237,7 @@ struct string_literal : public expression {
std::string val;
explicit string_literal(const std::string & val) : val(val) {}
std::string type() const override { return "StringLiteral"; }
value execute(context & ctx) override {
value execute(context &) override {
return std::make_unique<value_string_t>(val);
}
};
@ -300,7 +317,6 @@ struct filter_statement : public statement {
chk_type<identifier, call_expression>(this->filter);
}
std::string type() const override { return "FilterStatement"; }
value execute(context & ctx) override {}
};
/**
@ -396,7 +412,6 @@ struct call_statement : public statement {
for (const auto& arg : this->caller_args) chk_type<expression>(arg);
}
std::string type() const override { return "CallStatement"; }
value execute(context & ctx) override {}
};
struct ternary_expression : public expression {
@ -413,6 +428,14 @@ struct ternary_expression : public expression {
std::string type() const override { return "Ternary"; }
};
struct raised_exception : public std::exception {
std::string message;
raised_exception(const std::string & msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
//////////////////////
struct vm {

View File

@ -11,11 +11,11 @@
#include "jinja/jinja-lexer.h"
int main(void) {
//std::string contents = "{% if messages[0]['role'] == 'system' %}{{ raise_exception('System role not supported') }}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if (message['role'] == 'assistant') %}{% set role = 'model' %}{% else %}{% set role = message['role'] %}{% endif %}{{ '<start_of_turn>' + role + '\\n' + message['content'] | trim + '<end_of_turn>\\n' }}{% endfor %}{% if add_generation_prompt %}{{'<start_of_turn>model\\n'}}{% endif %}";
std::string contents = "{% if messages[0]['role'] == 'system' %}{{ raise_exception('System role not supported') }}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if (message['role'] == 'assistant') %}{% set role = 'model' %}{% else %}{% set role = message['role'] %}{% endif %}{{ '<start_of_turn>' + role + '\\n' + message['content'] | trim + '<end_of_turn>\\n' }}{% endfor %}{% if add_generation_prompt %}{{'<start_of_turn>model\\n'}}{% endif %}";
//std::string contents = "{% if messages[0]['role'] != 'system' %}nice {{ messages[0]['content'] }}{% endif %}";
std::string contents = "<some_tokens> {{ messages[0]['content'] }} <another_token>";
//std::string contents = "<some_tokens> {{ messages[0]['content'] }} <another_token>";
std::cout << "=== INPUT ===\n" << contents << "\n\n";
@ -34,11 +34,11 @@ int main(void) {
std::cout << "stmt type: " << stmt->type() << "\n";
}
std::cout << "\n=== OUTPUT ===\n";
std::cout << "\n=== RUN ===\n";
jinja::context ctx;
auto make_non_special_string = [](const std::string & s) {
jinja::value_string str_val = std::make_unique<jinja::value_string_t>(s);
jinja::value_string str_val = jinja::mk_val<jinja::value_string>(s);
str_val->is_user_input = true;
return str_val;
};
@ -57,7 +57,12 @@ int main(void) {
jinja::vm vm(ctx);
auto results = vm.execute(ast);
std::cout << "\n=== RESULTS ===\n";
for (const auto & res : results) {
if (res->is_null()) {
continue;
}
auto str_ptr = dynamic_cast<jinja::value_string_t*>(res.get());
std::string is_user_input = "false";
if (str_ptr) {