Skip to content

Commit ac848ea

Browse files
committed
refactor(graph): 重构图谱适配器与服务层架构
将 GraphDatabase 重构为 UploadGraphService 并引入 BaseNeo4jAdapter 作为基础查询层 优化适配器工厂支持自动检测图谱类型,统一查询接口 添加 GraphMetadata 元数据管理,增强类型安全与能力声明 重构 LightRAGGraphAdapter 使用标准查询接口,改进节点边规范化处理 更新路由层使用新的适配器工厂方法,简化图谱类型检测逻辑
1 parent 92518e5 commit ac848ea

File tree

8 files changed

+1123
-718
lines changed

8 files changed

+1123
-718
lines changed

server/routers/graph_router.py

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,25 @@ async def _get_graph_adapter(db_id: str) -> GraphAdapter:
2727
Returns:
2828
GraphAdapter: 对应的图谱适配器实例
2929
"""
30-
# 1. 检查是否是 LightRAG 数据库
31-
if knowledge_base.is_lightrag_database(db_id):
32-
rag_instance = await knowledge_base._get_lightrag_instance(db_id)
33-
if not rag_instance:
34-
raise HTTPException(status_code=404, detail=f"LightRAG database {db_id} not found or inaccessible")
35-
return GraphAdapterFactory.create_adapter("lightrag", lightrag_instance=rag_instance)
36-
37-
# 2. 默认为 Upload/Neo4j 数据库 (假设 db_id 为 "neo4j" 或其他 Neo4j 数据库名)
38-
# 这里我们假设非 LightRAG 的 ID 都是 Neo4j 的数据库名
39-
# 如果未来有更多类型,需要更完善的 ID 区分机制 (例如前缀)
30+
# 检查图数据库服务状态 (仅对 Upload 类型需要)
4031
if not graph_base.is_running():
41-
raise HTTPException(status_code=503, detail="Graph database service is not running")
32+
# 先尝试检测图谱类型,如果是不需要 graph_base 的类型则允许
33+
graph_type = GraphAdapterFactory.detect_graph_type(db_id, knowledge_base)
34+
if graph_type == "upload":
35+
raise HTTPException(status_code=503, detail="Graph database service is not running")
36+
37+
# 使用工厂方法自动创建适配器
38+
return GraphAdapterFactory.create_adapter_by_db_id(
39+
db_id=db_id, knowledge_base_manager=knowledge_base, graph_db_instance=graph_base
40+
)
41+
4242

43-
return GraphAdapterFactory.create_adapter("upload", graph_db_instance=graph_base, config={"kgdb_name": db_id})
43+
def _get_capabilities_from_metadata(metadata) -> dict:
44+
"""从 GraphMetadata 对象提取 capabilities 字典"""
45+
return {
46+
"supports_embedding": metadata.supports_embedding,
47+
"supports_threshold": metadata.supports_threshold,
48+
}
4449

4550

4651
@graph.get("/list")
@@ -49,39 +54,53 @@ async def get_graphs(current_user: User = Depends(get_admin_user)):
4954
获取所有可用的知识图谱列表
5055
5156
Returns:
52-
包含所有图谱信息的列表 (包括 Neo4j 和 LightRAG)
57+
包含所有图谱信息的列表 (包括 Neo4j 和 LightRAG),以及每个类型的 capability 信息
5358
"""
5459
try:
5560
graphs = []
5661

57-
# 1. 获取默认 Neo4j 图谱信息
62+
# 1. 获取默认 Neo4j 图谱信息 (Upload 类型)
5863
neo4j_info = graph_base.get_graph_info()
5964
if neo4j_info:
65+
# 直接使用 Upload 适配器的默认 metadata
66+
from src.knowledge.adapters.upload import UploadGraphAdapter
67+
68+
capabilities = _get_capabilities_from_metadata(UploadGraphAdapter._get_metadata(None))
69+
6070
graphs.append(
6171
{
6272
"id": "neo4j",
6373
"name": "默认图谱",
64-
"type": "neo4j",
74+
"type": "upload",
6575
"description": "Default graph database for uploaded documents",
6676
"status": neo4j_info.get("status", "unknown"),
6777
"created_at": neo4j_info.get("last_updated"),
6878
"node_count": neo4j_info.get("entity_count", 0),
6979
"edge_count": neo4j_info.get("relationship_count", 0),
80+
"capabilities": capabilities,
7081
}
7182
)
7283

7384
# 2. 获取 LightRAG 数据库信息
7485
lightrag_dbs = knowledge_base.get_lightrag_databases()
86+
# 直接使用 LightRAG 适配器的默认 metadata
87+
from src.knowledge.adapters.lightrag import LightRAGGraphAdapter
88+
89+
capabilities = _get_capabilities_from_metadata(LightRAGGraphAdapter._get_metadata(None))
90+
7591
for db in lightrag_dbs:
92+
db_id = db.get("db_id")
93+
7694
graphs.append(
7795
{
78-
"id": db.get("db_id"),
96+
"id": db_id,
7997
"name": db.get("name"),
8098
"type": "lightrag",
8199
"description": db.get("description"),
82100
"status": "active", # LightRAG DBs are usually active if listed
83101
"created_at": db.get("created_at"),
84102
"metadata": db,
103+
"capabilities": capabilities,
85104
}
86105
)
87106

