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

1"""SourceClusterer adapter backed by the concept graph.""" 

2 

3from __future__ import annotations 

4 

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 

10 

11 

12class ConceptGraphClusterer: 

13 """SourceClusterer backed by the concept graph (requires ``[graph]`` extra). 

14 

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 """ 

20 

21 def __init__(self, config: Config, store: Store) -> None: 

22 self._graph = ConceptGraph(config, store) 

23 

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()) 

27 

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 ]