1- #include " jinja.hpp"
1+ /*
2+ jinja.hpp - A lightweight C++11 Jinja2 template engine for LLM chat templates.
3+ https://github.com/wangzhaode/jinja.cpp
4+
5+ Licensed under the Apache License, Version 2.0 (the "License");
6+ you may not use this file except in compliance with the License.
7+ You may obtain a copy of the License at
8+
9+ http://www.apache.org/licenses/LICENSE-2.0
10+
11+ Unless required by applicable law or agreed to in writing, software
12+ distributed under the License is distributed on an "AS IS" BASIS,
13+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+ See the License for the specific language governing permissions and
15+ limitations under the License.
16+ */
17+
18+ #pragma once
19+
20+ #include < string>
21+ #include < memory>
22+ #include < vector>
23+ #include < map>
24+ #include < functional>
25+ #include < algorithm>
26+ #include < sstream>
27+ #include < iostream>
28+
29+ // External dependency: nlohmann/json
30+ #include < nlohmann/json.hpp>
31+
32+ #define JINJA_VERSION_MAJOR 0
33+ #define JINJA_VERSION_MINOR 0
34+ #define JINJA_VERSION_PATCH 1
35+ #define JINJA_VERSION_STRING STR (JINJA_VERSION_MAJOR) "." STR(JINJA_VERSION_MINOR) "." STR(JINJA_VERSION_PATCH)
36+
37+ namespace jinja {
38+
39+ using json = nlohmann::json;
40+ using UserFunction = std::function<json (const std::vector<json>&)>;
41+
42+ /* *
43+ * @brief A lightweight, C++11 compatible Jinja2 template renderer.
44+ *
45+ * Designed specifically for LLM chat templates (HuggingFace style).
46+ * It supports a subset of Jinja2 syntax used in modern models.
47+ */
48+ class Template {
49+ public:
50+ /* *
51+ * @brief Construct and compile a Jinja template.
52+ *
53+ * @param template_str The Jinja2 template string.
54+ * @param default_context Optional global variables.
55+ */
56+ inline Template (const std::string& template_str, const json& default_context = json::object());
57+
58+ /* *
59+ * @brief Destructor.
60+ */
61+ inline ~Template ();
62+
63+ // Move semantics
64+ Template (Template&&) noexcept ;
65+ Template& operator =(Template&&) noexcept ;
66+
67+ // Copy semantics deleted
68+ Template (const Template&) = delete ;
69+ Template& operator =(const Template&) = delete ;
70+
71+ /* *
72+ * @brief Core rendering function.
73+ */
74+ inline std::string render (const json& context) const ;
75+
76+ /* *
77+ * @brief Register a custom function.
78+ */
79+ inline void add_function (const std::string& name, UserFunction func);
80+
81+ /* *
82+ * @brief Helper function mimicking HuggingFace's `apply_chat_template`.
83+ */
84+ inline std::string apply_chat_template (
85+ const json& messages,
86+ bool add_generation_prompt = true ,
87+ const json& tools = json::array(),
88+ const json& extra_context = json::object()
89+ ) const ;
90+
91+ private:
92+ struct Impl ;
93+ std::unique_ptr<Impl> m_impl;
94+ };
95+
96+ } // namespace jinja
97+
98+ // --- Implementation ---
99+
2100#include < utility>
3101#include < iostream>
4102#include < ctime>
@@ -17,9 +115,9 @@ std::unique_ptr<T> make_unique(Args&&... args) {
17115 return std::unique_ptr<T>(new T (std::forward<Args>(args)...));
18116}
19117
20- std::string to_python_string (const json& val);
118+ inline std::string to_python_string (const json& val);
21119
22- std::string to_python_repr (const json& val) {
120+ inline std::string to_python_repr (const json& val) {
23121 if (val.is_string ()) {
24122 std::string s = val.get <std::string>();
25123 std::string out = " '" ;
@@ -36,7 +134,7 @@ std::string to_python_repr(const json& val) {
36134 return to_python_string (val);
37135}
38136
39- std::string to_json_string (const json& val, int indent = -1 , int level = 0 ) {
137+ inline std::string to_json_string (const json& val, int indent = -1 , int level = 0 ) {
40138 if (val.is_null ()) return " null" ;
41139 if (val.is_boolean ()) return val.get <bool >() ? " true" : " false" ;
42140 if (val.is_number ()) return val.dump ();
@@ -101,7 +199,7 @@ std::string to_json_string(const json& val, int indent = -1, int level = 0) {
101199 return val.dump ();
102200}
103201
104- std::string to_python_string (const json& val) {
202+ inline std::string to_python_string (const json& val) {
105203 if (val.is_null ()) return " None" ;
106204 if (val.is_boolean ()) return val.get <bool >() ? " True" : " False" ;
107205 if (val.is_number ()) return val.dump ();
@@ -441,11 +539,11 @@ struct Macro;
441539static bool is_truthy (const json& val);
442540static const json UNDEFINED = {{" __jinja_undefined__" , true }};
443541
444- bool is_undefined (const json& val) {
542+ inline bool is_undefined (const json& val) {
445543 return val.is_object () && val.contains (" __jinja_undefined__" );
446544}
447545
448- bool is_truthy (const json& val) {
546+ inline bool is_truthy (const json& val) {
449547 if (is_undefined (val)) return false ;
450548 if (val.is_boolean ()) return val.get <bool >();
451549 if (val.is_string ()) return !val.get <std::string>().empty ();
@@ -1069,7 +1167,7 @@ struct MacroNode : Node {
10691167 }
10701168};
10711169
1072- json CallExpr::evaluate (Context& context) {
1170+ inline json CallExpr::evaluate (Context& context) {
10731171 if (auto func = context.get_function (func_name)) {
10741172 std::vector<json> arg_vals;
10751173 for (auto & arg : args) {
@@ -1947,19 +2045,19 @@ struct Template::Impl {
19472045 }
19482046};
19492047
1950- Template::Template (const std::string& template_str, const json& default_context)
2048+ inline Template::Template (const std::string& template_str, const json& default_context)
19512049 : m_impl(make_unique<Impl>()) {
19522050 m_impl->template_str = template_str;
19532051 m_impl->default_context = default_context;
19542052 m_impl->parse ();
19552053}
19562054
1957- Template::~Template () = default ;
2055+ inline Template::~Template () = default ;
19582056
1959- Template::Template (Template&& other) noexcept = default ;
1960- Template& Template::operator =(Template&& other) noexcept = default ;
2057+ inline Template::Template (Template&& other) noexcept = default;
2058+ inline Template& Template::operator =(Template&& other) noexcept = default ;
19612059
1962- std::string Template::render (const json& context) const {
2060+ inline std::string Template::render (const json& context) const {
19632061 Context ctx (m_impl->default_context );
19642062 ctx.set_functions (&m_impl->functions );
19652063 if (!context.empty ()) {
@@ -1972,11 +2070,11 @@ std::string Template::render(const json& context) const {
19722070 return output;
19732071}
19742072
1975- void Template::add_function (const std::string& name, UserFunction func) {
2073+ inline void Template::add_function (const std::string& name, UserFunction func) {
19762074 m_impl->functions [name] = std::move (func);
19772075}
19782076
1979- std::string Template::apply_chat_template (
2077+ inline std::string Template::apply_chat_template (
19802078 const json& messages,
19812079 bool add_generation_prompt,
19822080 const json& tools,
0 commit comments