choizhang commited on
Commit
48b5396
·
2 Parent(s): 3659583 0a1b5ba

Merge branch 'main' into edit-node

Browse files
lightrag/api/README-zh.md CHANGED
@@ -102,6 +102,10 @@ lightrag-gunicorn --workers 4
102
  - `--log-level`:日志级别(默认:INFO)
103
  - --input-dir:指定要扫描文档的目录(默认:./input)
104
 
 
 
 
 
105
  ### 启动时自动扫描
106
 
107
  当使用 `--auto-scan-at-startup` 参数启动任何服务器时,系统将自动:
 
102
  - `--log-level`:日志级别(默认:INFO)
103
  - --input-dir:指定要扫描文档的目录(默认:./input)
104
 
105
+ > ** 要求将.env文件置于启动目录中是经过特意设计的**。 这样做的目的是支持用户同时启动多个LightRAG实例,并为不同实例配置不同的.env文件。
106
+
107
+ > **修改.env文件后,您需要重新打开终端以使新设置生效**。 这是因为每次启动时,LightRAG Server会将.env文件中的环境变量加载至系统环境变量,且系统环境变量的设置具有更高优先级。
108
+
109
  ### 启动时自动扫描
110
 
111
  当使用 `--auto-scan-at-startup` 参数启动任何服务器时,系统将自动:
lightrag/api/README.md CHANGED
@@ -106,6 +106,8 @@ Here are some commonly used startup parameters:
106
 
107
  > The requirement for the .env file to be in the startup directory is intentionally designed this way. The purpose is to support users in launching multiple LightRAG instances simultaneously, allowing different .env files for different instances.
108
 
 
 
109
  ### Auto scan on startup
110
 
111
  When starting any of the servers with the `--auto-scan-at-startup` parameter, the system will automatically:
 
106
 
107
  > The requirement for the .env file to be in the startup directory is intentionally designed this way. The purpose is to support users in launching multiple LightRAG instances simultaneously, allowing different .env files for different instances.
108
 
109
+ > **After changing the .env file, you need to open a new terminal to make the new settings take effect.** This because the LightRAG Server will load the environment variables from .env into the system environment variables each time it starts, and LightRAG Server will prioritize the settings in the system environment variables.
110
+
111
  ### Auto scan on startup
112
 
113
  When starting any of the servers with the `--auto-scan-at-startup` parameter, the system will automatically:
lightrag/api/__init__.py CHANGED
@@ -1 +1 @@
1
- __api_version__ = "0146"
 
1
+ __api_version__ = "0148"
lightrag/api/routers/document_routes.py CHANGED
@@ -499,7 +499,10 @@ async def pipeline_enqueue_file(rag: LightRAG, file_path: Path) -> bool:
499
  content = result.document.export_to_markdown()
500
  else:
501
  if not pm.is_installed("python-docx"): # type: ignore
502
- pm.install("docx")
 
 
 
503
  from docx import Document # type: ignore
504
  from io import BytesIO
505
 
 
499
  content = result.document.export_to_markdown()
500
  else:
501
  if not pm.is_installed("python-docx"): # type: ignore
502
+ try:
503
+ pm.install("python-docx")
504
+ except Exception:
505
+ pm.install("docx")
506
  from docx import Document # type: ignore
507
  from io import BytesIO
508
 
lightrag/api/routers/ollama_api.py CHANGED
@@ -308,7 +308,7 @@ class OllamaAPI:
308
  "Cache-Control": "no-cache",
309
  "Connection": "keep-alive",
310
  "Content-Type": "application/x-ndjson",
311
- "X-Accel-Buffering": "no", # 确保在Nginx代理时正确处理流式响应
312
  },
313
  )
314
  else:
 
308
  "Cache-Control": "no-cache",
309
  "Connection": "keep-alive",
310
  "Content-Type": "application/x-ndjson",
311
+ "X-Accel-Buffering": "no", # Ensure proper handling of streaming responses in Nginx proxy
312
  },
313
  )
314
  else:
lightrag/api/routers/query_routes.py CHANGED
@@ -22,7 +22,7 @@ class QueryRequest(BaseModel):
22
  description="The query text",
23
  )
24
 
25
- mode: Literal["local", "global", "hybrid", "naive", "mix"] = Field(
26
  default="hybrid",
27
  description="Query mode",
28
  )
 
22
  description="The query text",
23
  )
24
 
25
+ mode: Literal["local", "global", "hybrid", "naive", "mix", "bypass"] = Field(
26
  default="hybrid",
27
  description="Query mode",
28
  )
lightrag/api/webui/assets/{index-DSVCuARS.js → index-CkwV8nfm.js} RENAMED
Binary files a/lightrag/api/webui/assets/index-DSVCuARS.js and b/lightrag/api/webui/assets/index-CkwV8nfm.js differ
 
lightrag/api/webui/index.html CHANGED
Binary files a/lightrag/api/webui/index.html and b/lightrag/api/webui/index.html differ
 
lightrag/base.py CHANGED
@@ -12,7 +12,6 @@ from typing import (
12
  TypeVar,
13
  Callable,
14
  )
15
- import numpy as np
16
  from .utils import EmbeddingFunc
17
  from .types import KnowledgeGraph
18
 
@@ -36,7 +35,7 @@ T = TypeVar("T")
36
  class QueryParam:
37
  """Configuration parameters for query execution in LightRAG."""
38
 
39
- mode: Literal["local", "global", "hybrid", "naive", "mix"] = "global"
40
  """Specifies the retrieval mode:
41
  - "local": Focuses on context-dependent information.
42
  - "global": Utilizes global knowledge.
@@ -281,63 +280,164 @@ class BaseGraphStorage(StorageNameSpace, ABC):
281
 
282
  @abstractmethod
283
  async def has_node(self, node_id: str) -> bool:
284
- """Check if an edge exists in the graph."""
 
 
 
 
 
 
 
285
 
286
  @abstractmethod
287
  async def has_edge(self, source_node_id: str, target_node_id: str) -> bool:
288
- """Get the degree of a node."""
 
 
 
 
 
 
 
 
289
 
290
  @abstractmethod
291
  async def node_degree(self, node_id: str) -> int:
292
- """Get the degree of an edge."""
 
 
 
 
 
 
 
293
 
294
  @abstractmethod
295
  async def edge_degree(self, src_id: str, tgt_id: str) -> int:
296
- """Get a node by its id."""
 
 
 
 
 
 
 
 
297
 
298
  @abstractmethod
299
  async def get_node(self, node_id: str) -> dict[str, str] | None:
300
- """Get node by its label identifier, return only node properties"""
 
 
 
 
 
 
 
301
 
302
  @abstractmethod
303
  async def get_edge(
304
  self, source_node_id: str, target_node_id: str
305
  ) -> dict[str, str] | None:
306
- """Get edge properties between two nodes"""
 
 
 
 
 
 
 
 
307
 
308
  @abstractmethod
309
  async def get_node_edges(self, source_node_id: str) -> list[tuple[str, str]] | None:
310
- """Upsert a node into the graph."""
 
 
 
 
 
 
 
 
311
 
312
  @abstractmethod
313
  async def upsert_node(self, node_id: str, node_data: dict[str, str]) -> None:
314
- """Upsert an edge into the graph."""
 
 
 
 
 
 
 
 
 
 
315
 
316
  @abstractmethod
317
  async def upsert_edge(
318
  self, source_node_id: str, target_node_id: str, edge_data: dict[str, str]
319
  ) -> None:
320
- """Delete a node from the graph.
321
 
