LLTSharp 1.6.0

dotnet add package LLTSharp --version 1.6.0
                    
NuGet\Install-Package LLTSharp -Version 1.6.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="LLTSharp" Version="1.6.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="LLTSharp" Version="1.6.0" />
                    
Directory.Packages.props
<PackageReference Include="LLTSharp" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add LLTSharp --version 1.6.0
                    
#r "nuget: LLTSharp, 1.6.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package LLTSharp@1.6.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=LLTSharp&version=1.6.0
                    
Install as a Cake Addin
#tool nuget:?package=LLTSharp&version=1.6.0
                    
Install as a Cake Tool

LLTSharp

Build Tests

A flexible and expressive template engine for Large Language Model (LLM) prompts and structured message generation in C#.
LLT is designed to make prompt engineering and content generation as powerful and maintainable as regular C# code.

✨ Key Features

  • Razor-inspired DSL with @if, @foreach, expressions, metadata, and inline variables
  • Full expression evaluator — arithmetic, logic, ternary, null-coalescing (??), property checks (?:), safe navigation (?.), array/object literals, and method calls
  • Message-oriented syntax for LLM role structures (system, user, assistant, tool)
  • Powerful metadata filtering — select the best template by language, model, or custom qualifiers
  • Metadata fallback schemes — hierarchical language fallback (en-USen → sibling → default)
  • Composable templates — reuse and render nested templates inside others, with optional context override (@render 'name' with ctx)
  • Extensible function system — built-in functions plus custom ones registered from C#
  • Deterministic formatting — indentation is normalized during parsing, inner spacing is preserved, output is predictable
  • Library-driven workflow — import templates from assemblies, files, or strings
  • Body parsing — parse template bodies without the @template wrapper via ParseTextTemplate / ParseMessagesTemplate

📦 Installation

Install the package from NuGet:

dotnet add package LLTSharp

Or via the Package Manager Console:

Install-Package LLTSharp

💡 Example

var parser = new LLTParser();

var templateStr = """
@template GreetingTemplate
{
    Greetings, @name!
    @if age > 18
    {
        You are an adult.
    }
    else
    {
        You are too young!
    }

    Have a nice day.
}
""";

var template = parser.Parse(templateStr).First();

var adult = new { name = "Andrew", age = 20 };
var young = new { name = "Alice", age = 15 };

Console.WriteLine(template.Render(adult));
Console.WriteLine(template.Render(young));

Output:

Greetings, Andrew!
You are an adult.

Have a nice day.

Greetings, Alice!
You are too young!

Have a nice day.

🧩 Template Library Usage

Multiple templates can be stored, versioned, and retrieved by language or model ID:

var lib = new TemplateLibrary();

lib.ImportFromString("""
@template greeting
{
    @metadata { lang: 'en' }
    Hello!
}

@template greeting
{
    @metadata { lang: 'en', model: 'gpt-4' }
    Hello GPT-4!
}

@template greeting
{
    @metadata { lang: 'es' }
    Hola!
}
""");

var template = lib.Retrieve("greeting", new LanguageMetadata("en"), new TargetModelMetadata("gpt-4"));
Console.WriteLine(template.Render()); // Hello GPT-4!

Templates are resolved by metadata specificity, similar to CSS selector priority. If no template matches exactly, use fallback retrieval:

// en-US → en → any sibling of the same language root → default language
lib.SetLanguageFallbackScheme(new HierarchicalLanguageFallbackScheme("en"));

var fallback = lib.RetrieveWithFallback("greeting", new LanguageMetadata("en-US"));

💬 Message Templates for LLM Chats

LLT supports special @messages syntax for structured chat prompts:

@messages template ChatBot
{
    @metadata { language: 'en', version: 1 }

    @system message {
        You are a helpful assistant.

        Here is your instructions:
        @foreach instruction in instructions {
            Instruction: @instruction
        }
    }

    @foreach name in names {
        @message {
            @role 'user'
            Hello, I am @name!
        }
    }
}

