Packages

AI agent framework for Elixir built on OTP. TEA-based agents with crash isolation, inter-agent messaging, team supervision, and real SSE streaming to Anthropic, OpenAI, Ollama, and more.

Current section

Files

Jump to
raxol_agent lib raxol agent policy cache.ex
Raw

lib/raxol/agent/policy/cache.ex

defmodule Raxol.Agent.Policy.Cache do
@moduledoc """
Cache policy: short-circuit an operation when its `:key_fn`
matches a cached value in the configured `Raxol.Agent.Cache`
adapter.
Implementation: `Raxol.Agent.PolicyApplier` calls `key_fn.(params)`
to compute the cache key. On hit (`{:ok, value}`), the wrapped
operation does not run and the cached value is returned. On miss,
the operation runs; on `{:ok, value}` the value is stored under
the key with `:ttl_ms` and the result is returned. Errors are
surfaced verbatim without caching.
## Fields
- `storage` -- `{module, config}` tuple resolved at construction
by the `.ets/1` and `.postgrex/1` helpers
- `ttl_ms` -- TTL in milliseconds; `0` means no expiry; must be
`>= 0`
- `key_fn` -- `(params -> term())` that derives the cache key
from the operation's input
## Constructors
Policy.Cache.ets(ttl_ms: 300_000, key_fn: &(&1.user_id))
Policy.Cache.ets(ttl_ms: 60_000, key_fn: &(&1.id), table: :my_cache)
Policy.Cache.postgrex(ttl_ms: 60_000, key_fn: &(&1.id), conn: MyApp.Postgrex)
Bring-your-own adapter: pass `:storage` directly to the struct
constructor for an adapter outside the two shipped defaults.
"""
@type t :: %__MODULE__{
storage: {module(), map()},
ttl_ms: non_neg_integer(),
key_fn: (term() -> term())
}
@enforce_keys [:storage, :ttl_ms, :key_fn]
defstruct [:storage, :ttl_ms, :key_fn]
@doc """
Construct a Cache policy backed by the Ets adapter. Required:
`:ttl_ms`, `:key_fn`. Optional: `:table` (defaults to the Ets
adapter's `:raxol_agent_cache`).
"""
@spec ets(keyword()) :: t()
def ets(opts) do
%__MODULE__{
storage: {Raxol.Agent.Cache.Ets, ets_config(opts)},
ttl_ms: fetch_non_neg!(opts, :ttl_ms),
key_fn: fetch_fun!(opts, :key_fn)
}
end
@doc """
Construct a Cache policy backed by the Postgrex adapter. Required:
`:ttl_ms`, `:key_fn`, `:conn`. Optional: `:table`.
"""
@spec postgrex(keyword()) :: t()
def postgrex(opts) do
%__MODULE__{
storage: {Raxol.Agent.Cache.Postgrex, postgrex_config(opts)},
ttl_ms: fetch_non_neg!(opts, :ttl_ms),
key_fn: fetch_fun!(opts, :key_fn)
}
end
defp ets_config(opts) do
case Keyword.fetch(opts, :table) do
{:ok, table} -> %{table: table}
:error -> %{}
end
end
defp postgrex_config(opts) do
conn = Keyword.fetch!(opts, :conn)
table = Keyword.get(opts, :table)
base = %{conn: conn}
if table, do: Map.put(base, :table, table), else: base
end
defp fetch_non_neg!(opts, key) do
case Keyword.fetch!(opts, key) do
n when is_integer(n) and n >= 0 ->
n
other ->
raise ArgumentError,
"#{inspect(key)} must be a non-negative integer, got: #{inspect(other)}"
end
end
defp fetch_fun!(opts, key) do
case Keyword.fetch!(opts, key) do
fun when is_function(fun, 1) ->
fun
other ->
raise ArgumentError,
"#{inspect(key)} must be a 1-arity function, got: #{inspect(other)}"
end
end
end