Lemma (lemma_engine v0.8.12)

Copy Markdown View Source

Lemma rules engine for Elixir.

Wraps the Lemma engine (Rust) via NIFs. Create an engine, load specs from string or paths, run evaluations, and introspect schemas.

Example

{:ok, engine} = Lemma.new()
:ok = Lemma.load(engine, "spec foo\nfact x: 1\nrule y: x + 1", "my_spec.lemma")
{:ok, response} = Lemma.run(engine, "foo", [])
# response is a map from decoded JSON

Engine lifecycle

Each engine is an opaque resource. Do not share the same engine ref across processes unless you serialize access (e.g. via a GenServer).

Summary

Functions

Returns the serialized execution plan for a spec as a map.

Formats Lemma source code. Does not require an engine instance.

Generates an OpenAPI 3.1 JSON document for the loaded specs (Lemma HTTP API shape).

Inverts a rule to find input domains that produce a desired outcome.

Lists all loaded specs. Each item is a map with :name, :effective_from, :effective_to, and :schema.

Loads a spec from a string. Source label is used for error reporting (e.g. "my_spec.lemma"). Use "inline" when no path.

Loads specs from paths (files and/or directories). Directories are expanded one level; only .lemma files are loaded.

Creates a new engine. Optionally pass a map of resource limits; omitted keys use defaults.

Removes a spec from the engine by name and effective datetime.

Runs a spec. Options: :effective (datetime string or nil), :data (map).

Returns the schema for a spec.

Temporal version options for API docs (same boundaries as lemma serve / Scalar).

Types

engine()

@type engine() :: reference()

limits_map()

@type limits_map() :: %{required(String.t()) => pos_integer()} | nil

spec_name()

@type spec_name() :: String.t()

Functions

execution_plan(engine, spec, opts \\ [])

@spec execution_plan(engine(), spec_name(), keyword()) ::
  {:ok, map()} | {:error, term()}

Returns the serialized execution plan for a spec as a map.

Options: :effective (datetime string or nil).

format(code)

@spec format(String.t()) :: {:ok, String.t()} | {:error, term()}

Formats Lemma source code. Does not require an engine instance.

Example

{:ok, formatted} = Lemma.format("spec foo\nfact   x:  1\nrule y: x +  1")

generate_openapi(engine, opts \\ [])

@spec generate_openapi(
  engine(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Generates an OpenAPI 3.1 JSON document for the loaded specs (Lemma HTTP API shape).

Options: :explanations (boolean, default false), :effective (datetime string or nil for "now").

invert(engine, spec_name, effective, rule_name, target, values \\ %{})

@spec invert(engine(), spec_name(), String.t() | nil, String.t(), map(), map()) ::
  {:ok, map()} | {:error, term()}

Inverts a rule to find input domains that produce a desired outcome.

effective is a datetime string or nil.

Target is a map with :outcome ("value""veto""any_value""any_veto"),
optionally :op ("eq""neq""lt"etc.), and for "value"/"veto": :value or :message.

list(engine)

@spec list(engine()) :: {:ok, [map()]} | {:error, term()}

Lists all loaded specs. Each item is a map with :name, :effective_from, :effective_to, and :schema.

Temporal versions form a half-open [effective_from, effective_to) range:

  • :effective_from is nil when the spec has no declared start date (the first version is unbounded at the start).
  • :effective_to is nil when the spec has no later version (this row is the latest and stays valid forward indefinitely).
  • Otherwise :effective_to equals the next version's :effective_from (exclusive end of this row's validity).

:schema is the decoded [Lemma.schema/3] envelope for this version so callers never need a second round-trip.

load(engine, code, source_label \\ "inline")

@spec load(engine(), String.t(), String.t()) :: :ok | {:error, [map()]}

Loads a spec from a string. Source label is used for error reporting (e.g. "my_spec.lemma"). Use "inline" when no path.

load_from_paths(engine, paths)

@spec load_from_paths(engine(), [String.t()]) :: :ok | {:error, [map()]}

Loads specs from paths (files and/or directories). Directories are expanded one level; only .lemma files are loaded.

new(limits \\ nil)

@spec new(limits_map()) :: {:ok, engine()} | {:error, term()}

Creates a new engine. Optionally pass a map of resource limits; omitted keys use defaults.

Options (limits map keys)

  • max_files - max .lemma files per load_from_paths
  • max_loaded_bytes - max total bytes to load
  • max_file_size_bytes - max single file size
  • max_total_expression_count - max expression nodes
  • max_expression_depth - max nesting depth
  • max_expression_count - max expressions per file
  • max_data_value_bytes - max data value size

Examples

{:ok, engine} = Lemma.new()
{:ok, engine} = Lemma.new(%{max_files: 100})

remove_spec(engine, spec_name, effective)

@spec remove_spec(engine(), spec_name(), String.t()) :: :ok | {:error, term()}

Removes a spec from the engine by name and effective datetime.

run(engine, spec, opts \\ [])

@spec run(engine(), spec_name(), keyword()) :: {:ok, map()} | {:error, term()}

Runs a spec. Options: :effective (datetime string or nil), :data (map).

Returns decoded JSON response.

schema(engine, spec, opts \\ [])

@spec schema(engine(), spec_name(), keyword()) :: {:ok, map()} | {:error, term()}

Returns the schema for a spec.

Options: :effective (datetime string or nil).

temporal_api_sources(engine)

@spec temporal_api_sources(engine()) :: {:ok, [map()]} | {:error, term()}

Temporal version options for API docs (same boundaries as lemma serve / Scalar).

Returns a list of maps with string keys "title" and "slug". Slug "now" means the latest interface at request time; other slugs match effective strings for generate_openapi/2.