diff --git a/management/server/routes/knowledgebases/routes.py b/management/server/routes/knowledgebases/routes.py index 9771a38..c3f7343 100644 --- a/management/server/routes/knowledgebases/routes.py +++ b/management/server/routes/knowledgebases/routes.py @@ -1,19 +1,22 @@ import traceback + from flask import request from services.knowledgebases.service import KnowledgebaseService -from utils import success_response, error_response +from utils import error_response, success_response + from .. import knowledgebase_bp -@knowledgebase_bp.route('', methods=['GET']) + +@knowledgebase_bp.route("", methods=["GET"]) def get_knowledgebase_list(): """获取知识库列表""" try: params = { - 'page': int(request.args.get('currentPage', 1)), - 'size': int(request.args.get('size', 10)), - 'name': request.args.get('name', ''), - 'sort_by': request.args.get("sort_by", "create_time"), - 'sort_order': request.args.get("sort_order", "desc") + "page": int(request.args.get("currentPage", 1)), + "size": int(request.args.get("size", 10)), + "name": request.args.get("name", ""), + "sort_by": request.args.get("sort_by", "create_time"), + "sort_order": request.args.get("sort_order", "desc"), } result = KnowledgebaseService.get_knowledgebase_list(**params) return success_response(result) @@ -22,87 +25,84 @@ def get_knowledgebase_list(): except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('/', methods=['GET']) + +@knowledgebase_bp.route("/", methods=["GET"]) def get_knowledgebase_detail(kb_id): """获取知识库详情""" try: - knowledgebase = KnowledgebaseService.get_knowledgebase_detail( - kb_id=kb_id - ) + knowledgebase = KnowledgebaseService.get_knowledgebase_detail(kb_id=kb_id) if not knowledgebase: - return error_response('知识库不存在', code=404) + return error_response("知识库不存在", code=404) return success_response(knowledgebase) except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('', methods=['POST']) + +@knowledgebase_bp.route("", methods=["POST"]) def create_knowledgebase(): """创建知识库""" try: data = request.json - if not data.get('name'): - return error_response('知识库名称不能为空', code=400) - + if not data.get("name"): + return error_response("知识库名称不能为空", code=400) + # 移除 created_by 参数 kb = KnowledgebaseService.create_knowledgebase(**data) return success_response(kb, "创建成功", code=0) except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('/', methods=['PUT']) + +@knowledgebase_bp.route("/", methods=["PUT"]) def update_knowledgebase(kb_id): """更新知识库""" try: data = request.json - kb = KnowledgebaseService.update_knowledgebase( - kb_id=kb_id, - **data - ) + kb = KnowledgebaseService.update_knowledgebase(kb_id=kb_id, **data) if not kb: - return error_response('知识库不存在', code=404) + return error_response("知识库不存在", code=404) return success_response(kb) except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('/', methods=['DELETE']) + +@knowledgebase_bp.route("/", methods=["DELETE"]) def delete_knowledgebase(kb_id): """删除知识库""" try: - result = KnowledgebaseService.delete_knowledgebase( - kb_id=kb_id - ) + result = KnowledgebaseService.delete_knowledgebase(kb_id=kb_id) if not result: - return error_response('知识库不存在', code=404) - return success_response(message='删除成功') + return error_response("知识库不存在", code=404) + return success_response(message="删除成功") except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('/batch', methods=['DELETE']) + +@knowledgebase_bp.route("/batch", methods=["DELETE"]) def batch_delete_knowledgebase(): """批量删除知识库""" try: data = request.json - if not data or not data.get('ids'): - return error_response('请选择要删除的知识库', code=400) - - result = KnowledgebaseService.batch_delete_knowledgebase( - kb_ids=data['ids'] - ) - return success_response(message=f'成功删除 {result} 个知识库') + if not data or not data.get("ids"): + return error_response("请选择要删除的知识库", code=400) + + result = KnowledgebaseService.batch_delete_knowledgebase(kb_ids=data["ids"]) + return success_response(message=f"成功删除 {result} 个知识库") except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('//documents', methods=['GET']) + +@knowledgebase_bp.route("//documents", methods=["GET"]) def get_knowledgebase_documents(kb_id): """获取知识库下的文档列表""" try: params = { - 'kb_id': kb_id, - 'page': int(request.args.get('currentPage', 1)), - 'size': int(request.args.get('size', 10)), - 'name': request.args.get('name', ''), - 'sort_by': request.args.get("sort_by", "create_time"), - 'sort_order': request.args.get("sort_order", "desc") + "kb_id": kb_id, + "page": int(request.args.get("currentPage", 1)), + "size": int(request.args.get("size", 10)), + "name": request.args.get("name", ""), + "sort_by": request.args.get("sort_by", "create_time"), + "sort_order": request.args.get("sort_order", "desc"), } result = KnowledgebaseService.get_knowledgebase_documents(**params) return success_response(result) @@ -111,7 +111,8 @@ def get_knowledgebase_documents(kb_id): except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('//documents', methods=['POST']) + +@knowledgebase_bp.route("//documents", methods=["POST"]) def add_documents_to_knowledgebase(kb_id): """添加文档到知识库""" try: @@ -119,89 +120,86 @@ def add_documents_to_knowledgebase(kb_id): data = request.json if not data: print("[ERROR] 请求数据为空") - return error_response('请求数据不能为空', code=400) - - file_ids = data.get('file_ids', []) + return error_response("请求数据不能为空", code=400) + + file_ids = data.get("file_ids", []) print(f"[DEBUG] 接收到的file_ids: {file_ids}, 类型: {type(file_ids)}") - + try: - result = KnowledgebaseService.add_documents_to_knowledgebase( - kb_id=kb_id, - file_ids=file_ids - ) + result = KnowledgebaseService.add_documents_to_knowledgebase(kb_id=kb_id, file_ids=file_ids) print(f"[DEBUG] 服务层处理成功,结果: {result}") - return success_response( - data=result, - message="添加成功", - code=201 - ) + return success_response(data=result, message="添加成功", code=201) except Exception as service_error: print(f"[ERROR] 服务层错误详情: {str(service_error)}") - + traceback.print_exc() return error_response(str(service_error), code=500) - + except Exception as e: print(f"[ERROR] 路由层错误详情: {str(e)}") traceback.print_exc() return error_response(str(e), code=500) -@knowledgebase_bp.route('/documents/', methods=['DELETE', 'OPTIONS']) + +@knowledgebase_bp.route("/documents/", methods=["DELETE", "OPTIONS"]) def delete_document(doc_id): """删除文档""" # 处理 OPTIONS 预检请求 - if request.method == 'OPTIONS': + if request.method == "OPTIONS": response = success_response({}) # 添加 CORS 相关头 - response.headers.add('Access-Control-Allow-Methods', 'DELETE') - response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') + response.headers.add("Access-Control-Allow-Methods", "DELETE") + response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization") return response - + try: KnowledgebaseService.delete_document(doc_id) return success_response(message="删除成功") except Exception as e: return error_response(str(e)) -@knowledgebase_bp.route('/documents//parse', methods=['POST']) + +@knowledgebase_bp.route("/documents//parse", methods=["POST"]) def parse_document(doc_id): """开始解析文档""" # 处理 OPTIONS 预检请求 - if request.method == 'OPTIONS': + if request.method == "OPTIONS": response = success_response({}) # 添加 CORS 相关头 - response.headers.add('Access-Control-Allow-Methods', 'POST') - response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') + response.headers.add("Access-Control-Allow-Methods", "POST") + response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization") return response - + try: result = KnowledgebaseService.async_parse_document(doc_id) return success_response(data=result) except Exception as e: return error_response(str(e), code=500) -@knowledgebase_bp.route('/documents//parse/progress', methods=['GET']) + +@knowledgebase_bp.route("/documents//parse/progress", methods=["GET"]) def get_parse_progress(doc_id): """获取文档解析进度""" # 处理 OPTIONS 预检请求 - if request.method == 'OPTIONS': + if request.method == "OPTIONS": response = success_response({}) # 添加 CORS 相关头 - response.headers.add('Access-Control-Allow-Methods', 'GET') - response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') + response.headers.add("Access-Control-Allow-Methods", "GET") + response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization") return response - + try: result = KnowledgebaseService.get_document_parse_progress(doc_id) - if isinstance(result, dict) and 'error' in result: - return error_response(result['error'], code=404) + if isinstance(result, dict) and "error" in result: + return error_response(result["error"], code=404) return success_response(data=result) except Exception as e: print(f"获取解析进度失败: {str(e)}") return error_response("解析进行中,请稍后重试", code=202) + # 获取系统 Embedding 配置路由 -@knowledgebase_bp.route('/system_embedding_config', methods=['GET']) +@knowledgebase_bp.route("/system_embedding_config", methods=["GET"]) def get_system_embedding_config_route(): """获取系统级 Embedding 配置的API端点""" try: @@ -209,69 +207,68 @@ def get_system_embedding_config_route(): return success_response(data=config_data) except Exception as e: print(f"获取系统 Embedding 配置失败: {str(e)}") - return error_response(message=f"获取配置失败: {str(e)}", code=500) # 返回通用错误信息 + return error_response(message=f"获取配置失败: {str(e)}", code=500) # 返回通用错误信息 + # 设置系统 Embedding 配置路由 -@knowledgebase_bp.route('/system_embedding_config', methods=['POST']) +@knowledgebase_bp.route("/system_embedding_config", methods=["POST"]) def set_system_embedding_config_route(): """设置系统级 Embedding 配置的API端点""" try: data = request.json if not data: - return error_response('请求数据不能为空', code=400) + return error_response("请求数据不能为空", code=400) - llm_name = data.get('llm_name', '').strip() - api_base = data.get('api_base', '').strip() - api_key = data.get('api_key', '').strip() # 允许空 + llm_name = data.get("llm_name", "").strip() + api_base = data.get("api_base", "").strip() + api_key = data.get("api_key", "").strip() # 允许空 if not llm_name or not api_base: - return error_response('模型名称和 API 地址不能为空', code=400) + return error_response("模型名称和 API 地址不能为空", code=400) # 调用服务层进行处理(包括连接测试和数据库操作) - success, message = KnowledgebaseService.set_system_embedding_config( - llm_name=llm_name, - api_base=api_base, - api_key=api_key - ) + success, message = KnowledgebaseService.set_system_embedding_config(llm_name=llm_name, api_base=api_base, api_key=api_key) if success: return success_response(message=message) else: # 如果服务层返回失败(例如连接测试失败或数据库错误),将消息返回给前端 - return error_response(message=message, code=400) # 使用 400 表示操作失败 + return error_response(message=message, code=400) # 使用 400 表示操作失败 except Exception as e: # 捕获路由层或未预料的服务层异常 print(f"设置系统 Embedding 配置失败: {str(e)}") return error_response(message=f"设置配置时发生内部错误: {str(e)}", code=500) -@knowledgebase_bp.route('/documents//parse', methods=['POST']) -def parse_document_async(doc_id): # 函数名改为 async 以区分 + +@knowledgebase_bp.route("/documents//parse", methods=["POST"]) +def parse_document_async(doc_id): # 函数名改为 async 以区分 """开始异步解析单个文档""" - if request.method == 'OPTIONS': + if request.method == "OPTIONS": response = success_response({}) - response.headers.add('Access-Control-Allow-Methods', 'POST') - response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') + response.headers.add("Access-Control-Allow-Methods", "POST") + response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization") return response try: - result = KnowledgebaseService.parse_document(doc_id) # 调用同步版本 + result = KnowledgebaseService.parse_document(doc_id) # 调用同步版本 if result.get("success"): - return success_response(data={"message": f"文档 {doc_id} 同步解析完成。", "details": result}) + return success_response(data={"message": f"文档 {doc_id} 同步解析完成。", "details": result}) else: - return error_response(result.get("message", "解析失败"), code=500) + return error_response(result.get("message", "解析失败"), code=500) except Exception as e: return error_response(str(e), code=500) + # 启动顺序批量解析路由 -@knowledgebase_bp.route('//batch_parse_sequential/start', methods=['POST']) +@knowledgebase_bp.route("//batch_parse_sequential/start", methods=["POST"]) def start_sequential_batch_parse_route(kb_id): """异步启动知识库的顺序批量解析任务""" - if request.method == 'OPTIONS': + if request.method == "OPTIONS": response = success_response({}) - response.headers.add('Access-Control-Allow-Methods', 'POST') - response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') + response.headers.add("Access-Control-Allow-Methods", "POST") + response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization") return response try: @@ -286,14 +283,15 @@ def start_sequential_batch_parse_route(kb_id): traceback.print_exc() return error_response(f"启动顺序批量解析失败: {str(e)}", code=500) + # 获取顺序批量解析进度路由 -@knowledgebase_bp.route('//batch_parse_sequential/progress', methods=['GET']) +@knowledgebase_bp.route("//batch_parse_sequential/progress", methods=["GET"]) def get_sequential_batch_parse_progress_route(kb_id): """获取知识库的顺序批量解析任务进度""" - if request.method == 'OPTIONS': + if request.method == "OPTIONS": response = success_response({}) - response.headers.add('Access-Control-Allow-Methods', 'GET') - response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization') + response.headers.add("Access-Control-Allow-Methods", "GET") + response.headers.add("Access-Control-Allow-Headers", "Content-Type,Authorization") return response try: @@ -303,4 +301,4 @@ def get_sequential_batch_parse_progress_route(kb_id): except Exception as e: print(f"获取顺序批量解析进度路由处理失败 (KB ID: {kb_id}): {str(e)}") traceback.print_exc() - return error_response(f"获取进度失败: {str(e)}", code=500) \ No newline at end of file + return error_response(f"获取进度失败: {str(e)}", code=500) diff --git a/management/server/services/knowledgebases/document_parser.py b/management/server/services/knowledgebases/document_parser.py index b307af7..78ae958 100644 --- a/management/server/services/knowledgebases/document_parser.py +++ b/management/server/services/knowledgebases/document_parser.py @@ -8,7 +8,6 @@ import shutil import tempfile import time from datetime import datetime -from io import BytesIO from urllib.parse import urlparse import requests @@ -61,7 +60,7 @@ def perform_parse(doc_id, doc_info, file_info, embedding_config, kb_info): if embedding_model_name == "netease-youdao/bce-embedding-base_v1": embedding_model_name = "BAAI/bge-m3" - embedding_api_base = embedding_config.get("api_base") if embedding_config and embedding_config.get("api_base") else "http://localhost:8000" # 默认基础 URL + embedding_api_base = embedding_config.get("api_base") if embedding_config and embedding_config.get("api_base") else "http://localhost:11434" # 默认基础 URL # 如果 API 基础地址为空字符串,设置为硅基流动的 API 地址 if embedding_api_base == "": diff --git a/management/server/services/knowledgebases/service.py b/management/server/services/knowledgebases/service.py index c527d5f..48938cf 100644 --- a/management/server/services/knowledgebases/service.py +++ b/management/server/services/knowledgebases/service.py @@ -105,7 +105,8 @@ class KnowledgebaseService: k.description, k.create_date, k.update_date, - k.doc_num + k.doc_num, + k.avatar FROM knowledgebase k WHERE k.id = %s """ @@ -334,6 +335,13 @@ class KnowledgebaseService: update_fields.append("permission = %s") params.append(data["permission"]) + if "avatar" in data and data["avatar"]: + avatar_base64 = data["avatar"] + # 拼接上前缀 + full_avatar_url = f"data:image/png;base64,{avatar_base64}" + update_fields.append("avatar = %s") + params.append(full_avatar_url) + # 更新时间 current_time = datetime.now() update_date = current_time.strftime("%Y-%m-%d %H:%M:%S") diff --git a/management/web/src/common/apis/kbs/knowledgebase.ts b/management/web/src/common/apis/kbs/knowledgebase.ts index f7a2bff..66694cb 100644 --- a/management/web/src/common/apis/kbs/knowledgebase.ts +++ b/management/web/src/common/apis/kbs/knowledgebase.ts @@ -1,5 +1,19 @@ import { request } from "@/http/axios" +// 定义修改数据库界面中显示的信息 +interface KbDetailData { + id: string + name: string + permission: string + avatar?: string +} + +interface ApiDetailResponse { + data: KbDetailData + code: number + message: string +} + // 获取知识库列表 export function getKnowledgeBaseListApi(params: { currentPage: number @@ -16,7 +30,7 @@ export function getKnowledgeBaseListApi(params: { } // 获取知识库详情 -export function getKnowledgeBaseDetailApi(id: string) { +export function getKbDetailApi(id: string): Promise { return request({ url: `/api/v1/knowledgebases/${id}`, method: "get" @@ -44,6 +58,7 @@ export function updateKnowledgeBaseApi(id: string, data: { description?: string language?: string permission?: string + avatar?: string }) { return request({ url: `/api/v1/knowledgebases/${id}`, diff --git a/management/web/src/pages/knowledgebase/index.vue b/management/web/src/pages/knowledgebase/index.vue index 0cc76f0..de0b35a 100644 --- a/management/web/src/pages/knowledgebase/index.vue +++ b/management/web/src/pages/knowledgebase/index.vue @@ -1,6 +1,6 @@