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,96 @@
/*
* 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-magic-numbers */
import { StandardNodeType } from '@coze-workflow/base';
/**
* 使用 V2 版本异常设置的节点
*/
export const SETTING_ON_ERROR_V2_NODES = [
StandardNodeType.Code,
StandardNodeType.LLM,
StandardNodeType.Api,
StandardNodeType.Database,
StandardNodeType.ImageGenerate,
StandardNodeType.DatabaseCreate,
StandardNodeType.DatabaseUpdate,
StandardNodeType.DatabaseQuery,
StandardNodeType.DatabaseDelete,
StandardNodeType.ImageCanvas,
StandardNodeType.Intent,
];
/**
* 使用V1的版本异常设置
*/
export const SETTING_ON_ERROR_V1_NODES = [StandardNodeType.Http];
/**
* 有动态port的节点
*/
export const SETTING_ON_ERROR_DYNAMIC_PORT_NODES = [StandardNodeType.Intent];
/**
* 开启异常的节点
*/
export const SETTING_ON_ERROR_NODES = [
...SETTING_ON_ERROR_V1_NODES,
...SETTING_ON_ERROR_V2_NODES,
];
/**
* 异常端口
*/
export const SETTING_ON_ERROR_PORT = 'branch_error';
/**
* 超时最小100ms
*/
export const SETTING_ON_ERROR_MIN_TIMEOUT = 100;
/**
* 其他节点默认1分钟最大1分钟
*/
export const SETTING_ON_ERROR_DEFAULT_TIMEOUT = {
default: 60 * 1000,
max: 60 * 1000,
};
/**
* 节点配置
* LLM默认3分钟最大10分钟
* 插件默认3分钟最大3分钟
*/
export const SETTING_ON_ERROR_NODES_CONFIG = {
[StandardNodeType.LLM]: {
timeout: {
default: 3 * 60 * 1000,
max: 10 * 60 * 1000,
init: 10 * 60 * 1000,
},
enableBackupModel: true,
},
[StandardNodeType.Api]: {
timeout: {
default: 3 * 60 * 1000,
max: 3 * 60 * 1000,
},
},
};
export const ERROR_BODY_NAME = 'errorBody';
export const IS_SUCCESS_NAME = 'isSuccess';

View File