@@ -115,15 +134,14 @@ async def get_subgraph(
115134

116135
adapter = await _get_graph_adapter(db_id)
117136

118-
# 统一查询参数
119-
# 对于 UploadGraphAdapter, kgdb_name 通常通过 kwargs 传递
120-
# 对于 LightRAGGraphAdapter, max_depth/max_nodes 通过 kwargs 传递
121-
result_data = await adapter.query_nodes(
122-
keyword=node_label,
123-
max_depth=max_depth,
124-
max_nodes=max_nodes,
125-
kgdb_name=db_id if not knowledge_base.is_lightrag_database(db_id) else "neo4j",
126-
)
137+
# 统一查询参数 - 适配器会根据自己的配置处理这些参数
138+
query_kwargs = {
139+
"keyword": node_label,
140+
"max_depth": max_depth,
141+
"max_nodes": max_nodes,
142+
}
143+
144+
result_data = await adapter.query_nodes(**query_kwargs)
127145

128146
return {
129147
"success": True,
@@ -146,6 +164,7 @@ async def get_graph_labels(
146164
获取图谱的所有标签
147165
"""
148166
try:
167+
# 使用统一的适配器获取标签
149168
adapter = await _get_graph_adapter(db_id)
150169
labels = await adapter.get_labels()
151170
return {"success": True, "data": {"labels": labels}}
@@ -163,33 +182,13 @@ async def get_graph_stats(
163182
获取图谱统计信息
164183
"""
165184
try:
166-
if knowledge_base.is_lightrag_database(db_id):
167-
# 复用原有的 LightRAG 统计逻辑
168-
# 这里暂时直接调用原有逻辑,理想情况下也应该封装进 Adapter
169-
rag_instance = await knowledge_base._get_lightrag_instance(db_id)
170-
if not rag_instance:
171-
raise HTTPException(status_code=404, detail="Database not found")
172-
173-
knowledge_graph = await rag_instance.get_knowledge_graph(node_label="*", max_depth=1, max_nodes=10000)
174-
entity_types = {}
175-
for node in knowledge_graph.nodes:
176-
entity_type = node.properties.get("entity_type", "unknown")
177-
entity_types[entity_type] = entity_types.get(entity_type, 0) + 1
178-
179-
entity_types_list = [
180-
{"type": k, "count": v} for k, v in sorted(entity_types.items(), key=lambda x: x[1], reverse=True)
181-
]
182-
183-
return {
184-
"success": True,
185-
"data": {
186-
"total_nodes": len(knowledge_graph.nodes),
187-
"total_edges": len(knowledge_graph.edges),
188-
"entity_types": entity_types_list,
189-
},
190-
}
185+
# 使用适配器的统计信息 (适用于 kb_ 开头的数据库和 LightRAG 数据库)
186+
if db_id.startswith("kb_") or knowledge_base.is_lightrag_database(db_id):
187+
adapter = await _get_graph_adapter(db_id)
188+
stats_data = await adapter.get_stats()
189+
return {"success": True, "data": stats_data}
191190
else:
192-
# Neo4j stats
191+
# Neo4j stats (直接管理的图谱)
193192
info = graph_base.get_graph_info(graph_name=db_id)
194193
if not info:
195194
raise HTTPException(status_code=404, detail="Graph info not found")
@@ -316,6 +315,14 @@ async def add_neo4j_entities(
316315
):
317316
"""通过JSONL文件添加图谱实体到Neo4j"""
318317
try:
318+
# 验证文件路径
319+
if not file_path or not isinstance(file_path, str):
320+
return {"success": False, "message": "文件路径不能为空", "status": "failed"}
321+
322+
file_path = file_path.strip()
323+
if not file_path:
324+
return {"success": False, "message": "文件路径不能为空", "status": "failed"}
325+
319326
if not file_path.endswith(".jsonl"):
320327
return {"success": False, "message": "文件格式错误,请上传jsonl文件", "status": "failed"}
321328

src/knowledge/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
from ..config import config
44
from .factory import KnowledgeBaseFactory
5-
from .graph import GraphDatabase
65
from .implementations.chroma import ChromaKB
76
from .implementations.lightrag import LightRagKB
87
from .implementations.milvus import MilvusKB
98
from .manager import KnowledgeBaseManager
9+
from .services.upload_graph_service import UploadGraphService
1010

1111
# 注册知识库类型
1212
KnowledgeBaseFactory.register("chroma", ChromaKB, {"description": "基于 ChromaDB 的轻量级向量知识库,适合开发和小规模"})
@@ -18,6 +18,9 @@
1818
knowledge_base = KnowledgeBaseManager(work_dir)
1919

2020
# 创建图数据库实例
21-
graph_base = GraphDatabase()
21+
graph_base = UploadGraphService()
2222

23-
__all__ = ["GraphDatabase", "knowledge_base", "graph_base"]
23+
# 向后兼容:让 GraphDatabase 指向 UploadGraphService
24+
GraphDatabase = UploadGraphService
25+
26+
__all__ = ["GraphDatabase", "UploadGraphService", "knowledge_base", "graph_base"]

0 commit comments

Comments
 (0)