fix: 修复编排中引用函数,原函数更新后,编排中未同步更新
This commit is contained in:
parent
eb60a0de2e
commit
eedb1be6d8
@ -13,6 +13,7 @@ from typing import Type, Dict, List
|
|||||||
from django.core import cache
|
from django.core import cache
|
||||||
from django.db.models import QuerySet
|
from django.db.models import QuerySet
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from rest_framework.exceptions import ValidationError, ErrorDetail
|
||||||
|
|
||||||
from application.models import ChatRecord
|
from application.models import ChatRecord
|
||||||
from application.models.api_key_model import ApplicationPublicAccessClient
|
from application.models.api_key_model import ApplicationPublicAccessClient
|
||||||
@ -123,12 +124,12 @@ class INode:
|
|||||||
self.err_message = ''
|
self.err_message = ''
|
||||||
self.node = node
|
self.node = node
|
||||||
self.node_params = node.properties.get('node_data')
|
self.node_params = node.properties.get('node_data')
|
||||||
|
self.workflow_params = workflow_params
|
||||||
self.workflow_manage = workflow_manage
|
self.workflow_manage = workflow_manage
|
||||||
self.node_params_serializer = None
|
self.node_params_serializer = None
|
||||||
self.flow_params_serializer = None
|
self.flow_params_serializer = None
|
||||||
self.context = {}
|
self.context = {}
|
||||||
self.id = node.id
|
self.id = node.id
|
||||||
self.valid_args(self.node_params, workflow_params)
|
|
||||||
|
|
||||||
def valid_args(self, node_params, flow_params):
|
def valid_args(self, node_params, flow_params):
|
||||||
flow_params_serializer_class = self.get_flow_params_serializer_class()
|
flow_params_serializer_class = self.get_flow_params_serializer_class()
|
||||||
@ -139,6 +140,8 @@ class INode:
|
|||||||
if node_params_serializer_class is not None:
|
if node_params_serializer_class is not None:
|
||||||
self.node_params_serializer = node_params_serializer_class(data=node_params)
|
self.node_params_serializer = node_params_serializer_class(data=node_params)
|
||||||
self.node_params_serializer.is_valid(raise_exception=True)
|
self.node_params_serializer.is_valid(raise_exception=True)
|
||||||
|
if self.node.properties.get('status', 200) != 200:
|
||||||
|
raise ValidationError(ErrorDetail(f'节点{self.node.properties.get("stepName")} 不可用'))
|
||||||
|
|
||||||
def get_reference_field(self, fields: List[str]):
|
def get_reference_field(self, fields: List[str]):
|
||||||
return self.get_field(self.context, fields)
|
return self.get_field(self.context, fields)
|
||||||
|
|||||||
@ -8,11 +8,13 @@
|
|||||||
"""
|
"""
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
|
from django.db.models import QuerySet
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from application.flow.i_step_node import INode, NodeResult
|
from application.flow.i_step_node import INode, NodeResult
|
||||||
from common.field.common import ObjectField
|
from common.field.common import ObjectField
|
||||||
from common.util.field_message import ErrMessage
|
from common.util.field_message import ErrMessage
|
||||||
|
from function_lib.models.function import FunctionLib
|
||||||
|
|
||||||
|
|
||||||
class InputField(serializers.Serializer):
|
class InputField(serializers.Serializer):
|
||||||
@ -27,6 +29,9 @@ class FunctionLibNodeParamsSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def is_valid(self, *, raise_exception=False):
|
def is_valid(self, *, raise_exception=False):
|
||||||
super().is_valid(raise_exception=True)
|
super().is_valid(raise_exception=True)
|
||||||
|
f_lib = QuerySet(FunctionLib).filter(id=self.data.get('function_lib_id')).first()
|
||||||
|
if f_lib is None:
|
||||||
|
raise Exception('函数库已被删除')
|
||||||
|
|
||||||
|
|
||||||
class IFunctionLibNode(INode):
|
class IFunctionLibNode(INode):
|
||||||
|
|||||||
@ -19,6 +19,7 @@ from application.flow import tools
|
|||||||
from application.flow.i_step_node import INode, WorkFlowPostHandler, NodeResult
|
from application.flow.i_step_node import INode, WorkFlowPostHandler, NodeResult
|
||||||
from application.flow.step_node import get_node
|
from application.flow.step_node import get_node
|
||||||
from common.exception.app_exception import AppApiException
|
from common.exception.app_exception import AppApiException
|
||||||
|
from function_lib.models.function import FunctionLib
|
||||||
from setting.models import Model
|
from setting.models import Model
|
||||||
from setting.models_provider import get_model_credential
|
from setting.models_provider import get_model_credential
|
||||||
|
|
||||||
@ -142,6 +143,13 @@ class Flow:
|
|||||||
model_params_setting = model_params_setting_form.get_default_form_data()
|
model_params_setting = model_params_setting_form.get_default_form_data()
|
||||||
node.properties.get('node_data', {})['model_params_setting'] = model_params_setting
|
node.properties.get('node_data', {})['model_params_setting'] = model_params_setting
|
||||||
model_params_setting_form.valid_form(model_params_setting)
|
model_params_setting_form.valid_form(model_params_setting)
|
||||||
|
if node.properties.get('status', 200) != 200:
|
||||||
|
raise ValidationError(ErrorDetail(f'节点{node.properties.get("stepName")} 不可用'))
|
||||||
|
node_list = [node for node in self.nodes if (node.type == 'function-lib-node')]
|
||||||
|
for node in node_list:
|
||||||
|
f_lib = QuerySet(FunctionLib).filter(id=node.properties.get('function_lib_id')).first()
|
||||||
|
if f_lib is None:
|
||||||
|
raise ValidationError(ErrorDetail(f'节点{node.properties.get("stepName")} 函数库不可用'))
|
||||||
|
|
||||||
def is_valid_base_node(self):
|
def is_valid_base_node(self):
|
||||||
base_node_list = [node for node in self.nodes if node.id == 'base-node']
|
base_node_list = [node for node in self.nodes if node.id == 'base-node']
|
||||||
@ -171,6 +179,7 @@ class WorkflowManage:
|
|||||||
try:
|
try:
|
||||||
while self.has_next_node(self.current_result):
|
while self.has_next_node(self.current_result):
|
||||||
self.current_node = self.get_next_node()
|
self.current_node = self.get_next_node()
|
||||||
|
self.current_node.valid_args(self.current_node.node_params, self.current_node.workflow_params)
|
||||||
self.node_context.append(self.current_node)
|
self.node_context.append(self.current_node)
|
||||||
self.current_result = self.current_node.run()
|
self.current_result = self.current_node.run()
|
||||||
result = self.current_result.write_context(self.current_node, self)
|
result = self.current_result.write_context(self.current_node, self)
|
||||||
@ -193,6 +202,7 @@ class WorkflowManage:
|
|||||||
while self.has_next_node(self.current_result):
|
while self.has_next_node(self.current_result):
|
||||||
self.current_node = self.get_next_node()
|
self.current_node = self.get_next_node()
|
||||||
self.node_context.append(self.current_node)
|
self.node_context.append(self.current_node)
|
||||||
|
self.current_node.valid_args(self.current_node.node_params, self.current_node.workflow_params)
|
||||||
self.current_result = self.current_node.run()
|
self.current_result = self.current_node.run()
|
||||||
result = self.current_result.write_context(self.current_node, self)
|
result = self.current_result.write_context(self.current_node, self)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
|
|||||||
@ -80,6 +80,15 @@ class FunctionLibView(APIView):
|
|||||||
return result.success(
|
return result.success(
|
||||||
FunctionLibSerializer.Operate(data={'user_id': request.user.id, 'id': function_lib_id}).delete())
|
FunctionLibSerializer.Operate(data={'user_id': request.user.id, 'id': function_lib_id}).delete())
|
||||||
|
|
||||||
|
@action(methods=['GET'], detail=False)
|
||||||
|
@swagger_auto_schema(operation_summary="获取函数详情",
|
||||||
|
operation_id="获取函数详情",
|
||||||
|
tags=['函数库'])
|
||||||
|
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
|
||||||
|
def get(self, request: Request, function_lib_id: str):
|
||||||
|
return result.success(
|
||||||
|
FunctionLibSerializer.Operate(data={'user_id': request.user.id, 'id': function_lib_id}).one())
|
||||||
|
|
||||||
class Page(APIView):
|
class Page(APIView):
|
||||||
authentication_classes = [TokenAuth]
|
authentication_classes = [TokenAuth]
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,18 @@ const delFunctionLib: (
|
|||||||
) => Promise<Result<boolean>> = (function_lib_id, loading) => {
|
) => Promise<Result<boolean>> = (function_lib_id, loading) => {
|
||||||
return del(`${prefix}/${function_lib_id}`, undefined, {}, loading)
|
return del(`${prefix}/${function_lib_id}`, undefined, {}, loading)
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 获取函数详情
|
||||||
|
* @param function_lib_id 函数id
|
||||||
|
* @param loading 加载器
|
||||||
|
* @returns 函数详情
|
||||||
|
*/
|
||||||
|
const getFunctionLibById: (
|
||||||
|
function_lib_id: String,
|
||||||
|
loading?: Ref<boolean>
|
||||||
|
) => Promise<Result<any>> = (function_lib_id, loading) => {
|
||||||
|
return get(`${prefix}/${function_lib_id}`, undefined, loading)
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getFunctionLib,
|
getFunctionLib,
|
||||||
@ -90,5 +102,6 @@ export default {
|
|||||||
putFunctionLib,
|
putFunctionLib,
|
||||||
postFunctionLibDebug,
|
postFunctionLibDebug,
|
||||||
getAllFunctionLib,
|
getAllFunctionLib,
|
||||||
delFunctionLib
|
delFunctionLib,
|
||||||
|
getFunctionLibById
|
||||||
}
|
}
|
||||||
|
|||||||
@ -256,7 +256,14 @@ function clickNodes(item: any, data?: any) {
|
|||||||
function onmousedown(item: any, data?: any) {
|
function onmousedown(item: any, data?: any) {
|
||||||
if (data) {
|
if (data) {
|
||||||
item['properties']['stepName'] = data.name
|
item['properties']['stepName'] = data.name
|
||||||
item['properties']['node_data'] = { ...data, function_lib_id: data.id }
|
item['properties']['node_data'] = {
|
||||||
|
...data,
|
||||||
|
function_lib_id: data.id,
|
||||||
|
input_field_list: data.input_field_list.map((field: any) => ({
|
||||||
|
...field,
|
||||||
|
value: field.source == 'reference' ? [] : ''
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
workflowRef.value?.onmousedown(item)
|
workflowRef.value?.onmousedown(item)
|
||||||
showPopover.value = false
|
showPopover.value = false
|
||||||
|
|||||||
@ -60,15 +60,13 @@ const open = (model_id: string, model_setting_data?: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reset_default = (model_id: string) => {
|
const reset_default = (model_id: string) => {
|
||||||
form_data.value = {}
|
|
||||||
modelAPi.getModelParamsForm(model_id, loading).then((ok) => {
|
modelAPi.getModelParamsForm(model_id, loading).then((ok) => {
|
||||||
model_form_field.value = ok.data
|
model_form_field.value = ok.data
|
||||||
const model_setting_data = ok.data
|
const model_setting_data = ok.data
|
||||||
.map((item) => ({ [item.field]: item.default_value }))
|
.map((item) => ({ [item.field]: item.default_value }))
|
||||||
.reduce((x, y) => ({ ...x, ...y }), {})
|
.reduce((x, y) => ({ ...x, ...y }), {})
|
||||||
// 渲染动态表单
|
|
||||||
dynamicsFormRef.value?.render(model_form_field.value, model_setting_data)
|
emit('refresh', model_setting_data)
|
||||||
emit('refresh', form_data.value)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,10 @@
|
|||||||
>
|
>
|
||||||
<div v-resize="resizeStepContainer">
|
<div v-resize="resizeStepContainer">
|
||||||
<div class="flex-between mb-16">
|
<div class="flex-between mb-16">
|
||||||
<div class="flex align-center" style="max-width: 90%">
|
<div
|
||||||
|
class="flex align-center"
|
||||||
|
:style="{ maxWidth: node_status == 200 ? 'calc(100% - 55px)' : 'calc(100% - 85px)' }"
|
||||||
|
>
|
||||||
<component :is="iconComponent(`${nodeModel.type}-icon`)" class="mr-8" :size="24" />
|
<component :is="iconComponent(`${nodeModel.type}-icon`)" class="mr-8" :size="24" />
|
||||||
<h4 v-if="showOperate(nodeModel.type)" style="max-width: 90%">
|
<h4 v-if="showOperate(nodeModel.type)" style="max-width: 90%">
|
||||||
<ReadWrite
|
<ReadWrite
|
||||||
@ -22,12 +25,16 @@
|
|||||||
</h4>
|
</h4>
|
||||||
<h4 v-else>{{ nodeModel.properties.stepName }}</h4>
|
<h4 v-else>{{ nodeModel.properties.stepName }}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@mousemove.stop
|
@mousemove.stop
|
||||||
@mousedown.stop
|
@mousedown.stop
|
||||||
@keydown.stop
|
@keydown.stop
|
||||||
@click.stop
|
@click.stop
|
||||||
v-if="showOperate(nodeModel.type)"
|
v-if="showOperate(nodeModel.type)"
|
||||||
|
>
|
||||||
|
<el-tag type="danger" v-if="node_status != 200" style="margin-right: 8px"
|
||||||
|
>不可用</el-tag
|
||||||
>
|
>
|
||||||
<el-dropdown :teleported="false" trigger="click">
|
<el-dropdown :teleported="false" trigger="click">
|
||||||
<el-button text>
|
<el-button text>
|
||||||
@ -73,7 +80,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watch } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { set } from 'lodash'
|
import { set } from 'lodash'
|
||||||
import { iconComponent } from '../icons/utils'
|
import { iconComponent } from '../icons/utils'
|
||||||
import { copyClick } from '@/utils/clipboard'
|
import { copyClick } from '@/utils/clipboard'
|
||||||
@ -89,8 +96,12 @@ const height = ref<{
|
|||||||
outputContainerHeight: 0
|
outputContainerHeight: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
const showEditIcon = ref(false)
|
const node_status = computed(() => {
|
||||||
|
if (props.nodeModel.properties.status) {
|
||||||
|
return props.nodeModel.properties.status
|
||||||
|
}
|
||||||
|
return 200
|
||||||
|
})
|
||||||
function editName(val: string) {
|
function editName(val: string) {
|
||||||
if (val.trim() && val.trim() !== props.nodeModel.properties.stepName) {
|
if (val.trim() && val.trim() !== props.nodeModel.properties.stepName) {
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -138,5 +138,8 @@ export class WorkFlowInstance {
|
|||||||
throw `${node.properties.stepName} 节点不能连接俩个节点`
|
throw `${node.properties.stepName} 节点不能连接俩个节点`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (node.properties.status && node.properties.status !== 200) {
|
||||||
|
throw `${node.properties.stepName} 节点不可用`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
>
|
>
|
||||||
<el-card shadow="never" class="card-never mb-16" style="--el-card-padding: 12px">
|
<el-card shadow="never" class="card-never mb-16" style="--el-card-padding: 12px">
|
||||||
<div v-if="chat_data.input_field_list?.length > 0">
|
<div v-if="chat_data.input_field_list?.length > 0">
|
||||||
<template v-for="(item, index) in chat_data.input_field_list" :key="index">
|
<template v-for="(item, index) in chat_data.input_field_list" :key="item.name">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:prop="'input_field_list.' + index + '.value'"
|
:prop="'input_field_list.' + index + '.value'"
|
||||||
@ -80,7 +80,7 @@ import NodeCascader from '@/workflow/common/NodeCascader.vue'
|
|||||||
import type { FormInstance } from 'element-plus'
|
import type { FormInstance } from 'element-plus'
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
import { isLastNode } from '@/workflow/common/data'
|
import { isLastNode } from '@/workflow/common/data'
|
||||||
|
import functionLibApi from '@/api/function-lib'
|
||||||
const props = defineProps<{ nodeModel: any }>()
|
const props = defineProps<{ nodeModel: any }>()
|
||||||
|
|
||||||
const nodeCascaderRef = ref()
|
const nodeCascaderRef = ref()
|
||||||
@ -112,12 +112,33 @@ const validate = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const update_field = () => {
|
||||||
|
functionLibApi
|
||||||
|
.getFunctionLibById(props.nodeModel.properties.node_data.function_lib_id)
|
||||||
|
.then((ok) => {
|
||||||
|
const old_input_field_list = props.nodeModel.properties.node_data.input_field_list
|
||||||
|
const merge_input_field_list = ok.data.input_field_list.map((item: any) => {
|
||||||
|
const find_field = old_input_field_list.find((old_item: any) => old_item.name == item.name)
|
||||||
|
if (find_field && find_field.source == item.source) {
|
||||||
|
return { ...item, value: JSON.parse(JSON.stringify(find_field.value)) }
|
||||||
|
}
|
||||||
|
return { ...item, value: item.source == 'reference' ? [] : '' }
|
||||||
|
})
|
||||||
|
set(props.nodeModel.properties.node_data, 'input_field_list', merge_input_field_list)
|
||||||
|
set(props.nodeModel.properties, 'status', 200)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
set(props.nodeModel.properties, 'status', 500)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') {
|
if (typeof props.nodeModel.properties.node_data?.is_result === 'undefined') {
|
||||||
if (isLastNode(props.nodeModel)) {
|
if (isLastNode(props.nodeModel)) {
|
||||||
set(props.nodeModel.properties.node_data, 'is_result', true)
|
set(props.nodeModel.properties.node_data, 'is_result', true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
update_field()
|
||||||
set(props.nodeModel, 'validate', validate)
|
set(props.nodeModel, 'validate', validate)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user