@@ -0,0 +1,204 @@
/*
* 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 ViewVariableTreeNode,
type StandardNodeType,
} from '@coze-workflow/base';
import { type NodeContext } from '@flowgram-adapter/free-layout-editor';
import { formatModelData } from '../utils';
import { getOutputsWithErrorBody } from './utils/outputs';
import { getTimeoutConfig } from './utils/get-timeout-config';
import { isSettingOnErrorV2 } from './utils';
import {
type SettingOnErrorDTO,
SettingOnErrorProcessType,
type SettingOnErrorExt,
type NodeValueWithSettingOnErrorDTO,
type NodeValueWithSettingOnErrorVO,
type SettingOnErrorVO,
} from './types';
const formatExtOnInit = (ext: SettingOnErrorDTO['ext']) => {
if (!ext) {
return ext;
}
const llmParam = ext?.backupLLmParam;
return {
backupLLmParam: llmParam ? JSON.parse(llmParam) : undefined,
};
};
const getDTOProcessType = (settingOnError?: SettingOnErrorDTO) =>
settingOnError?.processType ||
(settingOnError?.switch
? SettingOnErrorProcessType.RETURN
: SettingOnErrorProcessType.BREAK);
const settingOnErrorInitV2 = (
settingOnError?: SettingOnErrorDTO,
context?: NodeContext,
value?: NodeValueWithSettingOnErrorDTO,
) => {
if (!isNodeContextV2(context)) {
return {};
}
let timeoutMs = settingOnError?.timeoutMs;
const timeoutConfig = getTimeoutConfig(context?.node);
if (!timeoutMs) {
// 如果没有设置超时时间,且有初始值配置,则设置初始值
// 如LLM后端默认是10min历史数据需要设置为init的10min新加的节点显示默认的default的3min
if (value && timeoutConfig?.init) {
timeoutMs = timeoutConfig.init;
} else {
timeoutMs = timeoutConfig?.default;
}
}
return {
processType: getDTOProcessType(settingOnError),
timeoutMs,
retryTimes: settingOnError?.retryTimes ?? 0,
ext: formatExtOnInit(settingOnError?.ext),
};
};
const formatExtOnSave = (
ext: SettingOnErrorExt | undefined,
playgroundContext,
) => {
if (!ext) {
return ext;
}
const models = playgroundContext?.models || [];
const llmParam = ext.backupLLmParam;
const modelMeta = models.find(m => m.model_type === llmParam?.modelType);
return {
backupLLmParam: llmParam
? JSON.stringify(formatModelData(llmParam, modelMeta))
: undefined,
};
};
const settingOnErrorSaveV2 = (
settingOnError: SettingOnErrorVO,
context?: NodeContext,
) => {
const playgroundContext = context?.playgroundContext;
const res: Partial<SettingOnErrorDTO> = {
processType:
settingOnError?.processType ||
(settingOnError?.settingOnErrorIsOpen
? SettingOnErrorProcessType.RETURN
: undefined),
timeoutMs: settingOnError?.timeoutMs,
retryTimes: settingOnError?.retryTimes,
};
if (settingOnError?.retryTimes) {
res.ext = formatExtOnSave(settingOnError?.ext, playgroundContext);
}
return res;
};
const isNodeContextV2 = (context?: NodeContext) =>
isSettingOnErrorV2(context?.node?.flowNodeType as StandardNodeType);
export const settingOnErrorToVO = (
settingOnError?: SettingOnErrorDTO,
context?: NodeContext,
value?: NodeValueWithSettingOnErrorDTO,
): SettingOnErrorVO => ({
settingOnErrorIsOpen: settingOnError?.switch,
settingOnErrorJSON: settingOnError?.dataOnErr,
...settingOnErrorInitV2(settingOnError, context, value),
});
export const settingOnErrorToDTO = (
settingOnError?: SettingOnErrorVO,
context?: NodeContext,
) => {
if (!settingOnError) {
return settingOnError;
}
return {
switch: settingOnError?.settingOnErrorIsOpen,
dataOnErr: settingOnError?.settingOnErrorJSON,
...(isNodeContextV2(context)
? settingOnErrorSaveV2(settingOnError, context)
: {}),
};
};
export const formatOutputsOnInit = (
value?: NodeValueWithSettingOnErrorDTO,
context?: NodeContext,
) => {
const outputs = value?.outputs;
const isV2 = isNodeContextV2(context);
const isOpen = !!value?.inputs?.settingOnError?.switch;
if (!outputs || !isSettingOnErrorV2 || !isOpen) {
return;
}
const isBatch = !!value?.inputs?.batch?.batchEnable;
return getOutputsWithErrorBody({
value: outputs,
isBatch,
isOpen,
isSettingOnErrorV2: isV2,
});
};
export const settingOnErrorInit = (
value?: NodeValueWithSettingOnErrorDTO,
context?: NodeContext,
): {
settingOnError?: SettingOnErrorVO;
outputs?: ViewVariableTreeNode[];
} => {
const outputs = formatOutputsOnInit(value, context);
return {
settingOnError: settingOnErrorToVO(
value?.inputs?.settingOnError,
context,
value,
),
...(outputs ? { outputs } : {}),
};
};
export const settingOnErrorSave = (
value: NodeValueWithSettingOnErrorVO,
context: NodeContext | undefined = undefined,
) => {
const settingOnError = value?.settingOnError;
if (value?.settingOnError) {
delete value.settingOnError;
}
return {
settingOnError: settingOnErrorToDTO(settingOnError, context),
};
};

View File

@@ -0,0 +1,34 @@
/*
* 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 StandardNodeType } from '@coze-workflow/base/types';
import { useCurrentEntity } from '@flowgram-adapter/free-layout-editor';
import { isSettingOnErrorV2, isSettingOnError } from '../utils';
export const useIsSettingOnErrorV2 = () => {
const node = useCurrentEntity();
return isSettingOnErrorV2(node.flowNodeType as StandardNodeType);
};
export const useIsSettingOnError = () => {
const node = useCurrentEntity();
return isSettingOnError(node.flowNodeType as StandardNodeType);
};
export { useTimeoutConfig } from './use-timeout-config';

View File

@@ -0,0 +1,33 @@
/*
* 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 { useCurrentEntity } from '@flowgram-adapter/free-layout-editor';
import { getTimeoutConfig } from '../utils/get-timeout-config';
/**
* 获取超时配置
* @returns
*/
export const useTimeoutConfig = (): {
default: number;
max: number;
min: number;
disabled: boolean;
} => {
const node = useCurrentEntity();
return getTimeoutConfig(node);
};

View File

