Foresight.Storage.LinkConstruction (Foresight v0.1.0)

Copy Markdown View Source

Retain-time construction of the memory_links graph (Track C1).

Builds directed unit->unit edges so the recall graph and temporal arms have real, precomputed structure to expand over — restoring genuine multi-way RRF fusion instead of recomputing signals per-candidate at query time.

Two link types are built here (mirrors Python Hindsight engine/retain/link_utils.py):

  • semantic — pgvector ANN. Each new unit is linked to its top-K most similar existing units in the same bank and fact_type whose cosine similarity is at least @semantic_threshold; weight = cosine similarity. Because the new batch is already committed when this runs, the ANN naturally covers both new-vs-existing and new-vs-new (sibling) neighbours.
  • temporal — each new unit is linked to units whose event time falls within @temporal_window_hours; weight = max(floor, 1 - Δhours/window), capped at @max_temporal_links_per_unit strongest per unit.

Deferred, explicitly tracked (must not be lost):

  • caused_by links via LlmCore (Track C4).
  • entity-typed unit-grain links (Track C2).

Runs POST-COMMIT (its own reads/writes, outside the retain transaction) so an ANN error can never poison or roll back the retain, and index probes never hold write locks. Best-effort: on any failure it logs and yields 0 links.

Summary

Functions

Build semantic + temporal links for a batch of just-inserted memory units.

Functions

build_links(repo, bank_id, inserted_units, repo_opts)

@spec build_links(
  Ecto.Repo.t(),
  String.t(),
  [Foresight.Schemas.MemoryUnit.t()],
  keyword()
) ::
  {:ok, non_neg_integer()}

Build semantic + temporal links for a batch of just-inserted memory units.

Returns {:ok, links_created}. Never raises — an internal error is logged and reported as {:ok, 0} so retain is unaffected.