feat: knowledge workflow

This commit is contained in:
shaohuzhang1 2025-11-11 17:41:52 +08:00
parent b82e12ac2e
commit 8d7734b9c6
11 changed files with 125 additions and 16 deletions

View File

@ -9,6 +9,7 @@
from .ai_chat_step_node import *
from .application_node import BaseApplicationNode
from .condition_node import *
from .data_source_local_node.impl.base_data_source_local_node import BaseDataSourceLocalNode
from .direct_reply_node import *
from .document_extract_node import *
from .form_node import *
@ -46,7 +47,8 @@ node_list = [BaseStartStepNode, BaseChatNode, BaseSearchKnowledgeNode, BaseSearc
BaseVideoUnderstandNode,
BaseIntentNode, BaseLoopNode, BaseLoopStartStepNode,
BaseLoopContinueNode,
BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode]
BaseLoopBreakNode, BaseVariableSplittingNode, BaseParameterExtractionNode, BaseVariableAggregationNode,
BaseDataSourceLocalNode]
def get_node(node_type):

View File

@ -16,7 +16,7 @@ from application.flow.i_step_node import INode, NodeResult
class DataSourceLocalNodeParamsSerializer(serializers.Serializer):
file_format = serializers.ListField(child=serializers.CharField)
file_format = serializers.ListField(child=serializers.CharField(label=('')), label='')
max_file_number = serializers.IntegerField(required=True, label=_("Number of uploaded files"))
file_max_size = serializers.IntegerField(required=True, label=_("Upload file size"))
@ -24,8 +24,9 @@ class DataSourceLocalNodeParamsSerializer(serializers.Serializer):
class IDataSourceLocalNode(INode):
type = 'data-source-local-node'
@staticmethod
@abstractmethod
def get_form_class(self):
def get_form_class():
pass
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:

View File

@ -20,7 +20,8 @@ class BaseDataSourceLocalNode(IDataSourceLocalNode):
def save_context(self, details, workflow_manage):
pass
def get_form_class(self):
@staticmethod
def get_form_class():
return BaseDataSourceLocalNodeForm()
def execute(self, file_format, max_file_number, file_max_size, **kwargs) -> NodeResult:

View File

@ -32,7 +32,7 @@ class KnowledgeWorkflowSerializer(serializers.Serializer):
self.is_valid(raise_exception=True)
if self.data.get('type') == 'local':
node = get_node(self.data.get('id'))
return node.get_form_class()().to_form_list()
return node.get_form_class().to_form_list()
elif self.data.get('type') == 'tool':
tool = QuerySet(Tool).filter(id=self.data.get("id")).first()
# todo 调用工具数据源的函数获取表单列表

View File

@ -69,5 +69,6 @@ urlpatterns = [
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/problem/<int:current_page>/<int:page_size>', views.ProblemView.Page.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/document/<int:current_page>/<int:page_size>', views.DocumentView.Page.as_view()),
path('workspace/<str:workspace_id>/knowledge/<int:current_page>/<int:page_size>', views.KnowledgeView.Page.as_view()),
path('workspace/<str:workspace_id>/knowledge/<str:knowledge_id>/form_list/<str:type>/<str:id>', views.KnowledgeWorkflowFormView.as_view()),
]

View File

@ -18,8 +18,8 @@ from knowledge.serializers.knowledge_workflow import KnowledgeWorkflowSerializer
class KnowledgeWorkflowFormView(APIView):
authentication_classes = [TokenAuth]
def get(self):
return result.success(KnowledgeWorkflowSerializer.Form().get_form_list())
def get(self, request: Request, workspace_id: str, knowledge_id: str, type: str, id: str):
return result.success(KnowledgeWorkflowSerializer.Form(data={'type': type, 'id': id}).get_form_list())
class KnowledgeWorkflowView(APIView):

View File

@ -315,6 +315,14 @@ const delMulTag: (
) => Promise<Result<any>> = (knowledge_id, tags, loading) => {
return put(`${prefix.value}/${knowledge_id}/tags/batch_delete`, tags, null, loading)
}
const getKnowledgeWorkflowFormList: (
knowledge_id: string,
type: 'loacl' | 'tool',
id: string,
loading?: Ref<boolean>,
) => Promise<Result<any>> = (knowledge_id: string, type: 'loacl' | 'tool', id: string, loading) => {
return get(`${prefix.value}/${knowledge_id}/form_list/${type}/${id}`, null, loading)
}
export default {
getKnowledgeList,
@ -340,4 +348,5 @@ export default {
delTag,
delMulTag,
createWorkflowKnowledge,
getKnowledgeWorkflowFormList,
}

View File

@ -0,0 +1,29 @@
<template>
<el-drawer
v-model="drawer"
title="I am the title"
direction="rtl"
destroy-on-close
:before-close="close"
>
<div style="width: 100%">
<ActionVue :workflow="_workflow"></ActionVue>
</div>
</el-drawer>
</template>
<script setup lang="ts">
import ActionVue from '@/views/knowledge-workflow/component/action/index.vue'
import { ref } from 'vue'
const drawer = ref<boolean>(false)
const _workflow = ref<any>(null)
const close = () => {
drawer.value = false
}
const open = (workflow: any) => {
console.log('ok')
drawer.value = true
_workflow.value = workflow
}
defineExpose({ close, open })
</script>
<style lang="scss" scoped></style>

View File

@ -1,11 +1,78 @@
<template>
<div></div>
<DynamicsForm
v-loading="loading"
v-model="form_data"
:render_data="model_form_field"
:model="form_data"
ref="dynamicsFormRef"
label-position="top"
require-asterisk-position="right"
>
<template #default>
<el-form-item prop="model_name" :rules="base_form_data_rule.model_name">
<el-radio-group @change="sourceChange" v-model="base_form_data.data_source">
<el-radio :value="node.id" size="large" v-for="node in source_node_list">{{
node.properties.stepName
}}</el-radio>
</el-radio-group>
</el-form-item>
</template>
</DynamicsForm>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { computed, ref } from 'vue'
import { WorkflowKind, WorkflowMode, WorkflowType } from '@/enums/application'
import DynamicsForm from '@/components/dynamics-form/index.vue'
import type { FormField } from '@/components/dynamics-form/type'
import type { Dict } from '@/api/type/common'
import type { FormRules } from 'element-plus'
import { loadSharedApi } from '@/utils/dynamics-api/shared-api'
import { useRoute } from 'vue-router'
const route = useRoute()
const apiType = computed(() => {
if (route.path.includes('resource-management')) {
return 'systemManage'
} else {
return 'workspace'
}
})
const model_form_field = ref<Array<FormField>>([])
const props = defineProps<{
workflow: any
}>()
const source_node_list = computed(() => {})
const loading = ref<boolean>(false)
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
const base_form_data = ref<{ data_source: string }>({ data_source: '' })
const dynamics_form_data = ref<Dict<any>>({})
const form_data = computed({
get: () => {
return { ...dynamics_form_data.value, ...base_form_data.value }
},
set: (event: any) => {
dynamics_form_data.value = event
},
})
const source_node_list = computed(() => {
return props.workflow?.nodes?.filter((n: any) => n.properties.kind === WorkflowKind.DataSource)
})
const {
params: { id, from },
} = route as any
const sourceChange = (node_id: string) => {
const n = source_node_list.value.find((n: any) => n.id == node_id)
node_id = n ? ([WorkflowType.DataSourceLocalNode].includes(n.type) ? n.type : node_id) : node_id
loadSharedApi({ type: 'knowledge', systemType: apiType.value })
.getKnowledgeWorkflowFormList(id, 'local', node_id)
.then((ok) => {
dynamicsFormRef.value?.render(ok.data)
})
}
const base_form_data_rule = ref<FormRules>({
data_source: {
required: true,
trigger: 'blur',
message: '数据源必选',
},
})
</script>
<style lang="scss" scoped></style>

View File

@ -125,11 +125,9 @@
</div>
</div>
</div>
<div class="scrollbar-height">
<AiChat :knowledge-details="detail" :type="'debug-ai-chat'"></AiChat>
</div>
</div>
</el-collapse-transition>
<DebugVue ref="DebugRef"></DebugVue>
<!-- 发布历史 -->
<PublishHistory
v-if="showHistory"
@ -153,6 +151,7 @@ import { mapToUrlParams } from '@/utils/application'
import useStore from '@/stores'
import { WorkFlowInstance } from '@/workflow/common/validate'
import { hasPermission } from '@/utils/permission'
import DebugVue from './component/Debug.vue'
import { t } from '@/locales'
import { ComplexPermission } from '@/utils/permission/type'
import { EditionConst, PermissionConst, RoleConst } from '@/utils/permission/data'
@ -184,6 +183,7 @@ const permissionPrecise = computed(() => {
const isDefaultTheme = computed(() => {
return theme.isDefaultTheme()
})
const DebugRef = ref<InstanceType<typeof DebugVue>>()
let interval: any
const workflowRef = ref()
@ -393,8 +393,8 @@ const clickShowDebug = () => {
...workflow.get_base_node()?.properties.node_data,
work_flow: getGraphData(),
}
showDebug.value = true
console.log('sss', DebugRef.value)
DebugRef.value?.open(graphData)
} catch (e: any) {
console.log(e)
MsgError(e.toString())

View File

@ -81,7 +81,6 @@ export const knowledgeBaseNode = {
},
}
export const dataSourceLocalNode = {
id: WorkflowType.DataSourceLocalNode,
type: WorkflowType.DataSourceLocalNode,
x: 360,
y: 2761.3875,