Source code for lalandre_rag.modes.hybrid_graph

"""
Graph enrichment for HybridMode — Neo4j expansion and community context.

Extracted from hybrid_mode.py to keep the orchestrator focused on the pipeline.
"""

import logging
import time
from typing import Any, Dict, List, Optional, Set

from lalandre_core.config import get_config

from ..graph import CommunityContextEnricher, GraphRAGService
from .hybrid_helpers import GraphFetchResult

logger = logging.getLogger(__name__)


[docs] def fetch_graph_context( *, act_ids: Set[int], graph_rag_service: GraphRAGService, community_enricher: Optional[CommunityContextEnricher], max_depth: Optional[int] = None, ) -> Optional[GraphFetchResult]: """Fetch graph data for acts found in retrieval results. Uses act_ids from hybrid retrieval (semantic + BM25) as seeds for Neo4j graph traversal. Returns structured data for ranking/budgeting. Non-fatal: returns None on failure. """ if not act_ids: return None started_at = time.perf_counter() try: config = get_config() effective_depth = max_depth if max_depth is not None else config.graph.hybrid_enrichment_depth graph_result = graph_rag_service.neo4j.expand_from_acts( act_ids=list(act_ids), max_depth=effective_depth, ) nodes = list(graph_result.nodes or []) expanded_act_ids: set[int] = {int(n["id"]) for n in nodes if n.get("id") is not None} relationships: List[Dict[str, Any]] = [] for rel in graph_result.relationships or []: relationships.append( { "type": rel.get("type", "RELATED_TO"), "start_node": rel.get("start_node"), "end_node": rel.get("end_node"), "description": rel.get("properties", {}).get("description", ""), } ) community_block = "" community_meta: Dict[str, Any] = {} if config.graph.use_communities_in_rag and community_enricher is not None and community_enricher.available: community_block, community_meta = community_enricher.build_context( seed_act_ids=act_ids | expanded_act_ids, ) duration_ms = round((time.perf_counter() - started_at) * 1000.0, 1) return GraphFetchResult( nodes=nodes, relationships=relationships, seed_act_ids=act_ids, expanded_act_ids=expanded_act_ids, community_block=community_block, community_meta=community_meta, duration_ms=duration_ms, ) except Exception as exc: logger.warning("Graph enrichment failed (non-fatal), continuing without: %s", exc) return None