322
  Importance notes for in-memory storage:
323
  1. Changes will be persisted to disk during the next index_done_callback
324
  2. Only one process should updating the storage at a time before index_done_callback,
325
  KG-storage-log should be used to avoid data corruption
 
 
 
 
 
326
  """
327
 
328
  @abstractmethod
329
  async def delete_node(self, node_id: str) -> None:
330
- """Embed nodes using an algorithm."""
 
 
 
 
 
 
 
 
 
331
 
332
  @abstractmethod
333
- async def embed_nodes(
334
- self, algorithm: str
335
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
336
- """Get all labels in the graph."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
  @abstractmethod
339
  async def get_all_labels(self) -> list[str]:
340
- """Get a knowledge graph of a node."""
 
 
 
 
341
 
342
  @abstractmethod
343
  async def get_knowledge_graph(
 
12
  TypeVar,
13
  Callable,
14
  )
 
15
  from .utils import EmbeddingFunc
16
  from .types import KnowledgeGraph
17
 
 
35
  class QueryParam:
36
  """Configuration parameters for query execution in LightRAG."""
37
 
38
+ mode: Literal["local", "global", "hybrid", "naive", "mix", "bypass"] = "global"
39
  """Specifies the retrieval mode:
40
  - "local": Focuses on context-dependent information.
41
  - "global": Utilizes global knowledge.
 
280
 
281
  @abstractmethod
282
  async def has_node(self, node_id: str) -> bool:
283
+ """Check if a node exists in the graph.
284
+
285
+ Args:
286
+ node_id: The ID of the node to check
287
+
288
+ Returns:
289
+ True if the node exists, False otherwise
290
+ """
291
 
292
  @abstractmethod
293
  async def has_edge(self, source_node_id: str, target_node_id: str) -> bool:
294
+ """Check if an edge exists between two nodes.
295
+
296
+ Args:
297
+ source_node_id: The ID of the source node
298
+ target_node_id: The ID of the target node
299
+
300
+ Returns:
301
+ True if the edge exists, False otherwise
302
+ """
303
 
304
  @abstractmethod
305
  async def node_degree(self, node_id: str) -> int:
306
+ """Get the degree (number of connected edges) of a node.
307
+
308
+ Args:
309
+ node_id: The ID of the node
310
+
311
+ Returns:
312
+ The number of edges connected to the node
313
+ """
314
 
315
  @abstractmethod
316
  async def edge_degree(self, src_id: str, tgt_id: str) -> int:
317
+ """Get the total degree of an edge (sum of degrees of its source and target nodes).
318
+
319
+ Args:
320
+ src_id: The ID of the source node
321
+ tgt_id: The ID of the target node
322
+
323
+ Returns:
324
+ The sum of the degrees of the source and target nodes
325
+ """
326
 
327
  @abstractmethod
328
  async def get_node(self, node_id: str) -> dict[str, str] | None:
329
+ """Get node by its ID, returning only node properties.
330
+
331
+ Args:
332
+ node_id: The ID of the node to retrieve
333
+
334
+ Returns:
335
+ A dictionary of node properties if found, None otherwise
336
+ """
337
 
338
  @abstractmethod
339
  async def get_edge(
340
  self, source_node_id: str, target_node_id: str
341
  ) -> dict[str, str] | None:
342
+ """Get edge properties between two nodes.
343
+
344
+ Args:
345
+ source_node_id: The ID of the source node
346
+ target_node_id: The ID of the target node
347
+
348
+ Returns:
349
+ A dictionary of edge properties if found, None otherwise
350
+ """
351
 
352
  @abstractmethod
353
  async def get_node_edges(self, source_node_id: str) -> list[tuple[str, str]] | None:
354
+ """Get all edges connected to a node.
355
+
356
+ Args:
357
+ source_node_id: The ID of the node to get edges for
358
+
359
+ Returns:
360
+ A list of (source_id, target_id) tuples representing edges,
361
+ or None if the node doesn't exist
362
+ """
363
 
364
  @abstractmethod
365
  async def upsert_node(self, node_id: str, node_data: dict[str, str]) -> None:
366
+ """Insert a new node or update an existing node in the graph.
367
+
368
+ Importance notes for in-memory storage:
369
+ 1. Changes will be persisted to disk during the next index_done_callback
370
+ 2. Only one process should updating the storage at a time before index_done_callback,
371
+ KG-storage-log should be used to avoid data corruption
372
+
373
+ Args:
374
+ node_id: The ID of the node to insert or update
375
+ node_data: A dictionary of node properties
376
+ """
377
 
378
  @abstractmethod
379
  async def upsert_edge(
380
  self, source_node_id: str, target_node_id: str, edge_data: dict[str, str]
381
  ) -> None:
382
+ """Insert a new edge or update an existing edge in the graph.
383
 
384
  Importance notes for in-memory storage:
385
  1. Changes will be persisted to disk during the next index_done_callback
386
  2. Only one process should updating the storage at a time before index_done_callback,
387
  KG-storage-log should be used to avoid data corruption
