2024-10-02 20:11:28 +08:00
|
|
|
|
import os
|
2025-03-06 23:55:20 +08:00
|
|
|
|
import asyncio
|
2025-03-17 19:58:00 +08:00
|
|
|
|
import traceback
|
2025-03-20 20:31:14 +08:00
|
|
|
|
from fastapi import APIRouter, File, UploadFile, HTTPException, Depends, Body, Form, Query
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-02-27 19:35:25 +08:00
|
|
|
|
from src.utils import logger, hashstr
|
2025-06-27 01:55:04 +08:00
|
|
|
|
from src import executor, config, knowledge_base, graph_base
|
2025-05-02 23:56:59 +08:00
|
|
|
|
from server.utils.auth_middleware import get_admin_user
|
|
|
|
|
|
from server.models.user_model import User
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
data = APIRouter(prefix="/data")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@data.get("/")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_databases(current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
try:
|
2025-03-20 19:51:46 +08:00
|
|
|
|
database = knowledge_base.get_databases()
|
2024-10-02 20:11:28 +08:00
|
|
|
|
except Exception as e:
|
2025-03-20 19:51:46 +08:00
|
|
|
|
logger.error(f"获取数据库列表失败 {e}, {traceback.format_exc()}")
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return {"message": f"获取数据库列表失败 {e}", "databases": []}
|
|
|
|
|
|
return database
|
|
|
|
|
|
|
|
|
|
|
|
@data.post("/")
|
|
|
|
|
|
async def create_database(
|
|
|
|
|
|
database_name: str = Body(...),
|
|
|
|
|
|
description: str = Body(...),
|
2025-06-27 01:55:04 +08:00
|
|
|
|
embed_model_name: str = Body(...),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
current_user: User = Depends(get_admin_user)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
):
|
|
|
|
|
|
logger.debug(f"Create database {database_name}")
|
2025-03-20 19:51:46 +08:00
|
|
|
|
try:
|
2025-06-27 12:22:35 +08:00
|
|
|
|
embed_info = config.embed_model_names[embed_model_name]
|
2025-03-20 19:51:46 +08:00
|
|
|
|
database_info = knowledge_base.create_database(
|
|
|
|
|
|
database_name,
|
|
|
|
|
|
description,
|
2025-06-27 01:55:04 +08:00
|
|
|
|
embed_info=embed_info
|
2025-03-20 19:51:46 +08:00
|
|
|
|
)
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"创建数据库失败 {e}, {traceback.format_exc()}")
|
|
|
|
|
|
return {"message": f"创建数据库失败 {e}", "status": "failed"}
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return database_info
|
|
|
|
|
|
|
|
|
|
|
|
@data.delete("/")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def delete_database(db_id, current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
logger.debug(f"Delete database {db_id}")
|
2025-03-20 19:51:46 +08:00
|
|
|
|
knowledge_base.delete_database(db_id)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return {"message": "删除成功"}
|
|
|
|
|
|
|
|
|
|
|
|
@data.post("/query-test")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def query_test(query: str = Body(...), meta: dict = Body(...), current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
logger.debug(f"Query test in {meta}: {query}")
|
2025-06-27 01:55:04 +08:00
|
|
|
|
result = await knowledge_base.aquery(query, **meta)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return result
|
|
|
|
|
|
|
2025-06-27 01:55:04 +08:00
|
|
|
|
@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')
|
|
|
|
|
|
|
2025-05-26 13:12:17 +08:00
|
|
|
|
try:
|
2025-06-27 01:55:04 +08:00
|
|
|
|
# 使用统一的 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"}
|
2025-05-26 13:12:17 +08:00
|
|
|
|
except Exception as e:
|
2025-06-27 01:55:04 +08:00
|
|
|
|
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)
|
2025-03-23 23:10:32 +08:00
|
|
|
|
|
2025-05-07 00:38:33 +08:00
|
|
|
|
@data.post("/url-to-chunk")
|
2025-05-26 13:12:17 +08:00
|
|
|
|
async def url_to_chunk(db_id: str = Body(...), urls: list[str] = Body(...), params: dict = Body(...), current_user: User = Depends(get_admin_user)):
|
2025-06-27 01:55:04 +08:00
|
|
|
|
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)
|
2025-05-07 00:38:33 +08:00
|
|
|
|
|
2024-10-02 20:11:28 +08:00
|
|
|
|
@data.post("/add-by-file")
|
2025-05-24 11:29:45 +08:00
|
|
|
|
async def create_document_by_file(db_id: str = Body(...), files: list[str] = Body(...), current_user: User = Depends(get_admin_user)):
|
2025-06-27 01:55:04 +08:00
|
|
|
|
raise ValueError("This method is deprecated. Use /add-files instead.")
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-03-23 23:10:32 +08:00
|
|
|
|
@data.post("/add-by-chunks")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def add_by_chunks(db_id: str = Body(...), file_chunks: dict = Body(...), current_user: User = Depends(get_admin_user)):
|
2025-06-27 01:55:04 +08:00
|
|
|
|
raise ValueError("This method is deprecated. Use /add-files instead.")
|
2025-03-23 23:10:32 +08:00
|
|
|
|
|
2024-10-08 22:16:17 +08:00
|
|
|
|
@data.get("/info")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_database_info(db_id: str, current_user: User = Depends(get_admin_user)):
|
2025-03-23 19:22:00 +08:00
|
|
|
|
# logger.debug(f"Get database {db_id} info")
|
2025-03-20 19:51:46 +08:00
|
|
|
|
database = knowledge_base.get_database_info(db_id)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
if database is None:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="Database not found")
|
|
|
|
|
|
return database
|
|
|
|
|
|
|
|
|
|
|
|
@data.delete("/document")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def delete_document(db_id: str = Body(...), file_id: str = Body(...), current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
logger.debug(f"DELETE document {file_id} info in {db_id}")
|
2025-06-27 01:55:04 +08:00
|
|
|
|
await knowledge_base.delete_file(db_id, file_id)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return {"message": "删除成功"}
|
|
|
|
|
|
|
|
|
|
|
|
@data.get("/document")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_document_info(db_id: str, file_id: str, current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
logger.debug(f"GET document {file_id} info in {db_id}")
|
2024-10-08 22:16:17 +08:00
|
|
|
|
|
|
|
|
|
|
try:
|
2025-06-27 01:55:04 +08:00
|
|
|
|
info = await knowledge_base.get_file_info(db_id, file_id)
|
2024-10-08 22:16:17 +08:00
|
|
|
|
except Exception as e:
|
2025-03-17 19:58:00 +08:00
|
|
|
|
logger.error(f"Failed to get file info, {e}, {db_id=}, {file_id=}, {traceback.format_exc()}")
|
2025-03-20 19:51:46 +08:00
|
|
|
|
info = {"message": "Failed to get file info", "status": "failed"}
|
2024-10-08 22:16:17 +08:00
|
|
|
|
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return info
|
|
|
|
|
|
|
|
|
|
|
|
@data.post("/upload")
|
2025-03-20 20:31:14 +08:00
|
|
|
|
async def upload_file(
|
|
|
|
|
|
file: UploadFile = File(...),
|
2025-05-24 11:29:45 +08:00
|
|
|
|
db_id: str | None = Query(None),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
current_user: User = Depends(get_admin_user)
|
2025-03-20 20:31:14 +08:00
|
|
|
|
):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
if not file.filename:
|
|
|
|
|
|
raise HTTPException(status_code=400, detail="No selected file")
|
|
|
|
|
|
|
2025-03-20 20:31:14 +08:00
|
|
|
|
# 根据db_id获取上传路径,如果db_id为None则使用默认路径
|
|
|
|
|
|
if db_id:
|
|
|
|
|
|
upload_dir = knowledge_base.get_db_upload_path(db_id)
|
|
|
|
|
|
else:
|
2025-06-28 20:19:26 +08:00
|
|
|
|
upload_dir = os.path.join(config.save_dir, "database", "uploads")
|
2025-03-20 20:31:14 +08:00
|
|
|
|
|
2024-10-23 16:24:29 +08:00
|
|
|
|
basename, ext = os.path.splitext(file.filename)
|
2025-03-06 23:55:20 +08:00
|
|
|
|
filename = f"{basename}_{hashstr(basename, 4, with_salt=True)}{ext}".lower()
|
2024-10-02 20:11:28 +08:00
|
|
|
|
file_path = os.path.join(upload_dir, filename)
|
2025-04-01 18:37:30 +08:00
|
|
|
|
os.makedirs(upload_dir, exist_ok=True)
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
with open(file_path, "wb") as buffer:
|
|
|
|
|
|
buffer.write(await file.read())
|
|
|
|
|
|
|
2025-03-20 20:31:14 +08:00
|
|
|
|
return {"message": "File successfully uploaded", "file_path": file_path, "db_id": db_id}
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
@data.get("/graph")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_graph_info(current_user: User = Depends(get_admin_user)):
|
2025-03-20 19:51:46 +08:00
|
|
|
|
graph_info = graph_base.get_graph_info()
|
|
|
|
|
|
if graph_info is None:
|
|
|
|
|
|
raise HTTPException(status_code=400, detail="图数据库获取出错")
|
2024-10-02 20:11:28 +08:00
|
|
|
|
return graph_info
|
|
|
|
|
|
|
2025-03-08 22:49:13 +08:00
|
|
|
|
@data.post("/graph/index-nodes")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def index_nodes(data: dict = Body(default={}), current_user: User = Depends(get_admin_user)):
|
2025-03-20 19:51:46 +08:00
|
|
|
|
if not graph_base.is_running():
|
2025-03-08 22:49:13 +08:00
|
|
|
|
raise HTTPException(status_code=400, detail="图数据库未启动")
|
|
|
|
|
|
|
|
|
|
|
|
# 获取参数或使用默认值
|
|
|
|
|
|
kgdb_name = data.get('kgdb_name', 'neo4j')
|
|
|
|
|
|
|
|
|
|
|
|
# 调用GraphDatabase的add_embedding_to_nodes方法
|
2025-03-20 19:51:46 +08:00
|
|
|
|
count = graph_base.add_embedding_to_nodes(kgdb_name=kgdb_name)
|
2025-03-08 22:49:13 +08:00
|
|
|
|
|
|
|
|
|
|
return {"status": "success", "message": f"已成功为{count}个节点添加嵌入向量", "indexed_count": count}
|
|
|
|
|
|
|
2024-10-02 20:11:28 +08:00
|
|
|
|
@data.get("/graph/node")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_graph_node(entity_name: str, current_user: User = Depends(get_admin_user)):
|
2025-03-20 19:51:46 +08:00
|
|
|
|
result = graph_base.query_node(entity_name=entity_name)
|
2025-04-15 10:49:30 +08:00
|
|
|
|
return {"result": graph_base.format_query_result_to_graph(result), "message": "success"}
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
@data.get("/graph/nodes")
|
2025-05-02 23:56:59 +08:00
|
|
|
|
async def get_graph_nodes(kgdb_name: str, num: int, current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
logger.debug(f"Get graph nodes in {kgdb_name} with {num} nodes")
|
2025-03-20 19:51:46 +08:00
|
|
|
|
result = graph_base.get_sample_nodes(kgdb_name, num)
|
2025-04-15 10:49:30 +08:00
|
|
|
|
return {"result": graph_base.format_general_results(result), "message": "success"}
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-02-28 00:27:58 +08:00
|
|
|
|
@data.post("/graph/add-by-jsonl")
|
2025-05-24 11:29:45 +08:00
|
|
|
|
async def add_graph_entity(file_path: str = Body(...), kgdb_name: str | None = Body(None), current_user: User = Depends(get_admin_user)):
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
|
|
|
|
|
if not file_path.endswith('.jsonl'):
|
2025-04-24 22:55:10 +08:00
|
|
|
|
return {"message": "文件格式错误,请上传jsonl文件", "status": "failed"}
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-04-24 22:55:10 +08:00
|
|
|
|
try:
|
|
|
|
|
|
await graph_base.jsonl_file_add_entity(file_path, kgdb_name)
|
|
|
|
|
|
return {"message": "实体添加成功", "status": "success"}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"添加实体失败: {e}, {traceback.format_exc()}")
|
|
|
|
|
|
return {"message": f"添加实体失败: {e}", "status": "failed"}
|
2024-10-02 20:11:28 +08:00
|
|
|
|
|
2025-05-10 10:24:18 +08:00
|
|
|
|
@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}")
|
|
|
|
|
|
try:
|
|
|
|
|
|
database = knowledge_base.update_database(db_id, name, description)
|
|
|
|
|
|
return {"message": "更新成功", "database": database}
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
logger.error(f"更新数据库失败 {e}, {traceback.format_exc()}")
|
|
|
|
|
|
raise HTTPException(status_code=400, detail=f"更新数据库失败: {e}")
|
|
|
|
|
|
|