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