feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { useFormDefaultValues } from './use-form-default-values';
export { useOpenWorkflow } from './use-open-workflow';

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type IFormSchema } from '@coze-workflow/test-run-next';
import { useTestFormService } from '@coze-workflow/test-run';
import { useGlobalState, useTestRunReporterService } from '@/hooks';
import { getNodeExecuteHistoryInput } from '../utils';
export const useFormDefaultValues = () => {
const testFormService = useTestFormService();
const globalState = useGlobalState();
const reporter = useTestRunReporterService();
const getDefaultValues = async (schema: IFormSchema) => {
if (!schema) {
return;
}
const nodeId = schema['x-node-id'] || '';
const nodeType = schema['x-node-type'] || '';
// 最高优:用户上次填写的值
const cacheData = testFormService.getCacheValues(nodeId);
if (cacheData) {
reporter.formGenDataOrigin({ gen_data_origin: 'cache' });
return cacheData;
}
const historyValues = await getNodeExecuteHistoryInput({
workflowId: globalState.workflowId,
spaceId: globalState.spaceId,
nodeId,
nodeType,
});
if (historyValues) {
reporter.formGenDataOrigin({ gen_data_origin: 'history' });
return historyValues;
}
};
return { getDefaultValues };
};

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** pick from master若有冲突请以 master 为准 */
import { useGlobalState } from '@/hooks';
/**
* 打开 workflow
* TODO有一部分依赖还没有迁移完暂时先把这个hook放在这里后面还需要迁移
*/
export const useOpenWorkflow = () => {
const { projectId, getProjectApi } = useGlobalState();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const open = (data: any) => {
const { workflowId, executeId, subExecuteId } = data;
const projectApi = getProjectApi();
if (projectId && projectApi) {
// 应用内跳转
projectApi.sendMsgOpenWidget(`/workflow/${workflowId}`, {
name: 'debug',
data: {
executeId,
subExecuteId,
},
});
} else {
// 资源库或者运维平台跳转
const url = new URL(window.location.href);
const params = new URLSearchParams();
// 新增/更新查询参数,只保留这 4 个参数,包括 space_id
params.append('space_id', url.searchParams.get('space_id') || '0');
params.append('workflow_id', workflowId);
params.append('execute_id', executeId);
params.append('sub_execute_id', subExecuteId);
// 构建新 URL
url.search = params.toString();
// 在新标签页打开
window.open(url.toString(), '_blank');
}
};
return { open };
};

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { useFormDefaultValues, useOpenWorkflow } from './hooks';
export {
generateFormSchema,
generateParametersToProperties,
generateFormNodeField,
generateEnvToRelatedContextProperties,
getRelatedInfo,
} from './utils';
export type { NodeTestMeta, WorkflowNodeEntity } from './types';
export { generateField, type IFormSchema } from '@coze-workflow/test-run-next';

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type WorkflowNodeEntity } from '@flowgram-adapter/free-layout-editor';
import { type IFormSchema } from '@coze-workflow/test-run-next';
/**
* 目前项目中从 flow-sdk 导入的类型位置比较混乱
* test run 全部收口到 kit
*/
export { type WorkflowNodeEntity } from '@flowgram-adapter/free-layout-editor';
interface Context {
isChatflow: boolean;
isInProject: boolean;
workflowId: string;
spaceId: string;
}
/**
* Node Registry Test Meta
*/
export type NodeTestMeta =
| {
/**
* 是否支持测试集
*/
testset?: boolean;
/**
* TestRun 运行所需的关联上下文
*/
generateRelatedContext?: (
node: WorkflowNodeEntity,
context: Context,
) => IFormSchema | null | Promise<IFormSchema | null>;
generateFormInputProperties?: (
node: WorkflowNodeEntity,
) => IFormSchema['properties'] | Promise<IFormSchema['properties']>;
generateFormBatchProperties?: (
node: WorkflowNodeEntity,
) => IFormSchema['properties'] | Promise<IFormSchema['properties']>;
generateFormSettingProperties?: (
node: WorkflowNodeEntity,
) => IFormSchema['properties'] | Promise<IFormSchema['properties']>;
}
| boolean;

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
type IFormSchema,
TestFormFieldName,
} from '@coze-workflow/test-run-next';
import { I18n } from '@coze-arch/i18n';
import { IntelligenceType } from '@coze-arch/bot-api/intelligence_api';
interface GenerateEnvToRelatedContextPropertiesOptions {
isNeedBot: boolean;
isNeedConversation?: boolean;
hasVariableAssignNode?: boolean;
hasLTMNode?: boolean;
hasConversationNode?: boolean;
disableBot?: boolean;
disableBotTooltip?: string;
disableProject?: boolean;
disableProjectTooltip?: string;
}
export const generateEnvToRelatedContextProperties = (
options: GenerateEnvToRelatedContextPropertiesOptions,
) => {
const field: IFormSchema = {
['x-component']: 'RelatedFieldCollapse',
};
const { isNeedBot, isNeedConversation } = options;
if (!isNeedBot && !isNeedConversation) {
return null;
}
field['x-component-props'] = options as any;
field['x-validator'] = (({ value }) => {
const botValue = value?.[TestFormFieldName.Bot];
const conversationValue = value?.[TestFormFieldName.Conversation];
if (isNeedBot && !botValue) {
return {
type: 'bot',
message: I18n.t('workflow_testset_required_tip', {
param_name: 'Bot',
}),
};
}
if (
isNeedConversation &&
botValue?.type === IntelligenceType.Project &&
!conversationValue
) {
return {
type: 'conversation',
message: I18n.t('workflow_testset_required_tip', {
param_name: 'Conversation',
}),
};
}
}) as any;
return field;
};

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type IFormSchema,
isFormSchemaPropertyEmpty,
TestFormFieldName,
} from '@coze-workflow/test-run-next';
import type { WorkflowNodeEntity } from '../types';
interface GroupOptions {
node: WorkflowNodeEntity;
fnName: string;
groupName: string;
properties: Required<IFormSchema>['properties'];
}
const generateNodeFieldGroup = async (options: GroupOptions) => {
const { node, fnName, groupName, properties } = options;
const registry = node.getNodeRegistry();
const fn = registry?.meta?.test?.[fnName];
if (fn) {
const group = await fn(node);
if (!isFormSchemaPropertyEmpty(group)) {
properties[groupName] = {
type: 'object',
properties: group,
};
}
}
};
interface GenerateFormNodeFieldOptions {
node: WorkflowNodeEntity;
}
export const generateFormNodeField = async ({
node,
}: GenerateFormNodeFieldOptions) => {
const properties = {};
await Promise.all([
generateNodeFieldGroup({
node,
fnName: 'generateFormBatchProperties',
groupName: TestFormFieldName.Batch,
properties,
}),
generateNodeFieldGroup({
node,
fnName: 'generateFormSettingProperties',
groupName: TestFormFieldName.Setting,
properties,
}),
generateNodeFieldGroup({
node,
fnName: 'generateFormInputProperties',
groupName: TestFormFieldName.Input,
properties,
}),
]);
if (isFormSchemaPropertyEmpty(properties)) {
return null;
}
return {
type: 'object',
properties,
['x-decorator']: 'NodeFieldCollapse',
};
};

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { WorkflowNodeRefVariablesData } from '@coze-workflow/variable';
import { type IFormSchema } from '@coze-workflow/test-run-next';
import type { WorkflowNodeEntity } from '../types';
import { generateEnvToRelatedContextProperties } from './generate-env-to-related-context-properties';
interface GenerateFormRelatedFieldOptions {
node: WorkflowNodeEntity;
isChatflow: boolean;
isInProject: boolean;
spaceId: string;
workflowId: string;
}
export const generateFormRelatedField = async ({
node,
isChatflow,
isInProject,
spaceId,
workflowId,
}: GenerateFormRelatedFieldOptions) => {
const registry = node.getNodeRegistry();
let field: IFormSchema | null = null;
if (registry?.meta?.test?.generateRelatedContext) {
field = await registry.meta.test.generateRelatedContext(node, {
isChatflow,
isInProject,
spaceId,
workflowId,
});
}
/** 若自定义逻辑判定无需选择环境,则还需要判定一下变量引用 */
if (
!field &&
!isInProject &&
node.getData(WorkflowNodeRefVariablesData).hasGlobalRef
) {
field = generateEnvToRelatedContextProperties({ isNeedBot: true });
}
return field;
};

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
type IFormSchema,
TestFormFieldName,
isFormSchemaPropertyEmpty,
} from '@coze-workflow/test-run-next';
import { PUBLIC_SPACE_ID } from '@coze-workflow/base';
import type { WorkflowNodeEntity } from '../types';
import { getTestsetField } from './generate-form-schema/testset-field';
import { generateFormRelatedField } from './generate-form-related-field';
import { generateFormNodeField } from './generate-form-node-field';
interface GenerateFormSchemaOptions {
node: WorkflowNodeEntity;
workflowId: string;
spaceId: string;
isChatflow: boolean;
isInProject: boolean;
isPreview?: boolean;
}
export const generateFormSchema = async (
options: GenerateFormSchemaOptions,
) => {
const { node, spaceId, isPreview } = options;
const formSchema = {
type: 'object',
['x-node-id']: node.id,
['x-node-type']: node.flowNodeType,
properties: {},
};
const relatedField = await generateFormRelatedField(options);
if (relatedField) {
formSchema.properties[TestFormFieldName.Related] = relatedField;
}
/**
* step1: 计算节点输入
*/
const nodeField = await generateFormNodeField(options);
if (nodeField) {
formSchema.properties[TestFormFieldName.Node] = nodeField;
}
const testset = node.getNodeRegistry().meta?.test?.testset;
/**
* 若支持测试集且输入不为空,则添加测试集的组件
*/
/* The community version does not currently support testset, for future expansion */
if (
!IS_OPEN_SOURCE &&
spaceId !== PUBLIC_SPACE_ID &&
!isPreview &&
testset &&
!isFormSchemaPropertyEmpty(formSchema.properties)
) {
Object.assign(formSchema.properties, getTestsetField());
}
return formSchema as IFormSchema;
};

