Refactor graph search and add local Vite config.

- Simplify graph search parameters in API
- Add inclusive search and min degree options
- Update Vite config for local development
- Enhance graph query endpoint in web UI
- Add new Vite config file for local setup
This commit is contained in:
yangdx
2025-03-05 10:39:27 +08:00
parent 002948d342
commit d52b6bead1
5 changed files with 53 additions and 46 deletions

View File

@@ -3,9 +3,9 @@ This module contains all graph-related routes for the LightRAG API.
""" """
from typing import Optional from typing import Optional
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends
from ...utils import logger
from ..utils_api import get_api_key_dependency from ..utils_api import get_api_key_dependency
router = APIRouter(tags=["graph"]) router = APIRouter(tags=["graph"])
@@ -25,7 +25,7 @@ def create_graph_routes(rag, api_key: Optional[str] = None):
return await rag.get_graph_labels() return await rag.get_graph_labels()
@router.get("/graphs", dependencies=[Depends(optional_api_key)]) @router.get("/graphs", dependencies=[Depends(optional_api_key)])
async def get_knowledge_graph(label: str, max_depth: int = 3): async def get_knowledge_graph(label: str, max_depth: int = 3, inclusive: bool = False, min_degree: int = 0):
""" """
Retrieve a connected subgraph of nodes where the label includes the specified label. Retrieve a connected subgraph of nodes where the label includes the specified label.
Maximum number of nodes is constrained by the environment variable `MAX_GRAPH_NODES` (default: 1000). Maximum number of nodes is constrained by the environment variable `MAX_GRAPH_NODES` (default: 1000).
@@ -34,50 +34,17 @@ def create_graph_routes(rag, api_key: Optional[str] = None):
2. Followed by nodes directly connected to the matching nodes 2. Followed by nodes directly connected to the matching nodes
3. Finally, the degree of the nodes 3. Finally, the degree of the nodes
Maximum number of nodes is limited to env MAX_GRAPH_NODES(default: 1000) Maximum number of nodes is limited to env MAX_GRAPH_NODES(default: 1000)
Control search mode by label content:
1. only label-name : exact search with the label name (selecting from the label list return previously)
2. label-name follow by '>n' : exact search of nodes with degree more than n
3. label-name follow by* : inclusive search of nodes with degree more than n
4. label-name follow by '>n*' : inclusive search
Args: Args:
label (str): Label to get knowledge graph for label (str): Label to get knowledge graph for
max_depth (int, optional): Maximum depth of graph. Defaults to 3. max_depth (int, optional): Maximum depth of graph. Defaults to 3.
inclusive_search (bool, optional): If True, search for nodes that include the label. Defaults to False.
min_degree (int, optional): Minimum degree of nodes. Defaults to 0.
Returns: Returns:
Dict[str, List[str]]: Knowledge graph for label Dict[str, List[str]]: Knowledge graph for label
""" """
# Parse label to extract search mode and min degree if specified logger.info(f"Inclusive search : {inclusive}, Min degree: {min_degree}, Label: {label}")
search_mode = "exact" # Default search mode return await rag.get_knowledge_graph(node_label=label, max_depth=max_depth, inclusive=inclusive, min_degree=min_degree)
min_degree = 0 # Default minimum degree
original_label = label
# First check if label ends with *
if label.endswith("*"):
search_mode = "inclusive" # Always set to inclusive if ends with *
label = label[:-1].strip() # Remove trailing *
# Try to parse >n if it exists
if ">" in label:
try:
degree_pos = label.rfind(">")
degree_str = label[degree_pos + 1:].strip()
min_degree = int(degree_str) + 1
label = label[:degree_pos].strip()
except ValueError:
# If degree parsing fails, just remove * and keep the rest as label
label = original_label[:-1].strip()
# If no *, check for >n pattern
elif ">" in label:
try:
degree_pos = label.rfind(">")
degree_str = label[degree_pos + 1:].strip()
min_degree = int(degree_str) + 1
label = label[:degree_pos].strip()
except ValueError:
# If degree parsing fails, treat the whole string as label
label = original_label
return await rag.get_knowledge_graph(node_label=label, max_depth=max_depth, search_mode=search_mode, min_degree=min_degree)
return router return router

View File

@@ -504,7 +504,7 @@ class LightRAG:
return text return text
async def get_knowledge_graph( async def get_knowledge_graph(
self, node_label: str, max_depth: int, search_mode: str = "exact", min_degree: int = 0 self, node_label: str, max_depth: int, inclusive: bool = False, min_degree: int = 0
) -> KnowledgeGraph: ) -> KnowledgeGraph:
"""Get knowledge graph for a given label """Get knowledge graph for a given label
@@ -520,8 +520,6 @@ class LightRAG:
return await self.chunk_entity_relation_graph.get_knowledge_graph( return await self.chunk_entity_relation_graph.get_knowledge_graph(
node_label=node_label, node_label=node_label,
max_depth=max_depth, max_depth=max_depth,
search_mode=search_mode,
min_degree=min_degree
) )
def _get_storage_class(self, storage_name: str) -> Callable[..., Any]: def _get_storage_class(self, storage_name: str) -> Callable[..., Any]:

View File

@@ -4,11 +4,11 @@
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bunx --bun vite", "dev": "bunx --bun vite --config vite.config.local.js",
"build": "bunx --bun vite build", "build": "bunx --bun vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "bunx --bun vite preview", "preview": "bunx --bun vite preview",
"dev-no-bun": "vite", "dev-no-bun": "vite --config vite.config.local.js",
"build-no-bun": "vite build --emptyOutDir", "build-no-bun": "vite build --emptyOutDir",
"preview-no-bun": "vite preview" "preview-no-bun": "vite preview"
}, },

View File

@@ -161,8 +161,12 @@ axiosInstance.interceptors.response.use(
) )
// API methods // API methods
export const queryGraphs = async (label: string, maxDepth: number): Promise<LightragGraphType> => { export const queryGraphs = async (
const response = await axiosInstance.get(`/graphs?label=${label}&max_depth=${maxDepth}`) label: string,
maxDepth: number,
inclusive: boolean = false
): Promise<LightragGraphType> => {
const response = await axiosInstance.get(`/graphs?label=${encodeURIComponent(label)}&max_depth=${maxDepth}&inclusive=${inclusive}`)
return response.data return response.data
} }

View File

@@ -0,0 +1,38 @@
import { defineConfig } from 'vite'
import baseConfig from './vite.config'
import { mergeConfig } from 'vite'
export default mergeConfig(
baseConfig,
defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:9621',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
},
'/documents': {
target: 'http://localhost:9621',
changeOrigin: true
},
'/graphs': {
target: 'http://localhost:9621',
changeOrigin: true
},
'/graph': {
target: 'http://localhost:9621',
changeOrigin: true
},
'/health': {
target: 'http://localhost:9621',
changeOrigin: true
},
'/query': {
target: 'http://localhost:9621',
changeOrigin: true
}
}
}
})
)