Arcana.Graph.CommunityDetector behaviour (Arcana v1.2.0)

View Source

Behaviour for community detection in GraphRAG.

Community detectors partition entities into groups based on their relationships. Arcana provides a built-in Leiden implementation, but you can implement custom detectors for different algorithms.

Built-in Implementations

Configuration

Configure your community detector in config.exs:

# Default: Leiden algorithm (requires ex_leiden)
config :arcana, :graph,
  community_detector: :leiden

# Disable community detection
config :arcana, :graph,
  community_detector: nil

# Custom module implementing this behaviour
config :arcana, :graph,
  community_detector: MyApp.LouvainDetector

# Custom module with options
config :arcana, :graph,
  community_detector: {MyApp.LouvainDetector, resolution: 0.5}

# Inline function
config :arcana, :graph,
  community_detector: fn entities, relationships, opts ->
    {:ok, my_detect(entities, relationships, opts)}
  end

Implementing a Custom Detector

Create a module that implements this behaviour:

defmodule MyApp.LouvainDetector do
  @behaviour Arcana.Graph.CommunityDetector

  @impl true
  def detect(entities, relationships, opts) do
    resolution = Keyword.get(opts, :resolution, 1.0)
    # Run Louvain algorithm...
    {:ok, communities}
  end
end

Community Format

Detectors must return communities as maps with:

  • :level - Hierarchy level (0 = finest, higher = coarser)
  • :entity_ids - List of entity IDs in this community

Summary

Callbacks

Detects communities in the entity graph.

Functions

Detects communities using the configured detector.

Types

community()

@type community() :: %{level: non_neg_integer(), entity_ids: [String.t()]}

entity()

@type entity() :: %{id: String.t(), name: String.t()}

relationship()

@type relationship() :: %{
  source_id: String.t(),
  target_id: String.t(),
  strength: integer() | nil
}

Callbacks

detect(entities, relationships, opts)

@callback detect(
  entities :: [entity()],
  relationships :: [relationship()],
  opts :: keyword()
) :: {:ok, [community()]} | {:error, term()}

Detects communities in the entity graph.

Parameters

  • entities - List of entity maps with :id and :name
  • relationships - List of relationship maps with :source_id, :target_id, :strength
  • opts - Options passed from the detector configuration

Returns

  • {:ok, communities} - List of community maps
  • {:error, reason} - On failure

Functions

detect(fun, entities, relationships)

@spec detect({module(), keyword()} | function() | nil, [entity()], [relationship()]) ::
  {:ok, [community()]} | {:error, term()}

Detects communities using the configured detector.

The detector can be:

  • A {module, opts} tuple where module implements this behaviour
  • A function (entities, relationships, opts) -> {:ok, communities} | {:error, reason}

  • nil to skip community detection (returns empty list)

Examples

# With module
detector = {Arcana.Graph.CommunityDetector.Leiden, resolution: 1.0}
{:ok, communities} = CommunityDetector.detect(detector, entities, relationships)

# With inline function
detector = fn entities, _rels, _opts ->
  {:ok, [%{level: 0, entity_ids: Enum.map(entities, & &1.id)}]}
end
{:ok, communities} = CommunityDetector.detect(detector, entities, relationships)

# Skip detection
{:ok, []} = CommunityDetector.detect(nil, entities, relationships)