Skip to main content

AI Template Runner

Small local models are cheap and fast — they also lose structure the moment a prompt gets long. ait turns that weakness into a workflow: each template is a single Markdown file that gathers context, prompts the model, validates the answer and renders the result. Deterministic scaffolding around a dumb-but-fast model, even one running on your phone.

A template is one file with a TOML section (model, endpoint, sampling), an optional system prompt, the user prompt (with {cmd.*} command output and {question} input slots), and optional [test] / [out] tables to validate and render the answer.

One file, one command, one predictable answer — no agent framework needed.

Why it matters: a small model cannot keep a workflow in its head, but it can fill in a well-shaped slot. Templates put the workflow in the file so the model only has to do the part it is good at. Failures become visible and reproducible through [test], instead of vague "the model is dumb" sessions.

Quick start

Point ait at any OpenAI-compatible chat endpoint (llama.cpp server, Ollama, LM Studio, ...):

  1. Start a model server, e.g. llama.cpp:
llama-server -hf unsloth/Qwen3-0.6B-GGUF --port 8080
  1. Run a ready template:
uvx ai-template-runner run TEMPLATE "What is Python?"

Or clone the repository and try it against a fixture without a model at all — the examples/simulation/ templates run against fixed responses, so you can feel the [test]/[out] mechanics in seconds:

git clone https://github.com/brunodavi/ai-template-runner.git
cd ai-template-runner
uvx ai-template-runner run examples/simulation/conventional-commits/feat.md --test

Running on a phone? See docs/termux.md.

Installation

Command When to use
uvx ai-template-runner --help zero-install trial from PyPI
pipx install ai-template-runner persistent install, any platform (Termux ships pipx)
pip install ai-template-runner any pip environment
uv tool install ai-template-runner uv managed installs
uv tool install git+https://github.com/brunodavi/ai-template-runner.git latest master

Requires Python 3.12+. The CLI is ait (or ai-template-runner); the package has zero runtime dependencies.

Usage

ait run examples/features/full.md "What is Python?"
ait run examples/features/full.md "What is Python?" --quiet
ait run examples/simulation/conventional-commits/feat.md --test
ait run "examples/features/*.md"                      # batch: quoted glob runs every template
ait run examples/features/args.md first second        # positional args fill {1}, {2}...
ait run examples/features/args.md -- "-not-a-flag"    # everything after -- is verbatim arg data
ait --help
ait run --help
  • -q / --quiet: display only the AI response (stdout stays clean for pipes).
  • -t / --test: validate the response against the template's [test] table. PASS/FAIL lines go to stderr and the exit code is 1 when any check fails.
  • --fail-fast: with a batch and --test, stop at the first template with a FAIL.

The template argument is always resolved the same way: an existing file is used directly (absolute or relative), and anything else is matched as a glob pattern relative to the current directory (** recursion works). A batch runs every match in sorted order; files that do not start with the ```toml configuration block are skipped with a warning on stderr. One resolved file that is not a template is a usage error.

Templates

Templates combine TOML configuration and Markdown prompts in a single file. The configuration is defined in a ```toml code block at the beginning of the file, and is parsed and removed from the prompt content. Two --- separators delimit an optional system prompt; everything after them becomes the user prompt. Templates can also execute terminal commands and inject their output directly into prompts.

```toml
[provider]
base_url = 'http://localhost:8080/v1/chat/completions'
model = 'lfm230m'
temperature = 0.2
top_p = 0.9
top_k = 40
min_p = 0.05
max_tokens = 220
seed = 42
stop = []
frequency_penalty = 0.0
presence_penalty = 0.0
repeat_penalty = 1.1

[provider.extras]
typical_p = 1.0
repeat_last_n = 64

[cmd]
message = 'echo hello'
```

---
You are a concise assistant. Use the command output as context.
---

Command output:
{cmd.message}

Answer the following question concisely: {question}

The TOML block is parsed and removed from the prompt content. The optional system prompt is delimited by two --- separators; everything after the second separator is the user prompt. When the separators are omitted, the whole content after the configuration block is the user prompt.

{question} is the alias of {1}: with run TEMPLATE "My Question", both render the first value after the template. Positional slots:

Placeholder Value
{0} absolute path of the running template (for a batch, each run gets its own path)
{1} / {question} first value after the template
{2}, {3}, ... subsequent values

{0} and every positional value are Smart strings, so pythonic slices and inline transforms work on them the same way they do inside [out]: {0[1:]}, {question:strip}, {2[:8]}, {2:replace:old:new}. The {cmd.message} placeholder executes the configured command and inserts its output into the prompt. Placeholders can be used in both the system and user prompts.

The provider supports these generation parameters:

  • temperature, top_p, top_k, min_p: control sampling.
  • max_tokens: limits the generated response length.
  • seed: sets the random seed when supported.
  • stop: defines stop sequences.
  • frequency_penalty, presence_penalty, repeat_penalty: control repetition.
  • extras: passes additional parameters to the API, such as llama.cpp-specific options.

base_url and model are required. Parameter support depends on the API and model server being used.

