diff --git a/lightrag/lightrag.py b/lightrag/lightrag.py index cef0f240..cdbec8fe 100644 --- a/lightrag/lightrag.py +++ b/lightrag/lightrag.py @@ -21,6 +21,7 @@ from typing import ( List, Dict, ) +from lightrag.prompt import PROMPTS from lightrag.constants import ( DEFAULT_MAX_GLEANING, DEFAULT_FORCE_LLM_SUMMARY_ON_MERGE, @@ -2287,20 +2288,35 @@ class LightRAG: else: raise ValueError(f"Unknown mode {data_param.mode}") - # Extract raw_data from QueryResult - final_data = query_result.raw_data if query_result else {} - - # Log final result counts - adapt to new data format from convert_to_user_format - if final_data and "data" in final_data: - data_section = final_data["data"] - entities_count = len(data_section.get("entities", [])) - relationships_count = len(data_section.get("relationships", [])) - chunks_count = len(data_section.get("chunks", [])) - logger.debug( - f"[aquery_data] Final result: {entities_count} entities, {relationships_count} relationships, {chunks_count} chunks" - ) + if query_result is None: + no_result_message = "Query returned no results" + if data_param.mode == "naive": + no_result_message = "No relevant document chunks found." + final_data: dict[str, Any] = { + "status": "failure", + "message": no_result_message, + "data": {}, + "metadata": { + "failure_reason": "no_results", + "mode": data_param.mode, + }, + } + logger.info("[aquery_data] Query returned no results.") else: - logger.warning("[aquery_data] No data section found in query result") + # Extract raw_data from QueryResult + final_data = query_result.raw_data or {} + + # Log final result counts - adapt to new data format from convert_to_user_format + if final_data and "data" in final_data: + data_section = final_data["data"] + entities_count = len(data_section.get("entities", [])) + relationships_count = len(data_section.get("relationships", [])) + chunks_count = len(data_section.get("chunks", [])) + logger.debug( + f"[aquery_data] Final result: {entities_count} entities, {relationships_count} relationships, {chunks_count} chunks" + ) + else: + logger.warning("[aquery_data] No data section found in query result") await self._query_done() return final_data @@ -2403,16 +2419,19 @@ class LightRAG: "status": "failure", "message": "Query returned no results", "data": {}, - "metadata": {}, + "metadata": { + "failure_reason": "no_results", + "mode": param.mode, + }, "llm_response": { - "content": None, + "content": PROMPTS["fail_response"], "response_iterator": None, "is_streaming": False, }, } # Extract structured data from query result - raw_data = query_result.raw_data if query_result else {} + raw_data = query_result.raw_data or {} raw_data["llm_response"] = { "content": query_result.content if not query_result.is_streaming diff --git a/lightrag/operate.py b/lightrag/operate.py index a12cb63f..525f2d27 100644 --- a/lightrag/operate.py +++ b/lightrag/operate.py @@ -2247,7 +2247,7 @@ async def kg_query( hashing_kv: BaseKVStorage | None = None, system_prompt: str | None = None, chunks_vdb: BaseVectorStorage = None, -) -> QueryResult: +) -> QueryResult | None: """ Execute knowledge graph query and return unified QueryResult object. @@ -2264,7 +2264,7 @@ async def kg_query( chunks_vdb: Document chunks vector database Returns: - QueryResult: Unified query result object containing: + QueryResult | None: Unified query result object containing: - content: Non-streaming response text content - response_iterator: Streaming response iterator - raw_data: Complete structured data (including references and metadata) @@ -2275,6 +2275,8 @@ async def kg_query( - only_need_prompt=True: content contains complete prompt - stream=True: response_iterator contains streaming response, raw_data contains complete data - default: content contains LLM response text, raw_data contains complete data + + Returns None when no relevant context could be constructed for the query. """ if not query: return QueryResult(content=PROMPTS["fail_response"]) @@ -2322,7 +2324,8 @@ async def kg_query( ) if context_result is None: - return QueryResult(content=PROMPTS["fail_response"]) + logger.info("[kg_query] No query context could be built; returning no-result.") + return None # Return different content based on query parameters if query_param.only_need_context and not query_param.only_need_prompt: @@ -3985,7 +3988,7 @@ async def naive_query( global_config: dict[str, str], hashing_kv: BaseKVStorage | None = None, system_prompt: str | None = None, -) -> QueryResult: +) -> QueryResult | None: """ Execute naive query and return unified QueryResult object. @@ -3998,11 +4001,13 @@ async def naive_query( system_prompt: System prompt Returns: - QueryResult: Unified query result object containing: + QueryResult | None: Unified query result object containing: - content: Non-streaming response text content - response_iterator: Streaming response iterator - raw_data: Complete structured data (including references and metadata) - is_streaming: Whether this is a streaming result + + Returns None when no relevant chunks are retrieved. """ if not query: @@ -4023,16 +4028,10 @@ async def naive_query( chunks = await _get_vector_context(query, chunks_vdb, query_param, None) if chunks is None or len(chunks) == 0: - # Build empty raw data structure for naive mode - empty_raw_data = convert_to_user_format( - [], # naive mode has no entities - [], # naive mode has no relationships - [], # no chunks - [], # no references - "naive", + logger.info( + "[naive_query] No relevant document chunks found; returning no-result." ) - empty_raw_data["message"] = "No relevant document chunks found." - return QueryResult(content=PROMPTS["fail_response"], raw_data=empty_raw_data) + return None # Calculate dynamic token limit for chunks max_total_tokens = getattr(