From 7c16456f694134f24f893c359d21ae21eec2f46e Mon Sep 17 00:00:00 2001 From: shaohuzhang1 Date: Mon, 1 Dec 2025 14:57:55 +0800 Subject: [PATCH] feat: Knowledge base workflow release, version management function --- apps/knowledge/api/knowledge_version.py | 96 +++++++++++++ apps/knowledge/api/knowledge_workflow.py | 55 ++++++++ ...wledgeworkflowversion_workflow_and_more.py | 22 +++ apps/knowledge/models/knowledge.py | 3 +- .../serializers/knowledge_version.py | 108 ++++++++++++++ .../serializers/knowledge_workflow.py | 56 +++++++- apps/knowledge/urls.py | 7 +- apps/knowledge/views/__init__.py | 1 + apps/knowledge/views/knowledge_workflow.py | 124 +++++++++++++++- .../views/knowledge_workflow_version.py | 133 ++++++++++++++++++ ui/src/api/knowledge/knowledge.ts | 51 ++++++- .../component/DebugDrawer.vue | 2 +- .../component/PublishHistory.vue | 8 +- ui/src/views/knowledge-workflow/index.vue | 32 +---- .../nodes/knowledge-base-node/index.vue | 6 +- 15 files changed, 651 insertions(+), 53 deletions(-) create mode 100644 apps/knowledge/api/knowledge_version.py create mode 100644 apps/knowledge/migrations/0007_remove_knowledgeworkflowversion_workflow_and_more.py create mode 100644 apps/knowledge/serializers/knowledge_version.py create mode 100644 apps/knowledge/views/knowledge_workflow_version.py diff --git a/apps/knowledge/api/knowledge_version.py b/apps/knowledge/api/knowledge_version.py new file mode 100644 index 000000000..3d60ce650 --- /dev/null +++ b/apps/knowledge/api/knowledge_version.py @@ -0,0 +1,96 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: application_version.py + @date:2025/6/4 17:33 + @desc: +""" +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import OpenApiParameter + +from common.mixins.api_mixin import APIMixin +from common.result import ResultSerializer, ResultPageSerializer +from knowledge.serializers.knowledge_version import KnowledgeVersionModelSerializer + + +class KnowledgeListVersionResult(ResultSerializer): + def get_data(self): + return KnowledgeVersionModelSerializer(many=True) + + +class KnowledgePageVersionResult(ResultPageSerializer): + def get_data(self): + return KnowledgeVersionModelSerializer(many=True) + + +class KnowledgeWorkflowVersionResult(ResultSerializer): + def get_data(self): + return KnowledgeVersionModelSerializer() + + +class KnowledgeVersionAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="knowledge_id", + description="knowledge ID", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + ] + + +class KnowledgeVersionOperateAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="knowledge_version_id", + description="工作流版本id", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + , *KnowledgeVersionAPI.get_parameters() + ] + + @staticmethod + def get_response(): + return KnowledgeWorkflowVersionResult + + +class KnowledgeVersionListAPI(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="name", + description="Version Name", + type=OpenApiTypes.STR, + required=False, + ) + , *KnowledgeVersionOperateAPI.get_parameters()] + + @staticmethod + def get_response(): + return KnowledgeListVersionResult + + +class KnowledgeVersionPageAPI(APIMixin): + @staticmethod + def get_parameters(): + return KnowledgeVersionListAPI.get_parameters() + + @staticmethod + def get_response(): + return KnowledgePageVersionResult diff --git a/apps/knowledge/api/knowledge_workflow.py b/apps/knowledge/api/knowledge_workflow.py index 38296bb89..b56599901 100644 --- a/apps/knowledge/api/knowledge_workflow.py +++ b/apps/knowledge/api/knowledge_workflow.py @@ -1,6 +1,9 @@ # coding=utf-8 +from drf_spectacular.types import OpenApiTypes +from drf_spectacular.utils import OpenApiParameter from common.mixins.api_mixin import APIMixin +from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowActionRequestSerializer class KnowledgeWorkflowApi(APIMixin): @@ -9,3 +12,55 @@ class KnowledgeWorkflowApi(APIMixin): class KnowledgeWorkflowVersionApi(APIMixin): pass + + +class KnowledgeWorkflowActionApi(APIMixin): + @staticmethod + def get_request(): + return KnowledgeWorkflowActionRequestSerializer + + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="knowledge_id", + description="知识库id", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + ] + + class Operate(APIMixin): + @staticmethod + def get_parameters(): + return [ + OpenApiParameter( + name="workspace_id", + description="工作空间id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="knowledge_id", + description="知识库id", + type=OpenApiTypes.STR, + location='path', + required=True, + ), + OpenApiParameter( + name="knowledge_action_id", + description="知识库执行id", + type=OpenApiTypes.STR, + location='path', + required=True, + ) + ] diff --git a/apps/knowledge/migrations/0007_remove_knowledgeworkflowversion_workflow_and_more.py b/apps/knowledge/migrations/0007_remove_knowledgeworkflowversion_workflow_and_more.py new file mode 100644 index 000000000..052a852b7 --- /dev/null +++ b/apps/knowledge/migrations/0007_remove_knowledgeworkflowversion_workflow_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2.8 on 2025-12-01 06:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('knowledge', '0006_paragraph_chunks'), + ] + + operations = [ + migrations.RemoveField( + model_name='knowledgeworkflowversion', + name='workflow', + ), + migrations.AddField( + model_name='knowledgeworkflowversion', + name='name', + field=models.CharField(default='', max_length=128, verbose_name='版本名称'), + ), + ] diff --git a/apps/knowledge/models/knowledge.py b/apps/knowledge/models/knowledge.py index ef72f4647..4f88f21f2 100644 --- a/apps/knowledge/models/knowledge.py +++ b/apps/knowledge/models/knowledge.py @@ -159,9 +159,8 @@ class KnowledgeWorkflowVersion(AppModelMixin): """ id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid7, editable=False, verbose_name="主键id") knowledge = models.ForeignKey(Knowledge, on_delete=models.CASCADE, verbose_name="知识库", db_constraint=False) - workflow = models.ForeignKey(KnowledgeWorkflow, on_delete=models.CASCADE, verbose_name="工作流", - db_constraint=False, related_name='versions') workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) + name = models.CharField(verbose_name="版本名称", max_length=128, default="") work_flow = models.JSONField(verbose_name="工作流数据", default=dict) publish_user_id = models.UUIDField(verbose_name="发布者id", max_length=128, default=None, null=True) publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="") diff --git a/apps/knowledge/serializers/knowledge_version.py b/apps/knowledge/serializers/knowledge_version.py new file mode 100644 index 000000000..02ebed4da --- /dev/null +++ b/apps/knowledge/serializers/knowledge_version.py @@ -0,0 +1,108 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: KnowledgeVersionSerializer.py + @date:2025/11/28 18:00 + @desc: +""" +from typing import Dict + +from django.db.models import QuerySet +from rest_framework import serializers + +from django.utils.translation import gettext_lazy as _ + +from common.db.search import page_search +from common.exception.app_exception import AppApiException +from knowledge.models import KnowledgeWorkflowVersion, Knowledge + + +class KnowledgeWorkflowVersionEditSerializer(serializers.Serializer): + name = serializers.CharField(required=False, max_length=128, allow_null=True, allow_blank=True, + label=_("Version Name")) + + +class KnowledgeVersionModelSerializer(serializers.ModelSerializer): + class Meta: + model = KnowledgeWorkflowVersion + fields = ['id', 'name', 'workspace_id', 'knowledge_id', 'work_flow', 'publish_user_id', 'publish_user_name', + 'create_time', + 'update_time'] + + +class KnowledgeWorkflowVersionQuerySerializer(serializers.Serializer): + knowledge_id = serializers.UUIDField(required=True, label=_("Knowledge ID")) + name = serializers.CharField(required=False, allow_null=True, allow_blank=True, + label=_("summary")) + + +class KnowledgeWorkflowVersionSerializer(serializers.Serializer): + workspace_id = serializers.CharField(required=False, label=_("Workspace ID")) + + class Query(serializers.Serializer): + workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID")) + + def get_query_set(self, query): + query_set = QuerySet(KnowledgeWorkflowVersion).filter(knowledge_id=query.get('knowledge_id')) + if 'name' in query and query.get('name') is not None: + query_set = query_set.filter(name__contains=query.get('name')) + if 'workspace_id' in self.data and self.data.get('workspace_id') is not None: + query_set = query_set.filter(workspace_id=self.data.get('workspace_id')) + return query_set.order_by("-create_time") + + def list(self, query, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + KnowledgeWorkflowVersionQuerySerializer(data=query).is_valid(raise_exception=True) + query_set = self.get_query_set(query) + return [KnowledgeVersionModelSerializer(v).data for v in query_set] + + def page(self, query, current_page, page_size, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + return page_search(current_page, page_size, + self.get_query_set(query), + post_records_handler=lambda v: KnowledgeVersionModelSerializer(v).data) + + class Operate(serializers.Serializer): + workspace_id = serializers.CharField(required=False, allow_null=True, allow_blank=True, label=_("Workspace ID")) + knowledge_id = serializers.UUIDField(required=True, label=_("Knowledge ID")) + knowledge_version_id = serializers.UUIDField(required=True, + label=_("Knowledge version ID")) + + def is_valid(self, *, raise_exception=False): + super().is_valid(raise_exception=True) + workspace_id = self.data.get('workspace_id') + query_set = QuerySet(Knowledge).filter(id=self.data.get('knowledge_id')) + if workspace_id: + query_set = query_set.filter(workspace_id=workspace_id) + if not query_set.exists(): + raise AppApiException(500, _('Knowledge id does not exist')) + + def one(self, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + knowledge_version = QuerySet(KnowledgeWorkflowVersion).filter(knowledge_id=self.data.get('knowledge_id'), + id=self.data.get( + 'knowledge_version_id')).first() + if knowledge_version is not None: + return KnowledgeVersionModelSerializer(knowledge_version).data + else: + raise AppApiException(500, _('Workflow version does not exist')) + + def edit(self, instance: Dict, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + KnowledgeWorkflowVersionEditSerializer(data=instance).is_valid(raise_exception=True) + knowledge_version = QuerySet(KnowledgeWorkflowVersion).filter(knowledge_id=self.data.get('knowledge_id'), + id=self.data.get( + 'knowledge_version_id')).first() + if knowledge_version is not None: + name = instance.get('name', None) + if name is not None and len(name) > 0: + knowledge_version.name = name + knowledge_version.save() + return KnowledgeVersionModelSerializer(knowledge_version).data + else: + raise AppApiException(500, _('Workflow version does not exist')) diff --git a/apps/knowledge/serializers/knowledge_workflow.py b/apps/knowledge/serializers/knowledge_workflow.py index 9e6b7ee2f..6423cf366 100644 --- a/apps/knowledge/serializers/knowledge_workflow.py +++ b/apps/knowledge/serializers/knowledge_workflow.py @@ -1,13 +1,16 @@ # coding=utf-8 import asyncio +import datetime import json from typing import Dict import uuid_utils.compat as uuid from django.db import transaction from django.db.models import QuerySet +from django.utils import timezone from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from rest_framework.views import APIView from application.flow.common import Workflow, WorkflowMode from application.flow.i_step_node import KnowledgeWorkflowPostHandler @@ -17,13 +20,14 @@ from application.serializers.application import get_mcp_tools from common.exception.app_exception import AppApiException from common.utils.rsa_util import rsa_long_decrypt from common.utils.tool_code import ToolExecutor -from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow +from knowledge.models import KnowledgeScope, Knowledge, KnowledgeType, KnowledgeWorkflow, KnowledgeWorkflowVersion from knowledge.models.knowledge_action import KnowledgeAction, State from knowledge.serializers.knowledge import KnowledgeModelSerializer from maxkb.const import CONFIG from system_manage.models import AuthTargetType from system_manage.serializers.user_resource_permission import UserResourcePermissionSerializer from tools.models import Tool +from users.models import User tool_executor = ToolExecutor(CONFIG.get('SANDBOX')) @@ -34,6 +38,11 @@ class KnowledgeWorkflowModelSerializer(serializers.ModelSerializer): fields = '__all__' +class KnowledgeWorkflowActionRequestSerializer(serializers.Serializer): + data_source = serializers.DictField(required=True, label=_('datasource data')) + knowledge_base = serializers.DictField(required=True, label=_('knowledge base data')) + + class KnowledgeWorkflowActionSerializer(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) @@ -54,6 +63,27 @@ class KnowledgeWorkflowActionSerializer(serializers.Serializer): return {'id': knowledge_action_id, 'knowledge_id': self.data.get("knowledge_id"), 'state': State.STARTED, 'details': {}} + def upload_document(self, instance: Dict, with_valid=True): + if with_valid: + self.is_valid(raise_exception=True) + knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).first() + if not knowledge_workflow.is_publish: + raise AppApiException(500, _("The knowledge base workflow has not been published")) + knowledge_workflow_version = QuerySet(KnowledgeWorkflowVersion).filter( + knowledge_id=self.data.get("knowledge_id")).order_by( + '-create_time')[0:1].first() + knowledge_action_id = uuid.uuid7() + KnowledgeAction(id=knowledge_action_id, knowledge_id=self.data.get("knowledge_id"), state=State.STARTED).save() + work_flow_manage = KnowledgeWorkflowManage( + Workflow.new_instance(knowledge_workflow_version.work_flow, WorkflowMode.KNOWLEDGE), + {'knowledge_id': self.data.get("knowledge_id"), 'knowledge_action_id': knowledge_action_id, 'stream': True, + 'workspace_id': self.data.get("workspace_id"), + **instance}, + KnowledgeWorkflowPostHandler(None, knowledge_action_id)) + work_flow_manage.run() + return {'id': knowledge_action_id, 'knowledge_id': self.data.get("knowledge_id"), 'state': State.STARTED, + 'details': {}} + class Operate(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) @@ -141,6 +171,26 @@ class KnowledgeWorkflowSerializer(serializers.Serializer): workspace_id = serializers.CharField(required=True, label=_('workspace id')) knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) + def publish(self, with_valid=True): + if with_valid: + self.is_valid() + user_id = self.data.get('user_id') + workspace_id = self.data.get("workspace_id") + user = QuerySet(User).filter(id=user_id).first() + knowledge_workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id"), + workspace_id=workspace_id).first() + work_flow_version = KnowledgeWorkflowVersion(work_flow=knowledge_workflow.work_flow, + knowledge_id=self.data.get("knowledge_id"), + name=timezone.localtime(timezone.now()).strftime( + '%Y-%m-%d %H:%M:%S'), + publish_user_id=user_id, + publish_user_name=user.username, + workspace_id=workspace_id) + work_flow_version.save() + QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get("knowledge_id")).update(is_publish=True, + publish_time=datetime.datetime.now()) + return True + def edit(self, instance: Dict): pass @@ -149,9 +199,11 @@ class KnowledgeWorkflowSerializer(serializers.Serializer): workflow = QuerySet(KnowledgeWorkflow).filter(knowledge_id=self.data.get('knowledge_id')).first() return {**KnowledgeWorkflowModelSerializer(workflow).data} + class McpServersSerializer(serializers.Serializer): mcp_servers = serializers.JSONField(required=True) + class KnowledgeWorkflowMcpSerializer(serializers.Serializer): knowledge_id = serializers.UUIDField(required=True, label=_('knowledge id')) user_id = serializers.UUIDField(required=True, label=_("User ID")) @@ -184,4 +236,4 @@ class KnowledgeWorkflowMcpSerializer(serializers.Serializer): 'args_schema': tool.args_schema, } for tool in asyncio.run(get_mcp_tools({server: servers[server]}))] - return tools \ No newline at end of file + return tools diff --git a/apps/knowledge/urls.py b/apps/knowledge/urls.py index 738ad05fe..695f510d2 100644 --- a/apps/knowledge/urls.py +++ b/apps/knowledge/urls.py @@ -71,7 +71,12 @@ urlpatterns = [ path('workspace//knowledge//', views.KnowledgeView.Page.as_view()), path('workspace//knowledge//datasource///form_list', views.KnowledgeDatasourceFormListView.as_view()), path('workspace//knowledge//datasource///', views.KnowledgeDatasourceView.as_view()), - path('workspace//knowledge//action', views.KnowledgeWorkflowActionView.as_view()), + path('workspace//knowledge//publish', views.KnowledgeWorkflowView.Publish.as_view()), + path('workspace//knowledge//debug', views.KnowledgeWorkflowActionView.as_view()), + path('workspace//knowledge//upload_document', views.KnowledgeWorkflowUploadDocumentView.as_view()), path('workspace//knowledge//action/', views.KnowledgeWorkflowActionView.Operate.as_view()), path('workspace//knowledge//mcp_tools', views.McpServers.as_view()), + path('workspace//knowledge//knowledge_version', views.KnowledgeWorkflowVersionView.as_view()), + path('workspace//knowledge//knowledge_version//', views.KnowledgeWorkflowVersionView.Page.as_view()), + path('workspace//knowledge//knowledge_version/', views.KnowledgeWorkflowVersionView.Operate.as_view()), ] diff --git a/apps/knowledge/views/__init__.py b/apps/knowledge/views/__init__.py index 98a57a228..7696e3f5e 100644 --- a/apps/knowledge/views/__init__.py +++ b/apps/knowledge/views/__init__.py @@ -4,3 +4,4 @@ from .paragraph import * from .problem import * from .tag import * from .knowledge_workflow import * +from .knowledge_workflow_version import * diff --git a/apps/knowledge/views/knowledge_workflow.py b/apps/knowledge/views/knowledge_workflow.py index 20f2c17ea..3c0eaf190 100644 --- a/apps/knowledge/views/knowledge_workflow.py +++ b/apps/knowledge/views/knowledge_workflow.py @@ -10,8 +10,8 @@ from common.auth import TokenAuth from common.auth.authentication import has_permissions from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants from common.log.log import log -from common.result import result -from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi +from common.result import result, DefaultResultSerializer +from knowledge.api.knowledge_workflow import KnowledgeWorkflowApi, KnowledgeWorkflowActionApi from knowledge.serializers.common import get_knowledge_operation_object from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer, KnowledgeWorkflowActionSerializer, \ KnowledgeWorkflowMcpSerializer @@ -31,9 +31,57 @@ class KnowledgeDatasourceView(APIView): data={'type': type, 'id': id, 'params': request.data, 'function_name': function_name}).action()) +class KnowledgeWorkflowUploadDocumentView(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Knowledge workflow upload document'), + summary=_('Knowledge workflow upload document'), + operation_id=_('Knowledge workflow upload document'), # type: ignore + parameters=KnowledgeWorkflowActionApi.get_parameters(), + request=KnowledgeWorkflowActionApi.get_request(), + responses=KnowledgeWorkflowActionApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ), + ) + def post(self, request: Request, workspace_id: str, knowledge_id: str): + return result.success(KnowledgeWorkflowActionSerializer( + data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).upload_document(request.data, True)) + + class KnowledgeWorkflowActionView(APIView): authentication_classes = [TokenAuth] + @extend_schema( + methods=['GET'], + description=_('Knowledge workflow debug'), + summary=_('Knowledge workflow debug'), + operation_id=_('Knowledge workflow debug'), # type: ignore + parameters=KnowledgeWorkflowActionApi.get_parameters(), + request=KnowledgeWorkflowActionApi.get_request(), + responses=KnowledgeWorkflowActionApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ), + ) def post(self, request: Request, workspace_id: str, knowledge_id: str): return result.success(KnowledgeWorkflowActionSerializer( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id}).action(request.data, True)) @@ -41,6 +89,25 @@ class KnowledgeWorkflowActionView(APIView): class Operate(APIView): authentication_classes = [TokenAuth] + @extend_schema( + methods=['GET'], + description=_('Get knowledge workflow action'), + summary=_('Get knowledge workflow action'), + operation_id=_('Get knowledge workflow action'), # type: ignore + parameters=KnowledgeWorkflowActionApi.get_parameters(), + responses=KnowledgeWorkflowActionApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ), + ) def get(self, request, workspace_id: str, knowledge_id: str, knowledge_action_id: str): return result.success(KnowledgeWorkflowActionSerializer.Operate( data={'workspace_id': workspace_id, 'knowledge_id': knowledge_id, 'id': knowledge_action_id}) @@ -68,6 +135,33 @@ class KnowledgeWorkflowView(APIView): data={'user_id': request.user.id, 'workspace_id': workspace_id} ).save_workflow(request.data)) + class Publish(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['PUT'], + description=_("Publishing an knowledge"), + summary=_("Publishing an knowledge"), + operation_id=_("Publishing an knowledge"), # type: ignore + parameters=KnowledgeWorkflowApi.get_parameters(), + request=None, + responses=DefaultResultSerializer, + tags=[_('Knowledge')] # type: ignore + ) + @has_permissions(PermissionConstants.KNOWLEDGE_EDIT.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_EDIT.get_workspace_permission_workspace_manage_role(), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + @log(menu='Knowledge', operate='Publishing an knowledge', + get_operation_object=lambda r, k: get_knowledge_operation_object(k.get('knowledge_id'))) + def put(self, request: Request, workspace_id: str, knowledge_id: str): + return result.success( + KnowledgeWorkflowSerializer.Operate( + data={'knowledge_id': knowledge_id, 'user_id': request.user.id, + 'workspace_id': workspace_id, }).publish()) + class Operate(APIView): authentication_classes = [TokenAuth] @@ -126,7 +220,31 @@ class KnowledgeWorkflowView(APIView): class KnowledgeWorkflowVersionView(APIView): - pass + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_('Get knowledge workflow version list'), + summary=_('Get knowledge workflow version list'), + operation_id=_('Get knowledge workflow version list'), # type: ignore + parameters=KnowledgeWorkflowApi.get_parameters(), + responses=KnowledgeWorkflowApi.get_response(), + tags=[_('Knowledge Base')] # type: ignore + ) + @has_permissions( + PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role(), + ViewPermission( + [RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND + ), + ) + def get(self, request: Request, workspace_id: str, knowledge_id: str): + return result.success(KnowledgeWorkflowSerializer.Operate( + data={'user_id': request.user.id, 'workspace_id': workspace_id, 'knowledge_id': knowledge_id} + ).one()) class McpServers(APIView): diff --git a/apps/knowledge/views/knowledge_workflow_version.py b/apps/knowledge/views/knowledge_workflow_version.py new file mode 100644 index 000000000..6d20f0de6 --- /dev/null +++ b/apps/knowledge/views/knowledge_workflow_version.py @@ -0,0 +1,133 @@ +# coding=utf-8 +""" + @project: MaxKB + @Author:虎虎 + @file: application_version.py.py + @date:2025/6/3 15:46 + @desc: +""" +from django.db.models import QuerySet +from django.utils.translation import gettext_lazy as _ +from drf_spectacular.utils import extend_schema +from rest_framework.request import Request +from rest_framework.views import APIView + +from common import result +from common.auth import TokenAuth +from common.auth.authentication import has_permissions +from common.constants.permission_constants import PermissionConstants, RoleConstants, ViewPermission, CompareConstants +from common.log.log import log +from knowledge.api.knowledge_version import KnowledgeVersionListAPI, KnowledgeVersionPageAPI, \ + KnowledgeVersionOperateAPI +from knowledge.models import Knowledge +from knowledge.serializers.knowledge_version import KnowledgeWorkflowVersionSerializer + + +def get_knowledge_operation_object(knowledge_id): + knowledge_model = QuerySet(model=Knowledge).filter(id=knowledge_id).first() + if knowledge_model is not None: + return { + 'name': knowledge_model.name + } + return {} + + +class KnowledgeWorkflowVersionView(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_("Get the knowledge version list"), + summary=_("Get the knowledge version list"), + operation_id=_("Get the knowledge version list"), # type: ignore + parameters=KnowledgeVersionListAPI.get_parameters(), + responses=KnowledgeVersionListAPI.get_response(), + tags=[_('Knowledge/Version')] # type: ignore + ) + @has_permissions(PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id, knowledge_id: str): + return result.success( + KnowledgeWorkflowVersionSerializer.Query( + data={'workspace_id': workspace_id}).list( + {'name': request.query_params.get("name"), 'knowledge_id': knowledge_id})) + + class Page(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_("Get the list of knowledge versions by page"), + summary=_("Get the list of knowledge versions by page"), + operation_id=_("Get the list of knowledge versions by page"), # type: ignore + parameters=KnowledgeVersionPageAPI.get_parameters(), + responses=KnowledgeVersionPageAPI.get_response(), + tags=[_('Knowledge/Version')] # type: ignore + ) + @has_permissions(PermissionConstants.KNOWLEDGE_READ.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_READ.get_workspace_permission_workspace_manage_role(), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, knowledge_id: str, current_page: int, page_size: int): + return result.success( + KnowledgeWorkflowVersionSerializer.Query( + data={'workspace_id': workspace_id}).page( + {'name': request.query_params.get("name"), 'knowledge_id': knowledge_id}, + current_page, page_size)) + + class Operate(APIView): + authentication_classes = [TokenAuth] + + @extend_schema( + methods=['GET'], + description=_("Get knowledge version details"), + summary=_("Get knowledge version details"), + operation_id=_("Get knowledge version details"), # type: ignore + parameters=KnowledgeVersionOperateAPI.get_parameters(), + responses=KnowledgeVersionOperateAPI.get_response(), + tags=[_('Knowledge/Version')] # type: ignore + ) + @has_permissions(PermissionConstants.KNOWLEDGE_EDIT.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_EDIT.get_workspace_permission_workspace_manage_role(), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + def get(self, request: Request, workspace_id: str, knowledge_id: str, knowledge_version_id: str): + return result.success( + KnowledgeWorkflowVersionSerializer.Operate( + data={'user_id': request.user, 'workspace_id': workspace_id, + 'knowledge_id': knowledge_id, 'knowledge_version_id': knowledge_version_id}).one()) + + @extend_schema( + methods=['PUT'], + description=_("Modify knowledge version information"), + summary=_("Modify knowledge version information"), + operation_id=_("Modify knowledge version information"), # type: ignore + parameters=KnowledgeVersionOperateAPI.get_parameters(), + request=None, + responses=KnowledgeVersionOperateAPI.get_response(), + tags=[_('Knowledge/Version')] # type: ignore + ) + @has_permissions(PermissionConstants.KNOWLEDGE_EDIT.get_workspace_knowledge_permission(), + PermissionConstants.KNOWLEDGE_EDIT.get_workspace_permission_workspace_manage_role(), + ViewPermission([RoleConstants.USER.get_workspace_role()], + [PermissionConstants.KNOWLEDGE.get_workspace_knowledge_permission()], + CompareConstants.AND), + RoleConstants.WORKSPACE_MANAGE.get_workspace_role()) + @log(menu='Knowledge', operate="Modify knowledge version information", + get_operation_object=lambda r, k: get_knowledge_operation_object(k.get('knowledge_id')), + ) + def put(self, request: Request, workspace_id: str, knowledge_id: str, knowledge_version_id: str): + return result.success( + KnowledgeWorkflowVersionSerializer.Operate( + data={'knowledge_id': knowledge_id, 'workspace_id': workspace_id, + 'knowledge_version_id': knowledge_version_id, + 'user_id': request.user.id}).edit( + request.data)) diff --git a/ui/src/api/knowledge/knowledge.ts b/ui/src/api/knowledge/knowledge.ts index ed345e002..d9e119580 100644 --- a/ui/src/api/knowledge/knowledge.ts +++ b/ui/src/api/knowledge/knowledge.ts @@ -317,13 +317,13 @@ const delMulTag: ( } const getKnowledgeWorkflowFormList: ( knowledge_id: string, - type: 'loacl' | 'tool', + type: 'local' | 'tool', id: string, node: any, loading?: Ref, ) => Promise> = ( knowledge_id: string, - type: 'loacl' | 'tool', + type: 'local' | 'tool', id: string, node, loading, @@ -337,14 +337,14 @@ const getKnowledgeWorkflowFormList: ( } const getKnowledgeWorkflowDatasourceDetails: ( knowledge_id: string, - type: 'loacl' | 'tool', + type: 'local' | 'tool', id: string, params: any, function_name: string, loading?: Ref, ) => Promise> = ( knowledge_id: string, - type: 'loacl' | 'tool', + type: 'local' | 'tool', id: string, params, function_name, @@ -362,8 +362,44 @@ const workflowAction: ( instance: Dict, loading?: Ref, ) => Promise> = (knowledge_id: string, instance, loading) => { - return post(`${prefix.value}/${knowledge_id}/action`, instance, {}, loading) + return post(`${prefix.value}/${knowledge_id}/debug`, instance, {}, loading) } + +const workflowUpload: ( + knowledge_id: string, + instance: Dict, + loading?: Ref, +) => Promise> = (knowledge_id: string, instance, loading) => { + return post(`${prefix.value}/${knowledge_id}/upload_document`, instance, {}, loading) +} + +const publish: (knowledge_id: string, loading?: Ref) => Promise> = ( + knowledge_id: string, + loading, +) => { + return put(`${prefix.value}/${knowledge_id}/publish`, {}, {}, loading) +} + +const listKnowledgeVersion: ( + knowledge_id: string, + loading?: Ref, +) => Promise> = (knowledge_id: string, loading) => { + return get(`${prefix.value}/${knowledge_id}/knowledge_version`, {}, loading) +} +const updateKnowledgeVersion: ( + knowledge_id: string, + knowledge_version_id: string, + data: any, + loading?: Ref, +) => Promise> = (knowledge_id: string, knowledge_version_id, data, loading) => { + return put( + `${prefix.value}/${knowledge_id}/knowledge_version/${knowledge_version_id}`, + data, + {}, + loading, + ) +} + const getWorkflowAction: ( knowledge_id: string, knowledge_action_id: string, @@ -383,7 +419,6 @@ const getMcpTools: ( return post(`${prefix.value}/${knowledge_id}/mcp_tools`, { mcp_servers }, {}, loading) } - export default { getKnowledgeList, getKnowledgeListPage, @@ -413,4 +448,8 @@ export default { getWorkflowAction, getKnowledgeWorkflowDatasourceDetails, getMcpTools, + listKnowledgeVersion, + updateKnowledgeVersion, + publish, + workflowUpload, } diff --git a/ui/src/views/knowledge-workflow/component/DebugDrawer.vue b/ui/src/views/knowledge-workflow/component/DebugDrawer.vue index dae795954..545d3f606 100644 --- a/ui/src/views/knowledge-workflow/component/DebugDrawer.vue +++ b/ui/src/views/knowledge-workflow/component/DebugDrawer.vue @@ -57,7 +57,7 @@ import KnowledgeApi from '@/api/knowledge/knowledge' provide('upload', (file: any, loading?: Ref) => { return applicationApi.postUploadFile(file, _knowledge_id.value, 'KNOWLEDGE', loading) }) -const ak = { +const ak: any = { data_source: DataSource, knowledge_base: KnowledgeBase, result: Result, diff --git a/ui/src/views/knowledge-workflow/component/PublishHistory.vue b/ui/src/views/knowledge-workflow/component/PublishHistory.vue index 3a475265f..3ba647a0e 100644 --- a/ui/src/views/knowledge-workflow/component/PublishHistory.vue +++ b/ui/src/views/knowledge-workflow/component/PublishHistory.vue @@ -118,8 +118,8 @@ function editName(val: string, item: any) { const obj = { name: val, } - loadSharedApi({ type: 'workflowVersion', systemType: apiType.value }) - .putWorkFlowVersion(id as string, item.id, obj, loading) + loadSharedApi({ type: 'knowledge', systemType: apiType.value }) + .updateKnowledgeVersion(id as string, item.id, obj, loading) .then(() => { MsgSuccess(t('common.modifySuccess')) item['writeStatus'] = false @@ -131,8 +131,8 @@ function editName(val: string, item: any) { } function getList() { - loadSharedApi({ type: 'workflowVersion', systemType: apiType.value }) - .getWorkFlowVersion(id, loading) + loadSharedApi({ type: 'knowledge', systemType: apiType.value }) + .listKnowledgeVersion(id, loading) .then((res: any) => { LogData.value = res.data }) diff --git a/ui/src/views/knowledge-workflow/index.vue b/ui/src/views/knowledge-workflow/index.vue index 5241f45cd..000fec2eb 100644 --- a/ui/src/views/knowledge-workflow/index.vue +++ b/ui/src/views/knowledge-workflow/index.vue @@ -313,7 +313,7 @@ const publish = () => { return } loadSharedApi({ type: 'knowledge', systemType: apiType.value }) - .putknowledge(id, { work_flow: workflow }, loading) + .putKnowledge(id, { work_flow: workflow }) .then(() => { return loadSharedApi({ type: 'knowledge', systemType: apiType.value }).publish( id, @@ -322,28 +322,6 @@ const publish = () => { ) }) .then((ok: any) => { - detail.value.name = ok.data.name - ok.data.work_flow?.nodes - ?.filter((v: any) => v.id === 'base-node') - .map((v: any) => { - apiInputParams.value = v.properties.api_input_field_list - ? v.properties.api_input_field_list.map((v: any) => { - return { - name: v.variable, - value: v.default_value, - } - }) - : v.properties.input_field_list - ? v.properties.input_field_list - .filter((v: any) => v.assignment_method === 'api_input') - .map((v: any) => { - return { - name: v.variable, - value: v.default_value, - } - }) - : [] - }) MsgSuccess(t('views.knowledge.tip.publishSuccess')) }) .catch((res: any) => { @@ -458,14 +436,6 @@ function getDetail() { workflowRef.value?.render(detail.value.work_flow) cloneWorkFlow.value = getGraphData() }) - // 企业版和专业版 - if (hasPermission([EditionConst.IS_EE, EditionConst.IS_PE], 'OR')) { - loadSharedApi({ type: 'knowledge', systemType: apiType.value }) - .getknowledgeSetting(id) - .then((ok: any) => { - detail.value = { ...detail.value, ...ok.data } - }) - } }) } diff --git a/ui/src/workflow/nodes/knowledge-base-node/index.vue b/ui/src/workflow/nodes/knowledge-base-node/index.vue index 16ebd572b..f5acb20c4 100644 --- a/ui/src/workflow/nodes/knowledge-base-node/index.vue +++ b/ui/src/workflow/nodes/knowledge-base-node/index.vue @@ -43,14 +43,14 @@ const UserInputFieldTableFef = ref() const nodeFields = computed(() => { if (props.nodeModel.properties.user_input_field_list) { - const fileds = props.nodeModel.properties.user_input_field_list.map((item: any) => ({ + const fields = props.nodeModel.properties.user_input_field_list.map((item: any) => ({ label: typeof item.label == 'string' ? item.label : item.label.label, value: item.field, globeLabel: `{{global.${typeof item.label == 'string' ? item.label : item.label.label}}}`, globeValue: `{{context['global'].${item.field}}}`, })) - set(props.nodeModel.properties.config, 'globalFields', fileds) - return fileds + set(props.nodeModel.properties.config, 'globalFields', fields) + return fields } set(props.nodeModel.properties.config, 'globalFields', []) return []