From 7af39b0737df9795d97f8adc017b2fccc0defebb Mon Sep 17 00:00:00 2001 From: yumoqing Date: Wed, 22 Jul 2026 18:31:38 +0800 Subject: [PATCH] feat: Tree CRUD APIs - dir_create/delete/list --- conf/config.json | 5 +++- init.py | 66 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/conf/config.json b/conf/config.json index 7222612..54532a8 100644 --- a/conf/config.json +++ b/conf/config.json @@ -52,7 +52,10 @@ {"leading": "/api/engines", "registerfunction": "engines"}, {"leading": "/api/search", "registerfunction": "search"}, {"leading": "/api/doc/upload", "registerfunction": "doc_upload"}, - {"leading": "/api/doc/delete", "registerfunction": "doc_delete"} + {"leading": "/api/doc/delete", "registerfunction": "doc_delete"}, + {"leading": "/api/dir/create", "registerfunction": "dir_create"}, + {"leading": "/api/dir/delete", "registerfunction": "dir_delete"}, + {"leading": "/api/dir/list", "registerfunction": "dir_list"} ] }, "hot_reload": true diff --git a/init.py b/init.py index 980ffa6..30212a5 100644 --- a/init.py +++ b/init.py @@ -242,6 +242,69 @@ def _call_graph(path, data, timeout=10): return json.loads(resp.read()) +async def dir_create_handler(request, params_kw, *args, **kwargs): + """创建目录""" + env = request._run_ns + try: + kb_id = params_kw.get("kb_id", "") + parent_id = params_kw.get("parent_id", "") + dir_name = params_kw.get("dir_name", "") + if not kb_id or not dir_name: + return json.dumps({"error": "kb_id and dir_name required"}) + async with get_sor_context(env, 'rag') as sor: + dir_id = uuid.uuid4().hex[:16] + await sor.sqlExe( + "INSERT INTO document_chunks (id, doc_id, kb_id, chunk_index, chunk_type, content, description, created_at) " + "VALUES (${id}$, '', ${kb_id}$, 0, 'directory', ${name}$, ${parent}$, NOW())", + {"id": dir_id, "kb_id": kb_id, "name": dir_name, "parent": parent_id}) + return json.dumps({"status": "SUCCEEDED", "dir_id": dir_id}) + except Exception as e: + exception(f"dir_create: {e}") + return json.dumps({"error": str(e)}) + + +async def dir_delete_handler(request, params_kw, *args, **kwargs): + """删除目录/文件""" + env = request._run_ns + try: + item_id = params_kw.get("item_id", "") + if not item_id: + return json.dumps({"error": "item_id required"}) + async with get_sor_context(env, 'rag') as sor: + await sor.sqlExe("DELETE FROM document_chunks WHERE id=${id}$ OR doc_id=${id}$", {"id": item_id}) + return json.dumps({"status": "SUCCEEDED"}) + except Exception as e: + exception(f"dir_delete: {e}") + return json.dumps({"error": str(e)}) + + +async def dir_list_handler(request, params_kw, *args, **kwargs): + """列出知识库的目录树""" + env = request._run_ns + try: + kb_id = params_kw.get("kb_id", "") + if not kb_id: + return json.dumps({"error": "kb_id required"}) + async with get_sor_context(env, 'rag') as sor: + # Get directories (chunk_type='directory') + dirs = await sor.sqlExe( + "SELECT id, content as label, description as parent_id FROM document_chunks WHERE kb_id=${kb_id}$ AND chunk_type='directory'", + {"kb_id": kb_id}) + # Get files (documents table) + docs = await sor.sqlExe( + "SELECT id, file_name as label, '' as parent_id FROM documents WHERE kb_id=${kb_id}$", + {"kb_id": kb_id}) + items = [] + for d in dirs: + items.append({"id": d.id, "label": d.label, "parent_id": d.parent_id or "", "type": "dir"}) + for d in docs: + items.append({"id": d.id, "label": d.label, "parent_id": "", "type": "file"}) + return json.dumps({"status": "SUCCEEDED", "items": items}) + except Exception as e: + exception(f"dir_list: {e}") + return json.dumps({"error": str(e)}) + + def init_ragserver(): env = ServerEnv() rf = RegisterFunction() @@ -251,3 +314,6 @@ def init_ragserver(): rf.register("search", search_handler) rf.register("doc_upload", doc_upload_handler) rf.register("doc_delete", doc_delete_handler) + rf.register("dir_create", dir_create_handler) + rf.register("dir_delete", dir_delete_handler) + rf.register("dir_list", dir_list_handler)