A template can inherit configuration, commands, and prompts from another template with a top-level extends key placed before any TOML table:

extends = 'base.md'

[provider]
temperature = 0.2

The extends path is resolved relative to the template file itself, so examples/features/ templates use extends = '../base.md'. Child values win per key; nested tables such as [provider.extras] and [cmd] are merged key by key. Empty child prompts fall back to the parent prompts, which lets a template reuse only the parent prompts and configuration.

A template can validate its own output with a [test] table and the --test flag:

[test]
type = 'feat'                              # JSON output field equality
message = 'add health check endpoint'      # exact value comparison
"user.name" = 'Alice'                      # nested JSON path (quoted dotted key)

text = 'exact raw output'                  # exact raw text comparison
text_contains = ['GET_health']             # every fragment present in the raw text
text_regex = '^\{'                         # regex search on the raw text

Run with validation:

ait run examples/simulation/conventional-commits/feat.md --test
ait run "examples/**/*.md" --test    # batch: aggregated exit code over every template

PASS/FAIL lines go to stderr (stdout stays clean for pipes) and the exit code is 1 when any check fails. Missing JSON fields and invalid JSON output fail instead of raising. The [test] table participates in extends inheritance like the other config tables.

A template can also render its own display output with an [out] table. The text key is a render template formatted with the parsed JSON fields ({out.type}, {out.message}, ...); missing fields render as empty:

[out]
text = '{out.type}: {out.message}'

Transforms run in two channels that never share the field namespace (out.count is always the JSON field, never str.count):

[out]
text = '{out.message:strip} | {out.message:replace:GET:SET} | {out.message[:4]}'

[[out.upper]]                               # one step: str.upper() with no args

[[out.replace]]                             # named args match the real str signature
old = 'GET_health'
new = 'GET'

[[out.slice]]                               # helper: builtin slice(start, end)
start = 1
end = 10

The [[out.<method>]] tables run in real document order (one table = one step, an empty table means no arguments) and apply to the rendered text — or to the raw response when there is no text. Steps are real str methods, with named arguments mapped positionally by the real signature. The inline spec is a single step from method:arg1:arg2 (argument values may not contain ':'), and {out.field[1:]} follows pythonic slice syntax. Without [out] (or an empty table) the raw AI response is printed. The [out] config participates in extends inheritance per key: the child text and step lists replace the parent's.

The same inline transforms work on every interpolated value in the prompts — {question:strip}, {question[:80]:lower}, {0[1:]} and {cmd.message:upper}, {cmd.status[1:20]} — since the value stays lazy for {cmd.*} (the command only runs when a field is actually used). See examples/features/args.md for the positional placeholders in action.

Examples and docs

See examples/base.md for the shared base used by the examples, examples/features/ for one example per feature, examples/ for ready-to-use task templates such as email-subject.md and conventional-commits.md, and examples/simulation/ for simulations that run task templates against fixed fixtures.

For deeper material, read docs/architecture.md (why the code is shaped the way it is), docs/termux.md (running local models on a phone), docs/benchmarks.md (what small models can and cannot do) and docs/maintenance.md (release routine for maintainers).

Development

git clone https://github.com/brunodavi/ai-template-runner.git
cd ai-template-runner
uv sync
uv pip install -e .

Editable installation (-e .) lets you run ait directly while developing, so source changes are available without reinstalling.

Run a template to test your changes:

ait run examples/features/full.md "What is Python?"

Run the test suite (from the repository root):

uv run python -m unittest

Contributions are welcome — CI runs the suite on Python 3.12, 3.13 and 3.14, so that is the floor.

Status

AIT is in active development for local AI experimentation and lightweight, template-driven workflows. Expect the template syntax to be stable and everything around it to keep evolving. See CHANGELOG.md for release history.

Licensed under the MIT license.

Metadata

Release files for ai-template-runner 1.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ai-template-runner 1.7.0
File Size Uploaded Provenance
ai_template_runner-1.7.0.tar.gz 15.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ai-template-runner 1.7.0
File Interpreter ABI Platform
ai_template_runner-1.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 33.7 kB

Release files / ai_template_runner-1.7.0.tar.gz

Download URL ai_template_runner-1.7.0.tar.gz
Size 15.4 kB
Tags Source
SHA-256 checksum
How to use checksums
27f8b9fc2f13d44cb625a904503420de01254ddadd733e00a424eb27c83bee39
BLAKE2b-256 checksum
How to use checksums
8d1fed3e9da8cd64b0ae0a408eb49950ef726355640a3ecee380c80a53b87d75
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Release files / ai_template_runner-1.7.0-py3-none-any.whl

Download URL ai_template_runner-1.7.0-py3-none-any.whl
Size 18.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
662ca83baae4e6233c57eb20f9b413e60a29e4cd8706d9406bc08a4c5da4d473
BLAKE2b-256 checksum
How to use checksums
9e6c5873349856866c79d3946a5dc38e18ebfb397d810d680c9811a3549579dc
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Security for ai-template-runner 1.7.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ai_template_runner-1.7.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page