diff --git a/api/apps/canvas_app.py b/api/apps/canvas_app.py index 0c4abe0596..21eff42366 100644 --- a/api/apps/canvas_app.py +++ b/api/apps/canvas_app.py @@ -24,6 +24,7 @@ from api.db import CanvasCategory from api.db.services.canvas_service import CanvasTemplateService, UserCanvasService, API4ConversationService from api.db.services.document_service import DocumentService from api.db.services.file_service import FileService +from api.db.services.knowledgebase_service import KnowledgebaseService from api.db.services.pipeline_operation_log_service import PipelineOperationLogService from api.db.services.task_service import queue_dataflow, CANVAS_DEBUG_DOC_ID, TaskService from api.db.services.user_service import TenantService @@ -153,6 +154,11 @@ def get(canvas_id): c = c.to_dict() c["last_publish_time"] = last_publish_time + # For pipeline type, get associated datasets + if c.get("canvas_category") == CanvasCategory.DataFlow: + datasets = list(KnowledgebaseService.query(pipeline_id=canvas_id)) + c["datasets"] = [{"id": d.id, "name": d.name, "avatar": d.avatar} for d in datasets] + return get_json_result(data=c) diff --git a/api/apps/sdk/session.py b/api/apps/sdk/session.py index 722965812c..7938841838 100644 --- a/api/apps/sdk/session.py +++ b/api/apps/sdk/session.py @@ -87,19 +87,20 @@ async def create(tenant_id, chat_id): async def create_agent_session(tenant_id, agent_id): req = await get_request_json() user_id = req.get("user_id") or request.args.get("user_id", tenant_id) - release_mode = req.get("release", request.args.get("release", False)) - e, cvs = UserCanvasService.get_by_id(agent_id) - if not e: - return get_error_data_result("Agent not found.") + release_mode = bool(req.get("release", request.args.get("release", False))) + if not UserCanvasService.query(user_id=tenant_id, id=agent_id): return get_error_data_result("You cannot access the agent.") - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) - if release_mode and not bool(cvs.release): - raise PermissionError("No available published version") + try: + cvs, dsl = UserCanvasService.get_agent_dsl_with_release(agent_id, release_mode, tenant_id) + except LookupError: + return get_error_data_result("Agent not found.") + except PermissionError as e: + return get_error_data_result(str(e)) + session_id = get_uuid() - canvas = Canvas(cvs.dsl, tenant_id, agent_id, canvas_id=cvs.id) + canvas = Canvas(dsl, tenant_id, agent_id, canvas_id=cvs.id) canvas.reset() cvs.dsl = json.loads(str(canvas)) diff --git a/api/db/services/canvas_service.py b/api/db/services/canvas_service.py index f6aa41c4a4..bd655d122b 100644 --- a/api/db/services/canvas_service.py +++ b/api/db/services/canvas_service.py @@ -22,6 +22,7 @@ from api.db import CanvasCategory, TenantPermission from api.db.db_models import DB, CanvasTemplate, User, UserCanvas, API4Conversation, UserCanvasVersion from api.db.services.api_service import API4ConversationService from api.db.services.common_service import CommonService +from api.db.services.user_canvas_version import UserCanvasVersionService from common.misc_utils import get_uuid from api.utils.api_utils import get_data_openai import tiktoken @@ -204,6 +205,27 @@ class UserCanvasService(CommonService): return False return True + @classmethod + def get_agent_dsl_with_release(cls, agent_id, release_mode=False, tenant_id=None): + e, cvs = cls.get_by_id(agent_id) + if not e: + raise LookupError("Agent not found.") + if tenant_id and cvs.user_id != tenant_id: + raise PermissionError("You do not own the agent.") + + if release_mode: + released_version = UserCanvasVersionService.get_latest_released(agent_id) + if not released_version: + raise PermissionError("No available published version") + dsl = released_version.dsl + else: + dsl = cvs.dsl + + if not isinstance(dsl, str): + dsl = json.dumps(dsl, ensure_ascii=False) + + return cvs, dsl + async def completion(tenant_id, agent_id, session_id=None, **kwargs): query = kwargs.get("query", "") or kwargs.get("question", "") @@ -223,28 +245,12 @@ async def completion(tenant_id, agent_id, session_id=None, **kwargs): conv.dsl = json.dumps(conv.dsl, ensure_ascii=False) canvas = Canvas(conv.dsl, tenant_id, agent_id, canvas_id=agent_id, custom_header=custom_header) else: - e, cvs = UserCanvasService.get_by_id(agent_id) - if not e: - raise LookupError("Agent not found.") - if cvs.user_id != tenant_id: - raise PermissionError("You do not own the agent.") - if release_mode == "true" and not bool(cvs.release): - raise PermissionError("No available published version") - if not isinstance(cvs.dsl, str): - cvs.dsl = json.dumps(cvs.dsl, ensure_ascii=False) + cvs, dsl = UserCanvasService.get_agent_dsl_with_release(agent_id, release_mode=release_mode == "true", tenant_id=tenant_id) - session_id=get_uuid() - canvas = Canvas(cvs.dsl, tenant_id, agent_id, canvas_id=cvs.id, custom_header=custom_header) + session_id = get_uuid() + canvas = Canvas(dsl, tenant_id, agent_id, canvas_id=cvs.id, custom_header=custom_header) canvas.reset() - conv = { - "id": session_id, - "dialog_id": cvs.id, - "user_id": user_id, - "message": [], - "source": "agent", - "dsl": cvs.dsl, - "reference": [] - } + conv = {"id": session_id, "dialog_id": cvs.id, "user_id": user_id, "message": [], "source": "agent", "dsl": dsl, "reference": []} API4ConversationService.save(**conv) conv = API4Conversation(**conv) diff --git a/api/db/services/user_canvas_version.py b/api/db/services/user_canvas_version.py index d2861d576e..067122e9b7 100644 --- a/api/db/services/user_canvas_version.py +++ b/api/db/services/user_canvas_version.py @@ -89,6 +89,17 @@ class UserCanvasVersionService(CommonService): except Exception: return None + @classmethod + @DB.connection_context() + def get_latest_released(cls, user_canvas_id): + try: + return cls.model.select().where((cls.model.user_canvas_id == user_canvas_id) & (cls.model.release)).order_by(cls.model.create_time.desc()).first() + except DoesNotExist: + return None + except Exception as e: + logging.exception(e) + return None + @classmethod @DB.connection_context() def save_or_replace_latest(cls, user_canvas_id, dsl, title=None, description=None, release=None): diff --git a/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py b/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py index a83a3564d4..109889b4b6 100644 --- a/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py +++ b/test/testcases/test_http_api/test_session_management/test_session_sdk_routes_unit.py @@ -543,12 +543,15 @@ def test_create_and_update_guard_matrix(monkeypatch): assert "Fail to create a session" in res["message"] monkeypatch.setattr(module, "request", SimpleNamespace(args=_Args())) - monkeypatch.setattr(module.UserCanvasService, "get_by_id", lambda _id: (False, None)) + monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: [SimpleNamespace(id="agent-1")]) + + def _raise_lookup(*_args, **_kwargs): + raise LookupError("Agent not found.") + + monkeypatch.setattr(module.UserCanvasService, "get_agent_dsl_with_release", _raise_lookup) res = _run(inspect.unwrap(module.create_agent_session)("tenant-1", "agent-1")) assert res["message"] == "Agent not found." - canvas = SimpleNamespace(dsl="{}", id="agent-1") - monkeypatch.setattr(module.UserCanvasService, "get_by_id", lambda _id: (True, canvas)) monkeypatch.setattr(module.UserCanvasService, "query", lambda **_kwargs: []) res = _run(inspect.unwrap(module.create_agent_session)("tenant-1", "agent-1")) assert res["message"] == "You cannot access the agent." diff --git a/test/testcases/test_web_api/test_canvas_app/test_canvas_routes_unit.py b/test/testcases/test_web_api/test_canvas_app/test_canvas_routes_unit.py index 540453e415..4431575800 100644 --- a/test/testcases/test_web_api/test_canvas_app/test_canvas_routes_unit.py +++ b/test/testcases/test_web_api/test_canvas_app/test_canvas_routes_unit.py @@ -183,6 +183,14 @@ def _load_canvas_module(monkeypatch): api_pkg.__path__ = [str(repo_root / "api")] monkeypatch.setitem(sys.modules, "api", api_pkg) + db_pkg = ModuleType("api.db") + db_pkg.__path__ = [str(repo_root / "api" / "db")] + monkeypatch.setitem(sys.modules, "api.db", db_pkg) + + db_services_pkg = ModuleType("api.db.services") + db_services_pkg.__path__ = [str(repo_root / "api" / "db" / "services")] + monkeypatch.setitem(sys.modules, "api.db.services", db_services_pkg) + apps_mod = ModuleType("api.apps") apps_mod.__path__ = [] apps_mod.current_user = SimpleNamespace(id="user-1") @@ -327,6 +335,12 @@ def _load_canvas_module(monkeypatch): ) monkeypatch.setitem(sys.modules, "api.db.services.file_service", file_service_mod) + knowledgebase_service_mod = ModuleType("api.db.services.knowledgebase_service") + knowledgebase_service_mod.KnowledgebaseService = SimpleNamespace( + query=lambda **_kwargs: [], + ) + monkeypatch.setitem(sys.modules, "api.db.services.knowledgebase_service", knowledgebase_service_mod) + pipeline_log_service_mod = ModuleType("api.db.services.pipeline_operation_log_service") pipeline_log_service_mod.PipelineOperationLogService = SimpleNamespace( get_documents_info=lambda *_args, **_kwargs: [], diff --git a/web/src/components/home-card.tsx b/web/src/components/home-card.tsx index 9e57a355f3..cefd9434b6 100644 --- a/web/src/components/home-card.tsx +++ b/web/src/components/home-card.tsx @@ -17,12 +17,11 @@ interface IProps { sharedBadge?: ReactNode; icon?: React.ReactNode; testId?: string; + showReleaseTime?: boolean; } function Time({ time }: { time: string | number | undefined }) { - return ( -
{formatDate(time)}
- ); + return{formatDate(time)}
; } export function HomeCard({ data, @@ -31,6 +30,7 @@ export function HomeCard({ sharedBadge, icon, testId, + showReleaseTime = false, }: IProps) { const { t } = useTranslation(); @@ -82,16 +82,18 @@ export function HomeCard({ {data.description}删除生成的 {{type}} 结果 - 将从此数据集中移除所有派生实体和关系。 + 将从此知识库中移除所有派生实体和关系。 您的原始文件将保持不变。
是否要继续?
@@ -449,7 +449,7 @@ export default {
setDefaultTip: '',
setDefault: '设置默认',
editLinkDataPipeline: '编辑pipeline',
- linkPipelineSetTip: '管理与此数据集的数据管道链接',
+ linkPipelineSetTip: '管理与此知识库的数据管道链接',
default: '默认',
dataPipeline: '切换或配置 ingestion pipeline。',
linkDataPipeline: '关联pipeline',
@@ -630,7 +630,7 @@ export default {
tagSetTip: `
请选择一个或多个标签集或标签知识库,用于对知识库中的每个文本块进行标记。
对这些文本块的查询也将自动关联相应标签。
-此功能基于文本相似度,能够为数据集的文本块批量添加更多领域知识,从而显著提高检索准确性。该功能还能提升大量文本块的操作效率。
+此功能基于文本相似度,能够为知识库的文本块批量添加更多领域知识,从而显著提高检索准确性。该功能还能提升大量文本块的操作效率。
为了更好地理解标签集的作用,以下是标签集和关键词之间的主要区别:
一旦取消链接,该数据集将不再连接到当前数据管道。
+一旦取消链接,该知识库将不再连接到当前数据管道。
正在解析的文件将继续解析,直到完成。
尚未解析的文件将不再被处理。
你确定要继续吗?
`, @@ -2217,8 +2222,8 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`, noMCP: '暂无 MCP 服务器可用', agentTitle: '尚未创建智能体', notFoundAgent: '未查询到智能体', - datasetTitle: '尚未创建数据集', - notFoundDataset: '未查询到数据集', + datasetTitle: '尚未创建知识库', + notFoundDataset: '未查询到知识库', chatTitle: '尚未创建聊天应用', notFoundChat: '未查询到聊天应用', searchTitle: '尚未创建搜索应用', diff --git a/web/src/pages/agent/components/publish-confirm-dialog.tsx b/web/src/pages/agent/components/publish-confirm-dialog.tsx index 85fe297515..5fa9f03c98 100644 --- a/web/src/pages/agent/components/publish-confirm-dialog.tsx +++ b/web/src/pages/agent/components/publish-confirm-dialog.tsx @@ -1,3 +1,4 @@ +import { RAGFlowAvatar } from '@/components/ragflow-avatar'; import { Button, ButtonLoading } from '@/components/ui/button'; import { Dialog, @@ -9,12 +10,12 @@ import { DialogTrigger, } from '@/components/ui/dialog'; import { IFlow } from '@/interfaces/database/agent'; -import { Operator } from '@/pages/agent/constant'; -import useGraphStore from '@/pages/agent/store'; +import { IKnowledge } from '@/interfaces/database/knowledge'; import { formatDate } from '@/utils/date'; import { BookPlus } from 'lucide-react'; -import { useMemo, useState } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import { useIsPipeline } from '../hooks/use-is-pipeline'; interface PublishConfirmDialogProps { agentDetail: IFlow; @@ -22,6 +23,43 @@ interface PublishConfirmDialogProps { onPublish: () => void; } +function AssociatedDataset({ + associatedDatasets, +}: { + associatedDatasets: Pick