From a68aebb1248b5db0f70400a28b7516d561a1f3b3 Mon Sep 17 00:00:00 2001 From: Saifeddine ALOUI Date: Mon, 27 Jan 2025 02:07:06 +0100 Subject: [PATCH] translated docstrings to english and enhanced the webui --- lightrag/api/lightrag_server.py | 23 +- lightrag/api/static/index.html | 1141 ++++++++-------------- lightrag/api/webui/static/js/lightrag.js | 13 +- lightrag/kg/neo4j_impl.py | 53 +- 4 files changed, 475 insertions(+), 755 deletions(-) diff --git a/lightrag/api/lightrag_server.py b/lightrag/api/lightrag_server.py index 28aaad8b..3a5f9e38 100644 --- a/lightrag/api/lightrag_server.py +++ b/lightrag/api/lightrag_server.py @@ -1789,6 +1789,11 @@ def create_app(args): except Exception as e: trace_exception(e) raise HTTPException(status_code=500, detail=str(e)) + + @app.get("/documents", dependencies=[Depends(optional_api_key)]) + async def get_status(): + """Get current system status""" + return doc_manager.indexed_files @app.get("/health", dependencies=[Depends(optional_api_key)]) async def get_status(): @@ -1798,7 +1803,7 @@ def create_app(args): "working_directory": str(args.working_dir), "input_directory": str(args.input_dir), "indexed_files": doc_manager.indexed_files, - "indexed_files_count": len(doc_manager.scan_directory()), + "indexed_files_count": len(doc_manager.indexed_files), "configuration": { # LLM configuration binding/host address (if applicable)/model (if applicable) "llm_binding": args.llm_binding, @@ -1817,16 +1822,16 @@ def create_app(args): } # webui mount /webui/index.html - app.mount( - "/webui", - StaticFiles( - directory=Path(__file__).resolve().parent / "webui" / "static", html=True - ), - name="webui_static", - ) + # app.mount( + # "/webui", + # StaticFiles( + # directory=Path(__file__).resolve().parent / "webui" / "static", html=True + # ), + # name="webui_static", + # ) # Serve the static files - static_dir = Path(__file__).parent / "static" + static_dir = Path(__file__).parent / "static" static_dir.mkdir(exist_ok=True) app.mount("/", StaticFiles(directory=static_dir, html=True), name="static") diff --git a/lightrag/api/static/index.html b/lightrag/api/static/index.html index 33a618a1..d473b5a6 100644 --- a/lightrag/api/static/index.html +++ b/lightrag/api/static/index.html @@ -1,738 +1,455 @@ - - - LightRag - - - - - - - - - - - -
- -
- -
- - - - -
- - -
-

LightRag

-

Lightweight Retrieval-Augmented Generation Interface

-
- - -
-
-

Upload Documents

- - -
- -
- -
- - - -

Drop files here or click to upload

-

PDF, MD, TXT, DOC, DOCX (Max 10MB)

-
-
- - -
- - - - - - -
-
-
- - -
-
-

Query Documents

- -
- - - -
- - -
-
-
-
- - - - - - - -
- - + + + + +
+ + - uploadProgress.classList.remove('hidden'); - const progressBar = uploadProgress.querySelector('.bg-blue-600'); - uploadStatus.textContent = 'Starting upload...'; + +
+
+
- try { - for (let i = 0; i < files.length; i++) { - const file = files[i]; - const formData = new FormData(); - formData.append('file', file); + + +
- uploadStatus.textContent = `Uploading ${file.name} (${i + 1}/${files.length})...`; - console.log(`Uploading file: ${file.name}`); + + saveBtn.addEventListener('click', () => { + state.apiKey = apiKeyInput.value; + localStorage.setItem('apiKey', state.apiKey); + showToast('Settings saved successfully'); + }); + } + }; + + // Navigation handling + document.querySelectorAll('.nav-item').forEach(item => { + item.addEventListener('click', (e) => { + e.preventDefault(); + const page = item.dataset.page; + document.getElementById('content').innerHTML = pages[page](); + if (handlers[page]) handlers[page](); + state.currentPage = page; + }); + }); + + // Initialize with file manager + document.getElementById('content').innerHTML = pages['file-manager'](); + handlers['file-manager'](); + + // Global functions + window.removeFile = (fileName) => { + state.files = state.files.filter(file => file.name !== fileName); + document.querySelector('#fileList div').innerHTML = state.files.map(file => ` +
+ ${file.name} + +
+ `).join(''); + }; + diff --git a/lightrag/api/webui/static/js/lightrag.js b/lightrag/api/webui/static/js/lightrag.js index f0d2e924..8fc26f7a 100644 --- a/lightrag/api/webui/static/js/lightrag.js +++ b/lightrag/api/webui/static/js/lightrag.js @@ -1,7 +1,4 @@ // lightrag.js -// Modify according to the actual API address -const API_BASE = 'http://localhost:9621'; - // init function initializeApp() { setupFileUpload(); @@ -37,7 +34,7 @@ async function apiRequest(endpoint, method = 'GET', body = null) { } try { - const response = await fetch(`${API_BASE}${endpoint}`, options); + const response = await fetch(`${endpoint}`, options); if (!response.ok) { throw new Error(`request failed: ${response.status}`); } @@ -72,7 +69,7 @@ async function handleTextUpload() { ...(description && {description}) }; - const response = await fetch(`${API_BASE}/documents/text`, { + const response = await fetch(`/documents/text`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -154,7 +151,7 @@ async function handleFiles(files) { statusDiv.textContent = ''; try { showStatus('loading', 'UPLOADING...', statusDiv); - const response = await fetch(`${API_BASE}/documents/upload`, { + const response = await fetch(`/documents/upload`, { method: 'POST', body: formData }); @@ -213,7 +210,7 @@ async function handleQuery() { // handle stream api async function handleStreamingQuery(payload, resultsDiv) { try { - const response = await fetch(`${API_BASE}/query/stream`, { + const response = await fetch(`/query/stream`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) @@ -313,7 +310,7 @@ async function handleChat(chatInput) { chatHistory.scrollTop = chatHistory.scrollHeight; try { - const response = await fetch(`${API_BASE}/api/chat`, { + const response = await fetch(`/api/chat`, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ diff --git a/lightrag/kg/neo4j_impl.py b/lightrag/kg/neo4j_impl.py index f6912aff..558d3e5a 100644 --- a/lightrag/kg/neo4j_impl.py +++ b/lightrag/kg/neo4j_impl.py @@ -340,17 +340,18 @@ class Neo4JStorage(BaseGraphStorage): async def _node2vec_embed(self): print("Implemented but never called.") + async def get_knowledge_graph( self, node_label: str, max_depth: int = 5 ) -> Dict[str, List[Dict]]: """ - 获取指定节点的完整连通子图(包含起始节点本身) + Get complete connected subgraph for specified node (including the starting node itself) - 修复要点: - 1. 包含起始节点自身 - 2. 处理多标签节点 - 3. 明确关系方向 - 4. 添加深度控制 + Key fixes: + 1. Include the starting node itself + 2. Handle multi-label nodes + 3. Clarify relationship directions + 4. Add depth control """ label = node_label.strip('"') result = {"nodes": [], "edges": []} @@ -359,14 +360,14 @@ class Neo4JStorage(BaseGraphStorage): async with self._driver.session(database=self._DATABASE) as session: try: - # 关键调试步骤:先验证起始节点是否存在 + # Critical debug step: first verify if starting node exists validate_query = f"MATCH (n:`{label}`) RETURN n LIMIT 1" validate_result = await session.run(validate_query) if not await validate_result.single(): - logger.warning(f"起始节点 {label} 不存在!") + logger.warning(f"Starting node {label} does not exist!") return result - # 优化后的查询语句(包含方向处理和自循环) + # Optimized query (including direction handling and self-loops) main_query = f""" MATCH (start:`{label}`) WITH start @@ -383,17 +384,17 @@ class Neo4JStorage(BaseGraphStorage): record = await result_set.single() if record: - # 处理节点(兼容多标签情况) + # Handle nodes (compatible with multi-label cases) for node in record["nodes"]: - # 使用节点ID + 标签组合作为唯一标识 + # Use node ID + label combination as unique identifier node_id = f"{node.id}_{'_'.join(node.labels)}" if node_id not in seen_nodes: node_data = dict(node) - node_data["labels"] = list(node.labels) # 保留所有标签 + node_data["labels"] = list(node.labels) # Keep all labels result["nodes"].append(node_data) seen_nodes.add(node_id) - # 处理关系(包含方向信息) + # Handle relationships (including direction information) for rel in record["relationships"]: edge_id = f"{rel.id}_{rel.type}" if edge_id not in seen_edges: @@ -414,11 +415,11 @@ class Neo4JStorage(BaseGraphStorage): seen_edges.add(edge_id) logger.info( - f"子图查询成功 | 节点数: {len(result['nodes'])} | 边数: {len(result['edges'])}" + f"Subgraph query successful | Node count: {len(result['nodes'])} | Edge count: {len(result['edges'])}" ) except neo4jExceptions.ClientError as e: - logger.error(f"APOC查询失败: {str(e)}") + logger.error(f"APOC query failed: {str(e)}") return await self._robust_fallback(label, max_depth) return result @@ -426,7 +427,7 @@ class Neo4JStorage(BaseGraphStorage): async def _robust_fallback( self, label: str, max_depth: int ) -> Dict[str, List[Dict]]: - """强化版降级查询方案""" + """Enhanced fallback query solution""" result = {"nodes": [], "edges": []} visited_nodes = set() visited_edges = set() @@ -435,7 +436,7 @@ class Neo4JStorage(BaseGraphStorage): if current_depth > max_depth: return - # 获取当前节点详情 + # Get current node details node = await self.get_node(current_label) if not node: return @@ -445,12 +446,12 @@ class Neo4JStorage(BaseGraphStorage): return visited_nodes.add(node_id) - # 添加节点数据(带完整标签) + # Add node data (with complete labels) node_data = {k: v for k, v in node.items()} - node_data["labels"] = [current_label] # 假设get_node方法返回包含标签信息 + node_data["labels"] = [current_label] # Assume get_node method returns label information result["nodes"].append(node_data) - # 获取所有出边和入边 + # Get all outgoing and incoming edges query = f""" MATCH (a)-[r]-(b) WHERE a:`{current_label}` OR b:`{current_label}` @@ -460,7 +461,7 @@ class Neo4JStorage(BaseGraphStorage): async with self._driver.session(database=self._DATABASE) as session: results = await session.run(query) async for record in results: - # 处理边 + # Handle edges rel = record["r"] edge_id = f"{rel.id}_{rel.type}" if edge_id not in visited_edges: @@ -476,7 +477,7 @@ class Neo4JStorage(BaseGraphStorage): result["edges"].append(edge_data) visited_edges.add(edge_id) - # 递归遍历相邻节点 + # Recursively traverse adjacent nodes next_label = ( list(record["b"].labels)[0] if record["direction"] == "OUTGOING" @@ -489,15 +490,15 @@ class Neo4JStorage(BaseGraphStorage): async def get_all_labels(self) -> List[str]: """ - 获取数据库中所有存在的节点标签 + Get all existing node labels in the database Returns: - ["Person", "Company", ...] # 按字母排序的标签列表 + ["Person", "Company", ...] # Alphabetically sorted label list """ async with self._driver.session(database=self._DATABASE) as session: - # 方法1:直接查询元数据(Neo4j 4.3+ 可用) + # Method 1: Direct metadata query (Available for Neo4j 4.3+) # query = "CALL db.labels() YIELD label RETURN label" - # 方法2:兼容旧版本的查询方式 + # Method 2: Query compatible with older versions query = """ MATCH (n) WITH DISTINCT labels(n) AS node_labels