388
+
389
+ Args:
390
+ source_node_id: The ID of the source node
391
+ target_node_id: The ID of the target node
392
+ edge_data: A dictionary of edge properties
393
  """
394
 
395
  @abstractmethod
396
  async def delete_node(self, node_id: str) -> None:
397
+ """Delete a node from the graph.
398
+
399
+ Importance notes for in-memory storage:
400
+ 1. Changes will be persisted to disk during the next index_done_callback
401
+ 2. Only one process should updating the storage at a time before index_done_callback,
402
+ KG-storage-log should be used to avoid data corruption
403
+
404
+ Args:
405
+ node_id: The ID of the node to delete
406
+ """
407
 
408
  @abstractmethod
409
+ async def remove_nodes(self, nodes: list[str]):
410
+ """Delete multiple nodes
411
+
412
+ Importance notes:
413
+ 1. Changes will be persisted to disk during the next index_done_callback
414
+ 2. Only one process should updating the storage at a time before index_done_callback,
415
+ KG-storage-log should be used to avoid data corruption
416
+
417
+ Args:
418
+ nodes: List of node IDs to be deleted
419
+ """
420
+
421
+ @abstractmethod
422
+ async def remove_edges(self, edges: list[tuple[str, str]]):
423
+ """Delete multiple edges
424
+
425
+ Importance notes:
426
+ 1. Changes will be persisted to disk during the next index_done_callback
427
+ 2. Only one process should updating the storage at a time before index_done_callback,
428
+ KG-storage-log should be used to avoid data corruption
429
+
430
+ Args:
431
+ edges: List of edges to be deleted, each edge is a (source, target) tuple
432
+ """
433
 
434
  @abstractmethod
435
  async def get_all_labels(self) -> list[str]:
436
+ """Get all labels in the graph.
437
+
438
+ Returns:
439
+ A list of all node labels in the graph, sorted alphabetically
440
+ """
441
 
442
  @abstractmethod
443
  async def get_knowledge_graph(
lightrag/kg/age_impl.py CHANGED
@@ -6,7 +6,6 @@ import sys
6
  from contextlib import asynccontextmanager
7
  from dataclasses import dataclass
8
  from typing import Any, Dict, List, NamedTuple, Optional, Union, final
9
- import numpy as np
10
  import pipmaster as pm
11
  from lightrag.types import KnowledgeGraph, KnowledgeGraphNode, KnowledgeGraphEdge
12
 
@@ -89,11 +88,6 @@ class AGEStorage(BaseGraphStorage):
89
 
90
  return None
91
 
92
- def __post_init__(self):
93
- self._node_embed_algorithms = {
94
- "node2vec": self._node2vec_embed,
95
- }
96
-
97
  async def close(self):
98
  if self._driver:
99
  await self._driver.close()
@@ -593,9 +587,6 @@ class AGEStorage(BaseGraphStorage):
593
  logger.error("Error during edge upsert: {%s}", e)
594
  raise
595
 
596
- async def _node2vec_embed(self):
597
- print("Implemented but never called.")
598
-
599
  @asynccontextmanager
600
  async def _get_pool_connection(self, timeout: Optional[float] = None):
601
  """Workaround for a psycopg_pool bug"""
@@ -668,21 +659,6 @@ class AGEStorage(BaseGraphStorage):
668
  logger.error(f"Error during edge deletion: {str(e)}")
669
  raise
670
 
671
- async def embed_nodes(
672
- self, algorithm: str
673
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
674
- """Embed nodes using the specified algorithm
675
-
676
- Args:
677
- algorithm: Name of the embedding algorithm
678
-
679
- Returns:
680
- tuple: (embedding matrix, list of node identifiers)
681
- """
682
- if algorithm not in self._node_embed_algorithms:
683
- raise ValueError(f"Node embedding algorithm {algorithm} not supported")
684
- return await self._node_embed_algorithms[algorithm]()
685
-
686
  async def get_all_labels(self) -> list[str]:
687
  """Get all node labels in the database
688
 
 
6
  from contextlib import asynccontextmanager
7
  from dataclasses import dataclass
8
  from typing import Any, Dict, List, NamedTuple, Optional, Union, final
 
9
  import pipmaster as pm
10
  from lightrag.types import KnowledgeGraph, KnowledgeGraphNode, KnowledgeGraphEdge
11
 
 
88
 
89
  return None
90
 
 
 
 
 
 
91
  async def close(self):
92
  if self._driver:
93
  await self._driver.close()
 
587
  logger.error("Error during edge upsert: {%s}", e)
588
  raise
589
 
 
 
 
590
  @asynccontextmanager
591
  async def _get_pool_connection(self, timeout: Optional[float] = None):
592
  """Workaround for a psycopg_pool bug"""
 
659
  logger.error(f"Error during edge deletion: {str(e)}")
660
  raise
661
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
662
  async def get_all_labels(self) -> list[str]:
663
  """Get all node labels in the database
664
 
lightrag/kg/gremlin_impl.py CHANGED
@@ -6,9 +6,6 @@ import pipmaster as pm
6
  from dataclasses import dataclass
7
  from typing import Any, Dict, List, final
8
 
9
- import numpy as np
10
-
11
-
12
  from tenacity import (
13
  retry,
14
  retry_if_exception_type,
@@ -72,11 +69,6 @@ class GremlinStorage(BaseGraphStorage):
72
  transport_factory=lambda: AiohttpTransport(call_from_event_loop=True),
73
  )
74
 
75
- def __post_init__(self):
76
- self._node_embed_algorithms = {
77
- "node2vec": self._node2vec_embed,
78
- }
79
-
80
  async def close(self):
81
  if self._driver:
82
  self._driver.close()
@@ -392,9 +384,6 @@ class GremlinStorage(BaseGraphStorage):
392
  logger.error("Error during edge upsert: {%s}", e)
393
  raise
394
 
395
- async def _node2vec_embed(self):
396
- print("Implemented but never called.")
397
-
398
  async def delete_node(self, node_id: str) -> None:
399
  """Delete a node with the specified entity_name
400
 
@@ -419,27 +408,6 @@ class GremlinStorage(BaseGraphStorage):
419
  logger.error(f"Error during node deletion: {str(e)}")
420
  raise
421
 
