Skip to content

Add Leiden community detection and LightRAG dual-level retrieval#2

Merged
ronaldmannak merged 12 commits into
mainfrom
claude/graphrag-rs-port-j0im9e
Jul 4, 2026
Merged

Add Leiden community detection and LightRAG dual-level retrieval#2
ronaldmannak merged 12 commits into
mainfrom
claude/graphrag-rs-port-j0im9e

Conversation

@ronaldmannak

Copy link
Copy Markdown
Contributor

Extends the Swift port with two optional subsystems from graphrag-rs that were previously out of scope: Leiden community detection and LightRAG dual-level retrieval.

Leiden community detection

Sources/GraphRAG/Graph/Leiden.swift — partitions the knowledge graph into communities via greedy modularity local-moving plus a refinement pass that splits internally disconnected communities.

Two deliberate improvements over the Rust source:

  • Deterministic: stable node ordering, so repeated runs produce identical assignments (the Rust version's iteration order was nondeterministic).
  • Weighted: uses each relationship's confidence as an edge weight; the Rust version was unweighted.

Reports Newman modularity of the final partition. Only result-affecting knobs are exposed via LeidenConfig (resolution, maxIterations, minModularityGain) — no dead config.

LightRAG dual-level retrieval

Sources/GraphRAG/LightRAG/ — answers queries by searching two levels at once:

  • Low-level store over document chunks (entity/detail-centric).
  • High-level store over per-community theme summaries derived from Leiden (global/relationship-centric).

KeywordExtractor splits the query into high-/low-level keywords via the LLM, with a deterministic offline fallback (tokenize words ≥ 4 chars). Each level is searched independently and the hits are merged (interleave default / highFirst / lowFirst / weighted) with dedup by id. LightRAGEngine ties it together with detectCommunities / retrieve / ask.

Wiring & docs

  • GraphRAG.detectCommunities() and GraphRAG.lightRAG() expose both subsystems from the actor.
  • README documents both, and they're moved out of the "out of scope" note.

Tests

Tests/GraphRAGTests/LightRAGTests.swift covers:

  • Leiden: separation of disconnected clusters, empty graph, edgeless singletons, determinism.
  • Keyword extraction: offline fallback tokenization and LLM dual-level JSON parsing.
  • Merge: interleave-with-dedup and weighted ordering.
  • End-to-end: offline lightRAG().ask(...) through GraphRAG.

Everything runs fully offline and builds under Swift 6 strict concurrency.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh


Generated by Claude Code

Extend the port with two optional subsystems from graphrag-rs:

- Leiden community detection (Graph/Leiden.swift): greedy modularity
  local-moving plus a refinement pass that splits internally disconnected
  communities. Made deterministic (stable node ordering) and weighted (uses
  relationship confidence as edge weight); reports Newman modularity. Only
  result-affecting config is exposed.

- LightRAG dual-level retrieval (LightRAG/): extract high-/low-level keywords
  from a query via the LLM with a deterministic offline fallback, search a
  low-level chunk store and a high-level per-community summary store, and merge
  (interleave/highFirst/lowFirst/weighted) with dedup by id. LightRAGEngine
  ties it together with detectCommunities/retrieve/ask.

Wire both into GraphRAG via detectCommunities() and lightRAG(), document them
in the README, and add unit tests covering community separation, determinism,
keyword extraction/fallback, merge strategies, and an offline end-to-end ask.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces community detection using the Leiden algorithm and dual-level retrieval via LightRAG. The review feedback focuses on critical performance, correctness, and concurrency improvements. Key recommendations include reusing pre-computed chunk embeddings to avoid redundant API calls, ensuring determinism in the Leiden detector by sorting dictionary keys during iteration, using structured concurrency to run semantic searches and searcher builds in parallel, robustly parsing LLM responses by stripping thinking tags, and filtering out virtual community IDs from grounding sources.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread Sources/GraphRAG/LightRAG/Searchers.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift Outdated
Comment thread Sources/GraphRAG/Graph/Leiden.swift
Comment thread Sources/GraphRAG/Graph/Leiden.swift
Comment thread Sources/GraphRAG/LightRAG/Keywords.swift
Comment thread Sources/GraphRAG/LightRAG/Keywords.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/DualRetrieval.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift Outdated
…bustness

- Searchers: InMemorySemanticSearcher.build now reuses each chunk's
  precomputed embedding from the graph instead of re-embedding on every query
  (community summaries still embed on demand). Avoids the async-`??` autoclosure
  pitfall with an explicit branch.
- Leiden: build adjacency by node index with sorted neighbors, and sum
  modularity in sorted community-id order, so neighbor ordering and
  floating-point sums are deterministic across runs.
- Keywords: strip thinking tags before locating the JSON braces, so a `{` in a
  <think> preamble can't derail parsing; deduplicate fallback keywords
  case-insensitively in first-seen order.
- DualRetrieval / LightRAGEngine: run the two independent searches, and the two
  store builds, concurrently via async let.
- LightRAGEngine: exclude virtual "community_<id>" ids from grounding sources so
  they only contain real chunk ids.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 20408af629

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift Outdated
…proof merge

- LightRAGEngine now builds the two dual-level stores once per engine (via a
  memoizing actor cache) instead of re-embedding the whole corpus on every
  retrieve/ask — with a remote embedder that was O(corpus) network calls per
  query.
- Searchers thread the real chunk ids each document grounds to: a community
  summary grounds to the chunks mentioning its member entities, so answers
  built from high-level hits cite actual evidence chunks rather than synthetic
  community ids. `ask` grounds `sources` on these real chunk ids.
- Merge deduplicates within a retrieval level only (keyed by level), so a chunk
  id that happens to collide with a "community_<id>" no longer silently evicts
  the community hit or vice versa.

Tests: split the interleave test into within-level dedup + colliding
cross-level id cases, and add community→member-chunk grounding coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f98e1ad031

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/Keywords.swift Outdated
Comment thread Sources/GraphRAG/GraphRAG/Engine.swift Outdated
…ck keywords

- GraphRAG.lightRAG() and detectCommunities() now require isBuilt (throwing
  GraphRAGError.notInitialized otherwise), mirroring the ask/search guard, so
  callers can't get answers from a raw, stale, or partially rebuilt graph.
- KeywordExtractor.extract now runs the non-LLM fallback through the same
  `capped` step, so a configured maxKeywords below 10 (or 0) is honored even
  without an LLM.

Update the end-to-end test and README for the now-throwing lightRAG().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 869131784f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/Keywords.swift Outdated
Comment thread Sources/GraphRAG/Graph/Leiden.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/Keywords.swift Outdated
…iterations

- KeywordExtractorConfig clamps maxKeywords to >= 0 (and capped() clamps at the
  slice site too, since the field is mutable), so a negative limit reads as "no
  keywords" instead of trapping in prefix(_:).
- The no-LLM keyword fallback now filters by a small stopword set instead of a
  length cutoff, so short but meaningful names/acronyms (Ada, Bob, IBM, AI, ML)
  survive; if every token is a stopword it falls back to the raw tokens so
  retrieval still runs rather than searching an empty query.
- Leiden iterates 0..<max(0, maxIterations), so maxIterations == 0 means "no
  local moving" (singleton/refinement-only baseline) and negatives can't trap.

Tests: short-entity fallback, all-stopword raw fallback, negative-limit clamp,
and zero-iteration singletons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0265f520e8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/Keywords.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift Outdated
…uild race

- Keyword fallback now populates both levels with the same terms, so the
  default no-LLM path actually searches the high-level community store instead
  of silently degrading global/theme questions to chunk search only.
- communitySourceChunks derives grounding from both TextChunk.entities and the
  members' Entity.mentions, so externally-constructed graphs that carry only
  mention evidence still resolve high-level hits to real chunks.
- LightRAGStoreCache memoizes an in-flight Task (stored before the first await)
  so concurrent first queries share one build instead of each re-embedding the
  whole corpus under actor reentrancy; a failed build is not cached.

Tests: mention-based community grounding, and updated the fallback test for the
now dual-populated offline keywords.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cef8821d71

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/Keywords.swift
Comment thread Sources/GraphRAG/LightRAG/Keywords.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/DualRetrieval.swift
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift Outdated
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift
…, grounding

- Keyword fallback splits tokens on non-alphanumerics (matching how BM25 and
  HashEmbedder tokenize the index), so terms like "graph-based" become
  ["graph","based"] and match indexed content instead of a collapsed
  "graphbased".
- The fallback caps each level to maxKeywords independently instead of routing
  the duplicated terms through the combined cap, so a small limit no longer
  starves the low-level (chunk) store while filling the high-level one.
- DualLevelRetriever.retrieve returns early for nonpositive topK, before
  forwarding it to stores that limit with prefix(topK) (which traps on negatives).
- InMemorySemanticSearcher sizes its candidate pool to the corpus, so a topK
  above the default 100 cap can still return enough matches for large stores.
- communitySourceChunks also includes Relationship.context chunks for internal
  edges, completing evidence coverage for externally-constructed graphs.

Tests: punctuation splitting, per-level cap, and nonpositive-topK early return.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f1fece1d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift Outdated
claude added 2 commits July 4, 2026 16:48
…g dimension

- LightRAGEngine.retrieve returns an empty result for topK <= 0 before building
  the stores, so a no-op request never triggers corpus-wide embedding (or a
  throw from a remote/unavailable embedder). Mirrors DualLevelRetriever's guard
  one level up.
- InMemorySemanticSearcher.build reuses a precomputed chunk embedding only when
  its dimension matches the current embedder; on a mismatch (e.g. a graph saved
  under a different embedder) it re-embeds, since cosine similarity would
  otherwise silently return 0 and drop the vector signal.

Test: retrieve(topK: 0) over an engine with a throwing embedder returns empty
without embedding.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh
EmbeddingModel requires isAvailable(); the new test double omitted it, breaking
the test build. Source is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 478d62339e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift
Comment thread Sources/GraphRAG/Graph/Leiden.swift
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift
claude added 2 commits July 4, 2026 17:08
…Keywords=0

- GraphRAG.lightRAG() rejects approach == "keyword" with a clear error: LightRAG
  needs chunk embeddings, which a keyword-only build leaves nil, so fail fast
  instead of forcing corpus-wide re-embedding (which would throw outright on an
  unavailable embedder).
- LightRAGEngine.retrieve also short-circuits when keywordConfig.maxKeywords <= 0
  (guaranteed-empty queries) before building the stores — a plain config check,
  so no embedding or LLM keyword call happens.
- Leiden: documented why local moving intentionally omits the empty-singleton
  candidate (keeps the port aligned with the simplified single-level upstream;
  splitting connected over-merges is a deliberate non-goal), so the review can
  skip re-flagging it.

Tests: keyword-only rejection, and maxKeywords=0 short-circuit via a throwing
embedder.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh
The local DualRetrievalConfig parameter named `config` shadowed the actor's
`self.config: Config`; qualify with `self.` so the keyword-approach guard reads
the GraphRAG Config that actually has `approach`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 696770c333

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift
Comment thread Sources/GraphRAG/LightRAG/LightRAGEngine.swift
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift
Comment thread Sources/GraphRAG/LightRAG/Searchers.swift
…dex review)

Round of Codex P2s all point at one design question: should LightRAG inherit the
owning GraphRAG's hybrid-path Config (approach / similarityThreshold /
topKResults) and short-circuit empty queries before building stores?

Deliberate decision: LightRAG is a self-contained dual-level retrieval subsystem
configured via DualRetrievalConfig / KeywordExtractorConfig and a per-call topK.
It intentionally does not inherit the GraphRAG hybrid-path Config, and empty
queries are handled by the (cached, built-once) retriever returning no hits.
Documented at both flagged sites so the decision is explicit rather than an
omission; can be threaded through if inheritance is later preferred.

No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUzx3BzstYuB68txcHvAUh
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@ronaldmannak

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 169e5334a0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@ronaldmannak ronaldmannak merged commit 4a8de42 into main Jul 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants