refactor(router): 更新系统路由

This commit is contained in:
Wenjie Zhang 2025-07-22 17:29:38 +08:00
parent 53b642ab6e
commit 9d9101a3e5
36 changed files with 1351 additions and 1234 deletions

View File

@ -36,7 +36,7 @@ services:
command: uv run uvicorn server.main:app --host 0.0.0.0 --port 5050 --reload
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:5050/api/health || exit 1"]
test: ["CMD-SHELL", "curl -f http://localhost:5050/api/system/health || exit 1"]
interval: 30s
timeout: 15s
retries: 8

View File

@ -1,13 +1,15 @@
from fastapi import APIRouter
from server.routers.chat_router import chat
from server.routers.data_router import data
from server.routers.base_router import base
from server.routers.system_router import system
from server.routers.auth_router import auth
from server.routers.chat_router import chat
from server.routers.knowledge_router import knowledge
from server.routers.graph_router import graph
router = APIRouter()
router.include_router(base)
router.include_router(chat)
router.include_router(data)
router.include_router(auth)
router.include_router(graph)
# 注册路由结构
router.include_router(system) # /api/system/*
router.include_router(auth) # /api/auth/*
router.include_router(chat) # /api/chat/*
router.include_router(knowledge) # /api/knowledge/*
router.include_router(graph) # /api/graph/*

View File