422
- async def embed_nodes(
423
- self, algorithm: str
424
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
425
- """
426
- Embed nodes using the specified algorithm.
427
- Currently, only node2vec is supported but never called.
428
-
429
- Args:
430
- algorithm: The name of the embedding algorithm to use
431
-
432
- Returns:
433
- A tuple of (embeddings, node_ids)
434
-
435
- Raises:
436
- NotImplementedError: If the specified algorithm is not supported
437
- ValueError: If the algorithm is not supported
438
- """
439
- if algorithm not in self._node_embed_algorithms:
440
- raise ValueError(f"Node embedding algorithm {algorithm} not supported")
441
- return await self._node_embed_algorithms[algorithm]()
442
-
443
  async def get_all_labels(self) -> list[str]:
444
  """
445
  Get all node entity_names in the graph
 
6
  from dataclasses import dataclass
7
  from typing import Any, Dict, List, final
8
 
 
 
 
9
  from tenacity import (
10
  retry,
11
  retry_if_exception_type,
 
69
  transport_factory=lambda: AiohttpTransport(call_from_event_loop=True),
70
  )
71
 
 
 
 
 
 
72
  async def close(self):
73
  if self._driver:
74
  self._driver.close()
 
384
  logger.error("Error during edge upsert: {%s}", e)
385
  raise
386
 
 
 
 
387
  async def delete_node(self, node_id: str) -> None:
388
  """Delete a node with the specified entity_name
389
 
 
408
  logger.error(f"Error during node deletion: {str(e)}")
409
  raise
410
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  async def get_all_labels(self) -> list[str]:
412
  """
413
  Get all node entity_names in the graph
lightrag/kg/mongo_impl.py CHANGED
@@ -663,20 +663,6 @@ class MongoGraphStorage(BaseGraphStorage):
663
  # Remove the node doc
664
  await self.collection.delete_one({"_id": node_id})
665
 
666
- #
667
- # -------------------------------------------------------------------------
668
- # EMBEDDINGS (NOT IMPLEMENTED)
669
- # -------------------------------------------------------------------------
670
- #
671
-
672
- async def embed_nodes(
673
- self, algorithm: str
674
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
675
- """
676
- Placeholder for demonstration, raises NotImplementedError.
677
- """
678
- raise NotImplementedError("Node embedding is not used in lightrag.")
679
-
680
  #
681
  # -------------------------------------------------------------------------
682
  # QUERY
 
663
  # Remove the node doc
664
  await self.collection.delete_one({"_id": node_id})
665
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  #
667
  # -------------------------------------------------------------------------
668
  # QUERY
lightrag/kg/neo4j_impl.py CHANGED
@@ -2,8 +2,7 @@ import inspect
2
  import os
3
  import re
4
  from dataclasses import dataclass
5
- from typing import Any, final
6
- import numpy as np
7
  import configparser
8
 
9
 
@@ -51,11 +50,6 @@ class Neo4JStorage(BaseGraphStorage):
51
  )
52
  self._driver = None
53
 
54
- def __post_init__(self):
55
- self._node_embed_algorithms = {
56
- "node2vec": self._node2vec_embed,
57
- }
58
-
59
  async def initialize(self):
60
  URI = os.environ.get("NEO4J_URI", config.get("neo4j", "uri", fallback=None))
61
  USERNAME = os.environ.get(
@@ -635,9 +629,6 @@ class Neo4JStorage(BaseGraphStorage):
635
  logger.error(f"Error during edge upsert: {str(e)}")
636
  raise
637
 
638
- async def _node2vec_embed(self):
639
- print("Implemented but never called.")
640
-
641
  async def get_knowledge_graph(
642
  self,
643
  node_label: str,
@@ -1126,11 +1117,6 @@ class Neo4JStorage(BaseGraphStorage):
1126
  logger.error(f"Error during edge deletion: {str(e)}")
1127
  raise
1128
 
1129
- async def embed_nodes(
1130
- self, algorithm: str
1131
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
1132
- raise NotImplementedError
1133
-
1134
  async def drop(self) -> dict[str, str]:
1135
  """Drop all data from storage and clean up resources
1136
 
 
2
  import os
3
  import re
4
  from dataclasses import dataclass
5
+ from typing import final
 
6
  import configparser
7
 
8
 
 
50
  )
51
  self._driver = None
52
 
 
 
 
 
 
53
  async def initialize(self):
54
  URI = os.environ.get("NEO4J_URI", config.get("neo4j", "uri", fallback=None))
