FAQ
General
Section titled “General”What is Prompting Press?
Section titled “What is Prompting Press?”A typed, variant-aware prompt-template library — the prompt analogue of a typed config system. It turns typed inputs and a template into rendered text and content-addressed hashes, and nothing else. It works from Rust, Python, and TypeScript via one shared compiled Rust engine.
Does it call an LLM?
Section titled “Does it call an LLM?”No. The library performs no I/O, makes no LLM calls, assembles no provider request body, counts no tokens, and parses no model output. It sits alongside any call layer (LangChain, OpenRouter, the direct Anthropic/OpenAI API, etc.).
Is it a registry / prompt store?
Section titled “Is it a registry / prompt store?”No. There is no Registry surface. Prompt objects are constructed from YAML/JSON/TOML text (or
from a shape dict/object) and held directly — in a module-level constant, a DI container, a config
struct, or wherever fits the application. Versioning and history are owned by git.
The agreement check
Section titled “The agreement check”What is the agreement check?
Section titled “What is the agreement check?”The sound agreement check: a template’s referenced variable names must be a subset of the
prompt’s declared variables map. If any referenced name is absent,
construction fails with undefined_variable — never a silent empty render. This is a
construction-time guarantee (not a CI-only optional).
Is the agreement check the same as check()?
Section titled “Is the agreement check the same as check()?”No. They cover different things:
- Agreement check — enforced at
Prompt::new/from_yaml/from_json/from_toml. Catchestemplate references ⊄ declared variables. Hard error; aPromptthat constructs is always agreement-sound. check()— pure advisory lint on a constructedPrompt. The only live finding it can return isuntrusted_without_guard: atrusted: falsevariable without a guard key. Advisory only — rendering still succeeds.
Trusted flag and types
Section titled “Trusted flag and types”Is trusted enforced at runtime?
Section titled “Is trusted enforced at runtime?”No. The trusted boolean is declarative metadata — the rendering kernel does not gate,
block, or alter rendering based on it. A trusted: false field renders just like a trusted: true
field in result.text. The flag drives two opt-in behaviors:
check()advisory — prompts withtrusted: falsevars and no guard key produce anuntrusted_without_guardfinding.- The opt-in guard — when
GuardConfig { enabled: true }is passed torender,trusted: falsevariable values are wrapped in<untrusted>…</untrusted>delimiters in the rendered body, andRenderResult.guardcarries an advisory instruction for the downstream model.
Neither of these is enforcement. Actual sanitization belongs in the application layer, before the vars are constructed.
Is variables[].type enforced at runtime?
Section titled “Is variables[].type enforced at runtime?”No — and that is intentional. The type field on a PromptVariable (e.g. type: string)
declares the variable’s logical type. The rendering kernel does not inspect it; runtime type
enforcement is owned by the Vars model (garde / Pydantic / Zod). What type does is record the
variable’s shape in the language-neutral prompt definition, so a typed Vars model can be
generated from it (string → str / string / String, integer → int / number /
i64, and so on) and then extended by hand. It is the one piece of per-variable metadata with a
concrete consumer: the definition carries enough to scaffold the per-language Vars model, rather
than each language re-declaring the field shape from scratch.
What is validation_required?
Section titled “What is validation_required?”validation_required: true on a PromptVariable signals that the calling code must supply a
validator covering that variable at construction time. In Python and TypeScript, construction
raises/throws if a validation_required variable is not covered by the supplied validators
class/schema. In Rust, coverage is structural — the generic V at the render<V>() call site
must declare the field or the code won’t compile.
Content hashes
Section titled “Content hashes”What is template_hash / render_hash?
Section titled “What is template_hash / render_hash?”Every RenderResult carries two content-addressed SHA-256 hashes:
template_hash—SHA256(resolved variant template source). The exact bytes the kernel renders against.render_hash—SHA256(rendered output text).
They are returned as data on the render result — nothing is sent anywhere (no telemetry, no OTel coupling). Storing them in a trace pins exactly which template produced which output. Because all three bindings share the same Rust engine, the hashes are byte-identical across Rust, Python, and TypeScript for the same inputs.
Is template_hash / render_hash the same as the per-variable trusted flag?
Section titled “Is template_hash / render_hash the same as the per-variable trusted flag?”No. They are different concepts:
trusted— a per-variable input-trust flag (true= trusted,false= untrusted). Set in the prompt definition. Declarative metadata.template_hash/render_hash— content-addressed fingerprints of the template source and the rendered output. Computed at render time and returned on the result.
Is the advisory guard a sanitizer?
Section titled “Is the advisory guard a sanitizer?”No. When the guard is enabled, trusted: false variable values are wrapped in
<untrusted>…</untrusted> delimiters in the rendered body (with &/</> entity-escaped), and
RenderResult.guard carries an advisory instruction for the downstream model. This is delimiting,
not sanitization — the library never inspects or rewrites the semantic content of a value. The
result.text with guard-on differs from guard-off only in those delimiter wrappers.
How do I actually protect against prompt injection?
Section titled “How do I actually protect against prompt injection?”The library’s scope is narrow. Prompt-injection protection is the application’s responsibility:
apply sanitization in the application layer before building the vars, and route the guard text as
a system-prompt addendum that instructs the downstream model. The library’s role is to delimit
which fields are untrusted (via trusted: false) and surface the advisory — it does not provide
the protection itself.
Integrations & positioning
Section titled “Integrations & positioning”How is this different from Jinja (or minijinja)?
Section titled “How is this different from Jinja (or minijinja)?”It isn’t a better template syntax — Prompting Press renders with minijinja under the hood,
so the {{ }} templating will feel familiar. The difference is everything around the
template that raw Jinja leaves you to build yourself:
- Somewhere to keep prompts. Raw Jinja gives you a template string and nothing else — you still invent how prompts are named, loaded, typed, and versioned. Prompting Press is that structure: named, typed prompt definitions in YAML/JSON/TOML (or code).
- A typed input contract with a build-time check. Jinja’s
StrictUndefinederrors only when a render actually hits a missing variable — at runtime, on that code path, if your data happens to exercise it. Prompting Press declares which variables a prompt has and checks that the template only uses those — at construction, with no data — so a template that references{{ customer }}when the prompt only declaresuserfails before you ship, not at 2am in production. - Variants. Named alternative versions of a prompt (formal/casual, v1/v2, per-language) that you select at render time instead of duplicating templates.
- The same prompt in every language. One shared engine renders a prompt byte-identically from Rust, Python, and TypeScript — Jinja (Python) and nunjucks (JS) would drift.
- A content hash of each render, so you can reproduce exactly what was sent.
How does this fit my framework’s system/user prompt split, or ChatPromptTemplate?
Section titled “How does this fit my framework’s system/user prompt split, or ChatPromptTemplate?”Prompting Press stays out of the way of the split. It produces neutral role-tagged text —
an ordered list of { role, text } messages, with role being system / user /
assistant. Each framework then wants that shaped differently: LangChain keeps the system
turn in the message list, Strands takes the system prompt as a separate argument, and
CrewAI has no message list at all (it uses Agent/Task string fields). Rather than pick one
framework’s convention, Prompting Press hands you the neutral messages and the per-framework
Integrations pages show the few lines that reshape them — including why you
should feed rendered text to the model directly instead of back through ChatPromptTemplate.
API surface
Section titled “API surface”Why is there no .chain() on Composition?
Section titled “Why is there no .chain() on Composition?”A fluent .chain() cannot cross the PyO3/napi FFI boundary (the bindings cannot return Self
through FFI), and it would collide with Iterator::chain in Rust. The builder pattern is
new() + append() — append returns Result<(), ConsumerError> / void / raises, not Self,
so it is intentionally not chainable.
Why does Composition use no guard expansion?
Section titled “Why does Composition use no guard expansion?”Composition renders each entry against a default GuardConfig (opt-out). The guard text is
per-render, not per-composition. To obtain the guard for entries in a multi-message sequence,
render each entry individually with prompt.render(...) and route the guard text as a separate
system message.
docs current as of 0.2.0