Rendered result: a sequence of chat messages with proper roles (system, user, assistant, tool). Roles can be declared statically (@system message, @user message, @assistant message, @tool message) or dynamically via @message { @role <expression> ... }.

🧠 Language Reference

Expressions

Literals
Literal Examples
Numbers 42, 3.14, -7
Strings 'single', "double"
Booleans true, false
Null null
Arrays [1, 2, 3], ['a', ctx?.value], [] (trailing commas allowed)
Objects { name: 'Fish', qty: 5 }, { 'key with spaces': 1, [expr]: 2 }
Access
Syntax Meaning
ctx The root of the rendering context
name Property of the root context (same as ctx.name)
?name Safe property access — returns null instead of throwing when missing
a.b / a.b.c Property access (chained)
a?.b Safe navigationnull if a is null or lacks b
a[expr] Index access (arrays, dictionaries, strings)
a?[expr] Safe index access
a.method(args) Method call (any public .NET method)
a?.method(args) Safe method call
func(args) Global template function (see Functions)
Unary operators
Operator Meaning Example
- Negation @(-x)
! Logical NOT @(!flag)
# Length (string / array / dictionary) @(#name), @if #items > 0
+ No-op (kept for symmetry) @(+x)
Binary operators (by precedence, high → low)
Precedence Operators Meaning
1 * / % Multiplication, division, modulus
2 + - Addition, subtraction, string concatenation
3 < <= > >= Relational
4 ?: Has operator — checks whether the left operand has the right property
5 == != Equality
6 && Logical AND
7 \|\| Logical OR
8 ?? Null-coalescing — right operand when the left is null or missing
9 ? : Ternary conditional
Null-coalescing, Has and Safe navigation
@template t {
    1: @(?value ?? 'No value')
    2: @(ctx ?: 'value' ? value ?? 'Null' : 'No value')
}
Render context Output
new { value = "Hello" } 1: Hello / 2: Hello
new { value = (string?)null } 1: No value / 2: Null — the property exists but is null
new { } 1: No value / 2: No value — the property is missing
  • ?? treats missing and null the same way.
  • ?: (has) distinguishes them: it only checks existence, so it works great as the condition when you need different fallbacks for null vs. missing:
@(user ?: 'name' ? user.name : 'Anonymous')
@(user?.name ?? 'Anonymous')        @/ Safe navigation + coalescing

@-statements in plain text accept simple expressions (unary + member access). Wrap binary expressions in parentheses: @(a + b) — inside @if, @while etc. full expressions are allowed.

Formatting

An expression can be followed by : and a format string:

Price: @price:'0.00'

Statements

Statement Syntax Description
If @if cond { ... } Conditional block
Else else { ... }, else if cond { ... } or @else { ... } Optional @ before else is allowed
Foreach @foreach item in items { ... } Iteration; loop variable is scoped to the block
While @while cond { ... } Conditional loop
Let @let x = expr Declares a new variable (lexically scoped)
Assign @x = expr Assigns to an existing variable
Render @render 'name', @render 'name' with expr Renders another template, optionally with a new context
Output @expr, @(expr), @expr:'format' Prints an expression value
Comment @/ line comment C#-style line comment — to the end of the line
Comment @* block comment *@ Razor-style block comment

Comments are skipped by the parser and can be placed wherever whitespace is allowed, including inside template bodies.

Escapes: @@ renders a literal @, {{ renders {, }} renders }.

Multi-line raw text can be wrapped in five backticks to avoid escaping:

@template code_sample
{
    `````
    @if isNotParsed { this is raw text, not a statement }
    `````
}

Metadata

Attach metadata to a template with the @metadata block (only constant values are allowed):

@template greeting
{
    @metadata { lang: 'en', model: 'gpt-4', version: 2 }
    Hello!
}

Built-in metadata keys (parsed by the corresponding factories):

Key Metadata type
lang LanguageMetadata
model TargetModelMetadata
model_family TargetModelFamilyMetadata
version VersionMetadata

Pass factories to Parse so the keys are recognized; unknown keys become AdditionalMetadata:

