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,87 @@
/*
* 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 { ViewVariableType, getFileAccept } from '@coze-workflow/base';
interface GenerateFieldComponentOptions {
type: ViewVariableType;
validateJsonSchema?: any;
}
export const generateFieldComponent = (
options: GenerateFieldComponentOptions,
) => {
const { type, validateJsonSchema } = options;
/** 音色类型 */
if (ViewVariableType.Voice === type) {
return {
['x-component']: 'SelectVoice',
};
}
/** 文件类型 */
if (ViewVariableType.isFileType(type)) {
const fileType = [
ViewVariableType.Image,
ViewVariableType.ArrayImage,
].includes(type)
? 'image'
: 'object';
return {
['x-component']: 'TypedFileInput',
['x-component-props']: {
// 如果是数组类型,则表明是多选的文件选择器
multiple: ViewVariableType.isArrayType(type),
accept: getFileAccept(type),
fileType,
},
};
}
/** 排除文件类型的对象类型、数组类型 */
if (ViewVariableType.isArrayType(type) || ViewVariableType.Object === type) {
return {
['x-component']: 'InputJson',
['x-component-props']: {
jsonSchema: validateJsonSchema,
},
defaultValue: ViewVariableType.Object === type ? '{}' : '[]',
};
}
if (type === ViewVariableType.Integer) {
return {
['x-component']: 'InputInteger',
};
}
if (type === ViewVariableType.Number) {
return {
['x-component']: 'InputNumber',
};
}
if (type === ViewVariableType.Boolean) {
return {
['x-component']: 'SelectBoolean',
defaultValue: true,
};
}
if (type === ViewVariableType.Time) {
return {
['x-component']: 'InputTime',
};
}
/** string 类型和其它未知类型都渲染普通输入框 */
return {
['x-component']: 'InputString',
};
};

View File

@@ -0,0 +1,72 @@
/*
* 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 Ajv from 'ajv';
import { I18n } from '@coze-arch/i18n';
import { type IFormSchemaValidate } from '../form-engine';
const isEmptyValue = (v: unknown) => v === undefined || v === null || v === '';
interface GenerateFieldValidatorOptions {
name: string;
title?: string;
required?: boolean;
validateJsonSchema?: any;
}
/**
* ajv 实例缓存
* 无需导入创建或者多次创建,优化内存开销
*/
let ajvCache: undefined | Ajv;
export const generateFieldValidator = (
options: GenerateFieldValidatorOptions,
) => {
const { required, title, name, validateJsonSchema } = options;
const validator: IFormSchemaValidate = ({ value }) => {
if (required && isEmptyValue(value)) {
return I18n.t('workflow_testset_required_tip', {
param_name: title || name,
});
}
// 如果有结构化描述,还需要对值进行反序列化校验
if (validateJsonSchema && value !== undefined) {
if (!ajvCache) {
ajvCache = new Ajv();
}
try {
const valueObject = JSON.parse(value);
const validate = ajvCache.compile(validateJsonSchema);
const valid = validate(valueObject);
return valid ? undefined : I18n.t('workflow_debug_wrong_json');
} catch {
/**
* 报错有多种可能,预期结果都是校验不通过
* 1. 值反序列化失败
* 2. 反序列化的值不合法
*/
return I18n.t('workflow_debug_wrong_json');
}
}
};
return {
['x-validator']: validator,
};
};

View File

@@ -0,0 +1,66 @@
/*
* 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 { ViewVariableType } from '@coze-workflow/base';
import { type IFormSchema } from '../form-engine';
import { generateFieldValidator } from './generate-field-validator';
import { generateFieldComponent } from './generate-field-component';
interface GenerateFieldOptions {
type: ViewVariableType;
name: string;
title?: string;
required?: boolean;
description?: string;
defaultValue?: string;
validateJsonSchema?: any;
extra?: IFormSchema;
}
/**
* 表单 Field Schema 计算
*/
export const generateField = (options: GenerateFieldOptions): IFormSchema => {
const {
type,
name,
title,
required = true,
description,
defaultValue,
validateJsonSchema,
extra,
} = options;
return {
name,
title,
description,
required,
['x-decorator']: 'FieldItem',
['x-decorator-props']: {
tag: ViewVariableType.LabelMap[type],
},
['x-origin-type']: type as unknown as string,
...generateFieldValidator(options),
// 渲染组件相关
...generateFieldComponent({ type, validateJsonSchema }),
// component 也自带默认值,入参的默认值优先级更高
defaultValue,
...extra,
};
};

View File

@@ -0,0 +1,20 @@
/*
* 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 { generateField } from './generate-field';
export { generateFieldValidator } from './generate-field-validator';
export { isFormSchemaPropertyEmpty } from './is-property-empty';
export { stringifyFormValuesFromBacked } from './stringify-form-values-from-backed';

View File

@@ -0,0 +1,23 @@
/*
* 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 { isObject } from 'lodash-es';
/**
* 是否是空的 properties
*/
export const isFormSchemaPropertyEmpty = (properties: unknown) =>
isObject(properties) ? !Object.keys(properties).length : true;

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 { isBoolean } from 'lodash-es';
export const stringifyFormValuesFromBacked = (value: object) => {
if (!value) {
return undefined;
}
return Object.keys(value).reduce((acc, key) => {
const val = value[key];
if (val === null || val === undefined) {
acc[key] = undefined;
} else if (typeof val === 'string' || isBoolean(val)) {
acc[key] = val;
} else {
acc[key] = JSON.stringify(value[key]);
}
return acc;
}, {});
};