feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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 type ItemType<T> = T extends (infer U)[] ? U : T;
|
||||
|
||||
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 shortcut_command } from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
import { type DSL } from '../../types';
|
||||
import { type DSLFormFieldCommonProps } from '../../components/short-cut-panel/widgets/types';
|
||||
import {
|
||||
getDSLTemplate,
|
||||
getFormItemDSLMap,
|
||||
getFormItemPlaceholderDSL,
|
||||
getLayoutDSL,
|
||||
} from './templates';
|
||||
|
||||
// 通过 Components 生成完整的 DSL
|
||||
export const getDSLFromComponents = (
|
||||
params: shortcut_command.Components[],
|
||||
): DSL => {
|
||||
const formItemsDSL = params.map(getFormElementFromComponent);
|
||||
const layoutDSL = getElementsLayout(formItemsDSL);
|
||||
const template = getDSLTemplate();
|
||||
template.elements.form?.children?.unshift(...layoutDSL.map(item => item.id));
|
||||
[...formItemsDSL, ...layoutDSL].forEach(
|
||||
item => (template.elements[item.id] = item),
|
||||
);
|
||||
// @ts-expect-error 支持直接传递 props
|
||||
template.elements.submitButton.props.formFields = formItemsDSL.map(
|
||||
item => item.id,
|
||||
);
|
||||
return template;
|
||||
};
|
||||
|
||||
type DSLElement = DSL['elements'][string];
|
||||
|
||||
// 通过 Components 创建 DSL 中对应的表单元素 Element
|
||||
export const getFormElementFromComponent = (
|
||||
param: shortcut_command.Components,
|
||||
): DSLElement => {
|
||||
if (param.input_type !== undefined) {
|
||||
return getFormItemDSLMap[param.input_type](param);
|
||||
}
|
||||
return getFormItemPlaceholderDSL();
|
||||
};
|
||||
|
||||
// 用于生成 DSL 语法中双列布局的容器元素
|
||||
const ITEMS_PER_LINE = 2;
|
||||
export const getElementsLayout = (elements: DSLElement[]): DSLElement[] => {
|
||||
const res: DSLElement[] = [];
|
||||
const elementsCopy = [...elements];
|
||||
// 如出现奇数个,最后一行占满
|
||||
while (elementsCopy.length) {
|
||||
res.push(getLayoutDSL(elementsCopy.splice(0, ITEMS_PER_LINE)));
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
export enum ElementType {
|
||||
Input = '@flowpd/cici-components/Input',
|
||||
}
|
||||
|
||||
export const findInputElementsWithDefault = (dsl: DSL) => {
|
||||
if (!dsl?.elements) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.values(dsl.elements)
|
||||
.map(c => {
|
||||
const defaultValue = c?.props
|
||||
?.defaultValue as DSLFormFieldCommonProps['defaultValue'];
|
||||
|
||||
return {
|
||||
defaultValue: defaultValue?.value,
|
||||
type: c.type,
|
||||
id: c.id,
|
||||
};
|
||||
})
|
||||
.filter(e => e.type === ElementType.Input && !!e.defaultValue);
|
||||
};
|
||||
|
||||
export const findInputElementById = (dsl: DSL, id: string) => {
|
||||
if (!dsl?.elements) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
Object.values(dsl.elements)
|
||||
.filter(e => e.type === ElementType.Input)
|
||||
.find(e => e.id === id) ?? null
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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 { shortcut_command } from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
import { shortid } from '../uuid';
|
||||
import { getAcceptByUploadItemTypes } from '../file-const';
|
||||
import { type DSL, ElementDirectiveType, ElementPropsType } from '../../types';
|
||||
|
||||
// @fixme DSL 类型定义需要更新
|
||||
export const getDSLTemplate = (): DSL => ({
|
||||
elements: {
|
||||
root: {
|
||||
id: 'root',
|
||||
name: 'Root',
|
||||
type: '@flowpd/cici-components/PageContainer',
|
||||
children: ['form'],
|
||||
directives: {},
|
||||
},
|
||||
form: {
|
||||
id: 'form',
|
||||
name: 'FlowpdCiciComponentsForm', // TODO 组件名 & type 最好也能事先就约定好
|
||||
type: '@flowpd/cici-components/Form',
|
||||
props: {
|
||||
value: {
|
||||
type: ElementPropsType.EXPRESSION,
|
||||
value: '{{formValue}}',
|
||||
},
|
||||
onChange: {
|
||||
type: ElementPropsType.ACTION,
|
||||
value: '{{onChange}}',
|
||||
},
|
||||
ruls: {
|
||||
// some rules value
|
||||
},
|
||||
},
|
||||
children: ['submitButton'], // 根据 variables + layout 生成
|
||||
},
|
||||
// 通过 getFormElementsFromcomponent 生成表单元素
|
||||
// ...inputElements,
|
||||
// 通过 getElementsLayout 生成表单元素 layout
|
||||
// ...inputLayoutElements,
|
||||
submitButton: {
|
||||
id: 'submitButton',
|
||||
name: 'FlowpdCiciComponentsButton', // TODO 组件名 & type 最好也能事先就约定好
|
||||
type: '@flowpd/cici-components/Button',
|
||||
props: {
|
||||
onClick: {
|
||||
type: ElementPropsType.ACTION,
|
||||
value: '{{onSubmit}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
rootID: 'root',
|
||||
variables: {
|
||||
formValue: {
|
||||
id: 'formValue',
|
||||
defaultValue: {}, // 不需要缺省值
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
onSubmit: {
|
||||
id: 'onSubmit',
|
||||
type: 'submit',
|
||||
data: {
|
||||
type: ElementDirectiveType.EXPRESSION,
|
||||
value: '{{formValue}}',
|
||||
},
|
||||
},
|
||||
onChange: {
|
||||
id: 'onChange',
|
||||
type: 'updateVar',
|
||||
target: 'formValue',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
type DSLElement = DSL['elements'][string];
|
||||
export type GetFormItemTemplate = (
|
||||
component: shortcut_command.Components,
|
||||
) => DSLElement;
|
||||
|
||||
export const getInputElementDSL: GetFormItemTemplate = component => ({
|
||||
id: component.name ?? shortid(),
|
||||
name: 'FlowpdCiciComponentsInput', // TODO 组件名最好也能事先就约定好
|
||||
type: '@flowpd/cici-components/Input',
|
||||
props: {
|
||||
name: component.name,
|
||||
description: component.description,
|
||||
defaultValue: component.default_value,
|
||||
rules: [],
|
||||
noErrorMessage: true,
|
||||
// @FIXME DSL 解析逻辑实际支持传入任意值作为 props,但是类型定义里没有兼容这种语法
|
||||
} as unknown as DSLElement['props'],
|
||||
});
|
||||
|
||||
export const getSelectElementDSL: GetFormItemTemplate = component => ({
|
||||
id: component.name ?? shortid(),
|
||||
name: 'FlowpdCiciComponentsSelect', // TODO 组件名最好也能事先就约定好
|
||||
type: '@flowpd/cici-components/Select',
|
||||
props: {
|
||||
name: component.name,
|
||||
description: component.description,
|
||||
defaultValue: component.default_value,
|
||||
optionList: component.options?.map(value => ({
|
||||
label: value,
|
||||
value,
|
||||
})),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
noErrorMessage: true,
|
||||
// @FIXME DSL 解析逻辑实际支持传入任意值作为 props,但是类型定义里没有兼容这种语法
|
||||
} as unknown as DSLElement['props'],
|
||||
});
|
||||
|
||||
// 单位 kb
|
||||
export const IMAGE_MAX_SIZE = 500 * 1024; // 500 mb
|
||||
export const FILE_MAX_SIZE = 500 * 1024; // 500 mb
|
||||
|
||||
const getAcceptByComponent = (component: shortcut_command.Components) => {
|
||||
let uploadItemTypes = [];
|
||||
const { input_type, upload_options } = component;
|
||||
if (input_type === shortcut_command.InputType.MixUpload) {
|
||||
uploadItemTypes = upload_options ?? [];
|
||||
} else {
|
||||
uploadItemTypes = input_type ? [input_type] : [];
|
||||
}
|
||||
return getAcceptByUploadItemTypes(uploadItemTypes);
|
||||
};
|
||||
|
||||
export const getUploadElementDSL: GetFormItemTemplate = component => ({
|
||||
id: component.name ?? shortid(),
|
||||
name: 'FlowpdCiciComponentsUpload', // TODO 组件名最好也能事先就约定好
|
||||
type: '@flowpd/cici-components/Upload',
|
||||
props: {
|
||||
name: component.name,
|
||||
description: component.description,
|
||||
maxSize:
|
||||
component.input_type === shortcut_command.InputType.UploadImage
|
||||
? IMAGE_MAX_SIZE
|
||||
: FILE_MAX_SIZE,
|
||||
inputType: component.input_type,
|
||||
accept: getAcceptByComponent(component),
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
noErrorMessage: true,
|
||||
// @FIXME DSL 解析逻辑实际支持传入任意值作为 props,但是类型定义里没有兼容这种语法
|
||||
} as unknown as DSLElement['props'],
|
||||
});
|
||||
|
||||
// 配置 & 预览模式占位符
|
||||
export const getFormItemPlaceholderDSL = () => ({
|
||||
id: shortid(),
|
||||
name: 'FlowpdCiciComponentsFormItemPlaceholder',
|
||||
type: '@flowpd/cici-components/Placeholder',
|
||||
});
|
||||
|
||||
export const getLayoutDSL = (items: DSLElement[]): DSLElement => ({
|
||||
id: shortid(),
|
||||
name: 'FlowpdCiciComponentsColumnLayout',
|
||||
type: '@flowpd/cici-components/ColumnLayout',
|
||||
props: {
|
||||
// 如出现奇数个,最后一行占满
|
||||
Columns: items.map(item => ({
|
||||
children: [item.id],
|
||||
config: { vertical: 'top', weight: 1, width: 'weighted' },
|
||||
type: 'slot',
|
||||
})),
|
||||
action: 'enableUrl',
|
||||
backgroundColor: 'transparent',
|
||||
enableClickEvent: false,
|
||||
// @FIXME card-buidler Layout 现有的数据结构,但是实际上类型非法
|
||||
} as unknown as DSLElement['props'],
|
||||
});
|
||||
export const getFormItemDSLMap: Record<
|
||||
shortcut_command.InputType,
|
||||
GetFormItemTemplate
|
||||
> = {
|
||||
[shortcut_command.InputType.TextInput]: getInputElementDSL,
|
||||
[shortcut_command.InputType.Select]: getSelectElementDSL,
|
||||
[shortcut_command.InputType.UploadImage]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.UploadDoc]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.UploadTable]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.UploadAudio]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.MixUpload]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.PPT]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.ARCHIVE]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.CODE]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.TXT]: getUploadElementDSL,
|
||||
[shortcut_command.InputType.VIDEO]: getUploadElementDSL,
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 {
|
||||
ZipIcon,
|
||||
VideoIcon,
|
||||
TextIcon as TxtIcon,
|
||||
ImageIcon,
|
||||
AudioIcon,
|
||||
CodeIcon,
|
||||
PptIcon,
|
||||
DocxIcon as DocIcon,
|
||||
XlsxIcon as TableIcon,
|
||||
} from '@coze-common/chat-uikit';
|
||||
import { FILE_TYPE_CONFIG, FileTypeEnum } from '@coze-common/chat-core';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
InputType,
|
||||
shortcut_command,
|
||||
} from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
export type UploadItemType =
|
||||
| InputType.UploadImage
|
||||
| InputType.UploadDoc
|
||||
| InputType.UploadTable
|
||||
| InputType.UploadAudio
|
||||
| InputType.CODE
|
||||
| InputType.ARCHIVE
|
||||
| InputType.PPT
|
||||
| InputType.VIDEO
|
||||
| InputType.TXT;
|
||||
|
||||
export const ACCEPT_UPLOAD_TYPES: {
|
||||
type: UploadItemType;
|
||||
label: string;
|
||||
icon: string;
|
||||
}[] = [
|
||||
{
|
||||
type: InputType.UploadImage,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_img'),
|
||||
icon: ImageIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.UploadTable,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_table'),
|
||||
icon: TableIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.UploadDoc,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_doc'),
|
||||
icon: DocIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.UploadAudio,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_audio'),
|
||||
icon: AudioIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.CODE,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_code'),
|
||||
icon: CodeIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.ARCHIVE,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_zip'),
|
||||
icon: ZipIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.PPT,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_ppt'),
|
||||
icon: PptIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.VIDEO,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_video'),
|
||||
icon: VideoIcon,
|
||||
},
|
||||
{
|
||||
type: InputType.TXT,
|
||||
label: I18n.t('shortcut_modal_upload_component_file_format_txt'),
|
||||
icon: TxtIcon,
|
||||
},
|
||||
];
|
||||
|
||||
// 和chat支持的文件格式做映射
|
||||
export const fileTypeToInputTypeMap = {
|
||||
[FileTypeEnum.IMAGE]: shortcut_command.InputType.UploadImage,
|
||||
[FileTypeEnum.AUDIO]: shortcut_command.InputType.UploadAudio,
|
||||
[FileTypeEnum.PDF]: shortcut_command.InputType.UploadDoc,
|
||||
[FileTypeEnum.DOCX]: shortcut_command.InputType.UploadDoc,
|
||||
[FileTypeEnum.EXCEL]: shortcut_command.InputType.UploadTable,
|
||||
[FileTypeEnum.CSV]: shortcut_command.InputType.UploadTable,
|
||||
[FileTypeEnum.VIDEO]: shortcut_command.InputType.VIDEO,
|
||||
[FileTypeEnum.PPT]: shortcut_command.InputType.PPT,
|
||||
[FileTypeEnum.TXT]: shortcut_command.InputType.TXT,
|
||||
[FileTypeEnum.ARCHIVE]: shortcut_command.InputType.ARCHIVE,
|
||||
[FileTypeEnum.CODE]: shortcut_command.InputType.CODE,
|
||||
[FileTypeEnum.DEFAULT_UNKNOWN]: shortcut_command.InputType.UploadDoc,
|
||||
};
|
||||
|
||||
export const acceptMap = FILE_TYPE_CONFIG.reduce<{
|
||||
[key in shortcut_command.InputType]?: string;
|
||||
}>((acc, cur) => {
|
||||
const inputType = fileTypeToInputTypeMap[cur.fileType];
|
||||
if (inputType) {
|
||||
const preAcc = acc[inputType];
|
||||
if (preAcc) {
|
||||
acc[inputType] = `${preAcc},${cur.accept.join(',')}`;
|
||||
} else {
|
||||
acc[inputType] = cur.accept.join(',');
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// 根据acceptUploadItemTypes获取accept
|
||||
export const getAcceptByUploadItemTypes = (
|
||||
acceptUploadItemTypes: shortcut_command.InputType[],
|
||||
) => {
|
||||
const accept: string[] = [];
|
||||
for (const type of acceptUploadItemTypes) {
|
||||
if (!type) {
|
||||
continue;
|
||||
}
|
||||
const acceptStr = acceptMap[type];
|
||||
if (!acceptStr) {
|
||||
continue;
|
||||
}
|
||||
accept.push(...acceptStr.split(','));
|
||||
}
|
||||
return accept.join(',');
|
||||
};
|
||||
|
||||
// 根据fileType获取对应的fileInfo
|
||||
export const getFileInfoByFileType = (fileType: FileTypeEnum) => {
|
||||
const inputType = fileTypeToInputTypeMap[fileType];
|
||||
if (!inputType) {
|
||||
return null;
|
||||
}
|
||||
return ACCEPT_UPLOAD_TYPES.find(item => item.type === inputType);
|
||||
};
|
||||
|
||||
// ACCEPT_UPLOAD_TYPES转化为map
|
||||
export const getAcceptUploadItemTypesMap = () =>
|
||||
ACCEPT_UPLOAD_TYPES.reduce<{
|
||||
[key in shortcut_command.InputType]?: string;
|
||||
}>((acc, cur) => {
|
||||
acc[cur.type] = cur.label;
|
||||
return acc;
|
||||
}, {});
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { exhaustiveCheckSimple } from '@coze-common/chat-area-utils';
|
||||
|
||||
import { type UIMode } from '../shortcut-bar/types';
|
||||
|
||||
export const getUIModeByBizScene: (props: {
|
||||
bizScene: 'debug' | 'store' | 'home' | 'agentApp';
|
||||
showBackground: boolean;
|
||||
}) => UIMode = ({ bizScene, showBackground }) => {
|
||||
if (bizScene === 'agentApp') {
|
||||
return 'grey';
|
||||
}
|
||||
if (bizScene === 'home') {
|
||||
if (showBackground) {
|
||||
return 'blur';
|
||||
}
|
||||
return 'white';
|
||||
}
|
||||
|
||||
if (bizScene === 'store' || bizScene === 'debug') {
|
||||
if (showBackground) {
|
||||
return 'blur';
|
||||
}
|
||||
return 'grey';
|
||||
}
|
||||
exhaustiveCheckSimple(bizScene);
|
||||
return 'white';
|
||||
};
|
||||
@@ -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 function isApiError(error: unknown): error is { name: 'ApiError' } {
|
||||
if (!error) {
|
||||
return false;
|
||||
}
|
||||
return (error as { name?: string }).name === 'ApiError';
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// 根据template_query和components拼接query
|
||||
export const getQueryFromTemplate = (
|
||||
templateQuery: string,
|
||||
values: Record<string, unknown>,
|
||||
) => {
|
||||
let query = templateQuery;
|
||||
// 替换模板中的{{key}}为values中key对应的值
|
||||
Object.keys(values).forEach(key => {
|
||||
query = query.replace(
|
||||
new RegExp(`\\{\\{${key}\\}\\}`, 'g'),
|
||||
values[key] as string,
|
||||
);
|
||||
});
|
||||
|
||||
return query;
|
||||
};
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 {
|
||||
shortcut_command,
|
||||
type ToolParams,
|
||||
} from '@coze-arch/bot-api/playground_api';
|
||||
|
||||
import { type ToolInfo } from '../shortcut-tool/types';
|
||||
|
||||
// 根据shortcut获取toolInfo
|
||||
export const getToolInfoByShortcut = (
|
||||
shortcut: shortcut_command.ShortcutCommand | undefined,
|
||||
): ToolInfo => {
|
||||
if (!shortcut) {
|
||||
return {
|
||||
tool_type: '',
|
||||
tool_name: '',
|
||||
plugin_id: '',
|
||||
plugin_api_name: '',
|
||||
tool_params_list: [],
|
||||
};
|
||||
}
|
||||
const {
|
||||
tool_info: { tool_params_list = [], tool_name = '' } = {},
|
||||
tool_type,
|
||||
plugin_id,
|
||||
plugin_api_name,
|
||||
work_flow_id,
|
||||
} = shortcut;
|
||||
return {
|
||||
tool_type,
|
||||
tool_name,
|
||||
plugin_id,
|
||||
plugin_api_name,
|
||||
tool_params_list,
|
||||
work_flow_id,
|
||||
};
|
||||
};
|
||||
|
||||
// 校验string:数字 + 英文 + _ & 不能是纯数字
|
||||
export const validateCmdString = (value: string) =>
|
||||
/^[a-zA-Z0-9_]+$/.test(value) && !/^[0-9]+$/.test(value);
|
||||
|
||||
// 根据tool_type判断是否开启了tool
|
||||
export const initToolEnabledByToolTYpe = (
|
||||
toolType: shortcut_command.ToolType | undefined,
|
||||
) =>
|
||||
toolType !== undefined &&
|
||||
[
|
||||
shortcut_command.ToolType.ToolTypeWorkFlow,
|
||||
shortcut_command.ToolType.ToolTypePlugin,
|
||||
].includes(toolType);
|
||||
|
||||
// 校验plugin和workflow参数是否为string|integer类型,不支持复杂的对象类型
|
||||
export const validatePluginAndWorkflowParams = (
|
||||
params: ToolParams[],
|
||||
enableEmpty = false,
|
||||
): {
|
||||
isSuccess: boolean;
|
||||
inValidType: 'empty' | 'complex' | '';
|
||||
} => {
|
||||
if (!params.length) {
|
||||
return {
|
||||
isSuccess: enableEmpty,
|
||||
inValidType: 'empty',
|
||||
};
|
||||
}
|
||||
const isComplex = params.every(param => {
|
||||
const { type } = param;
|
||||
return type !== undefined && !['array', 'object'].includes(type);
|
||||
});
|
||||
return {
|
||||
isSuccess: isComplex,
|
||||
inValidType: isComplex ? '' : 'complex',
|
||||
};
|
||||
};
|
||||
|
||||
// 校验shortcut_command是否重复
|
||||
export const validateCommandNameRepeat = (
|
||||
checkShortcut: shortcut_command.ShortcutCommand,
|
||||
shortcuts: shortcut_command.ShortcutCommand[],
|
||||
): boolean => {
|
||||
const { shortcut_command: shortcutCommand, command_id } = checkShortcut;
|
||||
return !shortcuts.some(
|
||||
shortcut =>
|
||||
command_id !== shortcut.command_id &&
|
||||
shortcut.shortcut_command === shortcutCommand,
|
||||
);
|
||||
};
|
||||
// 校验按钮名称command_name是否重复
|
||||
export const validateButtonNameRepeat = (
|
||||
checkShortcut: shortcut_command.ShortcutCommand,
|
||||
shortcuts: shortcut_command.ShortcutCommand[],
|
||||
): boolean => {
|
||||
const { command_name, command_id } = checkShortcut;
|
||||
return !shortcuts.some(
|
||||
shortcut =>
|
||||
command_id !== shortcut.command_id &&
|
||||
shortcut.command_name === command_name,
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// 对齐 card-builder 生成 ID 的逻辑,暂时拷贝一份,未来计划直接使用 card-buidler 的底层能力
|
||||
import { nanoid, customAlphabet } from 'nanoid';
|
||||
|
||||
/**
|
||||
* @param prefix - id前缀
|
||||
* @param options - alphabet: 字母表; length: 长度,默认10;
|
||||
*/
|
||||
export const shortid = (
|
||||
prefix = '',
|
||||
options?: {
|
||||
alphabet?: string;
|
||||
length?: number;
|
||||
},
|
||||
) => {
|
||||
const {
|
||||
alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
|
||||
length = 10,
|
||||
} = options || {};
|
||||
const genId = customAlphabet(alphabet, length);
|
||||
return `${prefix}${genId()}`;
|
||||
};
|
||||
|
||||
export const uuid = () => nanoid();
|
||||
|
||||
export const id = shortid;
|
||||
|
||||
export const generate = shortid;
|
||||
Reference in New Issue
Block a user