Per-group Graphiti instance cache, plus the gateway for graphiti operations.
Holds one shared AsyncFalkorDB (the embedded redis-server child lives
here) and lazily constructs one Graphiti instance per group_id. Cached
in ETS for concurrent reads — for/1 only hits the GenServer on a cache
miss (i.e. the first time any caller asks for a given group). Once cached,
thousands of callers can read the instance simultaneously without going
through the GenServer.
This is intentional. The spike (pythonx-spike/LEARNINGS.md) showed that
Pythonx releases the GIL during graphiti's awaited I/O, so concurrent
Elixir callers parallelise naturally. Serialising calls through a single
GenServer would throw that away.
See ex-graphiti-pool in gralkor/TEST_TREES.md.
Summary
Functions
Ingest one episode (text content) into group_id via graphiti's
add_episode. Auto-generates name and idempotency_key.
Build communities for group_id.
Build indices and constraints across the whole graph.
Returns a specification to start this module under a supervisor.
Return the Graphiti instance for group_id, creating it on first use.
Run graphiti's hybrid search against group_id. Returns
{:ok, [%{fact:, created_at:, valid_at:, invalid_at:, expired_at:}]}
ready for Gralkor.Format.format_facts/1.
Functions
@spec add_episode(GenServer.server(), String.t(), String.t(), String.t()) :: :ok | {:error, term()}
Ingest one episode (text content) into group_id via graphiti's
add_episode. Auto-generates name and idempotency_key.
@spec build_communities(GenServer.server(), String.t()) :: {:ok, %{communities: non_neg_integer(), edges: non_neg_integer()}} | {:error, term()}
Build communities for group_id.
@spec build_indices(GenServer.server()) :: {:ok, %{status: String.t()}} | {:error, term()}
Build indices and constraints across the whole graph.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec for(GenServer.server(), String.t()) :: any()
Return the Graphiti instance for group_id, creating it on first use.
Concurrent callers do not block each other once the instance is cached. Construction itself is serialised through the GenServer so two callers asking for the same group_id at the same time don't both construct it.
@spec search(GenServer.server(), String.t(), String.t(), pos_integer()) :: {:ok, [map()]} | {:error, term()}
Run graphiti's hybrid search against group_id. Returns
{:ok, [%{fact:, created_at:, valid_at:, invalid_at:, expired_at:}]}
ready for Gralkor.Format.format_facts/1.