Skip to content
Prompting Press v0.2

Metadata

Both the prompt and each individual variant carry one opaque metadata map. The library stores these maps, echoes them through accessors, and never interprets or acts on them. They exist so application code can attach whatever annotations are needed and read them back.

WhereFieldPurpose
Prompt (top-level)metadataArbitrary prompt annotations — model hints, param hints, tags, ownership.
Each variantmetadataPer-variant labels for the caller’s selection logic — weights, group labels, tags.

Neither map is schema-constrained beyond “an object”, and neither affects rendering or the content hashes. output_model is a separate top-level string field with the same opaque, carried-only contract.

There is one documented exception: if the prompt-level metadata map contains a key named "guard" (any value), check() suppresses the untrusted_without_guard advisory for that prompt. The library only checks for the key’s presence — its value is not read.

summary.yaml
name: summary
role: user
body: "Summarise {{ article }}."
variables:
article:
type: string
trusted: false
metadata: # prompt-level: anything to carry
model_hint: claude-sonnet-4-6
max_tokens: 512
owner: team-content
variants:
terse:
body: "TL;DR of {{ article }}."
metadata: # per-variant: drives the caller's selection logic
weight: 0.2
group: experiment-q4

The accessors return the maps as-is; application code interprets them.

guides_metadata_reading_it_back.rs
//! Reading prompt-level and per-variant metadata back out. The library stores the
//! opaque `metadata` maps and echoes them through accessors; it never interprets
//! them. Standalone — `cargo run --example guides_metadata_reading_it_back`.
use prompting_press::Prompt;
use std::fs;
/// Stand-in for the caller's routing logic — the library never calls this.
fn route_to_model(_hint: &str) {}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/examples");
let p = Prompt::from_yaml(&fs::read_to_string(format!("{dir}/summary_metadata.yaml"))?)?;
// metadata() returns &serde_json::Map<String, serde_json::Value>.
if let Some(hint) = p.metadata().get("model_hint") {
route_to_model(hint.as_str().unwrap_or("default"));
}
// Per-variant metadata is on each Variant in p.variants().
// The caller reads it in selection logic — the library never does.
if let Some(terse) = p.variants().get("terse") {
let _weight = terse.metadata.get("weight");
}
// The accessors return the maps as-is; nothing is interpreted or mutated.
assert_eq!(
p.metadata().get("model_hint").and_then(|v| v.as_str()),
Some("claude-sonnet-4-6")
);
assert_eq!(
p.metadata().get("max_tokens").and_then(|v| v.as_i64()),
Some(512)
);
let terse = p.variants().get("terse").expect("terse variant exists");
assert_eq!(
terse.metadata.get("weight").and_then(|v| v.as_f64()),
Some(0.2)
);
assert_eq!(
terse.metadata.get("group").and_then(|v| v.as_str()),
Some("experiment-q4")
);
println!("model_hint = {:?}", p.metadata().get("model_hint"));
Ok(())
}

metadata is overlayable like any other top-level field — derive replaces the whole map (shallow), so read-then-spread to add a key without dropping the rest. See Deriving a prompt.

docs current as of 0.2.0