Deterministic identity hashing, confidence-aware cacheability rules, and pluggable
two-tier (L1 fast key-value + L2 durable store) caching behaviour for JevDSL.
Cache Key Invariance & Auto-Invalidation
Every cache key ("jev_<sha256>") is derived deterministically from:
- The recursively canonicalized input
statemap (canonicalize/1). - The compile-time
%SystemOneSDK.Prepared{fingerprint: fp}SHA-256 digest of the contract's instructions, question types, and criteria.
Because prepared.fingerprint changes whenever a prompt or criterion is edited in defnoul,
defchoice, defscore, or defjev, stale cached evaluations are invalidated automatically
at compile time without manual cache flushing.
Telemetry Events
JevDSL.Cache emits the following :telemetry events:
[:jev_dsl, :cache, :hit]— Measurements:%{duration: native_time}. Metadata:%{cache_key: String.t(), adapter: module(), tier: :l1 | :l2, contract: atom() | String.t() | nil}.[:jev_dsl, :cache, :miss]— Measurements:%{duration: native_time}. Metadata:%{cache_key: String.t(), adapter: module(), tier: :l1 | :l2, contract: atom() | String.t() | nil}.[:jev_dsl, :cache, :write]— Measurements:%{duration: native_time}. Metadata:%{cache_key: String.t(), adapter: module(), tier: :l1 | :l2, contract: atom() | String.t() | nil}.
Implementing a Custom Cache Adapter
defmodule MyApp.JevCacheAdapter do
@behaviour JevDSL.Cache
@impl true
def get(key, _opts) do
case MyApp.Cache.get(key) do
{:ok, value} when not is_nil(value) -> {:ok, value}
_ -> :miss
end
end
@impl true
def put(key, value, opts) do
ttl = Keyword.get(opts, :ttl, 604_800)
MyApp.Cache.put(key, value, ttl: ttl)
end
end
Summary
Functions
Reads a key from the given adapter module implementing JevDSL.Cache.
Batch-reads keys from adapter using get_many/2 if exported, falling back to adapter_get/3.
Returns a map of %{cache_key => cached_value} for all hits.
Writes a key/value to the given adapter module if resolve_ttl/2 approves caching.
Batch-writes [batch_entry()] to an adapter via put_many/2 if exported, falling back to put/3.
Returns true if a SystemOneSDK response meets the minimum confidence threshold
for long-lived caching (:min_confidence, default 0.65).
Recursively normalizes structs, maps, lists, and tuples into a canonical form with string keys for deterministic binary serialization.
Computes a deterministic <prefix>_<sha256> key for any map structure.
Builds the canonical identity map combining the evaluation state and contract fingerprint.
Computes a deterministic "jev_<sha256>" cache key from state and a %SystemOneSDK.Prepared{}
contract (or fingerprint string / contract key with :contract_module).
Returns true if the evaluation result is a valid SystemOneResponse eligible for
durable Tier-2 persistence.
Determines the appropriate TTL in seconds for an evaluation result (:ok tuple),
or :skip if the result is an error that should not be cached.
Types
Callbacks
Functions
Reads a key from the given adapter module implementing JevDSL.Cache.
@spec adapter_get_many(module() | nil, [cache_key()], keyword()) :: %{ optional(cache_key()) => term() }
Batch-reads keys from adapter using get_many/2 if exported, falling back to adapter_get/3.
Returns a map of %{cache_key => cached_value} for all hits.
Writes a key/value to the given adapter module if resolve_ttl/2 approves caching.
@spec adapter_put_many(module() | nil, [batch_entry()], keyword()) :: :ok
Batch-writes [batch_entry()] to an adapter via put_many/2 if exported, falling back to put/3.
Returns true if a SystemOneSDK response meets the minimum confidence threshold
for long-lived caching (:min_confidence, default 0.65).
Recursively normalizes structs, maps, lists, and tuples into a canonical form with string keys for deterministic binary serialization.
Examples
iex> JevDSL.Cache.canonicalize(%{a: 1, nested: {%{b: 2}}})
%{"a" => 1, "nested" => {:__tuple__, [%{"b" => 2}]}}
Computes a deterministic <prefix>_<sha256> key for any map structure.
Builds the canonical identity map combining the evaluation state and contract fingerprint.
Computes a deterministic "jev_<sha256>" cache key from state and a %SystemOneSDK.Prepared{}
contract (or fingerprint string / contract key with :contract_module).
Examples
iex> key1 = JevDSL.Cache.key(%{a: 1, b: [%{c: 2}]}, "fp_1")
iex> key2 = JevDSL.Cache.key(%{"a" => 1, "b" => [%{"c" => 2}]}, "fp_1")
iex> key1 == key2
true
iex> String.starts_with?(key1, "jev_")
true
Returns true if the evaluation result is a valid SystemOneResponse eligible for
durable Tier-2 persistence.
@spec resolve_ttl(term(), keyword()) :: {:ok, pos_integer()} | :skip
Determines the appropriate TTL in seconds for an evaluation result (:ok tuple),
or :skip if the result is an error that should not be cached.