var parser = new LLTParser();
var templates = parser.Parse(src, new MetadataFactory[]
{
    new LanguageMetadataFactory(),
    new TargetModelMetadataFactory(),
    new VersionMetadataFactory(),
});
Custom metadata
public class MyMetadata : IMetadata
{
    public MyMetadata(string value) => Value = value;
    public string Value { get; }
}

public class MyMetadataFactory : MetadataFactory
{
    public override bool TryCreateMetadata(string key, TemplateDataAccessor value, out IMetadata metadata)
    {
        if (key == "my_key")
        {
            metadata = new MyMetadata(value.ToString());
            return true;
        }
        metadata = null;
        return false;
    }
}
Fallback schemes
  • lib.SetLanguageFallbackScheme(new HierarchicalLanguageFallbackScheme("en")) — walks up the language hierarchy (en-USen), then tries siblings, then the default language.
  • lib.SetLanguageFallbackScheme(new MajorLanguageFallbackScheme()) — falls back to the major language group.
  • lib.SetFallbackScheme(typeof(LanguageMetadata), scheme) — register a custom MetadataFallbackScheme<T> for any metadata type.

Retrieval API: Retrieve, TryRetrieve, TryRetrieveBest, RetrieveWithFallback, RetrieveAll — each with optional identifier and metadata arguments.

🔧 Functions

Built-in functions (TemplateFunctions.All):

Function Description Example
type(x) Type name of the value (string, number, boolean, object, array, null, ...) @type(item)
length(x) Length of a string, array or dictionary @length(items)
strcat(a, b, ...) Concatenates values into a string @strcat('Hi, ', name)
substr(s, start, len) Substring @substr(name, 0, 3)

Register custom functions and pass them to Render:

var functions = new TemplateFunctionSet(includeDefault: true,
    new TemplateFunction("shout", (self, args) => args[0].ToString().ToUpperInvariant()));

var rendered = template.Render(new { name = "Andrew" }, functions);
Hello, @shout(name)!   @/ → Hello, ANDREW!

🧾 Formatting & Scoping Rules

  • Leading indentation is normalized during parsing: every line loses up to depth × TabSize leading whitespace, where depth is the block nesting level. You can write templates with comfortable indentation — the common base indent is stripped and the output stays clean
  • Inner whitespace, line breaks and blank lines are preserved as written
  • Leading/trailing blank lines of blocks are trimmed; indentation before complex statements (@if, @foreach, @while) is removed
  • Lines containing only non-rendering constructs — @/ and @* *@ comments, @let declarations, variable assignments — are removed entirely: the surrounding line breaks are trimmed so they leave no blank lines in the output
  • TabSize (default 4) on LLTParser controls how many columns one indent level takes during refinement
  • @let variables are lexically scoped
  • Loop variables do not leak outside their block
  • Nested @if and @foreach blocks behave predictably, matching C#‑like logical semantics

🏗️ Architecture Overview

  • LLTParser – parses source text into ASTs (TemplateNode, TemplateExpressionNode, etc.); supports full-file parsing (Parse) and body-only parsing (ParseTextTemplate, ParseMessagesTemplate)
  • Template – runtime object capable of rendering with dynamic context (TextTemplate, MessagesTemplate)
  • TemplateLibrary – registry and loader for collections of templates with filtering and fallbacks
  • Metadata System – extensible mechanism for attaching attributes such as language, model, or custom version
  • Data Accessors – reflection / dictionary / array wrappers (DataAccessorFactory) with PropertiesToLowerCase, KeysToLowerCase and Snapshot options
  • Function SystemTemplateFunctionSet / TemplateFunction for extending template logic
  • ChatMessage Model – unified representation for structured message templates (system, user, assistant, tool)

🔖 License

MIT License © 2025

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.6.0 54 8/23/2026
1.5.0 151 6/21/2026
1.4.0 123 6/20/2026
1.3.0 124 6/15/2026
1.2.4 115 5/31/2026
1.2.3 124 4/24/2026
1.2.2 122 4/21/2026
1.2.1 116 4/18/2026
1.2.0 144 1/25/2026
1.1.0 234 10/21/2025
1.0.0 226 10/15/2025