dinja 1.2.0
dinja: ^1.2.0 copied to clipboard
Jinja engine for Hugging Face and GGUF LLM chat templates: messages, tools, generation prompts. Parity-tested against llama.cpp and Jinja2. All platforms, including web and Wasm.
1.2.0 #
- Rewrote the README and example around rendering a chat template, and updated the pubspec description and topics.
- Fixed
lstrip_blockskeeping the indentation before a{%or{#on the line after a block tag, as in{% if x %}\n {% set y = 1 %}. It is now removed, as in llama.cpp and Jinja2. Qwen3-Coder, GLM-4.6, GLM-4.7-Flash, MiniMax-M2, Nemotron-3-Nano and Command R7B prompts no longer contain stray spaces. - Fixed
strftime_nowsupporting only%Y %m %d %H %M %S. It now formats as llama.cpp does, with Cstrftimein the C locale as glibc implements it: every conversion (%b,%B,%a,%c,%%and the rest), flags and widths. Like llama.cpp, it throws without a string argument and when the result is empty or 100 bytes or longer. - Fixed
continueandbreakdropping what the loop pass had already rendered:{% for i in [1, 2] %}{{ i }}{% continue %}{% endfor %}is now12. Aforloop'selseblock now runs when no pass finishes withoutcontinueorbreak, as in llama.cpp and Jinja2. Output rendered in a nested block before the signal is kept, as in Jinja2; llama.cpp drops it. Output captured by a blockset, afilterblock, a macro or acaller()body that the signal leaves is discarded, as in llama.cpp. - Fixed input-marked values in a
forloop, including itselseblock, being escaped twice:{% for i in [1] %}{{ x }}{% endfor %}withx = JinjaString.user('<b>')is now<b>. - Fixed
caller()renderingNonefor a body statement without output, such as{% set %}or a comment, and not escapingJinjaString.uservalues in the body. - Fixed
select,reject,selectattr,rejectattranduniqueon none returning none, sonone | selectattr(...) | listwas[None]. They now return an empty list, as in llama.cpp. This fixes the functionary v3.1 prompt without tools.defaultnow also replaces none, as in llama.cpp. - Fixed macro keyword arguments binding by position, which rendered
greet(last='Smith', first='John')with empty values and madeadd(1, b=10)throw. Arguments now bind positionally, then by name, then from defaults, as in llama.cpp and Jinja2, includingcaller(...)arguments in a{% call %}block. A missing required argument throwsNot enough arguments provided, as in llama.cpp; an unknown keyword or a keyword repeating a positional argument throws, as in Jinja2. - Fixed unary
-and+failing to parse before anything but a number literal, as in{{ -n }},{{ items[:-n] }}and{{ not -n }}. As in llama.cpp and Jinja2, they bind tighter than filters, tests and**:-n|absis(-n)|abs. - Fixed
minandmaxwithattributereturning the attribute value instead of the item, as Jinja2 does.attributecan also be the second positional argument. - Added llama.cpp's numeric member access (
{{ items.0 }},{{ {10: 'Bob'}.10 }}), empty subscript (a[]is undefined),int * strrepetition, stringindentwidth (indent('> ')) andstr.formatwith{}placeholders. Otherformatfields, such as{0},{name},{{and{:>5}, throw, as in llama.cpp. - Added
BlankExpressiontopackage:dinja/ast.dartfor thea[]subscript. - Fixed the README and
example/security_example.dartclaiming that plain strings passed torenderare escaped; only values wrapped inJinjaString.userare. - Fixed template text containing
" "(eight spaces in double quotes) being deleted from the output. - Fixed
indentadding a newline to input that ends with one:'foo\n'|indentis nowfoo\n, as in llama.cpp and Jinja2. An empty string stays empty withfirst=true, as in llama.cpp. - Changed tests that take an argument, such as
divisibleby,eqandin, to throw when called without one, as llama.cpp and Jinja2 do.a is divisibleby -ais(a is divisibleby) - ain both, so it now throws instead of rendering-2. - Changed
*argument unpacking, as inf(*items), to throw, as llama.cpp does. It used to pass the list as a single argument. - Added the
formatfilter as llama.cpp has it: it callsstr.format, so'{}-{}'|format(1, 2)is1-2. Jinja2's%-style formatting is not supported:'%s'|format(x)returns%s.
1.1.1 #
- Fixed
tojsonto match llama.cpp:json.dumpsspacing ({"a": 1, "b": [1, 2]}), non-ASCII kept unlessensure_ascii=true, floats formatted as C++%gwith 6 significant digits (1.0is1,3.14159265is3.14159), non-string keys converted to strings, and positional arguments read as(ensure_ascii, indent, separators, sort_keys). A negative or non-integerindentnow gives one-line output, and one separator sets only the item separator. - Fixed printing a list, dict or tuple HTML-escaping its quotes:
{{ [1, '1'] }}now prints[1, '1']. Input-marked items are still escaped. - Fixed
-0.0and numbers outside the 64-bit integer range passed torenderbecoming integers on the web; they are now floats, as on the VM. Other whole-number doubles are still integers on the web, where they are indistinguishable fromint. - Changed list, dict and tuple string conversion to match llama.cpp: a string item containing
'is written in JSON form, as in["it's"].
1.1.0 #
- Added
package:dinja/ast.dartfor template analysis without importingsrc/:parseTemplate, which parses a template into aProgram; the AST node types; andLexerExceptionandParserException.package:dinja/dinja.dartis unchanged. - Fixed string equality to compare content only:
''.strip() == ''is now true, andsafeor input-marked strings equal plain ones. - Removed a debug
printto stdout when aforloop iterates a function; the error now names the function. - Fixed tests with one unparenthesized argument, such as
1 is eq 1and'a' is in ['a'], which failed to parse. As in Jinja2, a value after a test without a comma is now its argument:[x is odd 1]means[x is odd(1)]. - Fixed
uniqueto compare items with Python equality:[1, '1']keeps both,[1, 1.0, true]keeps one. It now ignores string case by default, as Jinja2 does. - Fixed a
ParserExceptionat the end of the template reporting position-1; it now reports the end of the template. - Fixed an incomplete expression at the end of the template, such as
{{ 1 +, throwingRangeErrorinstead ofParserException. - Fixed
ParserExceptionfromTemplateon templates with\r\nor a trailing newline: itssource,pos,lineandcolnow all refer to the normalized text, as withparseTemplate. - Changed
uniqueto follow Jinja2's signature,unique(case_sensitive=false, attribute=none): the first positional argument is nowcase_sensitive, notattribute.uniquenow also deduplicates the characters of a string and the keys of a dict. - Fixed input marking being dropped by
~, string repetition (*),replace,capitalize,title,string,indentand the last piece ofsplit(first ofrsplit), matching llama.cpp. Input-marked text from these is now escaped on output like{{ value }}.joinstill drops the marking, as in llama.cpp. - Fixed double escaping when an escaped block, such as a
{% set %}block or macro output, is joined with+to plain text.
1.0.0 #
- Initial release of Dinja.
- Minimal Jinja2 implementation ported from llama.cpp.
- Support for LLM chat templates.
- Secure input marking system.
- Fixed unused declaration in
nodes.dart. - Fixed
not inoperator logic for Maps. - Fixed duplicate key in
builtins.dart. - Added cross-compatibility generation script (
script/generate_cross_test.py).