Jido.Storage behaviour (Jido v2.0.0-rc.4)

View Source

Unified storage behaviour for agent checkpoints and thread journals.

Implementations handle both:

  • Checkpoints: key-value overwrite semantics for agent state snapshots
  • Journals: append-only thread entries with sequence ordering

Built-in Adapters

AdapterDurabilityUse Case
Jido.Storage.ETSEphemeralDevelopment, testing

Implementing Custom Adapters

Implement all 6 callbacks to create a custom storage adapter:

defmodule MyApp.Storage do
  @behaviour Jido.Storage

  @impl true
  def get_checkpoint(key, opts), do: ...

  @impl true
  def put_checkpoint(key, data, opts), do: ...

  @impl true
  def delete_checkpoint(key, opts), do: ...

  @impl true
  def load_thread(thread_id, opts), do: ...

  @impl true
  def append_thread(thread_id, entries, opts), do: ...

  @impl true
  def delete_thread(thread_id, opts), do: ...
end

Concurrency

The append_thread/3 callback accepts an :expected_rev option for optimistic concurrency control. Implementations should reject appends when the current revision doesn't match the expected value.

Summary

Callbacks

Append entries to a thread.

Delete a checkpoint by key.

Delete a thread and all its entries.

Retrieve a checkpoint by key.

Load a thread by ID, reconstructing from stored entries.

Store a checkpoint, overwriting any existing value for the key.

Functions

Normalize a storage configuration to {module, opts} tuple.

Callbacks

append_thread(thread_id, entries, opts)

@callback append_thread(
  thread_id :: String.t(),
  entries :: [Jido.Thread.Entry.t()],
  opts :: keyword()
) ::
  {:ok, Jido.Thread.t()} | {:error, term()}

Append entries to a thread.

Options

  • :expected_rev - If provided, the append should fail with {:error, :conflict} if the current thread revision doesn't match.
  • :metadata - Thread metadata to set (typically only for new threads).

Returns {:ok, updated_thread} on success.

delete_checkpoint(key, opts)

@callback delete_checkpoint(key :: term(), opts :: keyword()) :: :ok | {:error, term()}

Delete a checkpoint by key.

Returns :ok even if the key didn't exist.

delete_thread(thread_id, opts)

@callback delete_thread(thread_id :: String.t(), opts :: keyword()) ::
  :ok | {:error, term()}

Delete a thread and all its entries.

Returns :ok even if the thread didn't exist.

get_checkpoint(key, opts)

@callback get_checkpoint(key :: term(), opts :: keyword()) ::
  {:ok, term()} | :not_found | {:error, term()}

Retrieve a checkpoint by key.

Returns {:ok, data} if found, :not_found if the key doesn't exist.

load_thread(thread_id, opts)

@callback load_thread(thread_id :: String.t(), opts :: keyword()) ::
  {:ok, Jido.Thread.t()} | :not_found | {:error, term()}

Load a thread by ID, reconstructing from stored entries.

Returns {:ok, thread} if entries exist, :not_found if the thread has no entries.

put_checkpoint(key, data, opts)

@callback put_checkpoint(key :: term(), data :: term(), opts :: keyword()) ::
  :ok | {:error, term()}

Store a checkpoint, overwriting any existing value for the key.

Functions

normalize_storage(mod)

@spec normalize_storage(module() | {module(), keyword()}) :: {module(), keyword()}

Normalize a storage configuration to {module, opts} tuple.

Examples

iex> Jido.Storage.normalize_storage(Jido.Storage.ETS)
{Jido.Storage.ETS, []}

iex> Jido.Storage.normalize_storage({Jido.Storage.File, path: "priv/jido"})
{Jido.Storage.File, [path: "priv/jido"]}