Coverage for src / lilbee / retrieval / concepts / clusterer.py: 100%
14 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-15 20:55 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-05-15 20:55 +0000
1"""SourceClusterer adapter backed by the concept graph."""
3from __future__ import annotations
5from lilbee.core.config import Config
6from lilbee.data.store import Store
7from lilbee.retrieval.clustering import SourceCluster
8from lilbee.retrieval.concepts.graph import ConceptGraph
9from lilbee.retrieval.concepts.nlp import concepts_available
12class ConceptGraphClusterer:
13 """SourceClusterer backed by the concept graph (requires ``[graph]`` extra).
15 Wraps :class:`ConceptGraph` so the wiki synthesis layer can consume
16 concept-based clusters through the generic ``SourceClusterer`` protocol
17 without importing ``ConceptGraph`` directly. Leaves ``ConceptGraph``
18 unchanged.
19 """
21 def __init__(self, config: Config, store: Store) -> None:
22 self._graph = ConceptGraph(config, store)
24 def available(self) -> bool:
25 """Concept-graph clustering needs both dependencies and a built graph."""
26 return bool(concepts_available() and self._graph.get_graph())
28 def get_clusters(self, min_sources: int = 3) -> list[SourceCluster]:
29 """Expose concept clusters as generic :class:`SourceCluster` values."""
30 cluster_sources = self._graph.get_cluster_sources(min_sources=min_sources)
31 return [
32 SourceCluster(
33 cluster_id=f"concept-{cid}",
34 label=self._graph.get_cluster_label(cid),
35 sources=frozenset(sources),
36 )
37 for cid, sources in cluster_sources.items()
38 if len(sources) >= min_sources
39 ]