View File

@@ -0,0 +1,17 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { getTestsetField } from './testset-field';

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestFormFieldName } from '@coze-workflow/test-run-next';
export const getTestsetField = () => ({
[TestFormFieldName.TestsetSelect]: {
// 排序在最前面
['x-index']: 0,
['x-component']: 'TestsetSelect',
},
[TestFormFieldName.TestsetSave]: {
['x-component']: 'TestsetSave',
},
});

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { type WorkflowNodeEntity } from '@flowgram-adapter/free-layout-editor';
import {
generateInputJsonSchema,
variableUtils,
} from '@coze-workflow/variable';
import { generateField } from '@coze-workflow/test-run-next';
import {
ValueExpressionType,
type VariableMetaDTO,
ViewVariableType,
} from '@coze-workflow/base';
/**
* 将结构化的 input 转化为 Field
* fork from packages/workflow/playground/src/components/test-run/utils/generate-test-form-fields.ts
* 在全量前需要及时观测两者变动
*/
export const generateInputToField = (
data: any,
{ node }: { node: WorkflowNodeEntity },
) => {
if (data.input.type === ValueExpressionType.OBJECT_REF) {
const dtoMeta = variableUtils.inputValueToDTO(
data,
node.context.variableService,
{ node },
);
const jsonSchema = generateInputJsonSchema(
(dtoMeta || {}) as VariableMetaDTO,
(v: any) => ({
name: v?.name,
...(v?.input || {}),
}),
);
return generateField({
type: ViewVariableType.Object,
title: data.title || data.label || data.name,
name: data.name,
description: data.description,
required: data?.required,
validateJsonSchema: jsonSchema,
extra: {
['x-dto-meta']: dtoMeta,
},
});
}
const workflowVariable =
node.context.variableService.getWorkflowVariableByKeyPath(
data.input.content.keyPath,
{ node },
);
const viewVariable = workflowVariable?.viewMeta;
const type: ViewVariableType =
variableUtils.getValueExpressionViewType(
data.input,
node.context.variableService,
{ node },
) || ViewVariableType.String;
const dtoMeta: VariableMetaDTO | undefined =
variableUtils.getValueExpressionDTOMeta(
data.input,
node.context.variableService,
{ node },
);
const jsonSchema = generateInputJsonSchema(
(dtoMeta || {}) as VariableMetaDTO,
);
return generateField({
title: data.title || data.label || data.name,
name: data.name,
type,
required: data?.required,
description: data.description,
validateJsonSchema: jsonSchema,
/**
* 部分创建变量的位置可以设置变量的默认值
* 在引用变量的位置,通过 meta 拿到默认值作为表单默认值
*/
defaultValue: viewVariable?.defaultValue,
extra: {
['x-dto-meta']: dtoMeta,
},
});
};

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { isGlobalVariableKey } from '@coze-workflow/variable';
import { getSortedInputParameters } from '@coze-workflow/nodes';
import { ValueExpressionType } from '@coze-workflow/base';
import { isStaticObjectRef } from '@/components/test-run/utils/is-static-object-ref';
import type { WorkflowNodeEntity } from '../types';
import { generateInputToField } from './generate-input-to-field';
export const generateParametersToProperties = (
parameters: any[],
{ node }: { node: WorkflowNodeEntity },
) => {
if (!parameters || !Array.isArray(parameters)) {
return {};
}
const fields = parameters.filter(i => {
/** 对象引用类型不需要过滤,全是静态字段的需要过滤 */
if (i.input?.type === ValueExpressionType.OBJECT_REF) {
return !isStaticObjectRef(i);
}
/** 非引用类型直接过滤,引用值不存在直接过滤 */
if (i.input?.type !== 'ref' || !i.input?.content) {
return false;
}
/** 如果引用来自于自身,则不需要再填写 */
const [nodeId] = i.input.content.keyPath || [];
if (nodeId && nodeId === node.id) {
return false;
}
if (isGlobalVariableKey(nodeId)) {
return false;
}
return true;
});
const sortedFields = getSortedInputParameters(fields);
return sortedFields
.map(field => generateInputToField(field, { node }))
.reduce((properties, field) => {
if (field.name) {
properties[field.name] = field;
}
return properties;
}, {});
};

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { invert } from 'lodash-es';
import {
safeJsonParse,
TestFormFieldName,
stringifyFormValuesFromBacked,
} from '@coze-workflow/test-run-next';
import { workflowApi, StandardNodeType } from '@coze-workflow/base';
import { NodeHistoryScene } from '@coze-arch/bot-api/workflow_api';
interface GetNodeExecuteHistoryInputOptions {
workflowId: string;
spaceId: string;
nodeId: string;
nodeType: string;
}
export const getNodeExecuteHistoryInput = async (
options: GetNodeExecuteHistoryInputOptions,
) => {
const { spaceId, workflowId, nodeId, nodeType } = options;
const map = invert(StandardNodeType);
const nodeTypeStr = map[nodeType];
if (!nodeId || !nodeTypeStr) {
return;
}
try {
const res = await workflowApi.GetNodeExecuteHistory({
workflow_id: workflowId,
space_id: spaceId,
node_id: nodeId,
node_type: nodeTypeStr,
execute_id: '',
node_history_scene: NodeHistoryScene.TestRunInput,
});
const inputValues = safeJsonParse(res.data?.input);
if (inputValues) {
return {
[TestFormFieldName.Node]: {
[TestFormFieldName.Input]: stringifyFormValuesFromBacked(inputValues),
},
};
}
// eslint-disable-next-line @coze-arch/no-empty-catch
} catch {
//
}
};

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { intersection } from 'lodash-es';
import {
workflowApi,
CONVERSATION_NODES,
StandardNodeType,
} from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
interface GetRelatedInfoOptions {
workflowId: string;
spaceId: string;
}
function checkHasConversationNode(typeList: StandardNodeType[]) {
return intersection(typeList, CONVERSATION_NODES).length > 0;
}
export const getRelatedInfo = async (options: GetRelatedInfoOptions) => {
const { workflowId, spaceId } = options;
const { data: nodeTypes } = await workflowApi.QueryWorkflowNodeTypes({
workflow_id: workflowId,
space_id: spaceId,
});
const flowTypeList = nodeTypes?.node_types ?? [];
const subFlowTypeList = nodeTypes?.sub_workflow_node_types ?? [];
const sumTypeList = [
...flowTypeList,
...subFlowTypeList,
] as StandardNodeType[];
const flowPropsList = nodeTypes?.nodes_properties ?? [];
const subFlowPropsList = nodeTypes?.sub_workflow_nodes_properties ?? [];
const sumPropsList = [...flowPropsList, ...subFlowPropsList];
const hasVariableNode = sumTypeList.includes(StandardNodeType.Variable);
const hasVariableAssignNode = sumTypeList.includes(
StandardNodeType.VariableAssign,
);
const hasIntentNode = sumTypeList.includes(StandardNodeType.Intent);
const hasLLMNode = sumTypeList.includes(StandardNodeType.LLM);
const hasLTMNode = sumTypeList.includes(StandardNodeType.LTM);
const hasConversationNode = checkHasConversationNode(sumTypeList);
const propsEnableChatHistory = sumPropsList.some(
item => item.is_enable_chat_history,
);
const hasNodeUseGlobalVariable = !!sumPropsList.find(
item => item.is_ref_global_variable,
);
const hasChatHistoryEnabledLLM =
(hasLLMNode || hasIntentNode) && propsEnableChatHistory;
const hasSubFlowNode = subFlowTypeList.some(it =>
[StandardNodeType.SubWorkflow].includes(it as StandardNodeType),
);
// 流程中(包含 subflow 下钻节点)有 Variable、Database、开启 chat history 的LLM || subflow 节点有 subflow
const isNeedBot =
hasNodeUseGlobalVariable ||
hasVariableAssignNode ||
hasVariableNode ||
hasLTMNode ||
hasChatHistoryEnabledLLM ||
hasSubFlowNode ||
hasConversationNode;
const isNeedConversation = hasChatHistoryEnabledLLM;
return {
isNeedBot,
isNeedConversation,
hasVariableNode,
hasVariableAssignNode,
hasNodeUseGlobalVariable,
hasLTMNode,
hasChatHistoryEnabledLLM,
hasConversationNode,
// 当包含会话类节点,需要禁用 bot 选项
disableBot: hasConversationNode,
disableBotTooltip: hasConversationNode ? I18n.t('wf_chatflow_141') : '',
// 包含 LTM 节点,需要禁用项目选项
disableProject: hasLTMNode,
disableProjectTooltip: hasLTMNode ? I18n.t('wf_chatflow_142') : '',
};
};

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { generateParametersToProperties } from './generate-parameters-to-properties';
export { generateFormNodeField } from './generate-form-node-field';
export { generateFormSchema } from './generate-form-schema';
export { getNodeExecuteHistoryInput } from './get-node-execute-history-input';
export { generateEnvToRelatedContextProperties } from './generate-env-to-related-context-properties';
export { getRelatedInfo } from './get-related-info';