@@ -0,0 +1,54 @@
/*
* 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 {
SettingOnErrorProcessType,
type SettingOnErrorExt,
type SettingOnErrorVO,
type SettingOnErrorValue,
} from './types';
export {
settingOnErrorInit,
settingOnErrorSave,
settingOnErrorToDTO,
settingOnErrorToVO,
} from './data-transformer';
export {
isSettingOnError,
isSettingOnErrorV2,
isSettingOnErrorDynamicPort,
} from './utils';
export {
generateErrorBodyMeta,
generateIsSuccessMeta,
} from './utils/generate-meta';
export {
useIsSettingOnError,
useIsSettingOnErrorV2,
useTimeoutConfig,
} from './hooks';
export {
SETTING_ON_ERROR_PORT,
SETTING_ON_ERROR_NODES_CONFIG,
ERROR_BODY_NAME,
IS_SUCCESS_NAME,
} from './constants';
export {
getOutputsWithErrorBody,
sortErrorBody,
getExcludeErrorBody,
} from './utils/outputs';

View File

@@ -0,0 +1,124 @@
/*
* 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 ViewVariableTreeNode } from '@coze-workflow/base';
/**
* 异常处理类型
* 1 直接中断
* 2 返回设定内容,
* 3 执行异常流程
*/
export enum SettingOnErrorProcessType {
BREAK = 1,
RETURN = 2,
EXCEPTION = 3,
}
/**
* 异常处理额外数据
*/
export interface SettingOnErrorExt {
/**
* LLM节点重试的备选模型
*/
backupLLmParam?: {
modelName?: string;
modelType?: number;
temperature?: number;
frequencyPenalty?: number;
presencePenalty?: number;
topP?: number;
topK?: number;
maxTokens?: number;
};
}
interface SettingOnErrorBase {
/**
* 处理类型
*/
processType?: SettingOnErrorProcessType;
/**
* 超时时间 毫秒
*/
timeoutMs?: number;
/**
* 重试次数 0 表示不重试
*/
retryTimes?: number;
}
/**
* 异常处理前端结构
*/
export interface SettingOnErrorVO extends SettingOnErrorBase {
/**
* 是否开启异常处理
*/
settingOnErrorIsOpen?: boolean;
/**
* 发生异常处理的默认值
*/
settingOnErrorJSON?: string;
/**
* 其他设置
*/
ext?: SettingOnErrorExt;
}
export type SettingOnErrorValue = SettingOnErrorVO;
/**
* 异常处理后端结构
*/
export interface SettingOnErrorDTO extends SettingOnErrorBase {
/**
* 是否开启异常处理
*/
switch: boolean;
/**
* 发生异常处理的默认值
*/
dataOnErr: string;
/**
* 其他设置
*/
ext?: {
backupLLmParam?: string;
};
}
/**
* 有异常设置的后端节点数据
*/
export interface NodeValueWithSettingOnErrorDTO {
inputs?: {
settingOnError?: SettingOnErrorDTO;
batch?: {
batchEnable?: boolean;
};
};
outputs?: ViewVariableTreeNode[];
}
/**
* 有异常设置的前端节点数据
*/
export interface NodeValueWithSettingOnErrorVO {
settingOnError?: SettingOnErrorVO;
}

View File

@@ -0,0 +1,47 @@
/*
* 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 StandardNodeType } from '@coze-workflow/base';
import {
SETTING_ON_ERROR_DYNAMIC_PORT_NODES,
SETTING_ON_ERROR_NODES,
SETTING_ON_ERROR_V2_NODES,
} from './constants';
/**
* 是不是v2版本的节点
* @param type
* @returns
*/
export const isSettingOnErrorV2 = (type?: StandardNodeType) =>
type && SETTING_ON_ERROR_V2_NODES.includes(type);
/**
* 是不是开启异常设置的节点
* @param type
* @returns
*/
export const isSettingOnError = (type?: StandardNodeType) =>
type && SETTING_ON_ERROR_NODES.includes(type);
/**
* 是不是动态通道的节点
* @param type
* @returns
*/
export const isSettingOnErrorDynamicPort = (type?: StandardNodeType) =>
type && SETTING_ON_ERROR_DYNAMIC_PORT_NODES.includes(type);

View File

