import os ns = params_kw.copy() kb_id = ns.get('kb_id', '') if not kb_id: return {"widgettype": "urlwidget", "options": {"url": entire_url('/rag/knowledge_bases_list/index.ui')}} # KB 维护角色校验(机构隔离 + 角色) from rag.init import check_kb_perm env = request._run_ns db = DBPools() async with db.sqlorContext(get_module_dbname('rag')) as sor: _ok, _kb, _msg = await check_kb_perm(env, sor, kb_id, 'maintain') if not _ok: return {"widgettype": "Text", "options": {"text": "⛔ " + _msg, "cfontsize": 13, "color": "#f56c6c", "halign": "center"}} env = request._run_ns userorgid = await env.get_userorgid() deleted = {"documents": 0, "chunks": 0, "tags": 0, "media_tags": 0, "files": 0, "vdb": 0, "graph": 0, "entities": 0} async with get_sor_context(env, 'rag') as sor: kb = await sor.sqlExe( "SELECT id, name FROM rag_knowledge_bases WHERE id=${id}$ AND (org_id=${org_id}$ OR org_id IS NULL)", {"id": kb_id, "org_id": userorgid}) if not kb: return {"widgettype": "urlwidget", "options": {"url": entire_url('/rag/knowledge_bases_list/index.ui')}} docs = await sor.sqlExe( "SELECT id, file_path FROM rag_documents WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) doc_ids = [d.id for d in docs] # 1. VDB cleanup if doc_ids: chunks = await sor.sqlExe( "SELECT vector_id FROM rag_document_chunks WHERE doc_id IN (${ids}$) AND vector_id IS NOT NULL AND vector_id != ''", {"ids": doc_ids}) vector_ids = [c.vector_id for c in chunks] if vector_ids: try: client = StreamHttpClient() await client.request('POST', 'https://vectordb.opencomputing.net:10443/v1/delete', json={"colname": kb_id, "ids": vector_ids}) deleted["vdb"] = len(vector_ids) except Exception as e: info(f'delete_kb vdb error: {e}') # 2. Graph DB cleanup try: client2 = StreamHttpClient() await client2.request('POST', 'http://localhost:9092/api/graph/delete', json={"graph": kb_id}) deleted["graph"] = 1 except Exception as e: info(f'delete_kb graph error: {e}') # 3. Entities & relations er = await sor.sqlExe( "SELECT COUNT(*) AS cnt FROM rag_entity_relations WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) await sor.sqlExe("DELETE FROM rag_entity_relations WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["entities"] += er[0].cnt if er else 0 ent = await sor.sqlExe( "SELECT COUNT(*) AS cnt FROM rag_entities WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) await sor.sqlExe("DELETE FROM rag_entities WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["entities"] += ent[0].cnt if ent else 0 # 4. Chunks ch = await sor.sqlExe( "SELECT COUNT(*) AS cnt FROM rag_document_chunks WHERE doc_id IN (${ids}$)", {"ids": doc_ids}) await sor.sqlExe("DELETE FROM rag_document_chunks WHERE doc_id IN (${ids}$)", {"ids": doc_ids}) deleted["chunks"] = ch[0].cnt if ch else 0 # Media tags mt = await sor.sqlExe("SELECT COUNT(*) AS cnt FROM rag_media_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) await sor.sqlExe("DELETE FROM rag_media_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["media_tags"] = mt[0].cnt if mt else 0 # Tags t = await sor.sqlExe("SELECT COUNT(*) AS cnt FROM rag_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) await sor.sqlExe("DELETE FROM rag_tags WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["tags"] = t[0].cnt if t else 0 # Documents await sor.sqlExe("DELETE FROM rag_documents WHERE kb_id=${kb_id}$", {"kb_id": kb_id}) deleted["documents"] = len(doc_ids) # KB itself await sor.sqlExe("DELETE FROM rag_knowledge_bases WHERE id=${id}$", {"id": kb_id}) # Physical files — paths are like /44/126/174/61/file.mp4 for d in docs: fp = d.file_path or '' if fp and fp.startswith("/"): try: real_path = env.realpath(fp) if os.path.exists(real_path): os.remove(real_path) deleted["files"] += 1 except Exception: pass info(f'delete_kb {kb_id}: {deleted}') return {"widgettype": "urlwidget", "options": {"url": entire_url('/rag/knowledge_bases_list/index.ui')}}