Gralkor.GraphitiPool (gralkor_ex v2.1.2)

Copy Markdown View Source

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

add_episode(server \\ __MODULE__, group_id, content, source_description)

@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.

build_communities(server \\ __MODULE__, group_id)

@spec build_communities(GenServer.server(), String.t()) ::
  {:ok, %{communities: non_neg_integer(), edges: non_neg_integer()}}
  | {:error, term()}

Build communities for group_id.

build_indices(server \\ __MODULE__)

@spec build_indices(GenServer.server()) ::
  {:ok, %{status: String.t()}} | {:error, term()}

Build indices and constraints across the whole graph.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

for(server \\ __MODULE__, group_id)

@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.

search(server \\ __MODULE__, group_id, query, max_results)

@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.

start_link(opts \\ [])