55
  USERNAME = os.environ.get(
 
629
  logger.error(f"Error during edge upsert: {str(e)}")
630
  raise
631
 
 
 
 
632
  async def get_knowledge_graph(
633
  self,
634
  node_label: str,
 
1117
  logger.error(f"Error during edge deletion: {str(e)}")
1118
  raise
1119
 
 
 
 
 
 
1120
  async def drop(self) -> dict[str, str]:
1121
  """Drop all data from storage and clean up resources
1122
 
lightrag/kg/networkx_impl.py CHANGED
@@ -1,7 +1,6 @@
1
  import os
2
  from dataclasses import dataclass
3
- from typing import Any, final
4
- import numpy as np
5
 
6
  from lightrag.types import KnowledgeGraph, KnowledgeGraphNode, KnowledgeGraphEdge
7
  from lightrag.utils import logger
@@ -16,7 +15,6 @@ if not pm.is_installed("graspologic"):
16
  pm.install("graspologic")
17
 
18
  import networkx as nx
19
- from graspologic import embed
20
  from .shared_storage import (
21
  get_storage_lock,
22
  get_update_flag,
@@ -42,40 +40,6 @@ class NetworkXStorage(BaseGraphStorage):
42
  )
43
  nx.write_graphml(graph, file_name)
44
 
45
- # TODO:deprecated, remove later
46
- @staticmethod
47
- def _stabilize_graph(graph: nx.Graph) -> nx.Graph:
48
- """Refer to https://github.com/microsoft/graphrag/index/graph/utils/stable_lcc.py
49
- Ensure an undirected graph with the same relationships will always be read the same way.
50
- """
51
- fixed_graph = nx.DiGraph() if graph.is_directed() else nx.Graph()
52
-
53
- sorted_nodes = graph.nodes(data=True)
54
- sorted_nodes = sorted(sorted_nodes, key=lambda x: x[0])
55
-
56
- fixed_graph.add_nodes_from(sorted_nodes)
57
- edges = list(graph.edges(data=True))
58
-
59
- if not graph.is_directed():
60
-
61
- def _sort_source_target(edge):
62
- source, target, edge_data = edge
63
- if source > target:
64
- temp = source
65
- source = target
66
- target = temp
67
- return source, target, edge_data
68
-
69
- edges = [_sort_source_target(edge) for edge in edges]
70
-
71
- def _get_edge_key(source: Any, target: Any) -> str:
72
- return f"{source} -> {target}"
73
-
74
- edges = sorted(edges, key=lambda x: _get_edge_key(x[0], x[1]))
75
-
76
- fixed_graph.add_edges_from(edges)
77
- return fixed_graph
78
-
79
  def __post_init__(self):
80
  self._graphml_xml_file = os.path.join(
81
  self.global_config["working_dir"], f"graph_{self.namespace}.graphml"
@@ -94,10 +58,6 @@ class NetworkXStorage(BaseGraphStorage):
94
  logger.info("Created new empty graph")
95
  self._graph = preloaded_graph or nx.Graph()
96
 
97
- self._node_embed_algorithms = {
98
- "node2vec": self._node2vec_embed,
99
- }
100
-
101
  async def initialize(self):
102
  """Initialize storage data"""
103
  # Get the update flag for cross-process update notification
@@ -191,24 +151,6 @@ class NetworkXStorage(BaseGraphStorage):
191
  else:
192
  logger.warning(f"Node {node_id} not found in the graph for deletion.")
193
 
194
- # TODO: NOT USED
195
- async def embed_nodes(
196
- self, algorithm: str
197
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
198
- if algorithm not in self._node_embed_algorithms:
199
- raise ValueError(f"Node embedding algorithm {algorithm} not supported")
200
- return await self._node_embed_algorithms[algorithm]()
201
-
202
- # TODO: NOT USED
203
- async def _node2vec_embed(self):
204
- graph = await self._get_graph()
205
- embeddings, nodes = embed.node2vec_embed(
206
- graph,
207
- **self.global_config["node2vec_params"],
208
- )
209
- nodes_ids = [graph.nodes[node_id]["id"] for node_id in nodes]
210
- return embeddings, nodes_ids
211
-
212
  async def remove_nodes(self, nodes: list[str]):
213
  """Delete multiple nodes
214
 
 
1
  import os
2
  from dataclasses import dataclass
3
+ from typing import final
 
4
 
5
  from lightrag.types import KnowledgeGraph, KnowledgeGraphNode, KnowledgeGraphEdge
6
  from lightrag.utils import logger
 
15
  pm.install("graspologic")
16
 
17
  import networkx as nx
 
18
  from .shared_storage import (
19
  get_storage_lock,
20
  get_update_flag,
 
40
  )
41
  nx.write_graphml(graph, file_name)
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  def __post_init__(self):
44
  self._graphml_xml_file = os.path.join(
45
  self.global_config["working_dir"], f"graph_{self.namespace}.graphml"
 
58
  logger.info("Created new empty graph")
59
  self._graph = preloaded_graph or nx.Graph()
60
 
 
 
 
 
61
  async def initialize(self):
62
  """Initialize storage data"""
63
  # Get the update flag for cross-process update notification
 
151
  else:
152
  logger.warning(f"Node {node_id} not found in the graph for deletion.")
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  async def remove_nodes(self, nodes: list[str]):
155
  """Delete multiple nodes
156
 
lightrag/kg/postgres_impl.py CHANGED
@@ -1021,9 +1021,6 @@ class PGGraphQueryException(Exception):
1021
  class PGGraphStorage(BaseGraphStorage):
1022
  def __post_init__(self):
1023
  self.graph_name = self.namespace or os.environ.get("AGE_GRAPH_NAME", "lightrag")
1024
- self._node_embed_algorithms = {
1025
- "node2vec": self._node2vec_embed,
1026
- }
1027
  self.db: PostgreSQLDB | None = None
1028
 
1029
  async def initialize(self):
@@ -1396,9 +1393,6 @@ class PGGraphStorage(BaseGraphStorage):
1396
  )
1397
  raise
1398
 
1399
- async def _node2vec_embed(self):
1400
- print("Implemented but never called.")
1401
-
1402
  async def delete_node(self, node_id: str) -> None:
1403
  """
1404
  Delete a node from the graph.
@@ -1485,24 +1479,6 @@ class PGGraphStorage(BaseGraphStorage):
1485
  labels = [result["label"] for result in results]
1486
  return labels
1487
 
1488
- async def embed_nodes(
1489
- self, algorithm: str
1490
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
1491
- """
1492
- Generate node embeddings using the specified algorithm.
1493
-
1494
- Args:
1495
- algorithm (str): The name of the embedding algorithm to use.
1496
-
1497
- Returns:
1498
- tuple[np.ndarray[Any, Any], list[str]]: A tuple containing the embeddings and the corresponding node IDs.
1499
- """
1500
- if algorithm not in self._node_embed_algorithms:
1501
- raise ValueError(f"Unsupported embedding algorithm: {algorithm}")
1502
-
1503
- embed_func = self._node_embed_algorithms[algorithm]
1504
- return await embed_func()
1505
-
1506
  async def get_knowledge_graph(
1507
  self,
1508
  node_label: str,
 
1021
  class PGGraphStorage(BaseGraphStorage):
1022
  def __post_init__(self):
1023
  self.graph_name = self.namespace or os.environ.get("AGE_GRAPH_NAME", "lightrag")
 
 
 
1024
  self.db: PostgreSQLDB | None = None
1025
 
1026
  async def initialize(self):
 
1393
  )
1394
  raise
1395
 
 
 
 
1396
  async def delete_node(self, node_id: str) -> None:
1397
  """
1398
  Delete a node from the graph.
 
1479
  labels = [result["label"] for result in results]
1480
  return labels
1481
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1482
  async def get_knowledge_graph(
1483
  self,
1484
  node_label: str,
lightrag/kg/tidb_impl.py CHANGED
@@ -800,13 +800,6 @@ class TiDBGraphStorage(BaseGraphStorage):
800
  }
801
  await self.db.execute(merge_sql, data)
802
 
803
- async def embed_nodes(
804
- self, algorithm: str
805
- ) -> tuple[np.ndarray[Any, Any], list[str]]:
806
- if algorithm not in self._node_embed_algorithms:
807
- raise ValueError(f"Node embedding algorithm {algorithm} not supported")
808
- return await self._node_embed_algorithms[algorithm]()
809
-
810
  # Query
811
 
812
  async def has_node(self, node_id: str) -> bool:
 
800
  }
801
  await self.db.execute(merge_sql, data)
802
 
 
 
 
 
 
 
 
803
  # Query
804
 
805
  async def has_node(self, node_id: str) -> bool:
lightrag/lightrag.py CHANGED
@@ -155,31 +155,6 @@ class LightRAG:
155
  Defaults to `chunking_by_token_size` if not specified.
156
  """
157
 
158
- # Node embedding
159
- # ---
160
-
161
- node_embedding_algorithm: str = field(default="node2vec")
162
- """Algorithm used for node embedding in knowledge graphs."""
163
-
164
- node2vec_params: dict[str, int] = field(
165
- default_factory=lambda: {
166
- "dimensions": 1536,
167
- "num_walks": 10,
168
- "walk_length": 40,
169
- "window_size": 2,
170
- "iterations": 3,
171
- "random_seed": 3,
172
- }
173
- )
174
- """Configuration for the node2vec embedding algorithm:
175
- - dimensions: Number of dimensions for embeddings.
176
- - num_walks: Number of random walks per node.
177
- - walk_length: Number of steps per random walk.
178
- - window_size: Context window size for training.
179
- - iterations: Number of iterations for training.
180
- - random_seed: Seed value for reproducibility.
181
- """
182
-
183
  # Embedding
184
  # ---
185
 
@@ -904,8 +879,10 @@ class LightRAG:
904
 
905
  async with pipeline_status_lock:
906
  log_message = f"Processing file: {file_path}"
 
907
  pipeline_status["history_messages"].append(log_message)
908
  log_message = f"Processing d-id: {doc_id}"
 
909
  pipeline_status["latest_message"] = log_message
910
  pipeline_status["history_messages"].append(log_message)
911
 
@@ -1381,6 +1358,16 @@ class LightRAG:
1381
  hashing_kv=self.llm_response_cache, # Directly use llm_response_cache
1382
  system_prompt=system_prompt,
1383
  )
 
 
 
 
 
 
 
 
 
 
1384
  else:
1385
  raise ValueError(f"Unknown mode {param.mode}")
1386
  await self._query_done()
 
155
  Defaults to `chunking_by_token_size` if not specified.
156
  """
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  # Embedding
159
  # ---
160
 
 
879
 
880
  async with pipeline_status_lock:
881
  log_message = f"Processing file: {file_path}"
882
+ logger.info(log_message)
883
  pipeline_status["history_messages"].append(log_message)
884
  log_message = f"Processing d-id: {doc_id}"
885
+ logger.info(log_message)
886
  pipeline_status["latest_message"] = log_message
887
  pipeline_status["history_messages"].append(log_message)
888
 
 
1358
  hashing_kv=self.llm_response_cache, # Directly use llm_response_cache
1359
  system_prompt=system_prompt,
1360
  )
1361
+ elif param.mode == "bypass":
1362
+ # Bypass mode: directly use LLM without knowledge retrieval
1363
+ use_llm_func = param.model_func or global_config["llm_model_func"]
1364
+ param.stream = True if param.stream is None else param.stream
1365
+ response = await use_llm_func(
1366
+ query.strip(),
1367
+ system_prompt=system_prompt,
1368
+ history_messages=param.conversation_history,
1369
+ stream=param.stream,
1370
+ )
1371
  else:
1372
  raise ValueError(f"Unknown mode {param.mode}")
1373
  await self._query_done()
lightrag/operate.py CHANGED
@@ -16,6 +16,7 @@ from .utils import (
16
  encode_string_by_tiktoken,
17
  is_float_regex,
18
  list_of_list_to_csv,
 
19
  pack_user_ass_to_openai_messages,
20
  split_string_by_multi_markers,
21
  truncate_list_by_token_size,
@@ -163,6 +164,9 @@ async def _handle_single_entity_extraction(
163
  )
164
  return None
165
 
 
 
 
166
  # Clean and validate entity type
167
  entity_type = clean_str(record_attributes[2]).strip('"')
168
  if not entity_type.strip() or entity_type.startswith('("'):
@@ -172,7 +176,9 @@ async def _handle_single_entity_extraction(
172
  return None
173
 
174
  # Clean and validate description
175
- entity_description = clean_str(record_attributes[3]).strip('"')
 
 
176
  if not entity_description.strip():
177
  logger.warning(
178
  f"Entity extraction error: empty description for entity '{entity_name}' of type '{entity_type}'"
@@ -196,13 +202,20 @@ async def _handle_single_relationship_extraction(
196
  if len(record_attributes) < 5 or record_attributes[0] != '"relationship"':
197
  return None
198
  # add this record as edge
199
- source = clean_str(record_attributes[1]).strip('"')
200
- target = clean_str(record_attributes[2]).strip('"')
201
- edge_description = clean_str(record_attributes[3]).strip('"')
202
- edge_keywords = clean_str(record_attributes[4]).strip('"')
 
 
 
 
 
 
 
203
  edge_source_id = chunk_key
204
  weight = (
205
- float(record_attributes[-1].strip('"'))
206
  if is_float_regex(record_attributes[-1])
207
  else 1.0
208
  )
@@ -642,7 +655,7 @@ async def extract_entities(
642
  processed_chunks += 1
643
  entities_count = len(maybe_nodes)
644
  relations_count = len(maybe_edges)
645
- log_message = f"Chk {processed_chunks}/{total_chunks}: extracted {entities_count} Ent + {relations_count} Rel (deduplicated)"
646
  logger.info(log_message)
647
  if pipeline_status is not None:
648
  async with pipeline_status_lock:
 
16
  encode_string_by_tiktoken,
17
  is_float_regex,
18
  list_of_list_to_csv,
19
+ normalize_extracted_info,
20
  pack_user_ass_to_openai_messages,
21
  split_string_by_multi_markers,
22
  truncate_list_by_token_size,
 
164
  )
165
  return None
166
 
167
+ # Normalize entity name
168
+ entity_name = normalize_extracted_info(entity_name, is_entity=True)
169
+
170
  # Clean and validate entity type
171
  entity_type = clean_str(record_attributes[2]).strip('"')
172
  if not entity_type.strip() or entity_type.startswith('("'):
 
176
  return None
177
 
178
  # Clean and validate description
179
+ entity_description = clean_str(record_attributes[3])
180
+ entity_description = normalize_extracted_info(entity_description)
181
+
182
  if not entity_description.strip():
183
  logger.warning(
184
  f"Entity extraction error: empty description for entity '{entity_name}' of type '{entity_type}'"
 
202
  if len(record_attributes) < 5 or record_attributes[0] != '"relationship"':
203
  return None
204
  # add this record as edge
205
+ source = clean_str(record_attributes[1])
206
+ target = clean_str(record_attributes[2])
207
+
208
+ # Normalize source and target entity names
209
+ source = normalize_extracted_info(source, is_entity=True)
210
+ target = normalize_extracted_info(target, is_entity=True)
211
+
212
+ edge_description = clean_str(record_attributes[3])
213
+ edge_description = normalize_extracted_info(edge_description)
214
+
215
+ edge_keywords = clean_str(record_attributes[4]).strip('"').strip("'")
216
  edge_source_id = chunk_key
217
  weight = (
218
+ float(record_attributes[-1].strip('"').strip("'"))
219
  if is_float_regex(record_attributes[-1])
220
  else 1.0
221
  )
 
655
  processed_chunks += 1
656
  entities_count = len(maybe_nodes)
657
  relations_count = len(maybe_edges)
658
+ log_message = f"Chk {processed_chunks}/{total_chunks}: extracted {entities_count} Ent + {relations_count} Rel"
659
  logger.info(log_message)
660
  if pipeline_status is not None:
661
  async with pipeline_status_lock:
lightrag/utils.py CHANGED
@@ -1006,6 +1006,50 @@ def get_content_summary(content: str, max_length: int = 250) -> str:
1006
  return content[:max_length] + "..."
1007
 
1008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1009
  def clean_text(text: str) -> str:
1010
  """Clean text by removing null bytes (0x00) and whitespace
1011
 
 
1006
  return content[:max_length] + "..."
1007
 
1008
 
1009
+ def normalize_extracted_info(name: str, is_entity=False) -> str:
1010
+ """Normalize entity/relation names and description with the following rules:
1011
+ 1. Remove spaces between Chinese characters
1012
+ 2. Remove spaces between Chinese characters and English letters/numbers
1013
+ 3. Preserve spaces within English text and numbers
1014
+ 4. Replace Chinese parentheses with English parentheses
1015
+ 5. Replace Chinese dash with English dash
1016
+
1017
+ Args:
1018
+ name: Entity name to normalize
1019
+
1020
+ Returns:
1021
+ Normalized entity name
1022
+ """
1023
+ # Replace Chinese parentheses with English parentheses
1024
+ name = name.replace("(", "(").replace(")", ")")
1025
+
1026
+ # Replace Chinese dash with English dash
1027
+ name = name.replace("—", "-").replace("-", "-")
1028
+
1029
+ # Use regex to remove spaces between Chinese characters
1030
+ # Regex explanation:
1031
+ # (?<=[\u4e00-\u9fa5]): Positive lookbehind for Chinese character
1032
+ # \s+: One or more whitespace characters
1033
+ # (?=[\u4e00-\u9fa5]): Positive lookahead for Chinese character
1034
+ name = re.sub(r"(?<=[\u4e00-\u9fa5])\s+(?=[\u4e00-\u9fa5])", "", name)
1035
+
1036
+ # Remove spaces between Chinese and English/numbers
1037
+ name = re.sub(r"(?<=[\u4e00-\u9fa5])\s+(?=[a-zA-Z0-9])", "", name)
1038
+ name = re.sub(r"(?<=[a-zA-Z0-9])\s+(?=[\u4e00-\u9fa5])", "", name)
1039
+
1040
+ # Remove English quotation marks from the beginning and end
1041
+ name = name.strip('"').strip("'")
1042
+
1043
+ if is_entity:
1044
+ # remove Chinese quotes
1045
+ name = name.replace("“", "").replace("”", "").replace("‘", "").replace("’", "")
1046
+ # remove English queotes in and around chinese
1047
+ name = re.sub(r"['\"]+(?=[\u4e00-\u9fa5])", "", name)
1048
+ name = re.sub(r"(?<=[\u4e00-\u9fa5])['\"]+", "", name)
1049
+
1050
+ return name
1051
+
1052
+
1053
  def clean_text(text: str) -> str:
1054
  """Clean text by removing null bytes (0x00) and whitespace
1055
 
lightrag_webui/src/api/lightrag.ts CHANGED
@@ -65,8 +65,9 @@ export type LightragDocumentsScanProgress = {
65
  * - "global": Utilizes global knowledge.
66
  * - "hybrid": Combines local and global retrieval methods.
67
  * - "mix": Integrates knowledge graph and vector retrieval.
 
68
  */
69
- export type QueryMode = 'naive' | 'local' | 'global' | 'hybrid' | 'mix'
70
 
71
  export type Message = {
72
  role: 'user' | 'assistant' | 'system'
 
65
  * - "global": Utilizes global knowledge.
66
  * - "hybrid": Combines local and global retrieval methods.
67
  * - "mix": Integrates knowledge graph and vector retrieval.
68
+ * - "bypass": Bypasses knowledge retrieval and directly uses the LLM.
69
  */
70
+ export type QueryMode = 'naive' | 'local' | 'global' | 'hybrid' | 'mix' | 'bypass'
71
 
72
  export type Message = {
73
  role: 'user' | 'assistant' | 'system'
lightrag_webui/src/components/retrieval/QuerySettings.tsx CHANGED
@@ -55,6 +55,7 @@ export default function QuerySettings() {
55
  <SelectItem value="global">{t('retrievePanel.querySettings.queryModeOptions.global')}</SelectItem>
56
  <SelectItem value="hybrid">{t('retrievePanel.querySettings.queryModeOptions.hybrid')}</SelectItem>
57
  <SelectItem value="mix">{t('retrievePanel.querySettings.queryModeOptions.mix')}</SelectItem>
 
58
  </SelectGroup>
59
  </SelectContent>
60
  </Select>
 
55
  <SelectItem value="global">{t('retrievePanel.querySettings.queryModeOptions.global')}</SelectItem>
56
  <SelectItem value="hybrid">{t('retrievePanel.querySettings.queryModeOptions.hybrid')}</SelectItem>
57
  <SelectItem value="mix">{t('retrievePanel.querySettings.queryModeOptions.mix')}</SelectItem>
58
+ <SelectItem value="bypass">{t('retrievePanel.querySettings.queryModeOptions.bypass')}</SelectItem>
59
  </SelectGroup>
60
  </SelectContent>
61
  </Select>
lightrag_webui/src/locales/ar.json CHANGED
@@ -306,13 +306,14 @@
306
  "parametersTitle": "المعلمات",
307
  "parametersDescription": "تكوين معلمات الاستعلام الخاص بك",
308
  "queryMode": "وضع الاستعلام",
309
- "queryModeTooltip": "حدد استراتيجية الاسترجاع:\n• ساذج: بحث أساسي بدون تقنيات متقدمة\n• محلي: استرجاع معلومات يعتمد على السياق\n• عالمي: يستخدم قاعدة المعرفة العالمية\n• مختلط: يجمع بين الاسترجاع المحلي والعالمي\n• مزيج: يدمج شبكة المعرفة مع الاسترجاع المتجهي",
310
  "queryModeOptions": {
311
  "naive": "ساذج",
312
  "local": "محلي",
313
  "global": "عالمي",
314
  "hybrid": "مختلط",
315
- "mix": "مزيج"
 
316
  },
317
  "responseFormat": "تنسيق الرد",
318
  "responseFormatTooltip": "يحدد تنسيق الرد. أمثلة:\n• فقرات متعددة\n• فقرة واحدة\n• نقاط نقطية",
 
306
  "parametersTitle": "المعلمات",
307
  "parametersDescription": "تكوين معلمات الاستعلام الخاص بك",
308
  "queryMode": "وضع الاستعلام",
309
+ "queryModeTooltip": "حدد استراتيجية الاسترجاع:\n• ساذج: بحث أساسي بدون تقنيات متقدمة\n• محلي: استرجاع معلومات يعتمد على السياق\n• عالمي: يستخدم قاعدة المعرفة العالمية\n• مختلط: يجمع بين الاسترجاع المحلي والعالمي\n• مزيج: يدمج شبكة المعرفة مع الاسترجاع المتجهي\n• تجاوز: يمرر الاستعلام مباشرة إلى LLM بدون استرجاع",
310
  "queryModeOptions": {
311
  "naive": "ساذج",
312
  "local": "محلي",
313
  "global": "عالمي",
314
  "hybrid": "مختلط",
315
+ "mix": "مزيج",
316
+ "bypass": "تجاوز"
317
  },
318
  "responseFormat": "تنسيق الرد",
319
  "responseFormatTooltip": "يحدد تنسيق الرد. أمثلة:\n• فقرات متعددة\n• فقرة واحدة\n• نقاط نقطية",
lightrag_webui/src/locales/en.json CHANGED
@@ -305,13 +305,14 @@
305
  "parametersTitle": "Parameters",
306
  "parametersDescription": "Configure your query parameters",
307
  "queryMode": "Query Mode",
308
- "queryModeTooltip": "Select the retrieval strategy:\n• Naive: Basic search without advanced techniques\n• Local: Context-dependent information retrieval\n• Global: Utilizes global knowledge base\n• Hybrid: Combines local and global retrieval\n• Mix: Integrates knowledge graph with vector retrieval",
309
  "queryModeOptions": {
310
  "naive": "Naive",
311
  "local": "Local",
312
  "global": "Global",
313
  "hybrid": "Hybrid",
314
- "mix": "Mix"
 
315
  },
316
  "responseFormat": "Response Format",
317
  "responseFormatTooltip": "Defines the response format. Examples:\n• Multiple Paragraphs\n• Single Paragraph\n• Bullet Points",
 
305
  "parametersTitle": "Parameters",
306
  "parametersDescription": "Configure your query parameters",
307
  "queryMode": "Query Mode",
308
+ "queryModeTooltip": "Select the retrieval strategy:\n• Naive: Basic search without advanced techniques\n• Local: Context-dependent information retrieval\n• Global: Utilizes global knowledge base\n• Hybrid: Combines local and global retrieval\n• Mix: Integrates knowledge graph with vector retrieval\n• Bypass: Passes query directly to LLM without retrieval",
309
  "queryModeOptions": {
310
  "naive": "Naive",
311
  "local": "Local",
312
  "global": "Global",
313
  "hybrid": "Hybrid",
314
+ "mix": "Mix",
315
+ "bypass": "Bypass"
316
  },
317
  "responseFormat": "Response Format",
318
  "responseFormatTooltip": "Defines the response format. Examples:\n• Multiple Paragraphs\n• Single Paragraph\n• Bullet Points",
lightrag_webui/src/locales/fr.json CHANGED
@@ -306,13 +306,14 @@
306
  "parametersTitle": "Paramètres",
307
  "parametersDescription": "Configurez vos paramètres de requête",
308
  "queryMode": "Mode de requête",
309
- "queryModeTooltip": "Sélectionnez la stratégie de récupération :\n• Naïf : Recherche de base sans techniques avancées\n• Local : Récupération d'informations dépendante du contexte\n• Global : Utilise une base de connaissances globale\n• Hybride : Combine récupération locale et globale\n• Mixte : Intègre le graphe de connaissances avec la récupération vectorielle",
310
  "queryModeOptions": {
311
  "naive": "Naïf",
312
  "local": "Local",
313
  "global": "Global",
314
  "hybrid": "Hybride",
315
- "mix": "Mixte"
 
316
  },
317
  "responseFormat": "Format de réponse",
318
  "responseFormatTooltip": "Définit le format de la réponse. Exemples :\n• Plusieurs paragraphes\n• Paragraphe unique\n• Points à puces",
 
306
  "parametersTitle": "Paramètres",
307
  "parametersDescription": "Configurez vos paramètres de requête",
308
  "queryMode": "Mode de requête",
309
+ "queryModeTooltip": "Sélectionnez la stratégie de récupération :\n• Naïf : Recherche de base sans techniques avancées\n• Local : Récupération d'informations dépendante du contexte\n• Global : Utilise une base de connaissances globale\n• Hybride : Combine récupération locale et globale\n• Mixte : Intègre le graphe de connaissances avec la récupération vectorielle\n• Bypass : Transmet directement la requête au LLM sans récupération",
310
  "queryModeOptions": {
311
  "naive": "Naïf",
312
  "local": "Local",
313
  "global": "Global",
314
  "hybrid": "Hybride",
315
+ "mix": "Mixte",
316
+ "bypass": "Bypass"
317
  },
318
  "responseFormat": "Format de réponse",
319
  "responseFormatTooltip": "Définit le format de la réponse. Exemples :\n• Plusieurs paragraphes\n• Paragraphe unique\n• Points à puces",
lightrag_webui/src/locales/zh.json CHANGED
@@ -306,13 +306,14 @@
306
  "parametersTitle": "参数",
307
  "parametersDescription": "配置查询参数",
308
  "queryMode": "查询模式",
309
- "queryModeTooltip": "选择检索策略:\n• Naive:基础搜索,无高级技术\n• Local:上下文相关信息检索\n• Global:利用全局知识库\n• Hybrid:结合本地和全局检索\n• Mix:整合知识图谱和向量检索",
310
  "queryModeOptions": {
311
  "naive": "Naive",
312
  "local": "Local",
313
  "global": "Global",
314
  "hybrid": "Hybrid",
315
- "mix": "Mix"
 
316
  },
317
  "responseFormat": "响应格式",
318
  "responseFormatTooltip": "定义响应格式。例如:\n• 多段落\n• 单段落\n• 要点",
 
306
  "parametersTitle": "参数",
307
  "parametersDescription": "配置查询参数",
308
  "queryMode": "查询模式",
309
+ "queryModeTooltip": "选择检索策略:\n• Naive:基础搜索,无高级技术\n• Local:上下文相关信息检索\n• Global:利用全局知识库\n• Hybrid:结合本地和全局检索\n• Mix:整合知识图谱和向量检索\n• Bypass:直接传递查询到LLM,不进行检索",
310
  "queryModeOptions": {
311
  "naive": "Naive",
312
  "local": "Local",
313
  "global": "Global",
314
  "hybrid": "Hybrid",
315
+ "mix": "Mix",
316
+ "bypass": "Bypass"
317
  },
318
  "responseFormat": "响应格式",
319
  "responseFormatTooltip": "定义响应格式。例如:\n• 多段落\n• 单段落\n• 要点",