From 6b30d1a02f917cb89c0e0cc2b7231300bc534f8a Mon Sep 17 00:00:00 2001
From: Dallas98 <990259227@qq.com>
Date: Thu, 12 Mar 2026 17:32:14 +0800
Subject: [PATCH 1/5] feat: enhance DetailHeader component with confirmation
dialog props and improve knowledge base detail handling
---
frontend/src/components/DetailHeader.tsx | 8 +++
.../Detail/KnowledgeBaseDetail.tsx | 53 +++++++++++++++----
2 files changed, 50 insertions(+), 11 deletions(-)
diff --git a/frontend/src/components/DetailHeader.tsx b/frontend/src/components/DetailHeader.tsx
index 001c8b47..646c86b2 100644
--- a/frontend/src/components/DetailHeader.tsx
+++ b/frontend/src/components/DetailHeader.tsx
@@ -20,6 +20,14 @@ interface OperationItem {
onMenuClick?: (key: string) => void;
onClick?: () => void;
danger?: boolean;
+ confirm?: {
+ title: string;
+ description?: string;
+ cancelText?: string;
+ okText?: string;
+ okType?: "default" | "primary" | "danger";
+ onConfirm?: () => void;
+ };
}
interface TagConfig {
diff --git a/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx b/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx
index 2802b4be..98851c44 100644
--- a/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx
+++ b/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx
@@ -15,7 +15,7 @@ import { useNavigate, useParams } from "react-router";
import DetailHeader from "@/components/DetailHeader";
import { SearchControls } from "@/components/SearchControls";
import { KBFile, KnowledgeBaseItem, KnowledgeGraphNode, KnowledgeGraphEdge, KBType } from "../knowledge-base.model";
-import { mapFileData, mapKnowledgeBase } from "../knowledge-base.const";
+import { getKBTypeMap, mapFileData, mapKnowledgeBase } from "../knowledge-base.const";
import {
deleteKnowledgeBaseByIdUsingDelete,
deleteKnowledgeBaseFileByIdUsingDelete,
@@ -30,11 +30,7 @@ import CreateKnowledgeBase from "../components/CreateKnowledgeBase";
import KnowledgeGraphView, { GraphEntitySelection } from "../components/KnowledgeGraphView";
import { useTranslation } from "react-i18next";
-interface StatisticItem {
- icon?: React.ReactNode;
- label: string;
- value: string | number;
-}
+type HeaderStatisticItem = React.ComponentProps["statistics"][number];
// Use UnifiedSearchResult from model - flat structure from backend
// Backend returns: { id, text, score, metadata, resultType, knowledgeBaseId, knowledgeBaseName }
interface RecallResult {
@@ -75,6 +71,35 @@ const KnowledgeBaseDetailPage: React.FC = () => {
const [graphData, setGraphData] = useState<{ nodes: KnowledgeGraphNode[]; edges: KnowledgeGraphEdge[] }>({ nodes: [], edges: [] });
const [graphSelection, setGraphSelection] = useState(null);
+ const kbTypeMap = getKBTypeMap(t);
+
+ const detailHeaderData = knowledgeBase
+ ? {
+ ...knowledgeBase,
+ tags: (() => {
+ const rawTags = Array.isArray((knowledgeBase as any)?.tags) ? ((knowledgeBase as any).tags as any[]) : [];
+ const normalized = rawTags
+ .map((tag) => {
+ if (!tag) return "";
+ if (typeof tag === "string") return tag;
+ return String(tag.label ?? tag.name ?? "");
+ })
+ .map((s) => s.trim())
+ .filter(Boolean);
+
+ const typeLabel = String(kbTypeMap[knowledgeBase.type as KBType]?.label ?? "").trim();
+ const all = typeLabel ? [typeLabel, ...normalized] : normalized;
+ return Array.from(new Set(all));
+ })(),
+ description:
+ knowledgeBase.description && knowledgeBase.description.trim().length > 0
+ ? knowledgeBase.description
+ : kbTypeMap[knowledgeBase.type as KBType]?.description ??
+ kbTypeMap[knowledgeBase.type as KBType]?.label ??
+ knowledgeBase.description,
+ }
+ : knowledgeBase;
+
const fetchKnowledgeBaseDetails = async (id: string) => {
const { data } = await queryKnowledgeBaseByIdUsingGet(id);
setKnowledgeBase(mapKnowledgeBase(data, true, t));
@@ -156,7 +181,8 @@ const KnowledgeBaseDetailPage: React.FC = () => {
threshold: 0.2,
knowledgeBaseIds: [knowledgeBase.id],
});
- setRecallResults(result?.data || []);
+ const data = Array.isArray(result) ? result : (result as any)?.data;
+ setRecallResults(Array.isArray(data) ? data : []);
} catch {
setRecallResults([]);
}
@@ -330,9 +356,9 @@ const KnowledgeBaseDetailPage: React.FC = () => {
@@ -457,7 +483,12 @@ const KnowledgeBaseDetailPage: React.FC = () => {
searchPlaceholder={t("knowledgeBase.detail.searchPlaceholder")}
filters={[]}
onFiltersChange={handleFiltersChange}
- onClearFilters={() => setSearchParams({ ...searchParams, filter: { type: [], status: [], tags: [] } })}
+ onClearFilters={() =>
+ setSearchParams({
+ ...searchParams,
+ filter: { type: [], status: [], tags: [], categories: [], selectedStar: false },
+ })
+ }
showViewToggle={false}
showReload={false}
/>
From 69801c1eb0490f8b0f1f84d5299945d0ee1ab6fb Mon Sep 17 00:00:00 2001
From: Dallas98 <990259227@qq.com>
Date: Thu, 12 Mar 2026 18:16:57 +0800
Subject: [PATCH 2/5] feat: enhance CardView and KnowledgeBaseDetail components
with additional props and improve API endpoint for knowledge retrieval
---
frontend/src/components/CardView.tsx | 12 ++++++----
frontend/src/components/DetailHeader.tsx | 3 +++
.../Detail/KnowledgeBaseDetail.tsx | 24 +++++++++++++++----
.../components/AddDataDialog.tsx | 14 ++++++++++-
.../pages/KnowledgeBase/knowledge-base.api.ts | 2 +-
.../module/rag/interface/knowledge_base.py | 12 +++++++++-
6 files changed, 56 insertions(+), 11 deletions(-)
diff --git a/frontend/src/components/CardView.tsx b/frontend/src/components/CardView.tsx
index b6746395..31856e65 100644
--- a/frontend/src/components/CardView.tsx
+++ b/frontend/src/components/CardView.tsx
@@ -301,11 +301,15 @@ function CardView(props: CardViewProps) {
{/* Description */}
-
-
+ triggerNode.parentElement || document.body}
+ >
+
{item?.description}
-
-
+
+
{/* Statistics */}
diff --git a/frontend/src/components/DetailHeader.tsx b/frontend/src/components/DetailHeader.tsx
index 646c86b2..fcf7480b 100644
--- a/frontend/src/components/DetailHeader.tsx
+++ b/frontend/src/components/DetailHeader.tsx
@@ -44,6 +44,7 @@ interface DetailHeaderProps
{
statistics: StatisticItem[];
operations: OperationItem[];
tagConfig?: TagConfig;
+ titleExtra?: React.ReactNode;
}
// 标签单行渲染组件
@@ -222,6 +223,7 @@ function DetailHeader({
statistics,
operations,
tagConfig,
+ titleExtra,
}: DetailHeaderProps): React.ReactNode {
return (
@@ -244,6 +246,7 @@ function DetailHeader({
{(data as any)?.name}
+ {titleExtra}
{(data as any)?.status && (
diff --git a/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx b/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx
index 98851c44..2b7c387a 100644
--- a/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx
+++ b/frontend/src/pages/KnowledgeBase/Detail/KnowledgeBaseDetail.tsx
@@ -1,6 +1,6 @@
import type React from "react";
import { useEffect, useState } from "react";
-import { Table, Badge, Button, Breadcrumb, Tooltip, App, Card, Input, Empty, Spin } from "antd";
+import { Table, Badge, Button, Breadcrumb, Tooltip, App, Card, Input, Empty, Spin, Tag } from "antd";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
@@ -72,6 +72,7 @@ const KnowledgeBaseDetailPage: React.FC = () => {
const [graphSelection, setGraphSelection] = useState(null);
const kbTypeMap = getKBTypeMap(t);
+ const kbTypeMeta = knowledgeBase ? kbTypeMap[knowledgeBase.type as KBType] : undefined;
const detailHeaderData = knowledgeBase
? {
@@ -87,9 +88,10 @@ const KnowledgeBaseDetailPage: React.FC = () => {
.map((s) => s.trim())
.filter(Boolean);
- const typeLabel = String(kbTypeMap[knowledgeBase.type as KBType]?.label ?? "").trim();
- const all = typeLabel ? [typeLabel, ...normalized] : normalized;
- return Array.from(new Set(all));
+ const typeLabel = String(kbTypeMeta?.tag?.label ?? "").trim();
+ const filtered = typeLabel ? normalized.filter((label) => label !== typeLabel) : normalized;
+
+ return Array.from(new Set(filtered));
})(),
description:
knowledgeBase.description && knowledgeBase.description.trim().length > 0
@@ -357,6 +359,20 @@ const KnowledgeBaseDetailPage: React.FC = () => {
+ {kbTypeMeta.tag.label}
+
+ ) : null
+ }
statistics={knowledgeBase && Array.isArray((knowledgeBase as { statistics?: HeaderStatisticItem[] }).statistics)
? ((knowledgeBase as { statistics?: HeaderStatisticItem[] }).statistics ?? [])
: []}
diff --git a/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx b/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx
index 7683cb81..13d4a781 100644
--- a/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx
+++ b/frontend/src/pages/KnowledgeBase/components/AddDataDialog.tsx
@@ -27,6 +27,18 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
const [selectedFilesMap, setSelectedFilesMap] = useState({});
+ const toBackendProcessType = (uiValue: string) => {
+ switch (uiValue) {
+ case "FIXED_LENGTH_CHUNK":
+ return "LENGTH_CHUNK";
+ case "CHAPTER_CHUNK":
+ // 后端暂不支持 CHAPTER_CHUNK:用段落分块策略兼容“按章节分块”的入口
+ return "PARAGRAPH_CHUNK";
+ default:
+ return uiValue;
+ }
+ };
+
// 定义分块选项
const sliceOptions = [
{ label: t("knowledgeBase.const.sliceMethod.default"), value: "DEFAULT_CHUNK" },
@@ -124,7 +136,7 @@ export default function AddDataDialog({ knowledgeBase, onDataAdded }) {
// 构造符合API要求的请求数据
const requestData = {
files: Object.values(selectedFilesMap),
- processType: newKB.processType,
+ processType: toBackendProcessType(newKB.processType),
chunkSize: Number(newKB.chunkSize), // 确保是数字类型
overlapSize: Number(newKB.overlapSize), // 确保是数字类型
delimiter: newKB.delimiter,
diff --git a/frontend/src/pages/KnowledgeBase/knowledge-base.api.ts b/frontend/src/pages/KnowledgeBase/knowledge-base.api.ts
index 0126762a..102520c8 100644
--- a/frontend/src/pages/KnowledgeBase/knowledge-base.api.ts
+++ b/frontend/src/pages/KnowledgeBase/knowledge-base.api.ts
@@ -67,7 +67,7 @@ export function retrieveKnowledgeBaseContent(data: {
threshold?: number;
knowledgeBaseIds: string[];
}): Promise
{
- return post("/api/knowledge-base/retrieve", data);
+ return post("/api/knowledge-base/v2/retrieve", data);
}
// 获取知识库文件详情(分页的切片数据)
diff --git a/runtime/datamate-python/app/module/rag/interface/knowledge_base.py b/runtime/datamate-python/app/module/rag/interface/knowledge_base.py
index c702f5b5..a6b26bc1 100644
--- a/runtime/datamate-python/app/module/rag/interface/knowledge_base.py
+++ b/runtime/datamate-python/app/module/rag/interface/knowledge_base.py
@@ -159,6 +159,16 @@ async def retrieve_knowledge_base(
results = await service.search(request)
return SuccessResponse(data=results)
+@router.post("/v2/retrieve", response_model=SuccessResponse)
+async def retrieve_knowledge_base(
+ request: RetrieveReq,
+ db: AsyncSession = Depends(get_db),
+):
+ """检索知识库内容(统一检索接口)"""
+ service = UnifiedRetrievalService(db)
+ results = await service.search(request)
+ return SuccessResponse(data=results)
+
@router.post("/query", response_model=SuccessResponse)
async def query_knowledge_base(
@@ -166,7 +176,7 @@ async def query_knowledge_base(
db: AsyncSession = Depends(get_db),
):
"""查询知识库(支持向量检索和知识图谱)
-
+
根据知识库类型自动选择查询策略:
- DOCUMENT: 向量检索
- GRAPH: 知识图谱查询
From 5c7b8cd56c85bced11537ec681a7b42d2a32b663 Mon Sep 17 00:00:00 2001
From: Dallas98 <990259227@qq.com>
Date: Thu, 12 Mar 2026 18:33:54 +0800
Subject: [PATCH 3/5] feat: enhance knowledge base retrieval API to support
legacy format
---
.../module/rag/interface/knowledge_base.py | 24 ++++++++++++++++---
1 file changed, 21 insertions(+), 3 deletions(-)
diff --git a/runtime/datamate-python/app/module/rag/interface/knowledge_base.py b/runtime/datamate-python/app/module/rag/interface/knowledge_base.py
index a6b26bc1..70f8d4b6 100644
--- a/runtime/datamate-python/app/module/rag/interface/knowledge_base.py
+++ b/runtime/datamate-python/app/module/rag/interface/knowledge_base.py
@@ -4,6 +4,8 @@
实现知识库相关的 REST API 接口。
对应 Java: com.datamate.rag.indexer.interfaces.KnowledgeBaseController
"""
+import json
+
from fastapi import APIRouter, Depends, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
@@ -154,13 +156,29 @@ async def retrieve_knowledge_base(
request: RetrieveReq,
db: AsyncSession = Depends(get_db),
):
- """检索知识库内容(统一检索接口)"""
+ """检索知识库内容(统一检索接口,兼容旧版本格式)"""
service = UnifiedRetrievalService(db)
results = await service.search(request)
- return SuccessResponse(data=results)
+
+ legacy_results = []
+ for item in results:
+ legacy_item = {
+ "entity": {
+ "metadata": json.dumps(item.get("metadata", {}), ensure_ascii=False),
+ "text": item.get("text", ""),
+ "id": item.get("id", ""),
+ },
+ "score": item.get("score", 0.0),
+ "id": item.get("id", ""),
+ "knowledgeBaseId": item.get("knowledgeBaseId", ""),
+ "knowledgeBaseName": item.get("knowledgeBaseName", ""),
+ }
+ legacy_results.append(legacy_item)
+
+ return SuccessResponse(data=legacy_results)
@router.post("/v2/retrieve", response_model=SuccessResponse)
-async def retrieve_knowledge_base(
+async def v2_retrieve_knowledge_base(
request: RetrieveReq,
db: AsyncSession = Depends(get_db),
):
From ab3ccd1c6915ff5a0b4f755eaf5eb3365ab5ae83 Mon Sep 17 00:00:00 2001
From: Dallas98 <990259227@qq.com>
Date: Thu, 12 Mar 2026 20:25:24 +0800
Subject: [PATCH 4/5] refactor: clean up type ignore comments in
graph_strategy.py
---
.../rag/service/strategy/graph_strategy.py | 29 +++++++++----------
1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py b/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py
index bf7e51e6..25380c34 100644
--- a/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py
+++ b/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py
@@ -122,7 +122,7 @@ async def search(
retrieval_results = await rag_instance.aquery_data(query_text, query_param)
unified_results = self._convert_retrieval_results_into_unified(
- retrieval_results, str(kb.id), str(kb.name) # type: ignore
+ retrieval_results, str(kb.id), str(kb.name)
)
all_results.extend(unified_results)
@@ -317,35 +317,34 @@ async def _get_knowledge_base(self, knowledge_base_id: str) -> KnowledgeBase:
return kb
async def _get_or_create_graph_rag(self, kb: KnowledgeBase) -> Any:
- kb_name = str(kb.name) # type: ignore
+ kb_name = str(kb.name)
if kb_name in self._rag_cache:
return self._rag_cache[kb_name]
- chat_model = await get_model_by_id(self.db, str(kb.chat_model)) # type: ignore
- embedding_model = await get_model_by_id(self.db, str(kb.embedding_model)) # type: ignore
+ chat_model = await get_model_by_id(self.db, str(kb.chat_model))
+ embedding_model = await get_model_by_id(self.db, str(kb.embedding_model))
if not chat_model or not embedding_model:
raise BusinessError(ErrorCodes.RAG_MODEL_NOT_FOUND)
llm_func = _create_llm_func(
- str(chat_model.model_name), # type: ignore
- str(chat_model.base_url), # type: ignore
- str(chat_model.api_key), # type: ignore
+ str(chat_model.model_name),
+ str(chat_model.base_url),
+ str(chat_model.api_key),
)
from app.module.shared.llm import LLMFactory
embedding_func = _create_embedding_func(
- str(embedding_model.model_name), # type: ignore
- str(embedding_model.base_url), # type: ignore
- str(embedding_model.api_key), # type: ignore
+ str(embedding_model.model_name),
+ str(embedding_model.base_url),
+ str(embedding_model.api_key),
LLMFactory.get_embedding_dimension(
- str(embedding_model.model_name), # type: ignore
- str(embedding_model.base_url), # type: ignore
- str(embedding_model.api_key), # type: ignore
+ str(embedding_model.model_name),
+ str(embedding_model.base_url),
+ str(embedding_model.api_key),
),
)
- working_dir = os.path.join(DEFAULT_WORKING_DIR, kb_name)
- rag = await _create_rag(llm_func, embedding_func, working_dir, workspace=kb_name)
+ rag = await _create_rag(llm_func, embedding_func, DEFAULT_WORKING_DIR, workspace=kb_name)
self._rag_cache[kb_name] = rag
return rag
From 17a535b2f8d9b371a45f49d637fc6ec03b36a41b Mon Sep 17 00:00:00 2001
From: Dallas98 <990259227@qq.com>
Date: Thu, 12 Mar 2026 20:53:31 +0800
Subject: [PATCH 5/5] feat: enhance GraphKnowledgeBaseStrategy with workspace
management and caching functionality
---
.../rag/service/knowledge_base_service.py | 44 +++++++++++++++----
.../rag/service/strategy/graph_strategy.py | 18 +++++++-
2 files changed, 52 insertions(+), 10 deletions(-)
diff --git a/runtime/datamate-python/app/module/rag/service/knowledge_base_service.py b/runtime/datamate-python/app/module/rag/service/knowledge_base_service.py
index ef2a2625..94e126f3 100644
--- a/runtime/datamate-python/app/module/rag/service/knowledge_base_service.py
+++ b/runtime/datamate-python/app/module/rag/service/knowledge_base_service.py
@@ -14,7 +14,7 @@
from app.core.exception import BusinessError, ErrorCodes
from app.db.models.dataset_management import DatasetFiles
-from app.db.models.knowledge_gen import KnowledgeBase, RagFile, FileStatus
+from app.db.models.knowledge_gen import KnowledgeBase, RagFile, FileStatus, RagType
from app.db.models.models import Models
from app.module.rag.infra.vectorstore import drop_collection, rename_collection, delete_chunks_by_rag_file_ids
from app.module.rag.repository import KnowledgeBaseRepository, RagFileRepository
@@ -88,15 +88,23 @@ async def update(self, knowledge_base_id: str, request: KnowledgeBaseUpdateReq)
if not knowledge_base:
raise BusinessError(ErrorCodes.RAG_KNOWLEDGE_BASE_NOT_FOUND)
- old_name = knowledge_base.name
+ old_name = str(knowledge_base.name)
+ new_name = request.name
+ kb_type = knowledge_base.type
+
knowledge_base.name = request.name
knowledge_base.description = request.description
await self.kb_repo.update(knowledge_base)
- if old_name != request.name:
+ if old_name != new_name:
try:
- rename_collection(old_name, request.name)
+ if kb_type == RagType.DOCUMENT.value:
+ rename_collection(old_name, new_name)
+ elif kb_type == RagType.GRAPH.value:
+ from app.module.rag.service.strategy.graph_strategy import GraphKnowledgeBaseStrategy
+ GraphKnowledgeBaseStrategy.rename_workspace(old_name, new_name)
+ GraphKnowledgeBaseStrategy.clear_cache(old_name)
except BusinessError:
await self.db.rollback()
raise
@@ -113,13 +121,30 @@ async def delete(self, knowledge_base_id: str) -> None:
if not knowledge_base:
raise BusinessError(ErrorCodes.RAG_KNOWLEDGE_BASE_NOT_FOUND)
+ kb_name = str(knowledge_base.name)
+ kb_type = knowledge_base.type
+
await self.file_repo.delete_by_knowledge_base(knowledge_base_id)
await self.kb_repo.delete(knowledge_base_id)
- try:
- drop_collection(knowledge_base.name)
- except Exception as e:
- logger.error("删除 Milvus 集合失败: %s", e)
+ if kb_type == RagType.DOCUMENT.value:
+ try:
+ drop_collection(kb_name)
+ except Exception as e:
+ logger.error("删除 Milvus 集合失败: %s", e)
+ elif kb_type == RagType.GRAPH.value:
+ try:
+ from app.module.rag.service.strategy.graph_strategy import GraphKnowledgeBaseStrategy
+ import shutil
+ from pathlib import Path
+ from app.core.config import settings
+ workspace_path = Path(settings.rag_storage_dir) / kb_name
+ if workspace_path.exists():
+ shutil.rmtree(workspace_path)
+ logger.info("已删除知识图谱 workspace: %s", kb_name)
+ GraphKnowledgeBaseStrategy.clear_cache(kb_name)
+ except Exception as e:
+ logger.error("删除知识图谱 workspace 失败: %s", e)
await self.db.commit()
@@ -141,7 +166,8 @@ async def get_by_id(self, knowledge_base_id: str) -> KnowledgeBaseResp:
})
return KnowledgeBaseResp(**data)
- def _kb_to_dict(self, kb: KnowledgeBase) -> dict:
+ @staticmethod
+ def _kb_to_dict(kb: KnowledgeBase) -> dict:
"""知识库实体转字典"""
return {
"id": kb.id,
diff --git a/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py b/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py
index 25380c34..8c7f532e 100644
--- a/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py
+++ b/runtime/datamate-python/app/module/rag/service/strategy/graph_strategy.py
@@ -71,11 +71,12 @@ async def _create_rag(
class GraphKnowledgeBaseStrategy(KnowledgeBaseStrategy):
+ # 类级别的缓存,允许跨实例共享
+ _rag_cache: Dict[str, Any] = {}
def __init__(self, db: AsyncSession):
super().__init__(db)
self.kb_repo = KnowledgeBaseRepository(db)
- self._rag_cache: Dict[str, Any] = {}
async def query(
self,
@@ -348,3 +349,18 @@ async def _get_or_create_graph_rag(self, kb: KnowledgeBase) -> Any:
rag = await _create_rag(llm_func, embedding_func, DEFAULT_WORKING_DIR, workspace=kb_name)
self._rag_cache[kb_name] = rag
return rag
+
+ @classmethod
+ def rename_workspace(cls, old_name: str, new_name: str) -> None:
+ old_path = Path(DEFAULT_WORKING_DIR) / old_name
+ new_path = Path(DEFAULT_WORKING_DIR) / new_name
+ if old_path.exists() and old_path.is_dir():
+ old_path.rename(new_path)
+ logger.info("知识图谱 workspace 重命名: %s -> %s", old_name, new_name)
+ cls.clear_cache(old_name)
+
+ @classmethod
+ def clear_cache(cls, name: str) -> None:
+ if name in cls._rag_cache:
+ del cls._rag_cache[name]
+ logger.info("已清除知识图谱缓存: %s", name)