@@ -0,0 +1,48 @@
/*
* 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 { nanoid } from 'nanoid';
import { ViewVariableType } from '@coze-workflow/base';
import { ERROR_BODY_NAME, IS_SUCCESS_NAME } from '../constants';
export const generateErrorBodyMeta = () => ({
key: nanoid(),
name: ERROR_BODY_NAME,
type: ViewVariableType.Object,
readonly: true,
children: [
{
key: nanoid(),
name: 'errorMessage',
type: ViewVariableType.String,
readonly: true,
},
{
key: nanoid(),
name: 'errorCode',
type: ViewVariableType.String,
readonly: true,
},
],
});
export const generateIsSuccessMeta = () => ({
key: nanoid(),
name: IS_SUCCESS_NAME,
type: ViewVariableType.Boolean,
readonly: true,
});

View File

@@ -0,0 +1,73 @@
/*
* 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 { get } from 'lodash-es';
import { PluginType, StandardNodeType } from '@coze-workflow/base';
import { type WorkflowNodeEntity } from '@flowgram-adapter/free-layout-editor';
import {
SETTING_ON_ERROR_DEFAULT_TIMEOUT,
SETTING_ON_ERROR_MIN_TIMEOUT,
SETTING_ON_ERROR_NODES_CONFIG,
} from '../constants';
import { WorkflowNodeData } from '../../entity-datas';
/**
* 是不是端插件
* @param node
* @returns
*/
const isLocalPlugin = (node?: WorkflowNodeEntity) => {
if (!node) {
return false;
}
const nodeDataEntity = node.getData<WorkflowNodeData>(WorkflowNodeData);
const nodeData = nodeDataEntity?.getNodeData();
return !!(
node?.flowNodeType === StandardNodeType.Api &&
get(nodeData, 'pluginType') === PluginType.LOCAL
);
};
/**
* 获取节点超时配置
*/
export const getTimeoutConfig = (
node?: WorkflowNodeEntity,
): {
max: number;
default: number;
min: number;
init?: number;
disabled: boolean;
} => {
let timeoutConfig = SETTING_ON_ERROR_DEFAULT_TIMEOUT;
if (
node?.flowNodeType &&
SETTING_ON_ERROR_NODES_CONFIG[node.flowNodeType]?.timeout
) {
timeoutConfig = SETTING_ON_ERROR_NODES_CONFIG[node.flowNodeType].timeout;
}
return {
...timeoutConfig,
min: SETTING_ON_ERROR_MIN_TIMEOUT,
disabled: isLocalPlugin(node),
};
};

View File

@@ -0,0 +1,145 @@
/*
* 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 ViewVariableTreeNode } from '@coze-workflow/base';
import { ERROR_BODY_NAME, IS_SUCCESS_NAME } from '../constants';
import { generateErrorBodyMeta, generateIsSuccessMeta } from './generate-meta';
const settingOnErrorNames = isSettingOnErrorV2 => [
ERROR_BODY_NAME,
...(isSettingOnErrorV2 ? [IS_SUCCESS_NAME] : []),
];
const excludeFn = isSettingOnErrorV2 => v =>
!settingOnErrorNames(isSettingOnErrorV2).includes(v.name);
const includeFn = isSettingOnErrorV2 => v =>
settingOnErrorNames(isSettingOnErrorV2).includes(v.name);
/**
* 向 output 中添加/剔除 errorBody
*/
export const getOutputsWithErrorBody = ({
value,
isBatch,
isOpen,
isSettingOnErrorV2,
}: {
value?: ViewVariableTreeNode[];
isBatch: boolean;
isOpen: boolean;
isSettingOnErrorV2?: boolean;
}) => {
if (!value) {
return value;
}
// 添加 errorBody
if (isOpen) {
// batch 模式下,在第一层的 children 里追加 errorBody
if (isBatch) {
return [
{
...value[0],
children: [
...(value[0]?.children ?? []).filter(excludeFn(isSettingOnErrorV2)),
generateErrorBodyMeta(),
...(isSettingOnErrorV2 ? [generateIsSuccessMeta()] : []),
],
},
];
// single 模式下,在第一层追加 errorBody
} else {
return [
...(value ?? []).filter(excludeFn(isSettingOnErrorV2)),
generateErrorBodyMeta(),
...(isSettingOnErrorV2 ? [generateIsSuccessMeta()] : []),
];
}
// 剔除 errorBody
} else {
// batch 模式下,从第一层的 children 中剔除
if (isBatch) {
const [one, ...rest] = value;
return [
{
...one,
children: [
...(one?.children ?? []).filter(excludeFn(isSettingOnErrorV2)),
],
},
...rest,
];
// single 模式下,从第一层的 children 中剔除
} else {
return [...(value ?? []).filter(excludeFn(isSettingOnErrorV2))];
}
}
};
/**
* output 属性排序,保证 errorBody 在最下面
*/
export const sortErrorBody = ({
value,
isBatch,
isSettingOnErrorV2,
}: {
value?: ViewVariableTreeNode[];
isBatch: boolean;
isSettingOnErrorV2?: boolean;
}) => {
if (!value) {
return value;
}
if (isBatch) {
const [one, ...rest] = value;
return [
{
...one,
children: [
...(one?.children ?? []).filter(excludeFn(isSettingOnErrorV2)),
...(one?.children ?? []).filter(includeFn(isSettingOnErrorV2)),
],
},
...rest,
];
}
return [
...value.filter(excludeFn(isSettingOnErrorV2)),
...value.filter(includeFn(isSettingOnErrorV2)),
];
};
/**
* 把 value 中的 errorBody 删除掉
*/
export const getExcludeErrorBody = ({
value,
isBatch,
isSettingOnErrorV2,
}: {
value?: ViewVariableTreeNode[];
isBatch: boolean;
isSettingOnErrorV2?: boolean;
}) =>
getOutputsWithErrorBody({
value,
isBatch,
isOpen: false,
isSettingOnErrorV2,
});