""" Text unit retrieval for finding relevant source chunks from entities. This module provides functions to find the most related text units (chunks) based on the entities retrieved from the knowledge graph. """ import asyncio from ..base import ( BaseGraphStorage, BaseKVStorage, TextChunkSchema, QueryParam, ) from ..prompts import GRAPH_FIELD_SEP from ..utils import ( logger, split_string_by_multi_markers, truncate_list_by_token_size, ) async def find_most_related_text_unit_from_entities( node_datas: list[dict], query_param: QueryParam, text_chunks_db: BaseKVStorage[TextChunkSchema], knowledge_graph_inst: BaseGraphStorage, ): """ Find the most related text units from entities. Parameters: ----------- node_datas : list[dict] List of node data dictionaries query_param : QueryParam Query parameters text_chunks_db : BaseKVStorage[TextChunkSchema] Text chunks database knowledge_graph_inst : BaseGraphStorage Knowledge graph storage instance Returns: -------- list List of text unit data """ text_units = [ split_string_by_multi_markers(dp["source_id"], [GRAPH_FIELD_SEP]) for dp in node_datas ] edges = await asyncio.gather( *[knowledge_graph_inst.get_node_edges(dp["entity_name"]) for dp in node_datas] ) all_one_hop_nodes = set() for this_edges in edges: if not this_edges: continue all_one_hop_nodes.update([e[1] for e in this_edges]) all_one_hop_nodes = list(all_one_hop_nodes) all_one_hop_nodes_data = await asyncio.gather( *[knowledge_graph_inst.get_node(e) for e in all_one_hop_nodes] ) all_one_hop_text_units_lookup = { k: set(split_string_by_multi_markers(v["source_id"], [GRAPH_FIELD_SEP])) for k, v in zip(all_one_hop_nodes, all_one_hop_nodes_data) if v is not None and "source_id" in v } all_text_units_lookup = {} for index, (this_text_units, this_edges) in enumerate(zip(text_units, edges)): for c_id in this_text_units: if c_id not in all_text_units_lookup: all_text_units_lookup[c_id] = { "data": await text_chunks_db.get_by_id(c_id), "order": index, "relation_counts": 0, } if this_edges: for e in this_edges: if ( e[1] in all_one_hop_text_units_lookup and c_id in all_one_hop_text_units_lookup[e[1]] ): all_text_units_lookup[c_id]["relation_counts"] += 1 all_text_units = [ {"id": k, **v} for k, v in all_text_units_lookup.items() if v is not None and v.get("data") is not None and "content" in v["data"] ] if not all_text_units: logger.warning("No valid text units found") return [] all_text_units = sorted( all_text_units, key=lambda x: (x["order"], -x["relation_counts"]) ) all_text_units = truncate_list_by_token_size( all_text_units, key=lambda x: x["data"]["content"], max_token_size=query_param.max_token_for_text_unit, ) all_text_units = [t["data"] for t in all_text_units] return all_text_units