Skip to content

Commit 2deec0f

Browse files
committed
[refact] change to single header file.
1 parent 1904044 commit 2deec0f

File tree

5 files changed

+132
-111
lines changed

5 files changed

+132
-111
lines changed

CMakeLists.txt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@ project(jinja_cpp VERSION 0.0.1 LANGUAGES CXX)
44
set(CMAKE_CXX_STANDARD 11)
55
set(CMAKE_CXX_STANDARD_REQUIRED ON)
66

7-
include_directories(include)
8-
include_directories(third_party)
9-
10-
# Library
11-
add_library(jinja src/jinja.cpp)
7+
# Header-only Library
8+
add_library(jinja INTERFACE)
9+
target_include_directories(jinja INTERFACE . third_party)
1210

1311
# Test
1412
enable_testing()

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,21 @@ It focuses on supporting the subset of Jinja2 used by modern Large Language Mode
1111
## Features
1212

1313
- **C++11 Compatible**: Ensures maximum compatibility across older compiler versions and embedded systems.
14-
- **Easy Integration**: The core library consists of just **one header file** (`include/jinja.hpp`) and **one source file** (`src/jinja.cpp`), making it extremely easy to copy and drop into any project.
14+
## Integration
15+
16+
The library is a single header file. Just copy `jinja.hpp` to your project's include directory (or root).
17+
18+
### Feature Checking (Versioning)
19+
20+
You can check the library version using standard macros:
21+
22+
```cpp
23+
#include "jinja.hpp"
24+
25+
#if JINJA_VERSION_MAJOR >= 0
26+
// Use jinja.cpp features
27+
#endif
28+
```
1529
- **Lightweight**: Minimal dependencies (only `nlohmann/json`).
1630
- **LLM Focused**: Native support for `messages`, `tools`, `add_generation_prompt`, and special tokens.
1731
- **Strictly Typed**: Uses `nlohmann::json` for context management.

README_CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
## 特性
1212

1313
- **C++11 兼容**:确保在旧版编译器和嵌入式系统上的最大兼容性。
14-
- **易于集成**:核心库仅包含**一个头文件** (`include/jinja.hpp`) 和**一个源文件** (`src/jinja.cpp`),非常方便拷贝并集成到任何项目中。
14+
- **易于集成**:核心库仅包含**一个头文件** (`jinja.hpp`,位于根目录),非常方便拷贝并集成到任何项目中。
1515
- **轻量级**:依赖极少 (仅依赖 `nlohmann/json`,已包含在项目中)。
1616
- **专注 LLM**:原生支持 `messages`, `tools`, `add_generation_prompt` 以及特殊 token 的处理。
1717
- **类型安全**:使用 `nlohmann::json` 进行上下文管理。

include/jinja.hpp

Lines changed: 0 additions & 89 deletions
This file was deleted.

src/jinja.cpp renamed to jinja.hpp

Lines changed: 113 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,102 @@
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;
441539
static bool is_truthy(const json& val);
442540
static 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

Comments
 (0)