@ -10,7 +10,7 @@ from server.utils.auth_utils import AuthUtils
from server.utils.auth_middleware import get_db, get_current_user, get_admin_user, get_superadmin_user, oauth2_scheme
# 创建路由器
auth = APIRouter(prefix="/auth", tags=["auth"])
auth = APIRouter(prefix="/auth", tags=["authentication"])
# 请求和响应模型
class Token(BaseModel):
@ -41,6 +41,10 @@ class InitializeAdmin(BaseModel):
username: str
password: str
# =============================================================================
# === 工具函数 ===
# =============================================================================
# 记录操作日志
def log_operation(db: Session, user_id: int, operation: str, details: str = None, request: Request = None):
ip_address = None
@ -57,6 +61,10 @@ def log_operation(db: Session, user_id: int, operation: str, details: str = None
db.commit()
# 路由:登录获取令牌
# =============================================================================
# === 认证分组 ===
# =============================================================================
@auth.post("/token", response_model=Token)
async def login_for_access_token(
form_data: OAuth2PasswordRequestForm = Depends(),
@ -141,11 +149,19 @@ async def initialize_admin(
}
# 路由:获取当前用户信息
# =============================================================================
# === 用户信息分组 ===
# =============================================================================
@auth.get("/me", response_model=UserResponse)
async def read_users_me(current_user: User = Depends(get_current_user)):
return current_user.to_dict()
# 路由:创建新用户(管理员权限)
# =============================================================================
# === 用户管理分组 ===
# =============================================================================
@auth.post("/users", response_model=UserResponse)
async def create_user(
user_data: UserCreate,

View File

@ -21,7 +21,11 @@ from server.utils.auth_middleware import get_required_user, get_db
from server.models.user_model import User
from server.models.thread_model import Thread
chat = APIRouter(prefix="/chat")
chat = APIRouter(prefix="/chat", tags=["chat"])
# =============================================================================
# > === 智能体管理分组 ===
# =============================================================================
@chat.get("/default_agent")
async def get_default_agent(current_user: User = Depends(get_required_user)):
@ -62,6 +66,10 @@ async def set_default_agent(agent_id: str = Body(..., embed=True), current_user
logger.error(f"设置默认智能体出错: {e}")
raise HTTPException(status_code=500, detail=f"设置默认智能体出错: {str(e)}")
# =============================================================================
# > === 对话分组 ===
# =============================================================================
@chat.get("/")
async def chat_get(current_user: User = Depends(get_required_user)):
"""聊天服务健康检查(需要登录)"""
@ -155,6 +163,10 @@ async def chat_agent(agent_name: str,
return StreamingResponse(stream_messages(), media_type='application/json')
# =============================================================================
# > === 模型管理分组 ===
# =============================================================================
@chat.get("/models")
async def get_chat_models(model_provider: str, current_user: User = Depends(get_admin_user)):
"""获取指定模型提供商的模型列表(需要登录)"""
@ -257,6 +269,10 @@ class ThreadResponse(BaseModel):
update_at: str
# =============================================================================
# > === 会话管理分组 ===
# =============================================================================
@chat.post("/thread", response_model=ThreadResponse)
async def create_thread(
thread: ThreadCreate,

View File

@ -1,16 +1,20 @@
import traceback
from fastapi import APIRouter, Query, HTTPException, Depends
from fastapi import APIRouter, Query, HTTPException, Depends, Body
from server.utils.auth_middleware import get_admin_user
from server.models.user_model import User
from src import knowledge_base
from src import knowledge_base, graph_base
from src.utils.logging_config import logger
graph = APIRouter()
graph = APIRouter(prefix="/graph", tags=["graph"])
@graph.get("/graph/subgraph")
async def get_subgraph(
# =============================================================================
# === 子图查询分组 ===
# =============================================================================
@graph.get("/lightrag/subgraph")
async def get_lightrag_subgraph(
db_id: str = Query(..., description="数据库ID"),
node_label: str = Query(..., description="节点标签或实体名称"),
max_depth: int = Query(2, description="最大深度", ge=1, le=5),
@ -94,8 +98,35 @@ async def get_subgraph(
raise HTTPException(status_code=500, detail=f"获取子图数据失败: {str(e)}")
@graph.get("/graph/labels")
async def get_graph_labels(
@graph.get("/lightrag/databases")
async def get_lightrag_databases(
current_user: User = Depends(get_admin_user)
):
"""
获取所有可用的 LightRAG 数据库
Returns:
可用的 LightRAG 数据库列表
"""
try:
lightrag_databases = knowledge_base.get_lightrag_databases()
return {
"success": True,
"data": {
"databases": lightrag_databases
}
}
except Exception as e:
logger.error(f"获取 LightRAG 数据库列表失败: {e}")
raise HTTPException(status_code=500, detail=f"获取 LightRAG 数据库列表失败: {str(e)}")
# =============================================================================
# === 节点管理分组 ===
# =============================================================================
@graph.get("/lightrag/labels")
async def get_lightrag_labels(
db_id: str = Query(..., description="数据库ID"),
current_user: User = Depends(get_admin_user)
):
@ -142,92 +173,71 @@ async def get_graph_labels(
raise HTTPException(status_code=500, detail=f"获取图谱标签失败: {str(e)}")
@graph.get("/graph/databases")
async def get_available_databases(
@graph.get("/neo4j/nodes")
async def get_neo4j_nodes(
kgdb_name: str = Query(..., description="知识图谱数据库名称"),
num: int = Query(100, description="节点数量", ge=1, le=1000),
current_user: User = Depends(get_admin_user)
):
"""
获取所有可用的 LightRAG 数据库
Returns:
可用的 LightRAG 数据库列表
获取图谱节点样本数据
"""
try:
lightrag_databases = knowledge_base.get_lightrag_databases()
logger.debug(f"Get graph nodes in {kgdb_name} with {num} nodes")
if not graph_base.is_running():
raise HTTPException(status_code=400, detail="图数据库未启动")
result = graph_base.get_sample_nodes(kgdb_name, num)
formatted_result = graph_base.format_general_results(result)
return {
"success": True,
"data": {
"databases": lightrag_databases
}
}
except Exception as e:
logger.error(f"获取 LightRAG 数据库列表失败: {e}")
raise HTTPException(status_code=500, detail=f"获取 LightRAG 数据库列表失败: {str(e)}")
# 保留原有的直接数据库查询方法作为备用(如果需要的话)
@graph.get("/graph/nodes")
async def get_graph_nodes_legacy(
db_id: str = Query(..., description="数据库ID"),
limit: int = Query(500, description="最大节点数量", ge=1, le=2000),
offset: int = Query(0, description="偏移量", ge=0),
entity_type: str | None = Query(None, description="实体类型筛选"),
search: str | None = Query(None, description="搜索关键词"),
current_user: User = Depends(get_admin_user)
):
"""
直接查询数据库获取节点数据备用方法
建议使用 /graph/subgraph 接口
"""
try:
# 这里可以添加直接数据库查询的逻辑
# 但建议用户使用 get_subgraph 接口
return {
"success": False,
"message": "建议使用 /graph/subgraph 接口获取图谱数据",
"data": {
"nodes": [],
"total": 0
}
"result": formatted_result,
"message": "success"
}
except Exception as e:
logger.error(f"获取图节点数据失败: {e}")
raise HTTPException(status_code=500, detail=f"获取图节点数据失败: {str(e)}")
@graph.get("/graph/edges")
async def get_graph_edges_legacy(
db_id: str = Query(..., description="数据库ID"),
limit: int = Query(500, description="最大边数量", ge=1, le=2000),
offset: int = Query(0, description="偏移量", ge=0),
min_weight: float | None = Query(None, description="最小权重筛选"),
@graph.get("/neo4j/node")
async def get_neo4j_node(
entity_name: str = Query(..., description="实体名称"),
current_user: User = Depends(get_admin_user)
):
"""
直接查询数据库获取边数据备用方法
建议使用 /graph/subgraph 接口
根据实体名称查询图节点
"""
try:
# 这里可以添加直接数据库查询的逻辑
# 但建议用户使用 get_subgraph 接口
if not graph_base.is_running():
raise HTTPException(status_code=400, detail="图数据库未启动")
result = graph_base.query_node(entity_name=entity_name)
formatted_result = graph_base.format_query_result_to_graph(result)
return {
"success": False,
"message": "建议使用 /graph/subgraph 接口获取图谱数据",
"data": {
"edges": [],
"total": 0
}
"success": True,
"result": formatted_result,
"message": "success"
}
except Exception as e:
logger.error(f"获取图边数据失败: {e}")
raise HTTPException(status_code=500, detail=f"获取图边数据失败: {str(e)}")
logger.error(f"查询图节点失败: {e}")
raise HTTPException(status_code=500, detail=f"查询图节点失败: {str(e)}")
# =============================================================================
# === 边管理分组 ===
# =============================================================================
@graph.get("/graph/stats")
async def get_graph_stats(
# 可以在这里添加边相关的管理功能
# =============================================================================
# === 图谱分析分组 ===
# =============================================================================
@graph.get("/lightrag/stats")
async def get_lightrag_stats(
db_id: str = Query(..., description="数据库ID"),
current_user: User = Depends(get_admin_user)
):
@ -284,3 +294,82 @@ async def get_graph_stats(
logger.error(f"获取图谱统计信息失败: {e}")
logger.error(f"Traceback: {traceback.format_exc()}")
raise HTTPException(status_code=500, detail=f"获取图谱统计信息失败: {str(e)}")
@graph.get("/neo4j/info")
async def get_neo4j_info(current_user: User = Depends(get_admin_user)):
"""获取Neo4j图数据库信息"""
try:
graph_info = graph_base.get_graph_info()
if graph_info is None:
raise HTTPException(status_code=400, detail="图数据库获取出错")
return {
"success": True,
"data": graph_info
}
except Exception as e:
logger.error(f"获取图数据库信息失败: {e}")
raise HTTPException(status_code=500, detail=f"获取图数据库信息失败: {str(e)}")
@graph.post("/neo4j/index-entities")
async def index_neo4j_entities(
data: dict = Body(default={}),
current_user: User = Depends(get_admin_user)
):
"""为Neo4j图谱节点添加嵌入向量索引"""
try:
if not graph_base.is_running():
raise HTTPException(status_code=400, detail="图数据库未启动")
# 获取参数或使用默认值
kgdb_name = data.get('kgdb_name', 'neo4j')
# 调用GraphDatabase的add_embedding_to_nodes方法
count = graph_base.add_embedding_to_nodes(kgdb_name=kgdb_name)
return {
"success": True,
"status": "success",
"message": f"已成功为{count}个节点添加嵌入向量",
"indexed_count": count
}
except Exception as e:
logger.error(f"索引节点失败: {e}")
raise HTTPException(status_code=500, detail=f"索引节点失败: {str(e)}")
@graph.post("/neo4j/add-entities")
async def add_neo4j_entities(
file_path: str = Body(...),
kgdb_name: str | None = Body(None),
current_user: User = Depends(get_admin_user)
):
"""通过JSONL文件添加图谱实体到Neo4j"""
try:
if not file_path.endswith('.jsonl'):
return {
"success": False,
"message": "文件格式错误请上传jsonl文件",
"status": "failed"
}
await graph_base.jsonl_file_add_entity(file_path, kgdb_name)
return {
"success": True,
"message": "实体添加成功",
"status": "success"
}
except Exception as e:
logger.error(f"添加实体失败: {e}, {traceback.format_exc()}")
return {
"success": False,
"message": f"添加实体失败: {e}",
"status": "failed"
}
# =============================================================================
# === 兼容性接口 (保持向后兼容) ===
# =============================================================================
@graph.get("/graph")
async def get_graph_info_compat(current_user: User = Depends(get_admin_user)):
"""兼容性接口:获取图数据库信息"""
return await get_graph_info(current_user)

View File

@ -4,44 +4,182 @@ import traceback
from fastapi import APIRouter, File, UploadFile, HTTPException, Depends, Body, Form, Query
from src.utils import logger, hashstr
from src import executor, config, knowledge_base, graph_base
from src import executor, config, knowledge_base
from server.utils.auth_middleware import get_admin_user
from server.models.user_model import User
data = APIRouter(prefix="/data")
knowledge = APIRouter(prefix="/knowledge", tags=["knowledge"])
# =============================================================================
# === 数据库管理分组 ===
# =============================================================================
@data.get("/")
@knowledge.get("/databases")
async def get_databases(current_user: User = Depends(get_admin_user)):
"""获取所有知识库"""
try:
database = knowledge_base.get_databases()
return database
except Exception as e:
logger.error(f"获取数据库列表失败 {e}, {traceback.format_exc()}")
return {"message": f"获取数据库列表失败 {e}", "databases": []}
@knowledge.post("/databases")
async def create_database(
database_name: str = Body(...),
description: str = Body(...),
embed_model_name: str = Body(...),
kb_type: str = Body("lightrag"),
current_user: User = Depends(get_admin_user)
):
"""创建知识库"""
logger.debug(f"Create database {database_name} with kb_type {kb_type}")
try:
embed_info = config.embed_model_names[embed_model_name]
database_info = knowledge_base.create_database(
database_name,
description,
kb_type=kb_type,
embed_info=embed_info
)
return database_info
except Exception as e:
logger.error(f"创建数据库失败 {e}, {traceback.format_exc()}")
return {"message": f"创建数据库失败 {e}", "status": "failed"}
@knowledge.get("/databases/{db_id}")
async def get_database_info(db_id: str, current_user: User = Depends(get_admin_user)):
"""获取知识库详细信息"""
database = knowledge_base.get_database_info(db_id)
if database is None:
raise HTTPException(status_code=404, detail="Database not found")
return database
@data.get("/kb-types")
async def get_knowledge_base_types(current_user: User = Depends(get_admin_user)):
"""获取支持的知识库类型"""
@knowledge.put("/databases/{db_id}")
async def update_database_info(
db_id: str,
name: str = Body(...),
description: str = Body(...),
current_user: User = Depends(get_admin_user)
):
"""更新知识库信息"""
logger.debug(f"Update database {db_id} info: {name}, {description}")
try:
kb_types = knowledge_base.get_supported_kb_types()
return {"kb_types": kb_types, "message": "success"}
database = knowledge_base.update_database(db_id, name, description)
return {"message": "更新成功", "database": database}
except Exception as e:
logger.error(f"获取知识库类型失败 {e}, {traceback.format_exc()}")
return {"message": f"获取知识库类型失败 {e}", "kb_types": {}}
logger.error(f"更新数据库失败 {e}, {traceback.format_exc()}")
raise HTTPException(status_code=400, detail=f"更新数据库失败: {e}")
@data.get("/stats")
async def get_knowledge_base_statistics(current_user: User = Depends(get_admin_user)):
"""获取知识库统计信息"""
@knowledge.delete("/databases/{db_id}")
async def delete_database(db_id: str, current_user: User = Depends(get_admin_user)):
"""删除知识库"""
logger.debug(f"Delete database {db_id}")
try:
stats = knowledge_base.get_statistics()
return {"stats": stats, "message": "success"}
knowledge_base.delete_database(db_id)
return {"message": "删除成功"}
except Exception as e:
logger.error(f"获取知识库统计失败 {e}, {traceback.format_exc()}")
return {"message": f"获取知识库统计失败 {e}", "stats": {}}
logger.error(f"删除数据库失败 {e}, {traceback.format_exc()}")
raise HTTPException(status_code=400, detail=f"删除数据库失败: {e}")
@data.get("/query-params/{db_id}")
async def get_knowledge_base_query_params(db_id: str, current_user: User = Depends(get_admin_user)):
# =============================================================================
# === 文档管理分组 ===
# =============================================================================
@knowledge.post("/databases/{db_id}/documents")
async def add_documents(
db_id: str,
items: list[str] = Body(...),
params: dict = Body(...),
current_user: User = Depends(get_admin_user)
):
"""添加文档到知识库"""
logger.debug(f"Add documents for db_id {db_id}: {items} {params=}")
content_type = params.get('content_type', 'file')
try:
processed_items = await knowledge_base.add_content(db_id, items, params=params)
item_type = "URLs" if content_type == 'url' else "files"
processed_failed_count = len([_p for _p in processed_items if _p['status'] == 'failed'])
processed_info = f"Processed {len(processed_items)} {item_type}, {processed_failed_count} {item_type} failed"
return {"message": processed_info, "items": processed_items, "status": "success"}
except Exception as e:
logger.error(f"Failed to process {content_type}s: {e}, {traceback.format_exc()}")
return {"message": f"Failed to process {content_type}s: {e}", "status": "failed"}
@knowledge.get("/databases/{db_id}/documents/{doc_id}")
async def get_document_info(
db_id: str,
doc_id: str,
current_user: User = Depends(get_admin_user)
):
"""获取文档详细信息"""
logger.debug(f"GET document {doc_id} info in {db_id}")
try:
info = await knowledge_base.get_file_info(db_id, doc_id)
return info
except Exception as e:
logger.error(f"Failed to get file info, {e}, {db_id=}, {doc_id=}, {traceback.format_exc()}")
return {"message": "Failed to get file info", "status": "failed"}
@knowledge.delete("/databases/{db_id}/documents/{doc_id}")
async def delete_document(
db_id: str,
doc_id: str,
current_user: User = Depends(get_admin_user)
):
"""删除文档"""
logger.debug(f"DELETE document {doc_id} info in {db_id}")
try:
await knowledge_base.delete_file(db_id, doc_id)
return {"message": "删除成功"}
except Exception as e:
logger.error(f"删除文档失败 {e}, {traceback.format_exc()}")
raise HTTPException(status_code=400, detail=f"删除文档失败: {e}")
# =============================================================================
# === 查询分组 ===
# =============================================================================
@knowledge.post("/databases/{db_id}/query")
async def query_knowledge_base(
db_id: str,
query: str = Body(...),
meta: dict = Body(...),
current_user: User = Depends(get_admin_user)
):
"""查询知识库"""
logger.debug(f"Query knowledge base {db_id}: {query}")
try:
result = await knowledge_base.aquery(query, db_id=db_id, **meta)
return {"result": result, "status": "success"}
except Exception as e:
logger.error(f"知识库查询失败 {e}, {traceback.format_exc()}")
return {"message": f"知识库查询失败: {e}", "status": "failed"}
@knowledge.post("/databases/{db_id}/query-test")
async def query_test(
db_id: str,
query: str = Body(...),
meta: dict = Body(...),
current_user: User = Depends(get_admin_user)
):
"""测试查询知识库"""
logger.debug(f"Query test in {db_id}: {query}")
try:
result = await knowledge_base.aquery(query, db_id=db_id, **meta)
return result
except Exception as e:
logger.error(f"测试查询失败 {e}, {traceback.format_exc()}")
return {"message": f"测试查询失败: {e}", "status": "failed"}
@knowledge.get("/databases/{db_id}/query-params")
async def get_knowledge_base_query_params(
db_id: str,
current_user: User = Depends(get_admin_user)
):
"""获取知识库类型特定的查询参数"""
try:
# 获取数据库信息
@ -193,113 +331,17 @@ async def get_knowledge_base_query_params(db_id: str, current_user: User = Depen
logger.error(f"获取知识库查询参数失败 {e}, {traceback.format_exc()}")
return {"message": f"获取知识库查询参数失败 {e}", "params": {}}
@data.post("/")
async def create_database(
database_name: str = Body(...),
description: str = Body(...),
embed_model_name: str = Body(...),
kb_type: str = Body("lightrag"), # 新增知识库类型参数默认为lightrag
current_user: User = Depends(get_admin_user)
):
logger.debug(f"Create database {database_name} with kb_type {kb_type}")
try:
embed_info = config.embed_model_names[embed_model_name]
database_info = knowledge_base.create_database(
database_name,
description,
kb_type=kb_type, # 传递知识库类型
embed_info=embed_info
)
except Exception as e:
logger.error(f"创建数据库失败 {e}, {traceback.format_exc()}")
return {"message": f"创建数据库失败 {e}", "status": "failed"}
return database_info
# =============================================================================
# === 文件管理分组 ===
# =============================================================================
@data.delete("/")
async def delete_database(db_id, current_user: User = Depends(get_admin_user)):
logger.debug(f"Delete database {db_id}")
knowledge_base.delete_database(db_id)
return {"message": "删除成功"}
@data.post("/query-test")
async def query_test(query: str = Body(...), meta: dict = Body(...), current_user: User = Depends(get_admin_user)):
logger.debug(f"Query test in {meta}: {query}")
result = await knowledge_base.aquery(query, **meta)
return result
@data.post("/add-files")
async def add_files(db_id: str = Body(...), items: list[str] = Body(...), params: dict = Body(...), current_user: User = Depends(get_admin_user)):
logger.debug(f"Add files/urls for db_id {db_id}: {items} {params=}")
# 从 params 中获取 content_type默认为 'file'
content_type = params.get('content_type', 'file')
try:
# 使用统一的 add_content 方法
processed_items = await knowledge_base.add_content(db_id, items, params=params)
item_type = "URLs" if content_type == 'url' else "files"
processed_failed_count = len([_p for _p in processed_items if _p['status'] == 'failed'])
processed_info = f"Processed {len(processed_items)} {item_type}, {processed_failed_count} {item_type} failed"
return {"message": processed_info, "items": processed_items, "status": "success"}
except Exception as e:
logger.error(f"Failed to process {content_type}s: {e}, {traceback.format_exc()}")
return {"message": f"Failed to process {content_type}s: {e}", "status": "failed"}
@data.post("/file-to-chunk")
async def file_to_chunk(db_id: str = Body(...), files: list[str] = Body(...), params: dict = Body(...), current_user: User = Depends(get_admin_user)):
logger.debug(f"File to chunk for db_id {db_id}: {files} {params=} (deprecated, use /add-files)")
# 兼容性路由,转发到新的统一接口
params['content_type'] = 'file'
return await add_files(db_id, files, params, current_user)
@data.post("/url-to-chunk")
async def url_to_chunk(db_id: str = Body(...), urls: list[str] = Body(...), params: dict = Body(...), current_user: User = Depends(get_admin_user)):
logger.debug(f"Url to chunk for db_id {db_id}: {urls} {params=} (deprecated, use /add-files)")
# 兼容性路由,转发到新的统一接口
params['content_type'] = 'url'
return await add_files(db_id, urls, params, current_user)
@data.post("/add-by-file")
async def create_document_by_file(db_id: str = Body(...), files: list[str] = Body(...), current_user: User = Depends(get_admin_user)):
raise ValueError("This method is deprecated. Use /add-files instead.")
@data.post("/add-by-chunks")
async def add_by_chunks(db_id: str = Body(...), file_chunks: dict = Body(...), current_user: User = Depends(get_admin_user)):
raise ValueError("This method is deprecated. Use /add-files instead.")
@data.get("/info")
async def get_database_info(db_id: str, current_user: User = Depends(get_admin_user)):
# logger.debug(f"Get database {db_id} info")
database = knowledge_base.get_database_info(db_id)
if database is None:
raise HTTPException(status_code=404, detail="Database not found")
return database
@data.delete("/document")
async def delete_document(db_id: str = Body(...), file_id: str = Body(...), current_user: User = Depends(get_admin_user)):
logger.debug(f"DELETE document {file_id} info in {db_id}")
await knowledge_base.delete_file(db_id, file_id)
return {"message": "删除成功"}
@data.get("/document")
async def get_document_info(db_id: str, file_id: str, current_user: User = Depends(get_admin_user)):
logger.debug(f"GET document {file_id} info in {db_id}")
try:
info = await knowledge_base.get_file_info(db_id, file_id)
except Exception as e:
logger.error(f"Failed to get file info, {e}, {db_id=}, {file_id=}, {traceback.format_exc()}")
info = {"message": "Failed to get file info", "status": "failed"}
return info
@data.post("/upload")
@knowledge.post("/files/upload")
async def upload_file(
file: UploadFile = File(...),
db_id: str | None = Query(None),
current_user: User = Depends(get_admin_user)
):
"""上传文件"""
if not file.filename:
raise HTTPException(status_code=400, detail="No selected file")
@ -319,63 +361,27 @@ async def upload_file(
return {"message": "File successfully uploaded", "file_path": file_path, "db_id": db_id}
@data.get("/graph")
async def get_graph_info(current_user: User = Depends(get_admin_user)):
graph_info = graph_base.get_graph_info()
if graph_info is None:
raise HTTPException(status_code=400, detail="图数据库获取出错")
return graph_info
@data.post("/graph/index-nodes")
async def index_nodes(data: dict = Body(default={}), current_user: User = Depends(get_admin_user)):
if not graph_base.is_running():
raise HTTPException(status_code=400, detail="图数据库未启动")
# 获取参数或使用默认值
kgdb_name = data.get('kgdb_name', 'neo4j')
# 调用GraphDatabase的add_embedding_to_nodes方法
count = graph_base.add_embedding_to_nodes(kgdb_name=kgdb_name)
return {"status": "success", "message": f"已成功为{count}个节点添加嵌入向量", "indexed_count": count}
@data.get("/graph/node")
async def get_graph_node(entity_name: str, current_user: User = Depends(get_admin_user)):
result = graph_base.query_node(entity_name=entity_name)
return {"result": graph_base.format_query_result_to_graph(result), "message": "success"}
@data.get("/graph/nodes")
async def get_graph_nodes(kgdb_name: str, num: int, current_user: User = Depends(get_admin_user)):
logger.debug(f"Get graph nodes in {kgdb_name} with {num} nodes")
result = graph_base.get_sample_nodes(kgdb_name, num)
return {"result": graph_base.format_general_results(result), "message": "success"}
@data.post("/graph/add-by-jsonl")
async def add_graph_entity(file_path: str = Body(...), kgdb_name: str | None = Body(None), current_user: User = Depends(get_admin_user)):
if not file_path.endswith('.jsonl'):
return {"message": "文件格式错误请上传jsonl文件", "status": "failed"}
# =============================================================================
# === 知识库类型分组 ===
# =============================================================================
@knowledge.get("/types")
async def get_knowledge_base_types(current_user: User = Depends(get_admin_user)):
"""获取支持的知识库类型"""
try:
await graph_base.jsonl_file_add_entity(file_path, kgdb_name)
return {"message": "实体添加成功", "status": "success"}
kb_types = knowledge_base.get_supported_kb_types()
return {"kb_types": kb_types, "message": "success"}
except Exception as e:
logger.error(f"添加实体失败: {e}, {traceback.format_exc()}")
return {"message": f"添加实体失败: {e}", "status": "failed"}
logger.error(f"获取知识库类型失败 {e}, {traceback.format_exc()}")
return {"message": f"获取知识库类型失败 {e}", "kb_types": {}}
@data.post("/update")
async def update_database_info(
db_id: str = Body(...),
name: str = Body(...),
description: str = Body(...),
current_user: User = Depends(get_admin_user)
):
logger.debug(f"Update database {db_id} info: {name}, {description}")
@knowledge.get("/stats")
async def get_knowledge_base_statistics(current_user: User = Depends(get_admin_user)):
"""获取知识库统计信息"""
try:
database = knowledge_base.update_database(db_id, name, description)
return {"message": "更新成功", "database": database}
stats = knowledge_base.get_statistics()
return {"stats": stats, "message": "success"}
except Exception as e:
logger.error(f"更新数据库失败 {e}, {traceback.format_exc()}")
raise HTTPException(status_code=400, detail=f"更新数据库失败: {e}")
logger.error(f"获取知识库统计失败 {e}, {traceback.format_exc()}")
return {"message": f"获取知识库统计失败 {e}", "stats": {}}

View File

@ -1,10 +1,10 @@
import os
import yaml
import asyncio
import requests
from pathlib import Path
from fastapi import Request, Body, Depends, HTTPException
from fastapi import APIRouter
from collections import deque
from src import config, knowledge_base, graph_base
from server.utils.auth_middleware import get_admin_user, get_superadmin_user
@ -12,7 +12,71 @@ from server.models.user_model import User
from src.utils.logging_config import logger
base = APIRouter()
system = APIRouter(prefix="/system", tags=["system"])
# =============================================================================
# === 健康检查分组 ===
# =============================================================================
@system.get("/health")
async def health_check():
"""系统健康检查接口(公开接口)"""
return {"status": "ok", "message": "服务正常运行"}
# =============================================================================
# === 配置管理分组 ===
# =============================================================================
@system.get("/config")
def get_config(current_user: User = Depends(get_admin_user)):
"""获取系统配置"""
return config.dump_config()
@system.post("/config")
async def update_config_single(
key = Body(...),
value = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
"""更新单个配置项"""
config[key] = value
config.save()
return config.dump_config()
@system.post("/config/update")
async def update_config_batch(
items: dict = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
"""批量更新配置项"""
config.update(items)
config.save()
return config.dump_config()
@system.post("/restart")
async def restart_system(current_user: User = Depends(get_superadmin_user)):
"""重启系统(仅超级管理员)"""
graph_base.start()
return {"message": "系统已重启"}
@system.get("/logs")
def get_system_logs(current_user: User = Depends(get_admin_user)):
"""获取系统日志"""
try:
from src.utils.logging_config import LOG_FILE
with open(LOG_FILE) as f:
last_lines = deque(f, maxlen=1000)
log = ''.join(last_lines)
return {"log": log, "message": "success", "log_file": LOG_FILE}
except Exception as e:
logger.error(f"获取系统日志失败: {e}")
raise HTTPException(status_code=500, detail=f"获取系统日志失败: {str(e)}")
# =============================================================================
# === 信息管理分组 ===
# =============================================================================
def load_info_config():
"""加载信息配置文件"""
@ -59,55 +123,7 @@ def get_default_info_config():
}
}
@base.get("/")
async def route_index():
return {"message": "You Got It!"}
@base.get("/health")
async def health_check():
"""简单的健康检查接口"""
return {"status": "ok", "message": "服务正常运行"}
@base.get("/config")
def get_config(current_user: User = Depends(get_admin_user)):
return config.dump_config()
@base.post("/config")
async def update_config(
key = Body(...),
value = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
config[key] = value
config.save()
return config.dump_config()
@base.post("/config/update")
async def update_config_item(
items: dict = Body(...),
current_user: User = Depends(get_admin_user)
) -> dict:
config.update(items)
config.save()
return config.dump_config()
@base.post("/restart")
async def restart(current_user: User = Depends(get_superadmin_user)):
graph_base.start()
return {"message": "Restarted!"}
@base.get("/log")
def get_log(current_user: User = Depends(get_admin_user)):
from src.utils.logging_config import LOG_FILE
from collections import deque
with open(LOG_FILE) as f:
last_lines = deque(f, maxlen=1000)
log = ''.join(last_lines)
return {"log": log, "message": "success", "log_file": LOG_FILE}
@base.get("/info")
@system.get("/info")
async def get_info_config():
"""获取系统信息配置(公开接口,无需认证)"""
try:
@ -120,10 +136,9 @@ async def get_info_config():
logger.error(f"获取信息配置失败: {e}")
raise HTTPException(status_code=500, detail="获取信息配置失败")
@base.get("/info/reload")
async def reload_info_config():
"""重新加载信息配置(管理员接口)"""
# 注:这里暂时不添加权限验证,后续可以根据需要添加
@system.post("/info/reload")
async def reload_info_config(current_user: User = Depends(get_admin_user)):
"""重新加载信息配置"""
try:
config = load_info_config()
return {
@ -135,7 +150,35 @@ async def reload_info_config():
logger.error(f"重新加载信息配置失败: {e}")
raise HTTPException(status_code=500, detail="重新加载信息配置失败")
@base.get("/ocr/health")
# =============================================================================
# === OCR服务分组 ===
# =============================================================================
@system.get("/ocr/stats")
async def get_ocr_stats(current_user: User = Depends(get_admin_user)):
"""
获取OCR服务使用统计信息
返回各个OCR服务的处理统计和性能指标
"""
try:
from src.plugins._ocr import get_ocr_stats
stats = get_ocr_stats()
return {
"status": "success",
"stats": stats,
"message": "OCR统计信息获取成功"
}
except Exception as e:
logger.error(f"获取OCR统计信息失败: {str(e)}")
return {
"status": "error",
"stats": {},
"message": f"获取OCR统计信息失败: {str(e)}"
}
@system.get("/ocr/health")
async def check_ocr_services_health(current_user: User = Depends(get_admin_user)):
"""
检查所有OCR服务的健康状态
@ -218,28 +261,3 @@ async def check_ocr_services_health(current_user: User = Depends(get_admin_user)
"services": health_status,
"message": "OCR服务健康检查完成"
}
@base.get("/ocr/stats")
async def get_ocr_stats(current_user: User = Depends(get_admin_user)):
"""
获取OCR服务使用统计信息
返回各个OCR服务的处理统计和性能指标
"""
try:
from src.plugins._ocr import get_ocr_stats
stats = get_ocr_stats()
return {
"status": "success",
"stats": stats,
"message": "OCR统计信息获取成功"
}
except Exception as e:
logger.error(f"获取OCR统计信息失败: {str(e)}")
return {
"status": "error",
"stats": {},
"message": f"获取OCR统计信息失败: {str(e)}"
}

View File

@ -17,9 +17,8 @@ PUBLIC_PATHS = [
r"^/api/auth/check-first-run$", # 检查是否首次运行
r"^/api/auth/initialize$", # 初始化系统
r"^/api$", # Health Check
r"^/api/login$", # 登录页面
r"^/api/info$", # 获取系统信息配置
r"^/api/info/.*$", # 系统信息配置相关接口
r"^/api/system/health$", # Health Check
r"^/api/system/info$", # 获取系统信息配置
]
# 获取数据库会话

View File

@ -1,7 +1,8 @@
import asyncio
from .chatbot import ChatbotAgent
from .react import ReActAgent
from .react.graph import ReActAgent
from .open_deep_research.graph import OpenDeepResearchAgent
class AgentManager:
def __init__(self):
@ -34,6 +35,7 @@ class AgentManager:
agent_manager = AgentManager()
agent_manager.register_agent(ChatbotAgent)
agent_manager.register_agent(ReActAgent)
agent_manager.register_agent(OpenDeepResearchAgent)
agent_manager.init_all_agents()
__all__ = ["agent_manager"]

View File

@ -18,7 +18,7 @@ from src.agents.chatbot.configuration import ChatbotConfiguration
from src.agents.tools_factory import get_all_tools
class ChatbotAgent(BaseAgent):
name = "chatbot"
name = "对话机器人Chatbot"
description = "基础的对话机器人,可以回答问题,默认不使用任何工具,可在配置中启用需要的工具。"
requirements = ["TAVILY_API_KEY", "ZHIPUAI_API_KEY"]
config_schema = ChatbotConfiguration

View File

@ -1,4 +0,0 @@
from .graph import ReActAgent
from .configuration import ReActConfiguration
__all__ = ["ReActAgent", "ReActConfiguration"]

View File

@ -1,12 +0,0 @@
from dataclasses import dataclass
from src.agents.registry import Configuration
@dataclass(kw_only=True)
class ReActConfiguration(Configuration):
"""配置"""
pass

View File

@ -1,23 +1,9 @@
import asyncio
import uuid
from src.utils import logger
from src.agents.registry import BaseAgent
from src.agents.react.configuration import ReActConfiguration
class ReActAgent(BaseAgent):
name = "ReAct"
description = "A react agent that can answer questions and help with tasks."
config_schema = ReActConfiguration
async def get_graph(self, **kwargs):
from .workflows import graph
return graph
if __name__ == "__main__":
pass
return graph

View File

@ -8,6 +8,7 @@ from typing import Annotated, TypedDict, Optional, Any
from abc import abstractmethod
from dataclasses import dataclass, fields, field
from pydantic import BaseModel, Field
from langchain_core.runnables import RunnableConfig
from langchain_core.messages import BaseMessage
from langgraph.graph.state import CompiledStateGraph
@ -158,6 +159,123 @@ class Configuration(dict):
class BaseModelConfiguration(BaseModel):
thread_id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
metadata={
"title": "线程ID",
"description": "用来描述智能体的角色和行为",
"configurable": False,
},
)
user_id: str = Field(
default_factory=lambda: str(uuid.uuid4()),
metadata={
"title": "用户ID",
"description": "用来描述智能体的角色和行为",
"configurable": False,
},
)
@classmethod
def from_runnable_config(
cls,
config: Optional["RunnableConfig"] = None,
agent_name: Optional[str] = None,
) -> "BaseModelConfiguration":
"""
RunnableConfig YAML 文件中构建 Configuration 实例
"""
# 默认配置
default_instance = cls()
default_values = default_instance.dict()
# 文件配置
file_config = cls.from_file(agent_name) if agent_name else {}
# 运行时配置(最高优先级)
runtime_config = config.get("configurable") if config else {}
merged_config = {
**default_values,
**file_config,
**runtime_config,
}
return cls(**merged_config)
@classmethod
def from_file(cls, agent_name: str) -> dict[str, Any]:
"""
YAML 文件加载配置
"""
config_file_path = Path(f"src/agents/{agent_name}/config.private.yaml")
if os.path.exists(config_file_path):
try:
with open(config_file_path, encoding="utf-8") as f:
return yaml.safe_load(f) or {}
except Exception as e:
logger.error(f"加载配置文件失败: {e}")
return {}
@classmethod
def save_to_file(cls, config: dict, agent_name: str) -> bool:
"""
保存配置到 YAML 文件
"""
try:
config_file_path = Path(f"src/agents/{agent_name}/config.private.yaml")
os.makedirs(config_file_path.parent, exist_ok=True)
with open(config_file_path, "w", encoding="utf-8") as f:
yaml.dump(config, f, indent=2, allow_unicode=True)
return True
except Exception as e:
logger.error(f"保存配置文件失败: {e}")
return False
@classmethod
def to_dict(cls) -> dict[str, Any]:
"""
提取类字段的默认值与字段元数据主要用于前端动态生成配置项
"""
# 创建实例以获得 default_factory 值
instance = cls()
confs: dict[str, Any] = {}
configurable_items: dict[str, Any] = {}
for name, field in cls.model_fields.items():
value = getattr(instance, name)
confs[name] = value
# 安全地处理 Pydantic 字段元数据
field_metadata = {}
if hasattr(field, 'json_schema_extra') and field.json_schema_extra:
if isinstance(field.json_schema_extra, dict):
field_metadata = field.json_schema_extra['metadata']
elif isinstance(field.json_schema_extra, (list, tuple)):
# 在 Pydantic v2 中metadata 可能是列表,合并所有字典项
for item in field.json_schema_extra:
if isinstance(item, dict):
field_metadata.update(item['metadata'])
# 检查字段是否应该可配置 - 支持不同的元数据格式
if field_metadata.get("configurable", True):
configurable_items[name] = {
"type": field_metadata.get("type", field.annotation.__name__),
"name": field_metadata.get("name") or name,
"options": field_metadata.get("options") or [],
"default": field.default if field.default is not None else None,
"description": field_metadata.get("description") or "",
"x_oap_ui_config": field_metadata.get("x_oap_ui_config", {}),
}
confs["configurable_items"] = configurable_items
return confs
class BaseAgent:
"""

View File

@ -305,26 +305,26 @@ class KnowledgeBaseManager:
# TODO: 实现数据库迁移逻辑
raise NotImplementedError("Database migration not implemented yet")
def get_statistics(self) -> Dict:
"""获取统计信息"""
stats = {
"total_databases": len(self.global_databases_meta),
"kb_types": {},
"total_files": 0
}
def get_statistics(self) -> Dict:
"""获取统计信息"""
stats = {
"total_databases": len(self.global_databases_meta),
"kb_types": {},
"total_files": 0
}
# 按知识库类型统计
for db_meta in self.global_databases_meta.values():
kb_type = db_meta.get("kb_type", "lightrag")
if kb_type not in stats["kb_types"]:
stats["kb_types"][kb_type] = 0
stats["kb_types"][kb_type] += 1
# 按知识库类型统计
for db_meta in self.global_databases_meta.values():
kb_type = db_meta.get("kb_type", "lightrag")
if kb_type not in stats["kb_types"]:
stats["kb_types"][kb_type] = 0
stats["kb_types"][kb_type] += 1
# 统计文件总数
for kb_instance in self.kb_instances.values():
stats["total_files"] += len(kb_instance.files_meta)
# 统计文件总数
for kb_instance in self.kb_instances.values():
stats["total_files"] += len(kb_instance.files_meta)
return stats
return stats
# =============================================================================
# 兼容性方法 - 为了支持现有的 graph_router.py

View File

@ -1,413 +0,0 @@
import { apiGet, apiPost, apiPut, apiDelete } from './base'
import { useUserStore } from '@/stores/user'
/**
* 管理员API模块
* 只有管理员和超级管理员可以访问的API
* 权限要求: admin superadmin
*
* 注意: 请确保在使用这些API之前检查用户是否具有管理员权限
*/
// 检查当前用户是否有管理员权限
const checkAdminPermission = () => {
const userStore = useUserStore()
if (!userStore.isAdmin) {
throw new Error('需要管理员权限')
}
return true
}
// 检查当前用户是否有超级管理员权限
const checkSuperAdminPermission = () => {
const userStore = useUserStore()
if (!userStore.isSuperAdmin) {
throw new Error('需要超级管理员权限')
}
return true
}
// 用户管理API
export const userManagementApi = {
/**
* 获取用户列表
* @returns {Promise} - 用户列表
*/
getUsers: async () => {
checkAdminPermission()
return apiGet('/api/auth/users', {}, true)
},
/**
* 创建新用户
* @param {Object} userData - 用户数据
* @returns {Promise} - 创建结果
*/
createUser: async (userData) => {
checkAdminPermission()
return apiPost('/api/auth/users', userData, {}, true)
},
/**
* 更新用户
* @param {number} userId - 用户ID
* @param {Object} userData - 用户数据
* @returns {Promise} - 更新结果
*/
updateUser: async (userId, userData) => {
checkAdminPermission()
return apiPut(`/api/auth/users/${userId}`, userData, {}, true)
},
/**
* 删除用户
* @param {number} userId - 用户ID
* @returns {Promise} - 删除结果
*/
deleteUser: async (userId) => {
checkAdminPermission()
return apiDelete(`/api/auth/users/${userId}`, {}, true)
},
}
// 知识库管理API
export const knowledgeBaseApi = {
/**
* 获取所有知识库
* @returns {Promise} - 知识库列表
*/
getDatabases: async () => {
checkAdminPermission()
return apiGet('/api/data/', {}, true)
},
/**
* 创建知识库
* @param {Object} databaseData - 知识库数据 (包含database_name, description, embed_model_name, kb_type等)
* @returns {Promise} - 创建结果
*/
createDatabase: async (databaseData) => {
checkAdminPermission()
return apiPost('/api/data/', {
database_name: databaseData.database_name,
description: databaseData.description,
embed_model_name: databaseData.embed_model_name,
kb_type: databaseData.kb_type || 'lightrag', // 默认为lightrag类型
...databaseData.extra_config // 额外配置如Vector的chunk_size等
}, {}, true)
},
/**
* 获取知识库详情
* @param {string} dbId - 知识库ID
* @returns {Promise} - 知识库详情
*/
getDatabaseInfo: async (dbId) => {
checkAdminPermission()
return apiGet(`/api/data/info?db_id=${dbId}`, {}, true)
},
/**
* 删除知识库
* @param {string} dbId - 知识库ID
* @returns {Promise} - 删除结果
*/
deleteDatabase: async (dbId) => {
checkAdminPermission()
return apiDelete(`/api/data/?db_id=${dbId}`, {}, true)
},
/**
* 上传文件到知识库
* @param {FormData} formData - 包含文件的FormData
* @param {string} dbId - 知识库ID
* @returns {Promise} - 上传结果
*/
uploadFile: async (formData, dbId) => {
checkAdminPermission()
const userStore = useUserStore()
const authHeaders = userStore.getAuthHeaders()
return fetch(`/api/data/upload?db_id=${dbId}`, {
method: 'POST',
headers: {
...authHeaders
},
body: formData
}).then(res => {
if (!res.ok) {
throw new Error(`上传失败: ${res.status} ${res.statusText}`)
}
return res.json()
})
},
/**
* 删除文件
* @param {string} dbId - 知识库ID
* @param {string} fileId - 文件ID
* @returns {Promise} - 删除结果
*/
deleteFile: async (dbId, fileId) => {
checkAdminPermission()
return apiDelete('/api/data/document', {
body: JSON.stringify({ db_id: dbId, file_id: fileId })
}, true)
},
/**
* 添加文件或URL到知识库
* @param {Object} data - 包含 db_id, items (文件路径或URL列表), params (包含 content_type 等参数)
* @returns {Promise} - 处理结果
*/
addFiles: async (data) => { // data: { db_id, items, params }
checkAdminPermission()
return apiPost('/api/data/add-files', data, {}, true)
},
/**
* 将分块添加到数据库
* @param {Object} data - 包含db_id和file_chunks的数据
* @returns {Promise} - 添加结果
*/
addByChunks: async (data) => {
checkAdminPermission()
return apiPost('/api/data/add-by-chunks', data, {}, true)
},
/**
* 查询测试
* @param {Object} data - 查询参数
* @returns {Promise} - 查询结果
*/
queryTest: async (data) => {
checkAdminPermission()
return apiPost('/api/data/query-test', data, {}, true)
},
/**
* 获取文档详情
* @param {string} dbId - 知识库ID
* @param {string} fileId - 文件ID
* @returns {Promise} - 文档详情
*/
getDocumentDetail: async (dbId, fileId) => {
checkAdminPermission()
return apiGet(`/api/data/document?db_id=${dbId}&file_id=${fileId}`, {}, true)
},
/**
* 更新知识库信息
* @param {string} dbId - 知识库ID
* @param {Object} data - 包含name和description的数据对象
* @returns {Promise} - 更新结果
*/
updateDatabaseInfo: async (dbId, data) => {
checkAdminPermission()
return apiPost('/api/data/update', {
db_id: dbId,
...data
}, {}, true)
},
/**
* 获取支持的知识库类型
* @returns {Promise} - 支持的知识库类型列表
*/
getSupportedKbTypes: async () => {
checkAdminPermission()
return apiGet('/api/data/kb-types', {}, true)
},
/**
* 获取知识库统计信息
* @returns {Promise} - 知识库统计信息
*/
getKbStatistics: async () => {
checkAdminPermission()
return apiGet('/api/data/stats', {}, true)
},
/**
* 获取知识库类型特定的查询参数
* @param {string} dbId - 知识库ID
* @returns {Promise} - 查询参数配置
*/
getKbQueryParams: async (dbId) => {
checkAdminPermission()
return apiGet(`/api/data/query-params/${dbId}`, {}, true)
},
}
// 图数据库管理API
export const graphApi = {
/**
* 获取图数据库状态
* @returns {Promise} - 图数据库状态
*/
getGraphInfo: async () => {
checkAdminPermission()
return apiGet('/api/data/graph', {}, true)
},
/**
* 获取节点
* @param {string} dbName - 图数据库名称
* @param {number} num - 节点数量
* @returns {Promise} - 节点数据
*/
getNodes: async (dbName, num) => {
checkAdminPermission()
return apiGet(`/api/data/graph/nodes?kgdb_name=${dbName}&num=${num}`, {}, true)
},
/**
* 查询实体
* @param {string} entityName - 实体名称
* @returns {Promise} - 查询结果
*/
queryNode: async (entityName) => {
checkAdminPermission()
return apiGet(`/api/data/graph/node?entity_name=${entityName}`, {}, true)
},
/**
* 添加JSONL文件到图数据库
* @param {string} filePath - 文件路径
* @returns {Promise} - 添加结果
*/
addByJsonl: async (filePath) => {
checkAdminPermission()
return apiPost('/api/data/graph/add-by-jsonl', { file_path: filePath }, {}, true)
},
/**
* 为未索引节点添加索引
* @param {string} dbName - 图数据库名称
* @returns {Promise} - 索引结果
*/
indexNodes: async (dbName) => {
checkAdminPermission()
return apiPost('/api/data/graph/index-nodes', { kgdb_name: dbName }, {}, true)
},
}
// 系统配置API
export const systemConfigApi = {
/**
* 设置默认智能体
* @param {string} agentId - 智能体ID
* @returns {Promise} - 设置结果
*/
setDefaultAgent: async (agentId) => {
checkAdminPermission()
return apiPost('/api/chat/set_default_agent', { agent_id: agentId }, {}, true)
},
/**
* 获取系统配置
* @returns {Promise} - 系统配置
*/
getSystemConfig: async () => {
checkAdminPermission()
return apiGet('/api/config', {}, true)
},
/**
* 获取智能体配置
* @param {string} agentId - 智能体ID
* @returns {Promise} - 智能体配置
*/
getAgentConfig: async (agentId) => {
checkAdminPermission()
return apiGet(`/api/chat/agent/${agentId}/config`, {}, true)
},
/**
* 保存智能体配置
* @param {string} agentId - 智能体ID
* @param {Object} config - 配置内容
* @returns {Promise} - 保存结果
*/
saveAgentConfig: async (agentId, config) => {
checkAdminPermission()
return apiPost(`/api/chat/agent/${agentId}/config`, config, {}, true)
},
/**
* 更新某个配置
* @param {Object} items - 配置项
* @returns {Promise} - 更新结果
*/
updateConfigItems: async (items) => {
checkAdminPermission()
console.log("updateConfigItems", items)
return apiPost('/api/config/update', items, {}, true)
},
/**
* 重启服务
* @returns {Promise} - 重启结果
*/
restartServer: async () => {
checkSuperAdminPermission()
return apiPost('/api/restart', {}, {}, true)
}
}
// 日志API
export const logApi = {
/**
* 获取系统日志
* @param {Object} params - 日志查询参数
* @returns {Promise} - 日志数据
*/
getLogs: async (params = {}) => {
checkAdminPermission()
return apiGet('/api/log', { params }, true)
},
}
// 通用admin
export const adminApi = {
/**
* 获取所有智能体
* @param {Object} params - 查询参数
* @returns {Promise} - 查询结果
*/
adminGet: async (params, url) => {
checkAdminPermission()
return apiGet(url, { params }, true)
},
/**
* 更新某个配置
* @param {Object} items - 配置项
* @returns {Promise} - 更新结果
*/
adminPost: async (data, url) => {
checkAdminPermission()
return apiPost(url, data, {}, true)
},
}
// OCR服务管理API
export const ocrApi = {
/**
* 检查OCR服务健康状态
* @returns {Promise} - OCR服务健康状态信息
*/
checkHealth: async () => {
checkAdminPermission()
return apiGet('/api/ocr/health', {}, true)
},
/**
* 获取OCR服务使用统计
* @returns {Promise} - OCR服务统计信息
*/
getStats: async () => {
checkAdminPermission()
return apiGet('/api/ocr/stats', {}, true)
}
}

View File

@ -155,20 +155,3 @@ export function apiDelete(url, options = {}, requiresAuth = false) {
return apiRequest(url, { method: 'DELETE', ...options }, requiresAuth)
}
/**
* 健康检查API
*/
export const healthApi = {
/**
* 检查服务端健康状态
* @returns {Promise} - 健康检查结果
*/
async checkHealth() {
try {
const response = await apiGet('/api/health')
return { status: 'ok', data: response }
} catch (error) {
return { status: 'error', error: error.message }
}
}
}

View File

@ -1,203 +1,158 @@
import { apiGet } from './base'
import { apiGet, apiPost } from './base'
/**
* 图数据API调用 - 基于LightRAG的新接口
* 图数据库API模块
* 包含LightRAG图知识库和Neo4j图数据库两种接口
* 采用命名空间分组模式清晰区分接口类型
*/
/**
* 获取所有可用的数据库
* @returns {Promise} - 数据库列表
*/
export const getAvailableDatabases = async () => {
return await apiGet('/api/graph/databases', {}, true)
}
// =============================================================================
// === LightRAG图知识库接口分组 ===
// =============================================================================
/**
* 获取图标签列表
* @param {string} dbId - 数据库ID
* @returns {Promise} - 标签列表
*/
export const getGraphLabels = async (dbId) => {
if (!dbId) {
throw new Error('db_id is required')
}
export const lightragApi = {
/**
* 获取LightRAG知识图谱子图数据
* @param {Object} params - 查询参数
* @param {string} params.db_id - LightRAG数据库ID
* @param {string} params.node_label - 节点标签"*"获取全图
* @param {number} params.max_depth - 最大深度
* @param {number} params.max_nodes - 最大节点数
* @returns {Promise} - 子图数据
*/
getSubgraph: async (params) => {
const { db_id, node_label = "*", max_depth = 2, max_nodes = 100 } = params
const queryParams = new URLSearchParams({
db_id: dbId
})
if (!db_id) {
throw new Error('db_id is required')
}
return await apiGet(`/api/graph/labels?${queryParams.toString()}`, {}, true)
}
/**
* 获取子图数据 - 主要接口
* @param {Object} params - 查询参数
* @param {string} params.db_id - 数据库ID
* @param {string} params.node_label - 节点标签 (使用 "*" 获取全图)
* @param {number} params.max_depth - 最大深度
* @param {number} params.max_nodes - 最大节点数
* @returns {Promise} - 子图数据
*/
export const getSubgraph = async (params) => {
const { db_id, node_label = "*", max_depth = 2, max_nodes = 100 } = params
if (!db_id) {
throw new Error('db_id is required')
}
const queryParams = new URLSearchParams({
db_id: db_id,
node_label: node_label,
max_depth: max_depth.toString(),
max_nodes: max_nodes.toString()
})
return await apiGet(`/api/graph/subgraph?${queryParams.toString()}`, {}, true)
}
/**
* 获取图统计信息
* @param {string} dbId - 数据库ID
* @returns {Promise} - 统计数据
*/
export const getGraphStats = async (dbId) => {
if (!dbId) {
throw new Error('db_id is required')
}
const queryParams = new URLSearchParams({
db_id: dbId
})
return await apiGet(`/api/graph/stats?${queryParams.toString()}`, {}, true)
}
/**
* 获取完整图数据 - 使用新的子图接口
* @param {Object} params - 查询参数
* @param {string} params.db_id - 数据库ID
* @param {string} params.node_label - 节点标签筛选
* @param {number} params.max_nodes - 最大节点数
* @param {number} params.max_depth - 最大深度
* @returns {Promise} - 完整图数据
*/
export const getFullGraph = async (params = {}) => {
const { db_id, node_label = "*", max_nodes = 200, max_depth = 2 } = params
if (!db_id) {
throw new Error('db_id is required for graph operations')
}
try {
// 使用子图接口获取数据
const response = await getSubgraph({
db_id,
node_label,
max_nodes,
max_depth
const queryParams = new URLSearchParams({
db_id: db_id,
node_label: node_label,
max_depth: max_depth.toString(),
max_nodes: max_nodes.toString()
})
if (!response.success) {
throw new Error('获取图数据失败')
return await apiGet(`/api/graph/lightrag/subgraph?${queryParams.toString()}`, {}, true)
},
/**
* 获取所有可用的LightRAG数据库
* @returns {Promise} - LightRAG数据库列表
*/
getDatabases: async () => {
return await apiGet('/api/graph/lightrag/databases', {}, true)
},
/**
* 获取LightRAG图谱标签列表
* @param {string} db_id - LightRAG数据库ID
* @returns {Promise} - 标签列表
*/
getLabels: async (db_id) => {
if (!db_id) {
throw new Error('db_id is required')
}
return {
success: true,
data: {
nodes: response.data.nodes,
edges: response.data.edges,
is_truncated: response.data.is_truncated,
stats: {
total_nodes: response.data.total_nodes,
total_edges: response.data.total_edges,
displayed_nodes: response.data.nodes.length,
displayed_edges: response.data.edges.length
}
}
const queryParams = new URLSearchParams({
db_id: db_id
})
return await apiGet(`/api/graph/lightrag/labels?${queryParams.toString()}`, {}, true)
},
/**
* 获取LightRAG图谱统计信息
* @param {string} db_id - LightRAG数据库ID
* @returns {Promise} - 统计信息
*/
getStats: async (db_id) => {
if (!db_id) {
throw new Error('db_id is required')
}
} catch (error) {
console.error('获取完整图数据失败:', error)
throw error
const queryParams = new URLSearchParams({
db_id: db_id
})
return await apiGet(`/api/graph/lightrag/stats?${queryParams.toString()}`, {}, true)
}
}
/**
* 根据特定标签获取子图
* @param {Object} params - 查询参数
* @param {string} params.db_id - 数据库ID
* @param {string} params.entity_type - 实体类型
* @param {number} params.max_nodes - 最大节点数
* @param {number} params.max_depth - 最大深度
* @returns {Promise} - 子图数据
*/
export const getGraphByEntityType = async (params = {}) => {
const { db_id, entity_type, max_nodes = 100, max_depth = 2 } = params
// =============================================================================
// === Neo4j图数据库接口分组 ===
// =============================================================================
if (!db_id) {
throw new Error('db_id is required')
export const neo4jApi = {
/**
* 获取Neo4j图数据库样例节点
* @param {string} kgdb_name - Neo4j数据库名称默认为'neo4j'
* @param {number} num - 节点数量
* @returns {Promise} - 样例节点数据
*/
getSampleNodes: async (kgdb_name = 'neo4j', num = 100) => {
const queryParams = new URLSearchParams({
kgdb_name: kgdb_name,
num: num.toString()
})
return await apiGet(`/api/graph/neo4j/nodes?${queryParams.toString()}`, {}, true)
},
/**
* 根据实体名称查询Neo4j图节点
* @param {string} entity_name - 实体名称
* @returns {Promise} - 节点数据
*/
queryNode: async (entity_name) => {
if (!entity_name) {
throw new Error('entity_name is required')
}
const queryParams = new URLSearchParams({
entity_name: entity_name
})
return await apiGet(`/api/graph/neo4j/node?${queryParams.toString()}`, {}, true)
},
/**
* 通过JSONL文件添加图谱实体到Neo4j
* @param {string} file_path - JSONL文件路径
* @param {string} kgdb_name - Neo4j数据库名称默认为'neo4j'
* @returns {Promise} - 添加结果
*/
addEntities: async (file_path, kgdb_name = 'neo4j') => {
return await apiPost('/api/graph/neo4j/add-entities', {
file_path: file_path,
kgdb_name: kgdb_name
}, {}, true)
},
/**
* 为Neo4j图谱节点添加嵌入向量索引
* @param {string} kgdb_name - Neo4j数据库名称默认为'neo4j'
* @returns {Promise} - 索引结果
*/
indexEntities: async (kgdb_name = 'neo4j') => {
return await apiPost('/api/graph/neo4j/index-entities', {
kgdb_name: kgdb_name
}, {}, true)
},
/**
* 获取Neo4j图数据库信息
* @returns {Promise} - 图数据库信息
*/
getInfo: async () => {
return await apiGet('/api/graph/neo4j/info', {}, true)
}
return await getSubgraph({
db_id,
node_label: entity_type || "*",
max_nodes,
max_depth
})
}
/**
* 展开指定节点的邻居
* @param {Object} params - 查询参数
* @param {string} params.db_id - 数据库ID
* @param {string} params.node_label - 节点标签
* @param {number} params.max_depth - 最大深度
* @param {number} params.max_nodes - 最大节点数
* @returns {Promise} - 邻居节点数据
*/
export const expandNodeNeighbors = async (params) => {
const { db_id, node_label, max_depth = 1, max_nodes = 50 } = params
if (!db_id || !node_label) {
throw new Error('db_id and node_label are required')
}
return await getSubgraph({
db_id,
node_label,
max_depth,
max_nodes
})
}
// ==================== 兼容性方法 ====================
/**
* 获取图节点数据 (已弃用建议使用 getSubgraph)
* @deprecated 请使用 getSubgraph 替代
*/
export const getGraphNodes = async (params = {}) => {
console.warn('getGraphNodes is deprecated, please use getSubgraph instead')
const { db_id } = params
if (!db_id) {
throw new Error('db_id is required. Please provide db_id parameter.')
}
return await getSubgraph({ ...params, node_label: "*" })
}
/**
* 获取图边数据 (已弃用建议使用 getSubgraph)
* @deprecated 请使用 getSubgraph 替代
*/
export const getGraphEdges = async (params = {}) => {
console.warn('getGraphEdges is deprecated, please use getSubgraph instead')
const { db_id } = params
if (!db_id) {
throw new Error('db_id is required. Please provide db_id parameter.')
}
return await getSubgraph({ ...params, node_label: "*" })
}
// ==================== 工具函数 ====================
// =============================================================================
// === 工具函数分组 ===
// =============================================================================
/**
* 根据实体类型获取颜色
@ -227,7 +182,7 @@ export const getEntityTypeColor = (entityType) => {
* 根据权重计算边的粗细
* @param {number} weight - 权重值
* @param {number} minWeight - 最小权重
* @param {number} maxWeight - 最大权重
* @param {number} maxWeight - 最大权重
* @returns {number} - 边的粗细
*/
export const calculateEdgeWidth = (weight, minWeight = 1, maxWeight = 10) => {
@ -235,4 +190,43 @@ export const calculateEdgeWidth = (weight, minWeight = 1, maxWeight = 10) => {
const maxWidth = 5
const normalizedWeight = (weight - minWeight) / (maxWeight - minWeight)
return minWidth + normalizedWeight * (maxWidth - minWidth)
}
// =============================================================================
// === 兼容性导出(可选,用于平滑迁移)===
// =============================================================================
// 保持向后兼容的导出,后续可以移除
export const getGraphNodes = async (params = {}) => {
console.warn('getGraphNodes is deprecated, use neo4jApi.getSampleNodes instead')
return neo4jApi.getSampleNodes(params.kgdb_name || 'neo4j', params.num || 100)
}
export const getGraphNode = async (params = {}) => {
console.warn('getGraphNode is deprecated, use neo4jApi.queryNode instead')
return neo4jApi.queryNode(params.entity_name)
}
export const addByJsonl = async (file_path, kgdb_name = 'neo4j') => {
console.warn('addByJsonl is deprecated, use neo4jApi.addEntities instead')
return neo4jApi.addEntities(file_path, kgdb_name)
}
export const indexNodes = async (kgdb_name = 'neo4j') => {
console.warn('indexNodes is deprecated, use neo4jApi.indexEntities instead')
return neo4jApi.indexEntities(kgdb_name)
}
export const getGraphStats = async () => {
console.warn('getGraphStats is deprecated, use neo4jApi.getInfo instead')
return neo4jApi.getInfo()
}
// 保持旧的分组导出,便于批量替换
export const graphApi = {
getSubgraph: lightragApi.getSubgraph,
getDatabases: lightragApi.getDatabases,
getLabels: lightragApi.getLabels,
getStats: lightragApi.getStats,
...neo4jApi // 临时兼容
}

View File

@ -3,31 +3,31 @@
* 导出所有API模块方便统一引入
*/
// 导出公共API模块
export * from './public_api'
// 导出需要用户认证的API模块
export * from './auth_api'
// 导出需要管理员权限的API模块
export * from './admin_api'
// 导出API模块
export * from './system_api' // 系统管理API
export * from './knowledge_api' // 知识库管理API
export * from './auth_api' // 认证API
export * from './graph_api' // 图谱API
// 导出基础工具函数
export { apiRequest, apiGet, apiPost, apiPut, apiDelete } from './base'
/**
* 权限说明:
* API模块说明:
*
* 1. public_api.js: 不需要认证就可以访问的API
* - 登录初始化管理员获取公共配置等
* 1. system_api.js: 系统管理API
* - 健康检查配置管理信息管理OCR服务
* - 权限要求: 部分公开部分需要管理员权限
*
* 2. auth_api.js: 需要用户认证才能访问的API
* - 权限要求: 任何已登录用户普通用户管理员超级管理员
* - 聊天功能个人设置等
* 2. knowledge_api.js: 知识库管理API
* - 数据库管理文档管理查询接口文件管理
* - 权限要求: 管理员权限
*
* 3. admin_api.js: 需要管理员权限才能访问的API
* - 权限要求: admin superadmin
* - 用户管理知识库管理系统配置等
* 3. auth_api.js: 认证API
* - 用户认证用户管理
*
* 注意本模块已处理权限验证和请求头使用时无需再手动添加认证头
* 4. graph_api.js: 图谱API
* - 知识图谱相关功能
*
* 注意API模块已处理权限验证和请求头使用时无需再手动添加认证头
*/

View File

@ -0,0 +1,222 @@
import { apiGet, apiPost, apiPut, apiDelete } from './base'
import { useUserStore } from '@/stores/user'
/**
* 知识库管理API模块
* 包含数据库管理文档管理查询接口等功能
*/
// 检查当前用户是否有管理员权限
const checkAdminPermission = () => {
const userStore = useUserStore()
if (!userStore.isAdmin) {
throw new Error('需要管理员权限')
}
return true
}
// =============================================================================
// === 数据库管理分组 ===
// =============================================================================
export const databaseApi = {
/**
* 获取所有知识库
* @returns {Promise} - 知识库列表
*/
getDatabases: async () => {
checkAdminPermission()
return apiGet('/api/knowledge/databases', {}, true)
},
/**
* 创建知识库
* @param {Object} databaseData - 知识库数据
* @returns {Promise} - 创建结果
*/
createDatabase: async (databaseData) => {
checkAdminPermission()
return apiPost('/api/knowledge/databases', {
database_name: databaseData.database_name,
description: databaseData.description,
embed_model_name: databaseData.embed_model_name,
kb_type: databaseData.kb_type || 'lightrag',
...databaseData.extra_config
}, {}, true)
},
/**
* 获取知识库详细信息
* @param {string} dbId - 知识库ID
* @returns {Promise} - 知识库信息
*/
getDatabaseInfo: async (dbId) => {
checkAdminPermission()
return apiGet(`/api/knowledge/databases/${dbId}`, {}, true)
},
/**
* 更新知识库信息
* @param {string} dbId - 知识库ID
* @param {Object} updateData - 更新数据
* @returns {Promise} - 更新结果
*/
updateDatabase: async (dbId, updateData) => {
checkAdminPermission()
return apiPut(`/api/knowledge/databases/${dbId}`, updateData, {}, true)
},
/**
* 删除知识库
* @param {string} dbId - 知识库ID
* @returns {Promise} - 删除结果
*/
deleteDatabase: async (dbId) => {
checkAdminPermission()
return apiDelete(`/api/knowledge/databases/${dbId}`, {}, true)
}
}
// =============================================================================
// === 文档管理分组 ===
// =============================================================================
export const documentApi = {
/**
* 添加文档到知识库
* @param {string} dbId - 知识库ID
* @param {Array} items - 文档列表
* @param {Object} params - 处理参数
* @returns {Promise} - 添加结果
*/
addDocuments: async (dbId, items, params = {}) => {
checkAdminPermission()
return apiPost(`/api/knowledge/databases/${dbId}/documents`, {
items,
params
}, {}, true)
},
/**
* 获取文档信息
* @param {string} dbId - 知识库ID
* @param {string} docId - 文档ID
* @returns {Promise} - 文档信息
*/
getDocumentInfo: async (dbId, docId) => {
checkAdminPermission()
return apiGet(`/api/knowledge/databases/${dbId}/documents/${docId}`, {}, true)
},
/**
* 删除文档
* @param {string} dbId - 知识库ID
* @param {string} docId - 文档ID
* @returns {Promise} - 删除结果
*/
deleteDocument: async (dbId, docId) => {
checkAdminPermission()
return apiDelete(`/api/knowledge/databases/${dbId}/documents/${docId}`, {}, true)
}
}
// =============================================================================
// === 查询分组 ===
// =============================================================================
export const queryApi = {
/**
* 查询知识库
* @param {string} dbId - 知识库ID
* @param {string} query - 查询文本
* @param {Object} meta - 查询参数
* @returns {Promise} - 查询结果
*/
queryKnowledgeBase: async (dbId, query, meta = {}) => {
checkAdminPermission()
return apiPost(`/api/knowledge/databases/${dbId}/query`, {
query,
meta
}, {}, true)
},
/**
* 测试查询知识库
* @param {string} dbId - 知识库ID
* @param {string} query - 查询文本
* @param {Object} meta - 查询参数
* @returns {Promise} - 测试结果
*/
queryTest: async (dbId, query, meta = {}) => {
checkAdminPermission()
return apiPost(`/api/knowledge/databases/${dbId}/query-test`, {
query,
meta
}, {}, true)
},
/**
* 获取知识库查询参数
* @param {string} dbId - 知识库ID
* @returns {Promise} - 查询参数
*/
getKnowledgeBaseQueryParams: async (dbId) => {
checkAdminPermission()
return apiGet(`/api/knowledge/databases/${dbId}/query-params`, {}, true)
}
}
// =============================================================================
// === 文件管理分组 ===
// =============================================================================
export const fileApi = {
/**
* 上传文件
* @param {File} file - 文件对象
* @param {string} dbId - 知识库ID可选
* @returns {Promise} - 上传结果
*/
uploadFile: async (file, dbId = null) => {
checkAdminPermission()
const formData = new FormData()
formData.append('file', file)
const url = dbId
? `/api/knowledge/files/upload?db_id=${dbId}`
: '/api/knowledge/files/upload'
return apiPost(url, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}, true)
}
}
// =============================================================================
// === 知识库类型分组 ===
// =============================================================================
export const typeApi = {
/**
* 获取支持的知识库类型
* @returns {Promise} - 知识库类型列表
*/
getKnowledgeBaseTypes: async () => {
checkAdminPermission()
return apiGet('/api/knowledge/types', {}, true)
},
/**
* 获取知识库统计信息
* @returns {Promise} - 统计信息
*/
getStatistics: async () => {
checkAdminPermission()
return apiGet('/api/knowledge/stats', {}, true)
}
}

View File

@ -1,74 +0,0 @@
import { apiGet, apiPost } from './base'
/**
* 公共API模块
* 包含所有不需要认证的公共接口
*/
// 登录相关API
export const authApi = {
/**
* 用户登录
* @param {Object} credentials - 登录凭证
* @returns {Promise} - 登录结果
*/
login: (credentials) => {
const formData = new FormData()
formData.append('username', credentials.username)
formData.append('password', credentials.password)
return apiRequest('/api/auth/token', {
method: 'POST',
body: formData
}, false)
},
/**
* 检查是否是首次运行
* @returns {Promise<boolean>} - 是否首次运行
*/
checkFirstRun: () => apiGet('/api/auth/check-first-run'),
/**
* 初始化管理员账户
* @param {Object} adminData - 管理员账户数据
* @returns {Promise} - 初始化结果
*/
initializeAdmin: (adminData) => apiPost('/api/auth/initialize', adminData),
}
// 配置相关API
export const configApi = {
/**
* 获取系统配置
* @returns {Promise} - 系统配置
*/
getConfig: () => apiGet('/api/config'),
}
// 系统信息配置API
export const infoApi = {
/**
* 获取系统信息配置
* @returns {Promise} - 系统信息配置
*/
getInfoConfig: () => apiGet('/api/info'),
/**
* 重新加载信息配置
* @returns {Promise} - 重新加载结果
*/
reloadInfoConfig: () => apiGet('/api/info/reload')
}
// 健康检查API
export const healthApi = {
/**
* 系统健康检查
* @returns {Promise} - 健康检查结果
*/
check: () => apiGet('/api/health'),
}
// 从base.js导入apiRequest以支持FormData
import { apiRequest } from './base'

183
web/src/apis/system_api.js Normal file
View File

@ -0,0 +1,183 @@
import { apiGet, apiPost } from './base'
import { useUserStore } from '@/stores/user'
/**
* 系统管理API模块
* 包含系统配置健康检查信息管理等功能
*/
// 检查当前用户是否有管理员权限
const checkAdminPermission = () => {
const userStore = useUserStore()
if (!userStore.isAdmin) {
throw new Error('需要管理员权限')
}
return true
}
// 检查当前用户是否有超级管理员权限
const checkSuperAdminPermission = () => {
const userStore = useUserStore()
if (!userStore.isSuperAdmin) {
throw new Error('需要超级管理员权限')
}
return true
}
// =============================================================================
// === 健康检查分组 ===
// =============================================================================
export const healthApi = {
/**
* 系统健康检查公开接口
* @returns {Promise} - 健康检查结果
*/
checkHealth: () => apiGet('/api/system/health'),
/**
* OCR服务健康检查
* @returns {Promise} - OCR服务健康状态
*/
checkOcrHealth: async () => {
checkAdminPermission()
return apiGet('/api/system/health/ocr', {}, true)
}
}
// =============================================================================
// === 配置管理分组 ===
// =============================================================================
export const configApi = {
/**
* 获取系统配置
* @returns {Promise} - 系统配置
*/
getConfig: async () => {
checkAdminPermission()
return apiGet('/api/system/config', {}, true)
},
/**
* 更新单个配置项
* @param {string} key - 配置键
* @param {any} value - 配置值
* @returns {Promise} - 更新结果
*/
updateConfig: async (key, value) => {
checkAdminPermission()
return apiPost('/api/system/config', { key, value }, {}, true)
},
/**
* 批量更新配置项
* @param {Object} items - 配置项对象
* @returns {Promise} - 更新结果
*/
updateConfigBatch: async (items) => {
checkAdminPermission()
return apiPost('/api/system/config/update', items, {}, true)
},
/**
* 重启系统仅超级管理员
* @returns {Promise} - 重启结果
*/
restartSystem: async () => {
checkSuperAdminPermission()
return apiPost('/api/system/restart', {}, {}, true)
},
/**
* 获取系统日志
* @returns {Promise} - 系统日志
*/
getLogs: async () => {
checkAdminPermission()
return apiGet('/api/system/logs', {}, true)
}
}
// =============================================================================
// === 信息管理分组 ===
// =============================================================================
export const infoApi = {
/**
* 获取系统信息配置公开接口
* @returns {Promise} - 系统信息配置
*/
getInfoConfig: () => apiGet('/api/system/info'),
/**
* 重新加载信息配置
* @returns {Promise} - 重新加载结果
*/
reloadInfoConfig: async () => {
checkAdminPermission()
return apiPost('/api/system/info/reload', {}, {}, true)
}
}
// =============================================================================
// === OCR服务分组 ===
// =============================================================================
export const ocrApi = {
/**
* 获取OCR服务统计信息
* @returns {Promise} - OCR统计信息
*/
getStats: async () => {
checkAdminPermission()
return apiGet('/api/system/ocr/stats', {}, true)
},
/**
* 获取OCR服务健康状态
* @returns {Promise} - OCR健康状态
*/
getHealth: async () => {
checkAdminPermission()
return apiGet('/api/system/ocr/health', {}, true)
}
}
// =============================================================================
// === 智能体配置分组 ===
// =============================================================================
export const agentConfigApi = {
/**
* 获取智能体配置
* @param {string} agentName - 智能体名称
* @returns {Promise} - 智能体配置
*/
getAgentConfig: async (agentName) => {
checkAdminPermission()
return apiGet(`/api/chat/agent/${agentName}/config`, {}, true)
},
/**
* 保存智能体配置
* @param {string} agentName - 智能体名称
* @param {Object} config - 配置对象
* @returns {Promise} - 保存结果
*/
saveAgentConfig: async (agentName, config) => {
checkAdminPermission()
return apiPost(`/api/chat/agent/${agentName}/config`, config, {}, true)
},
/**
* 设置默认智能体
* @param {string} agentId - 智能体ID
* @returns {Promise} - 设置结果
*/
setDefaultAgent: async (agentId) => {
checkAdminPermission()
return apiPost('/api/chat/agent/default', { agent_id: agentId }, {}, true)
}
}

View File

@ -1056,9 +1056,9 @@ const getAgentHistory = async () => {
}
try {
console.log(`正在获取智能体[${props.agentId}]的历史记录对话ID: ${currentChatId.value}`);
console.debug(`正在获取智能体[${props.agentId}]的历史记录对话ID: ${currentChatId.value}`);
const response = await chatApi.getAgentHistory(props.agentId, currentChatId.value);
console.log('智能体历史记录:', response);
console.debug('智能体历史记录:', response);
//
if (response && Array.isArray(response.history)) {
@ -1133,12 +1133,13 @@ const convertServerHistoryToMessages = (serverHistory) => {
}
}
console.log("conversations", conversations);
console.debug("conversations", conversations);
return conversations;
};
//
onMounted(async () => {
// AgentView
await initAll();
});
@ -1147,7 +1148,7 @@ onMounted(async () => {
onMounted(() => {
watch(() => props.agentId, async (newAgentId, oldAgentId) => {
try {
console.log("智能体ID变化", oldAgentId, "->", newAgentId);
console.debug("智能体ID变化", oldAgentId, "->", newAgentId);
//
if (newAgentId !== oldAgentId) {

View File

@ -156,7 +156,7 @@ import MessageComponent from '@/components/MessageComponent.vue'
import RefsSidebar from '@/components/RefsSidebar.vue'
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue'
import { chatApi } from '@/apis/auth_api'
import { knowledgeBaseApi } from '@/apis/admin_api'
import { databaseApi } from '@/apis/knowledge_api'
const props = defineProps({
conv: Object,
@ -461,7 +461,7 @@ const loadDatabases = () => {
}
try {
knowledgeBaseApi.getDatabases()
databaseApi.getDatabases()
.then(data => {
console.log(data)
opts.databases = data.databases

View File

@ -114,7 +114,7 @@ import {
RobotOutlined
} from '@ant-design/icons-vue';
import dayjs from 'dayjs';
import { logApi, systemConfigApi } from '@/apis/admin_api';
import { configApi, agentConfigApi } from '@/apis/system_api';
import { chatApi } from '@/apis/auth_api';
const configStore = useConfigStore()
@ -210,7 +210,7 @@ const fetchLogs = async () => {
state.fetching = true;
try {
error.value = '';
const logData = await logApi.getLogs();
const logData = await configApi.getLogs();
state.rawLogs = logData.log.split('\n').filter(line => line.trim());
await nextTick();
@ -399,7 +399,7 @@ const printAgentConfig = async () => {
//
for (const agent of agentsData.agents) {
try {
const agentConfig = await systemConfigApi.getAgentConfig(agent.name);
const agentConfig = await agentConfigApi.getAgentConfig(agent.name);
console.log(`智能体 "${agent.name}" 配置:`, JSON.stringify(agentConfig, null, 2));
} catch (err) {
console.log(`智能体 "${agent.name}" 配置获取失败:`, err.message);

View File

@ -11,15 +11,15 @@
:loading="loadingDatabases"
@change="onDatabaseChange"
>
<a-select-option
v-for="db in availableDatabases"
:key="db.db_id"
<a-select-option
v-for="db in availableDatabases"
:key="db.db_id"
:value="db.db_id"
>
{{ db.name }} ({{ db.row_count || 0 }} 文件)
</a-select-option>
</a-select>
<a-select
v-model:value="selectedLabel"
placeholder="选择标签/实体类型"
@ -101,8 +101,8 @@
></div>
<!-- 节点详情面板 -->
<div
v-if="selectedNodeData"
<div
v-if="selectedNodeData"
class="detail-panel node-panel"
:style="{ transform: `translate(${nodePanelPosition.x}px, ${nodePanelPosition.y}px)` }"
@mousedown="startDragPanel('node', $event)"
@ -146,8 +146,8 @@
</div>
<!-- 边详情面板 -->
<div
v-if="selectedEdgeData"
<div
v-if="selectedEdgeData"
class="detail-panel edge-panel"
:style="{ transform: `translate(${edgePanelPosition.x}px, ${edgePanelPosition.y}px)` }"
@mousedown="startDragPanel('edge', $event)"
@ -234,28 +234,21 @@
<script setup>
import { ref, reactive, onMounted, onUnmounted, computed, watch, nextTick } from 'vue'
import { message } from 'ant-design-vue'
import {
SearchOutlined,
ReloadOutlined,
import {
SearchOutlined,
ReloadOutlined,
ClearOutlined,
CloseOutlined,
PlusOutlined,
MinusOutlined,
HomeOutlined
CloseOutlined,
PlusOutlined,
MinusOutlined,
HomeOutlined
} from '@ant-design/icons-vue'
import Sigma from 'sigma'
import { NodeBorderProgram } from '@sigma/node-border'
import EdgeCurveProgram, { EdgeCurvedArrowProgram } from '@sigma/edge-curve'
import { EdgeArrowProgram } from 'sigma/rendering'
import {
getAvailableDatabases,
getGraphLabels,
getSubgraph,
getFullGraph,
getGraphStats,
expandNodeNeighbors
} from '@/apis/graph_api'
import { lightragApi } from '@/apis/graph_api'
import { useGraphStore } from '@/stores/graphStore'
import '@/assets/css/sigma.css'
@ -340,13 +333,13 @@ const loadAvailableDatabases = async () => {
await loadGraphLabels(selectedDatabase.value)
return
}
loadingDatabases.value = true
try {
const response = await getAvailableDatabases()
const response = await lightragApi.getDatabases()
if (response.success) {
availableDatabases.value = response.data.databases || []
// ID
if (props.initialDatabaseId && availableDatabases.value.some(db => db.db_id === props.initialDatabaseId)) {
selectedDatabase.value = props.initialDatabaseId
@ -367,10 +360,10 @@ const loadAvailableDatabases = async () => {
//
const loadGraphLabels = async (dbId) => {
if (!dbId) return
loadingLabels.value = true
try {
const response = await getGraphLabels(dbId)
const response = await lightragApi.getLabels(dbId)
if (response.success) {
availableLabels.value = response.data.labels || []
}
@ -385,16 +378,16 @@ const loadGraphLabels = async (dbId) => {
//
const onDatabaseChange = async (dbId) => {
if (!dbId) return
selectedDatabase.value = dbId
selectedLabel.value = '*'
//
clearGraph()
//
await loadGraphLabels(dbId)
message.info(`已切换到数据库: ${availableDatabases.value.find(db => db.db_id === dbId)?.name || dbId}`)
}
@ -472,14 +465,14 @@ const registerEvents = () => {
const graph = sigmaInstance.getGraph()
if (graph.hasNode(node)) {
console.log('Clicked node:', node)
//
graphStore.setSelectedNode(node, false) //
//
const nodeData = graph.getNodeAttributes(node)
console.log('Node data:', nodeData)
//
setTimeout(() => {
if (graphStore.selectedNode === node) {
@ -487,7 +480,7 @@ const registerEvents = () => {
graphStore.setSelectedNode(node, true) //
}
}, 100)
} else {
console.warn('Clicked node does not exist in graph:', node)
}
@ -629,13 +622,13 @@ const loadGraphData = async () => {
try {
const [graphResponse, statsResponse] = await Promise.all([
getSubgraph({
lightragApi.getSubgraph({
db_id: selectedDatabase.value,
node_label: selectedLabel.value || '*',
max_depth: searchParams.max_depth,
max_nodes: searchParams.max_nodes
}),
getGraphStats(selectedDatabase.value)
lightragApi.getStats(selectedDatabase.value)
])
if (graphResponse.success && statsResponse.success) {
@ -798,7 +791,7 @@ const expandNode = async (nodeId) => {
expanding.value = true
try {
const response = await expandNodeNeighbors({
const response = await lightragApi.getSubgraph({
db_id: selectedDatabase.value,
node_label: nodeId,
max_depth: 1,
@ -862,18 +855,18 @@ const resetCamera = () => {
try {
const camera = sigmaInstance.getCamera()
const graph = sigmaInstance.getGraph()
//
if (graph.order === 0) {
camera.animatedReset({ duration: 500 })
return
}
//
const nodes = graph.nodes()
if (nodes.length > 0) {
const bounds = { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
nodes.forEach(node => {
const attrs = graph.getNodeAttributes(node)
if (typeof attrs.x === 'number' && typeof attrs.y === 'number') {
@ -883,13 +876,13 @@ const resetCamera = () => {
bounds.maxY = Math.max(bounds.maxY, attrs.y)
}
})
// 使fitBounds
if (isFinite(bounds.minX) && isFinite(bounds.maxX) && isFinite(bounds.minY) && isFinite(bounds.maxY)) {
const centerX = (bounds.minX + bounds.maxX) / 2
const centerY = (bounds.minY + bounds.maxY) / 2
const padding = 50
camera.animate({
x: centerX,
y: centerY,
@ -905,7 +898,7 @@ const resetCamera = () => {
} else {
camera.animatedReset({ duration: 500 })
}
console.log('Camera reset completed')
message.success('视图已重置')
} catch (error) {
@ -940,16 +933,16 @@ const getEntityColor = (entityType) => {
//
const startDragPanel = (type, event) => {
event.preventDefault()
if (!sigmaContainer.value) return
const currentPosition = type === 'node' ? nodePanelPosition.value : edgePanelPosition.value
//
const containerRect = sigmaContainer.value.getBoundingClientRect()
const relativeX = event.clientX - containerRect.left
const relativeY = event.clientY - containerRect.top
dragging.value = {
active: true,
type: type,
@ -958,33 +951,33 @@ const startDragPanel = (type, event) => {
initialX: currentPosition.x,
initialY: currentPosition.y
}
document.addEventListener('mousemove', onDragPanel)
document.addEventListener('mouseup', stopDragPanel)
}
const onDragPanel = (event) => {
if (!dragging.value.active || !sigmaContainer.value) return
//
const containerRect = sigmaContainer.value.getBoundingClientRect()
//
const currentRelativeX = event.clientX - containerRect.left
const currentRelativeY = event.clientY - containerRect.top
//
const deltaX = currentRelativeX - dragging.value.startX
const deltaY = currentRelativeY - dragging.value.startY
const maxX = containerRect.width - 320 // 300px +
const maxY = containerRect.height - 200 // 200px
const newPosition = {
x: Math.max(0, Math.min(maxX, dragging.value.initialX + deltaX)),
y: Math.max(0, Math.min(maxY, dragging.value.initialY + deltaY))
}
if (dragging.value.type === 'node') {
nodePanelPosition.value = newPosition
} else {
@ -1008,7 +1001,7 @@ onUnmounted(() => {
//
document.removeEventListener('mousemove', onDragPanel)
document.removeEventListener('mouseup', stopDragPanel)
if (sigmaInstance) {
sigmaInstance.kill()
sigmaInstance = null
@ -1029,7 +1022,7 @@ watch(() => graphStore.selectedNode, (nodeId) => {
if (nodeId && graphStore.moveToSelectedNode && sigmaInstance) {
try {
const graph = sigmaInstance.getGraph()
//
if (!graph.hasNode(nodeId)) {
console.warn('Selected node does not exist in graph:', nodeId)
@ -1038,7 +1031,7 @@ watch(() => graphStore.selectedNode, (nodeId) => {
}
const nodeAttributes = graph.getNodeAttributes(nodeId)
//
if (!nodeAttributes || typeof nodeAttributes.x !== 'number' || typeof nodeAttributes.y !== 'number') {
console.warn('Invalid node attributes for node:', nodeId, nodeAttributes)
@ -1048,40 +1041,40 @@ watch(() => graphStore.selectedNode, (nodeId) => {
const camera = sigmaInstance.getCamera()
const currentState = camera.getState()
console.log('Moving camera to node:', nodeId, {
x: nodeAttributes.x,
console.log('Moving camera to node:', nodeId, {
x: nodeAttributes.x,
y: nodeAttributes.y,
currentRatio: currentState.ratio
currentRatio: currentState.ratio
})
//
const currentRatio = currentState.ratio || 1.0
const targetRatio = Math.max(0.1, Math.min(currentRatio * 0.7, 0.6))
//
const isValidPosition = (
typeof nodeAttributes.x === 'number' &&
typeof nodeAttributes.x === 'number' &&
typeof nodeAttributes.y === 'number' &&
!isNaN(nodeAttributes.x) &&
!isNaN(nodeAttributes.x) &&
!isNaN(nodeAttributes.y) &&
isFinite(nodeAttributes.x) &&
isFinite(nodeAttributes.x) &&
isFinite(nodeAttributes.y)
)
if (!isValidPosition) {
console.warn('Invalid node position, skipping camera movement:', nodeAttributes)
return
}
// 使
camera.animate(
{
x: nodeAttributes.x,
y: nodeAttributes.y,
{
x: nodeAttributes.x,
y: nodeAttributes.y,
ratio: targetRatio // 使
},
{
{
duration: 600 //
}
)
@ -1216,7 +1209,7 @@ watch(() => graphStore.selectedNode, (nodeId) => {
.detail-item {
display: flex;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
@ -1312,20 +1305,20 @@ watch(() => graphStore.selectedNode, (nodeId) => {
max-height: 240px;
overflow-y: auto;
overflow-x: hidden;
/* 自定义滚动条样式 */
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: #d9d9d9;
border-radius: 2px;
&:hover {
background: #bfbfbf;
}
@ -1342,7 +1335,7 @@ watch(() => graphStore.selectedNode, (nodeId) => {
font-size: 12px;
min-width: 0;
transition: background-color 0.2s ease;
span {
white-space: nowrap;
overflow: hidden;
@ -1351,7 +1344,7 @@ watch(() => graphStore.selectedNode, (nodeId) => {
min-width: 0;
color: #595959;
}
&:hover {
background-color: #f5f5f5;
}

View File

@ -18,8 +18,6 @@ app.use(Antd)
// 预加载信息配置
import { useInfoStore } from '@/stores/info'
const infoStore = useInfoStore()
infoStore.loadInfoConfig().then(() => {
console.log('应用信息配置预加载完成')
})
infoStore.loadInfoConfig()
app.mount('#app')

View File

@ -1,6 +1,6 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { systemConfigApi } from '@/apis/admin_api'
import { configApi } from '@/apis/system_api'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
@ -21,7 +21,7 @@ export const useConfigStore = defineStore('config', () => {
function setConfigValue(key, value) {
config.value[key] = value
systemConfigApi.updateConfigItems({ [key]: value })
configApi.updateConfigBatch({ [key]: value })
.then(data => {
console.debug('Success:', data)
setConfig(data)
@ -35,7 +35,7 @@ export const useConfigStore = defineStore('config', () => {
}
// 发送到服务器
systemConfigApi.updateConfigItems(items)
configApi.updateConfigBatch(items)
.then(data => {
console.debug('Success:', data)
setConfig(data)
@ -43,7 +43,7 @@ export const useConfigStore = defineStore('config', () => {
}
function refreshConfig() {
systemConfigApi.getSystemConfig()
configApi.getConfig()
.then(data => {
console.log("config", data)
setConfig(data)

View File

@ -1,6 +1,6 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { knowledgeBaseApi } from '@/apis/admin_api'
import { databaseApi } from '@/apis/knowledge_api'
export const useDatabaseStore = defineStore('database', () => {
const db = ref({})
@ -9,7 +9,7 @@ export const useDatabaseStore = defineStore('database', () => {
}
async function refreshDatabase() {
const res = await knowledgeBaseApi.getDatabases()
const res = await databaseApi.getDatabases()
console.log("database", res)
setDatabase(res.databases)
}

View File

@ -1,6 +1,6 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { infoApi } from '@/apis/public_api'
import { infoApi } from '@/apis/system_api'
export const useInfoStore = defineStore('info', () => {
// 状态
@ -53,7 +53,7 @@ export const useInfoStore = defineStore('info', () => {
if (response.success && response.data) {
setInfoConfig(response.data)
console.log('信息配置加载成功:', response.data)
console.debug('信息配置加载成功:', response.data)
return response.data
} else {
console.warn('信息配置加载失败,使用默认配置')
@ -74,7 +74,7 @@ export const useInfoStore = defineStore('info', () => {
if (response.success && response.data) {
setInfoConfig(response.data)
console.log('信息配置重新加载成功:', response.data)
console.debug('信息配置重新加载成功:', response.data)
return response.data
} else {
console.warn('信息配置重新加载失败')

View File

@ -229,7 +229,7 @@ import AgentChatComponent from '@/components/AgentChatComponent.vue';
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
import { useUserStore } from '@/stores/user';
import { chatApi } from '@/apis/auth_api';
import { systemConfigApi } from '@/apis/admin_api';
import { agentConfigApi } from '@/apis/system_api';
//
const router = useRouter();
@ -276,7 +276,7 @@ const setAsDefaultAgent = async () => {
if (!selectedAgentId.value || !userStore.isAdmin) return;
try {
await systemConfigApi.setDefaultAgent(selectedAgentId.value);
await agentConfigApi.setDefaultAgent(selectedAgentId.value);
defaultAgentId.value = selectedAgentId.value;
message.success('已将当前智能体设为默认');
} catch (error) {
@ -326,7 +326,7 @@ const fetchDefaultAgent = async () => {
try {
const data = await chatApi.getDefaultAgent();
defaultAgentId.value = data.default_agent_id;
console.log("Default agent ID:", defaultAgentId.value);
console.debug("Default agent ID:", defaultAgentId.value);
} catch (error) {
console.error('获取默认智能体错误:', error);
}
@ -394,7 +394,7 @@ const loadAgentConfig = async () => {
try {
//
const response = await systemConfigApi.getAgentConfig(selectedAgentId.value);
const response = await agentConfigApi.getAgentConfig(selectedAgentId.value);
if (response.success && response.config) {
//
Object.keys(response.config).forEach(key => {
@ -408,7 +408,7 @@ const loadAgentConfig = async () => {
}
}
});
console.log(`从服务器加载 ${selectedAgentId.value} 配置成功, ${JSON.stringify(agentConfig.value)}`);
// console.log(` ${selectedAgentId.value} , ${JSON.stringify(agentConfig.value)}`);
}
} catch (error) {
console.error('从服务器加载配置出错:', error);
@ -428,7 +428,7 @@ const saveConfig = async () => {
try {
//
await systemConfigApi.saveAgentConfig(selectedAgentId.value, agentConfig.value);
await agentConfigApi.saveAgentConfig(selectedAgentId.value, agentConfig.value);
//
message.success('配置已保存到服务器');
console.log("保存配置:", agentConfig.value);
@ -447,7 +447,7 @@ const resetConfig = async () => {
try {
//
await systemConfigApi.saveAgentConfig(selectedAgentId.value, {});
await agentConfigApi.saveAgentConfig(selectedAgentId.value, {});
//
await loadAgentConfig();
message.info('配置已重置');

View File

@ -136,7 +136,7 @@
name="file"
:multiple="true"
:disabled="state.chunkLoading"
:action="'/api/data/upload?db_id=' + databaseId"
:action="'/api/knowledge/files/upload?db_id=' + databaseId"
:headers="getAuthHeaders()"
@change="handleFileUpload"
@drop="handleDrop"
@ -435,7 +435,8 @@ import { message, Modal } from 'ant-design-vue';
import { useRoute, useRouter } from 'vue-router';
import { useConfigStore } from '@/stores/config'
import { useUserStore } from '@/stores/user'
import { knowledgeBaseApi, ocrApi } from '@/apis/admin_api'
import { databaseApi, documentApi, queryApi, fileApi } from '@/apis/knowledge_api'
import { ocrApi } from '@/apis/system_api'
import {
ReadOutlined,
LeftOutlined,
@ -509,7 +510,7 @@ const checkOcrHealth = async () => {
state.ocrHealthChecking = true;
try {
const healthData = await ocrApi.checkHealth();
const healthData = await ocrApi.getHealth();
ocrHealthStatus.value = healthData.services;
} catch (error) {
console.error('OCR健康检查失败:', error);
@ -636,7 +637,7 @@ const loadQueryParams = async () => {
state.queryParamsLoading = true
try {
const response = await knowledgeBaseApi.getKbQueryParams(databaseId.value)
const response = await queryApi.getKnowledgeBaseQueryParams(databaseId.value)
queryParams.value = response.params?.options || []
// meta
@ -677,10 +678,7 @@ const onQuery = () => {
meta.db_id = database.value.db_id
try {
knowledgeBaseApi.queryTest({
query: queryText.value.trim(),
meta: meta
})
queryApi.queryTest(database.value.db_id, queryText.value.trim(), meta)
.then(data => {
console.log(data)
queryResult.value = data
@ -737,7 +735,7 @@ const deleteDatabse = () => {
cancelText: '取消',
onOk: () => {
state.lock = true
knowledgeBaseApi.deleteDatabase(databaseId.value)
databaseApi.deleteDatabase(databaseId.value)
.then(data => {
console.log(data)
message.success(data.message || '删除成功')
@ -774,7 +772,7 @@ const openFileDetail = (record) => {
state.lock = true;
try {
knowledgeBaseApi.getDocumentDetail(databaseId.value, record.file_id)
documentApi.getDocumentInfo(databaseId.value, record.file_id)
.then(data => {
console.log(data);
if (data.status == "failed") {
@ -837,7 +835,7 @@ const getDatabaseInfo = () => {
state.lock = true
state.databaseLoading = true
return new Promise((resolve, reject) => {
knowledgeBaseApi.getDatabaseInfo(db_id)
databaseApi.getDatabaseInfo(db_id)
.then(async data => {
database.value = data
//
@ -859,7 +857,7 @@ const getDatabaseInfo = () => {
const deleteFile = (fileId) => {
state.lock = true
console.debug("deleteFile", databaseId.value, fileId)
return knowledgeBaseApi.deleteFile(databaseId.value, fileId)
return documentApi.deleteDocument(databaseId.value, fileId)
.then(data => {
console.log(data)
message.success(data.message || '删除成功')
@ -964,11 +962,7 @@ const addFiles = (items, contentType = 'file') => {
content_type: contentType
};
knowledgeBaseApi.addFiles({
db_id: databaseId.value,
items: items,
params: params
})
documentApi.addDocuments(databaseId.value, items, params)
.then(data => {
console.log('处理结果:', data);
if (data.status === 'success') {
@ -1116,7 +1110,7 @@ const handleEditSubmit = () => {
const updateDatabaseInfo = async () => {
try {
state.lock = true;
const response = await knowledgeBaseApi.updateDatabaseInfo(databaseId.value, {
const response = await databaseApi.updateDatabase(databaseId.value, {
name: editForm.name,
description: editForm.description
});

View File

@ -131,7 +131,7 @@ import { useConfigStore } from '@/stores/config';
import { message } from 'ant-design-vue'
import { ReadFilled, DatabaseOutlined, ThunderboltOutlined } from '@ant-design/icons-vue'
import { BookPlus, Database, Zap } from 'lucide-vue-next';
import { knowledgeBaseApi } from '@/apis/admin_api';
import { databaseApi, typeApi } from '@/apis/knowledge_api';
import HeaderComponent from '@/components/HeaderComponent.vue';
const route = useRoute()
@ -172,7 +172,7 @@ const supportedKbTypes = ref({})
//
const loadSupportedKbTypes = async () => {
try {
const data = await knowledgeBaseApi.getSupportedKbTypes()
const data = await typeApi.getKnowledgeBaseTypes()
supportedKbTypes.value = data.kb_types
console.log('支持的知识库类型:', supportedKbTypes.value)
} catch (error) {
@ -190,7 +190,7 @@ const loadSupportedKbTypes = async () => {
const loadDatabases = () => {
state.loading = true
// loadGraph()
knowledgeBaseApi.getDatabases()
databaseApi.getDatabases()
.then(data => {
console.log(data)
databases.value = data.databases
@ -309,7 +309,7 @@ const createDatabase = () => {
}
}
knowledgeBaseApi.createDatabase(requestData)
databaseApi.createDatabase(requestData)
.then(data => {
console.log('创建成功:', data)
loadDatabases()

View File

@ -72,7 +72,7 @@
:fileList="fileList"
:max-count="1"
:disabled="disabled"
action="/api/data/upload"
action="/api/knowledge/files/upload"
:headers="getAuthHeaders()"
@change="handleFileUpload"
@drop="handleDrop"
@ -94,7 +94,7 @@ import { message, Button as AButton } from 'ant-design-vue';
import { useConfigStore } from '@/stores/config';
import { UploadOutlined, SyncOutlined } from '@ant-design/icons-vue';
import HeaderComponent from '@/components/HeaderComponent.vue';
import { graphApi } from '@/apis/admin_api';
import { neo4jApi } from '@/apis/graph_api';
import { useUserStore } from '@/stores/user';
const configStore = useConfigStore();
@ -130,10 +130,10 @@ const unindexedCount = computed(() => {
const loadGraphInfo = () => {
state.loadingGraphInfo = true
graphApi.getGraphInfo()
neo4jApi.getInfo()
.then(data => {
console.log(data)
graphInfo.value = data
graphInfo.value = data.data
state.loadingGraphInfo = false
})
.catch(error => {
@ -187,7 +187,7 @@ const getGraphData = () => {
const addDocumentByFile = () => {
state.precessing = true
const files = fileList.value.filter(file => file.status === 'done').map(file => file.response.file_path)
graphApi.addByJsonl(files[0])
neo4jApi.addEntities(files[0])
.then((data) => {
if (data.status === 'success') {
message.success(data.message);
@ -205,7 +205,7 @@ const addDocumentByFile = () => {
const loadSampleNodes = () => {
state.fetching = true
graphApi.getNodes('neo4j', sampleNodeCount.value)
neo4jApi.getSampleNodes('neo4j', sampleNodeCount.value)
.then((data) => {
graphData.nodes = data.result.nodes
graphData.edges = data.result.edges
@ -242,7 +242,7 @@ const onSearch = () => {
}
state.searchLoading = true
graphApi.queryNode(state.searchInput)
neo4jApi.queryNode(state.searchInput)
.then((data) => {
if (!data.result || !data.result.nodes || !data.result.edges) {
throw new Error('返回数据格式不正确');
@ -368,7 +368,7 @@ const indexNodes = () => {
}
state.indexing = true;
graphApi.indexNodes('neo4j')
neo4jApi.indexEntities('neo4j')
.then(data => {
message.success(data.message || '索引添加成功');
//

View File

@ -155,7 +155,7 @@ import { useRouter } from 'vue-router';
import { useUserStore } from '@/stores/user';
import { message } from 'ant-design-vue';
import { chatApi } from '@/apis/auth_api';
import { authApi, healthApi } from '@/apis/public_api';
import { healthApi } from '@/apis/system_api';
import { UserOutlined, LockOutlined, WechatOutlined, QrcodeOutlined, ThunderboltOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
import loginBg from '@/assets/pics/login_bg.jpg';
@ -304,7 +304,7 @@ const checkFirstRunStatus = async () => {
const checkServerHealth = async () => {
try {
healthChecking.value = true;
const response = await healthApi.check();
const response = await healthApi.checkHealth();
if (response.status === 'ok') {
serverStatus.value = 'ok';
} else {

View File

@ -131,7 +131,7 @@ import TableConfigComponent from '@/components/TableConfigComponent.vue';
import ModelProvidersComponent from '@/components/ModelProvidersComponent.vue';
import UserManagementComponent from '@/components/UserManagementComponent.vue';
import { notification, Button } from 'ant-design-vue';
import { systemConfigApi } from '@/apis/admin_api'
import { configApi } from '@/apis/system_api'
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
const configStore = useConfigStore()
@ -207,7 +207,7 @@ const sendRestart = () => {
console.log('Restarting...')
message.loading({ content: '重新加载模型中', key: "restart", duration: 0 });
systemConfigApi.restartServer()
configApi.restartSystem()
.then(() => {
console.log('Restarted')
message.success({ content: '重新加载完成!', key: "restart", duration: 2 });