prosaic-core 1.0.0

General-purpose natural language generation from structured data
Documentation

General-purpose natural language generation from structured data.

Takes structured events and produces natural-sounding prose, not just grammatically correct output. The engine tracks discourse state across calls, so multiple renders flow together like human-written prose — using pronouns, varying phrasing, matching verbosity to impact, and structuring multi-paragraph narratives.

English, Spanish, and German grammars ship out of the box via the prosaic-grammar-en, -es, and -de sibling crates. Add more languages by implementing the [Language] trait.

Quick start

use prosaic_core::{Context, Engine, Session, Strictness, Value, Variation};
use prosaic_grammar_en::English;

let mut engine = Engine::new(English::new())
    .strictness(Strictness::Strict)
    .variation(Variation::Fixed);

engine.register_template(
    "entity.renamed",
    "{old_name|refer} was renamed to {new_name}",
).unwrap();

let mut ctx = Context::new();
ctx.insert("entity_type", Value::String("class".into()));
ctx.insert("old_name", Value::String("Foo".into()));
ctx.insert("new_name", Value::String("Foobar".into()));

let mut session = Session::new();
let sentence = engine.render(&mut session, "entity.renamed", &ctx).unwrap();
assert_eq!(sentence, "The class Foo was renamed to Foobar.");

Type-aware template validation

Context types that derive IntoContext also get a HasProsaicSchema impl for free. Pair it with the context: argument of prosaic_template! to validate slot types at compile time:

use prosaic_core::{Engine, Session};
use prosaic_derive::{IntoContext, prosaic_template};
use prosaic_grammar_en::English;

#[derive(IntoContext)]
struct RenameCtx {
    old_name: String,
    new_name: String,
    consumer_count: i64,
}

// Compile error if `consumer_count` were declared as `String`:
let tpl = prosaic_template! {
    template: "{old_name} → {new_name} ({consumer_count|pluralize:consumer})",
    slots: [old_name, new_name, consumer_count],
    context: RenameCtx,
};

let mut engine = Engine::new(English::new());
engine.register_template("rename", tpl).unwrap();

For templates loaded dynamically (JSON manifests, on-disk sources), use [Engine::register_template_with_schema] to get the same check at registration time.

Feature flags

  • std (default): std::error::Error on ProsaicError, SystemTime::now() fallbacks. Disable for no_std + alloc targets.
  • time (default): {ts|relative} and {ts|since_last} pipes.
  • polish (default): sentence-length budgeting and smart quotes.
  • reg (default): referring expression generation (Dale-Reiter + graph-based).
  • serde (off): Serialize/Deserialize on public types.
  • parallel (off): DocumentPlan::render_parallel via rayon.

no_std support

Disable the std feature to compile under no_std + alloc:

prosaic-core = { version = "1.0.0", default-features = false }

Without the std feature:

  • Variation::Random falls back to Variation::Fixed (variant 0).
  • {ts|relative} and {ts|since_last} require engine.reference_time().
  • ProsaicError does not implement std::error::Error.