refactor(router): 更新系统路由
This commit is contained in:
parent
53b642ab6e
commit
9d9101a3e5
@ -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
|
||||
|
||||
@ -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/*
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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": {}}
|
||||
|
||||
@ -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)}"
|
||||
}
|
||||
|
||||
|
||||
@ -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$", # 获取系统信息配置
|
||||
]
|
||||
|
||||
# 获取数据库会话
|
||||
|
||||
@ -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"]
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
from .graph import ReActAgent
|
||||
from .configuration import ReActConfiguration
|
||||
|
||||
__all__ = ["ReActAgent", "ReActConfiguration"]
|
||||
@ -1,12 +0,0 @@
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from src.agents.registry import Configuration
|
||||
|
||||
|
||||
@dataclass(kw_only=True)
|
||||
class ReActConfiguration(Configuration):
|
||||
"""配置"""
|
||||
|
||||
pass
|
||||
|
||||
@ -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
|
||||
@ -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:
|
||||
|
||||
"""
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
@ -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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 // 临时兼容
|
||||
}
|
||||
@ -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模块已处理权限验证和请求头,使用时无需再手动添加认证头
|
||||
*/
|
||||
222
web/src/apis/knowledge_api.js
Normal file
222
web/src/apis/knowledge_api.js
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
183
web/src/apis/system_api.js
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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('信息配置重新加载失败')
|
||||
|
||||
@ -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('配置已重置');
|
||||
|
||||
@ -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
|
||||
});
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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 || '索引添加成功');
|
||||
// 刷新图谱信息
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 });
|
||||
|
||||
Loading…
Reference in New Issue
Block a user