Spectre.Router.SemanticCache.Learned (Spectre v0.3.0)

Copy Markdown View Source

Built-in semantic cache backed by static dataset rows and online examples.

Static rows come from labeled classifier datasets and route examples. Online rows are mutable runtime examples stored in ETS. Exact lookup reads the merged row set directly. Semantic search only indexes embeddings already stored with those rows; it never embeds the full dataset from a request process.

The cache is split by concern: Learned.Rows owns the row and label vocabulary, Learned.Online the ETS-backed online store, Learned.Sources the static dataset and route-example rows, Learned.Index the Vettore index and embedding acquisition, and Learned.Snapshot portable export/import. This module keeps the public API and review workflow.

Summary

Functions

Clears online rows and cached Vettore indexes for an agent.

Deletes an online learned example.

Returns semantic-cache rows for review.

Fetches one review row by identifier.

Loads online examples and their stored embeddings from snapshot rows or a JSONL path.

Looks up text in the built-in semantic cache.

Returns the monotonic in-memory revision of an agent's online examples.

Stores or updates an online learned row.

Changes the route label of an online example and marks it verified.

Returns the merged row set used by built-in exact lookup and semantic search.

Exports review rows and their stored embeddings as portable snapshot maps or an atomic JSONL file.

Edits an online learned example in place.

Marks an online example as reviewed and eligible for normal lookup.

Types

row()

@type row() :: %{
  id: String.t(),
  agent: module(),
  text: String.t(),
  normalized_text: String.t(),
  label: atom(),
  source: source(),
  source_strategy: atom() | nil,
  accepted?: boolean(),
  confidence: float() | nil,
  margin: float() | nil,
  verified?: boolean(),
  editable?: boolean(),
  embedding: [float()] | nil,
  metadata: map(),
  inserted_at: DateTime.t(),
  updated_at: DateTime.t()
}

source()

@type source() :: :offline_dataset | :static_route_example | :online_learned

Functions

clear(agent, opts \\ [])

@spec clear(
  module(),
  keyword()
) :: :ok

Clears online rows and cached Vettore indexes for an agent.

delete(agent, id, opts \\ [])

@spec delete(module(), String.t(), keyword()) :: :ok | {:error, term()}

Deletes an online learned example.

Static route examples and offline dataset rows are read-only. The function therefore returns {:error, :read_only_example} for their identifiers and {:error, :not_found} for an unknown identifier.

:ok = Learned.delete(MyApp.Agent, example_id)

Deleting a row also advances online_revision/1 and refreshes the local Vettore projection so it cannot keep serving the removed example.

examples(agent, opts \\ [])

@spec examples(
  module(),
  keyword()
) :: {:ok, [row()]} | {:error, term()}

Returns semantic-cache rows for review.

By default this returns online learned rows only. Use source: :all, source: :offline_dataset, or source: :static_route_example to inspect other sources.

get_example(agent, id, opts \\ [])

@spec get_example(module(), String.t(), keyword()) :: {:ok, row()} | {:error, term()}

Fetches one review row by identifier.

The lookup includes online, dataset, and static route examples. Static rows are returned with editable?: false; only rows whose source is :online_learned can be changed by relabel/4, verify/3, or delete/3.

{:ok, rows} = Learned.examples(MyApp.Agent)
{:ok, row} = Learned.get_example(MyApp.Agent, hd(rows).id)

Agent router options are loaded first and may be overridden through opts. Returns {:error, :not_found} when no source contains the identifier.

load_snapshot(agent, snapshot_or_opts, opts \\ [])

@spec load_snapshot(module(), term(), keyword()) :: {:ok, map()} | {:error, term()}

Loads online examples and their stored embeddings from snapshot rows or a JSONL path.

The second argument may be a path, a list of snapshot maps, or a keyword list containing :path or :rows:

{:ok, %{loaded: loaded, skipped: skipped, errors: errors}} =
  Learned.load_snapshot(MyApp.Agent, "priv/cache/support.jsonl")

Each label is resolved against the agent's current routes. Blank, malformed, unknown, and no-longer-cacheable rows are skipped and reported in the summary. Pass strict?: true to return {:error, {:invalid_snapshot, summary}} when any row is skipped. Valid rows loaded before that strict error remain stored, so validate untrusted files before using strict loading as a deployment gate.

lookup(text, opts)

@spec lookup(
  String.t(),
  keyword()
) :: {:ok, map()} | {:error, term()}

Looks up text in the built-in semantic cache.

online_revision(agent)

@spec online_revision(module()) :: non_neg_integer()

Returns the monotonic in-memory revision of an agent's online examples.

The revision starts at zero and advances after successful mutations. Semantic index keys are derived from the searchable row contents themselves, while this counter remains available for host diagnostics.

put(text, result, opts)

@spec put(String.t(), map(), keyword()) :: {:ok, row()} | {:error, term()}

Stores or updates an online learned row.

relabel(agent, id, new_label, opts \\ [])

@spec relabel(module(), String.t(), atom(), keyword()) ::
  {:ok, row()} | {:error, term()}

Changes the route label of an online example and marks it verified.

new_label must name a route declared by the agent and that route must allow semantic caching. Offline dataset rows and static route examples are immutable and return {:error, :read_only_example}.

{:ok, updated} =
  Learned.relabel(MyApp.Agent, example_id, :support_request)

true = updated.verified?

A successful mutation increments online_revision/1 and refreshes the local semantic index from the embeddings already stored on the rows.

rows(opts)

@spec rows(keyword()) :: {:ok, [row()]} | {:error, term()}

Returns the merged row set used by built-in exact lookup and semantic search.

snapshot(agent, opts \\ [])

@spec snapshot(
  module(),
  keyword()
) :: {:ok, String.t() | [map()]} | {:error, term()}

Exports review rows and their stored embeddings as portable snapshot maps or an atomic JSONL file.

Without :path, the return value contains JSON-compatible maps:

{:ok, rows} = Learned.snapshot(MyApp.Agent)

With a path, Spectre writes a temporary file and renames it into place:

{:ok, "priv/cache/support.jsonl"} =
  Learned.snapshot(MyApp.Agent, path: "priv/cache/support.jsonl")

Online rows are exported by default. Set source: :all, :offline_dataset, or :static_route_example to select another review view. Snapshot data can be restored with load_snapshot/3. Legacy snapshots without embeddings remain readable for exact lookup, but are not eligible for vector search until the row is learned again with an embedding.

update_example(agent, id, attrs, opts \\ [])

@spec update_example(module(), String.t(), map(), keyword()) ::
  {:ok, row()} | {:error, term()}

Edits an online learned example in place.

Supported attrs: :text (re-embedded through the configured embedding adapter), :label (must name a cacheable route), and :verified. Static route examples and offline dataset rows are read-only and return {:error, :read_only_example}.

{:ok, updated} =
  Learned.update_example(MyApp.Agent, id, %{text: "nuovo testo", label: :PRICING})

A successful mutation increments online_revision/1 and refreshes the local semantic index.

verify(agent, id, opts \\ [])

@spec verify(module(), String.t(), keyword()) :: {:ok, row()} | {:error, term()}

Marks an online example as reviewed and eligible for normal lookup.

Learned rows may be stored as unverified and are excluded unless :semantic_cache_include_unverified? is enabled. Verification sets verified?: true, records :verified_at in metadata, and refreshes the local index from the row's stored embedding.

{:ok, verified} = Learned.verify(MyApp.Agent, example_id)
true = verified.verified?

Static and dataset-backed examples are already trusted and read-only.