feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
63
frontend/packages/workflow/base/src/api/index.ts
Normal file
63
frontend/packages/workflow/base/src/api/index.ts
Normal 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 { workflowApi as archWorkflowApi } from '@coze-arch/bot-api';
|
||||
|
||||
// eslint-disable-next-line @coze-arch/no-batch-import-or-export
|
||||
export * from '@coze-arch/bot-api/workflow_api';
|
||||
|
||||
export { withQueryClient, workflowQueryClient } from './with-query-client';
|
||||
|
||||
/** 运营接口平台,会替换权限验证 */
|
||||
const workflowOperationApiNameMap = {
|
||||
GetHistorySchema: 'OPGetHistorySchema',
|
||||
GetWorkFlowProcess: 'OPGetWorkFlowProcess',
|
||||
GetCanvasInfo: 'OPGetCanvasInfo',
|
||||
GetWorkflowReferences: 'OPGetWorkflowReferences',
|
||||
GetReleasedWorkflows: 'OPGetReleasedWorkflows',
|
||||
GetApiDetail: 'OPGetApiDetail',
|
||||
NodeTemplateList: 'OPNodeTemplateList',
|
||||
GetWorkflowGrayFeature: 'OPGetWorkflowGrayFeature',
|
||||
CheckLatestSubmitVersion: 'OPCheckLatestSubmitVersion',
|
||||
GetImageflowBasicNodeList: 'OPGetImageflowBasicNodeList',
|
||||
GetWorkflowDetail: 'OPGetWorkflowDetail',
|
||||
GetLLMNodeFCSettingDetail: 'OPGetLLMNodeFCSettingDetail',
|
||||
ListTriggerAppEvents: 'OPListTriggerAppEvents',
|
||||
GetTrigger: 'OPGetTrigger',
|
||||
GetWorkflowDetailInfo: 'OPGetWorkflowDetailInfo',
|
||||
GetNodeExecuteHistory: 'OPGetNodeExecuteHistory',
|
||||
VersionHistoryList: 'OPVersionHistoryList',
|
||||
GetChatFlowRole: 'OPGetChatFlowRole',
|
||||
ListRootSpans: 'OPListRootSpans',
|
||||
GetTraceSDK: 'OPGetTraceSDK',
|
||||
};
|
||||
|
||||
const workflowApi: typeof archWorkflowApi = new Proxy(
|
||||
{} as unknown as typeof archWorkflowApi,
|
||||
{
|
||||
get: (target, name: string) => {
|
||||
if (IS_BOT_OP && workflowOperationApiNameMap[name]) {
|
||||
return archWorkflowApi[workflowOperationApiNameMap[name]].bind(
|
||||
archWorkflowApi,
|
||||
);
|
||||
} else {
|
||||
return archWorkflowApi[name].bind(archWorkflowApi);
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
export { workflowApi };
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
import React, { type FC } from 'react';
|
||||
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
export const workflowQueryClient = new QueryClient();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-explicit-any
|
||||
export function withQueryClient<T extends FC<any>>(Component: T): T {
|
||||
return function WrappedComponent(props) {
|
||||
return (
|
||||
<QueryClientProvider client={workflowQueryClient}>
|
||||
<Component {...props} />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
} as T;
|
||||
}
|
||||
48
frontend/packages/workflow/base/src/constants/index.ts
Normal file
48
frontend/packages/workflow/base/src/constants/index.ts
Normal 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.
|
||||
*/
|
||||
|
||||
/** 空方法 */
|
||||
export const EmptyFunction = () => {
|
||||
/** 空方法 */
|
||||
};
|
||||
export const EmptyAsyncFunction = () => Promise.resolve();
|
||||
|
||||
/** 公共空间 ID */
|
||||
export const PUBLIC_SPACE_ID = '999999';
|
||||
|
||||
/** BOT_USER_INPUT 变量名 */
|
||||
export const BOT_USER_INPUT = 'BOT_USER_INPUT';
|
||||
|
||||
/** USER_INPUT 参数,新版 BOT_USER_INPUT 参数,作用和 BOT_USER_INPUT 相同,Coze2.0 Chatflow 需求引入 */
|
||||
export const USER_INPUT = 'USER_INPUT';
|
||||
|
||||
/** CONVERSATION_NAME 变量名,start 节点会话名称入参 */
|
||||
export const CONVERSATION_NAME = 'CONVERSATION_NAME';
|
||||
|
||||
/**
|
||||
* workflow 名称最大字符数
|
||||
*/
|
||||
export const WORKFLOW_NAME_MAX_LEN = 30;
|
||||
|
||||
/**
|
||||
* 工作流命名正则
|
||||
*/
|
||||
export const WORKFLOW_NAME_REGEX = /^[a-zA-Z][a-zA-Z0-9_]{0,63}$/;
|
||||
|
||||
/**
|
||||
* 节点测试ID前缀
|
||||
*/
|
||||
export const NODE_TEST_ID_PREFIX = 'playground.node';
|
||||
17
frontend/packages/workflow/base/src/contexts/index.ts
Normal file
17
frontend/packages/workflow/base/src/contexts/index.ts
Normal 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 { WorkflowNodeContext } from './workflow-node-context';
|
||||
@@ -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 { createContext } from 'react';
|
||||
|
||||
import { type WorkflowNode } from '../entities';
|
||||
|
||||
export const WorkflowNodeContext = createContext<WorkflowNode | undefined>(
|
||||
undefined,
|
||||
);
|
||||
17
frontend/packages/workflow/base/src/entities/index.ts
Normal file
17
frontend/packages/workflow/base/src/entities/index.ts
Normal 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 { WorkflowNode } from './workflow-node';
|
||||
149
frontend/packages/workflow/base/src/entities/workflow-node.ts
Normal file
149
frontend/packages/workflow/base/src/entities/workflow-node.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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 {
|
||||
FlowNodeErrorData,
|
||||
FlowNodeFormData,
|
||||
isFormV2,
|
||||
type FormModelV2,
|
||||
type FlowNodeEntity,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { getFormValueByPathEnds } from '../utils';
|
||||
import {
|
||||
type StandardNodeType,
|
||||
type WorkflowNodeRegistry,
|
||||
type InputValueVO,
|
||||
type OutputValueVO,
|
||||
} from '../types';
|
||||
export class WorkflowNode {
|
||||
private node: FlowNodeEntity;
|
||||
|
||||
constructor(node: FlowNodeEntity) {
|
||||
this.node = node;
|
||||
this.setData = this.setData.bind(this);
|
||||
this.setError = this.setError.bind(this);
|
||||
}
|
||||
|
||||
public get registry(): WorkflowNodeRegistry {
|
||||
return this.node.getNodeRegistry() as WorkflowNodeRegistry;
|
||||
}
|
||||
|
||||
public get inputParameters(): InputValueVO[] | undefined {
|
||||
if (this.registry.getNodeInputParameters) {
|
||||
return this.registry.getNodeInputParameters(this.node);
|
||||
}
|
||||
|
||||
if (this.node.getNodeMeta()?.inputParametersPath) {
|
||||
return this.node
|
||||
.getData<FlowNodeFormData>(FlowNodeFormData)
|
||||
.formModel.getFormItemValueByPath(
|
||||
this.node.getNodeMeta()?.inputParametersPath,
|
||||
);
|
||||
}
|
||||
|
||||
return this.getFormValueByPathEnds<InputValueVO[]>('/inputParameters');
|
||||
}
|
||||
|
||||
public get outputs(): OutputValueVO[] | undefined {
|
||||
if (this.registry.getNodeOutputs) {
|
||||
return this.registry.getNodeOutputs(this.node);
|
||||
} else {
|
||||
return this.data?.outputs;
|
||||
}
|
||||
}
|
||||
|
||||
// 这个方法建议暂时先不要使用 原因是该方法没有体现业务逻辑 只是底层方法的简化
|
||||
protected getFormValueByPathEnds<T = unknown>(
|
||||
pathEnds: string,
|
||||
): T | undefined {
|
||||
return getFormValueByPathEnds(this.node, pathEnds);
|
||||
}
|
||||
|
||||
get type() {
|
||||
return this.node.flowNodeType as StandardNodeType;
|
||||
}
|
||||
|
||||
get isError() {
|
||||
return !!this.error;
|
||||
}
|
||||
|
||||
get error() {
|
||||
const errorData = this.node.getData<FlowNodeErrorData>(FlowNodeErrorData);
|
||||
const error = errorData.getError();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
public setError(error: Error) {
|
||||
const errorData = this.node.getData<FlowNodeErrorData>(FlowNodeErrorData);
|
||||
errorData.setError(error);
|
||||
}
|
||||
|
||||
get isInitialized() {
|
||||
return this.form.initialized;
|
||||
}
|
||||
|
||||
get data() {
|
||||
return this.form.getFormItemValueByPath('/');
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public setData(data: any) {
|
||||
if (data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { form } = this;
|
||||
|
||||
if (isFormV2(this.node)) {
|
||||
Object.keys(data).forEach(key => {
|
||||
(form as FormModelV2).setValueIn(key, data[key]);
|
||||
});
|
||||
} else {
|
||||
const formItem = form.getFormItemByPath('/');
|
||||
if (formItem) {
|
||||
formItem.value = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this?.data?.nodeMeta?.icon;
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this?.data?.nodeMeta?.title;
|
||||
}
|
||||
|
||||
get description() {
|
||||
return this?.data?.nodeMeta?.description;
|
||||
}
|
||||
|
||||
private get form() {
|
||||
return this.node.getData(FlowNodeFormData).formModel;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
public getValueByPath<T = any>(pathname: string): T {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const value = (this.form as any).getValueIn(pathname);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
17
frontend/packages/workflow/base/src/global.d.ts
vendored
Normal file
17
frontend/packages/workflow/base/src/global.d.ts
vendored
Normal 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.
|
||||
*/
|
||||
|
||||
/// <reference types='@coze-arch/bot-typings' />
|
||||
18
frontend/packages/workflow/base/src/hooks/index.ts
Normal file
18
frontend/packages/workflow/base/src/hooks/index.ts
Normal 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 { useNodeTestId } from './use-node-test-id';
|
||||
export { useWorkflowNode } from './use-workflow-node';
|
||||
@@ -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 { CustomError } from '@coze-arch/bot-error';
|
||||
import { useCurrentEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { concatTestId } from '../utils';
|
||||
import { NODE_TEST_ID_PREFIX } from '../constants';
|
||||
|
||||
/**
|
||||
* 仅限在 node 内使用
|
||||
*/
|
||||
type UseNodeTestId = () => {
|
||||
/**
|
||||
* 返回当前节点的 test-id,也就是当前节点的 node id
|
||||
* 'playground.11001
|
||||
*/
|
||||
getNodeTestId: () => string;
|
||||
/**
|
||||
* 返回当前 setter 的 test-id,会自动带上节点的 test-id
|
||||
* 'playground.11001.llm'
|
||||
*/
|
||||
getNodeSetterId: (setterName: string) => string;
|
||||
/**
|
||||
* 连接两个 test-id,生成一个新的 test-id
|
||||
* ('a', 'b') => 'a.b'
|
||||
*/
|
||||
concatTestId: typeof concatTestId;
|
||||
};
|
||||
|
||||
export const useNodeTestId: UseNodeTestId = () => {
|
||||
const node = useCurrentEntity();
|
||||
|
||||
if (!node?.id) {
|
||||
throw new CustomError(
|
||||
'useNodeTestId must be called in a workflow node',
|
||||
'',
|
||||
);
|
||||
}
|
||||
|
||||
const getNodeTestId = () => concatTestId(NODE_TEST_ID_PREFIX, node.id);
|
||||
|
||||
return {
|
||||
/**
|
||||
* 返回当前节点的 test-id,也就是当前节点的 node id
|
||||
* 'playground.11001
|
||||
*/
|
||||
getNodeTestId,
|
||||
/**
|
||||
* 返回当前 setter 的 test-id,会自动带上节点的 test-id
|
||||
* 'playground.11001.llm'
|
||||
*/
|
||||
getNodeSetterId: setterName => concatTestId(getNodeTestId(), setterName),
|
||||
/**
|
||||
* 连接两个 test-id,生成一个新的 test-id
|
||||
* ('a', 'b') => 'a.b'
|
||||
*/
|
||||
concatTestId,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 { useContext } from 'react';
|
||||
|
||||
import type { WorkflowNode } from '../entities';
|
||||
import { WorkflowNodeContext } from '../contexts';
|
||||
|
||||
export function useWorkflowNode() {
|
||||
const workflowNode = useContext(WorkflowNodeContext) as WorkflowNode;
|
||||
|
||||
return workflowNode;
|
||||
}
|
||||
28
frontend/packages/workflow/base/src/index.ts
Normal file
28
frontend/packages/workflow/base/src/index.ts
Normal 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @coze-arch/no-batch-import-or-export */
|
||||
export * from './types';
|
||||
|
||||
export * from './utils';
|
||||
|
||||
export * from './api';
|
||||
export * from './store';
|
||||
export * from './constants';
|
||||
|
||||
export * from './hooks';
|
||||
export { WorkflowNode } from './entities';
|
||||
export { WorkflowNodeContext } from './contexts';
|
||||
30
frontend/packages/workflow/base/src/services/index.ts
Normal file
30
frontend/packages/workflow/base/src/services/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 {
|
||||
ValidationService,
|
||||
useValidationService,
|
||||
useValidationServiceStore,
|
||||
type ValidateError,
|
||||
type ValidationState,
|
||||
type ValidateResult,
|
||||
type ValidateErrorMap,
|
||||
type WorkflowValidateError,
|
||||
type WorkflowValidateErrorMap,
|
||||
} from './validation-service';
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 UseBoundStoreWithEqualityFn } from 'zustand/traditional';
|
||||
import { type StoreApi } from 'zustand';
|
||||
import { type FeedbackStatus } from '@flowgram-adapter/free-layout-editor';
|
||||
import { useService } from '@flowgram-adapter/free-layout-editor';
|
||||
import { type WorkflowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
export const ValidationService = Symbol('ValidationService');
|
||||
|
||||
export const useValidationService = () =>
|
||||
useService<ValidationService>(ValidationService);
|
||||
export const useValidationServiceStore = <T>(
|
||||
selector: (s: ValidationState) => T,
|
||||
) => useValidationService().store(selector);
|
||||
|
||||
export interface ValidateError {
|
||||
// 错误描述
|
||||
errorInfo: string;
|
||||
// 错误等级
|
||||
errorLevel: FeedbackStatus;
|
||||
// 错误类型: 节点 / 连线
|
||||
errorType: 'node' | 'line';
|
||||
// 节点id
|
||||
nodeId: string;
|
||||
// 若为连线错误,还需要目标节点来确认这条连线
|
||||
targetNodeId?: string;
|
||||
}
|
||||
export interface WorkflowValidateError {
|
||||
workflowId: string;
|
||||
/** 流程名 */
|
||||
name?: string;
|
||||
errors: ValidateErrorMap;
|
||||
}
|
||||
|
||||
export type ValidateErrorMap = Record<string, ValidateError[]>;
|
||||
export type WorkflowValidateErrorMap = Record<string, WorkflowValidateError>;
|
||||
|
||||
export interface ValidateResult {
|
||||
hasError: boolean;
|
||||
nodeErrorMap: ValidateErrorMap;
|
||||
}
|
||||
export interface ValidateResultV2 {
|
||||
hasError: boolean;
|
||||
errors: WorkflowValidateErrorMap;
|
||||
}
|
||||
|
||||
export interface ValidationState {
|
||||
/**
|
||||
* @deprecated 请使用 errorsV2
|
||||
*/
|
||||
errors: ValidateErrorMap;
|
||||
/** 按照流程归属分类的错误 */
|
||||
errorsV2: WorkflowValidateErrorMap;
|
||||
/** 正在校验中 */
|
||||
validating: boolean;
|
||||
}
|
||||
|
||||
export interface ValidationService {
|
||||
store: UseBoundStoreWithEqualityFn<StoreApi<ValidationState>>;
|
||||
|
||||
/**
|
||||
* 前端流程校验,包括节点、表单、端口等
|
||||
*/
|
||||
validateWorkflow: () => Promise<ValidateResult>;
|
||||
|
||||
/**
|
||||
* 对节点的校验,包括表单、端口等
|
||||
*/
|
||||
validateNode: (node: WorkflowNodeEntity) => Promise<ValidateResult>;
|
||||
|
||||
/**
|
||||
* @deprecated 请使用 validateSchemaV2
|
||||
* 流程定义合法性校验,通常为后端校验
|
||||
*/
|
||||
validateSchema: () => Promise<ValidateResult>;
|
||||
/**
|
||||
* 流程定义合法性校验,通常为后端校验
|
||||
*/
|
||||
validateSchemaV2: () => Promise<ValidateResultV2>;
|
||||
|
||||
/**
|
||||
* 获取指定 id 的错误列表
|
||||
*/
|
||||
getErrors: (id: string) => ValidateError[];
|
||||
/**
|
||||
* @deprecated 请使用 setErrorsV2
|
||||
* 设置错误
|
||||
* @param errors
|
||||
* @param force 强制覆盖所有错误
|
||||
*/
|
||||
setErrors: (errors: ValidationState['errors'], force?: boolean) => void;
|
||||
|
||||
/**
|
||||
* 设置错误
|
||||
* @param errors
|
||||
* @returns
|
||||
*/
|
||||
setErrorsV2: (errors: ValidationState['errorsV2']) => void;
|
||||
/**
|
||||
* 清空所有错误
|
||||
*/
|
||||
clearErrors: () => void;
|
||||
/** 线条是否存在错误 */
|
||||
isLineError: (fromId: string, toId?: string) => boolean;
|
||||
|
||||
get validating(): boolean;
|
||||
set validating(value: boolean);
|
||||
}
|
||||
17
frontend/packages/workflow/base/src/store/index.ts
Normal file
17
frontend/packages/workflow/base/src/store/index.ts
Normal 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 { useWorkflowStore } from './workflow';
|
||||
58
frontend/packages/workflow/base/src/store/workflow/index.ts
Normal file
58
frontend/packages/workflow/base/src/store/workflow/index.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// workflow store,目前保存 flow 的 nodes 和 edges 数据
|
||||
|
||||
import { devtools } from 'zustand/middleware';
|
||||
import { create } from 'zustand';
|
||||
import {
|
||||
type WorkflowEdgeJSON,
|
||||
type WorkflowNodeJSON,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
interface WorkflowStoreState {
|
||||
/** 节点数据 */
|
||||
nodes: WorkflowNodeJSON[];
|
||||
|
||||
/** 边数据 */
|
||||
edges: WorkflowEdgeJSON[];
|
||||
|
||||
/** 是否在创建 workflow */
|
||||
isCreatingWorkflow: boolean;
|
||||
}
|
||||
|
||||
interface WorkflowStoreAction {
|
||||
setNodes: (value: WorkflowNodeJSON[]) => void;
|
||||
setEdges: (value: WorkflowEdgeJSON[]) => void;
|
||||
setIsCreatingWorkflow: (value: boolean) => void;
|
||||
}
|
||||
|
||||
const initialStore: WorkflowStoreState = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
isCreatingWorkflow: false,
|
||||
};
|
||||
|
||||
export const useWorkflowStore = create<
|
||||
WorkflowStoreState & WorkflowStoreAction
|
||||
>()(
|
||||
devtools(set => ({
|
||||
...initialStore,
|
||||
setNodes: nodes => set({ nodes: nodes ?? [] }),
|
||||
setEdges: edges => set({ edges: edges ?? [] }),
|
||||
setIsCreatingWorkflow: value => set({ isCreatingWorkflow: value }),
|
||||
})),
|
||||
);
|
||||
125
frontend/packages/workflow/base/src/types/block-input-dto.ts
Normal file
125
frontend/packages/workflow/base/src/types/block-input-dto.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 './view-variable-type';
|
||||
import { type DTODefine, type InputValueDTO } from './dto';
|
||||
|
||||
/**
|
||||
* BlockInput是后端定义的类型,对应的就是 InputValueDTO
|
||||
*/
|
||||
export type BlockInput = InputValueDTO;
|
||||
|
||||
/**
|
||||
* BlockInput 转换方法
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace BlockInput {
|
||||
/**
|
||||
* @param name
|
||||
* @param value
|
||||
* @param type
|
||||
* @example
|
||||
* {
|
||||
* name: 'apiName',
|
||||
* input: {
|
||||
* type: 'string',
|
||||
* value: {
|
||||
* type: 'literal',
|
||||
* content: 'xxxxxxxxx'
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export function create(
|
||||
name: string,
|
||||
value = '',
|
||||
type: DTODefine.BasicVariableType = 'string',
|
||||
): BlockInput {
|
||||
const blockInput: BlockInput = {
|
||||
name,
|
||||
input: {
|
||||
type,
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: String(value),
|
||||
},
|
||||
},
|
||||
};
|
||||
let rawMetaType: ViewVariableType = ViewVariableType.String;
|
||||
switch (type) {
|
||||
case 'string':
|
||||
rawMetaType = ViewVariableType.String;
|
||||
break;
|
||||
case 'integer':
|
||||
rawMetaType = ViewVariableType.Integer;
|
||||
break;
|
||||
case 'float':
|
||||
rawMetaType = ViewVariableType.Number;
|
||||
break;
|
||||
case 'boolean':
|
||||
rawMetaType = ViewVariableType.Boolean;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
blockInput.input.value.rawMeta = { type: rawMetaType };
|
||||
return blockInput;
|
||||
}
|
||||
export function createString(name: string, value: string): BlockInput {
|
||||
return create(name, value, 'string');
|
||||
}
|
||||
export function createInteger(name: string, value: string): BlockInput {
|
||||
return create(name, value, 'integer');
|
||||
}
|
||||
export function createFloat(name: string, value: string): BlockInput {
|
||||
return create(name, value, 'float');
|
||||
}
|
||||
|
||||
export function createArray<T>(
|
||||
name: string,
|
||||
value: Array<T>,
|
||||
schema: unknown,
|
||||
): BlockInput {
|
||||
return {
|
||||
name,
|
||||
input: {
|
||||
type: 'list',
|
||||
schema,
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: value,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createBoolean(name: string, value: boolean): BlockInput {
|
||||
const booleanInput = create(name, value as never, 'boolean');
|
||||
booleanInput.input.value.content = value;
|
||||
return booleanInput;
|
||||
}
|
||||
|
||||
export function toLiteral<T>(blockInput: BlockInput): T {
|
||||
return blockInput.input.value.content as unknown as T;
|
||||
}
|
||||
|
||||
export function isBlockInput(d: unknown): d is BlockInput {
|
||||
return (
|
||||
Boolean((d as BlockInput)?.name) &&
|
||||
typeof (d as BlockInput)?.input?.value?.content !== undefined
|
||||
);
|
||||
}
|
||||
}
|
||||
41
frontend/packages/workflow/base/src/types/condition.ts
Normal file
41
frontend/packages/workflow/base/src/types/condition.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 enum ConditionLogic {
|
||||
OR = 1,
|
||||
AND = 2,
|
||||
}
|
||||
|
||||
export enum ConditionLogicDTO {
|
||||
OR = 'OR',
|
||||
AND = 'AND',
|
||||
}
|
||||
|
||||
export type ConditionOperator =
|
||||
| 'EQUAL' // "="
|
||||
| 'NOT_EQUAL' // "<>" 或 "!="
|
||||
| 'GREATER_THAN' // ">"
|
||||
| 'LESS_THAN' // "<"
|
||||
| 'GREATER_EQUAL' // ">="
|
||||
| 'LESS_EQUAL' // "<="
|
||||
| 'IN' // "IN"
|
||||
| 'NOT_IN' // "NOT IN"
|
||||
| 'IS_NULL' // "IS NULL"
|
||||
| 'IS_NOT_NULL' // "IS NOT NULL"
|
||||
| 'LIKE' // "LIKE" 模糊匹配字符串
|
||||
| 'NOT_LIKE' // "NOT LIKE" 反向模糊匹配
|
||||
| 'BE_TRUE' // "BE TRUE" 布尔值为 true
|
||||
| 'BE_FALSE'; // "BE FALSE" 布尔值为 false
|
||||
68
frontend/packages/workflow/base/src/types/data-set.ts
Normal file
68
frontend/packages/workflow/base/src/types/data-set.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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-next-line @coze-arch/no-batch-import-or-export
|
||||
import * as t from 'io-ts';
|
||||
|
||||
export const datasetParams = t.array(
|
||||
t.union([
|
||||
t.type({
|
||||
name: t.literal('datasetList'),
|
||||
input: t.type({
|
||||
type: t.literal('list'),
|
||||
schema: t.type({
|
||||
type: t.literal('string'),
|
||||
}),
|
||||
value: t.type({
|
||||
type: t.literal('literal'),
|
||||
content: t.array(t.string),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
t.type({
|
||||
name: t.literal('topK'),
|
||||
input: t.type({
|
||||
type: t.literal('integer'),
|
||||
value: t.type({
|
||||
type: t.literal('literal'),
|
||||
content: t.number,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
t.type({
|
||||
name: t.literal('minScore'),
|
||||
input: t.type({
|
||||
type: t.literal('number'),
|
||||
value: t.type({
|
||||
type: t.literal('literal'),
|
||||
content: t.number,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
t.type({
|
||||
name: t.literal('strategy'),
|
||||
input: t.type({
|
||||
type: t.literal('number'),
|
||||
value: t.type({
|
||||
type: t.literal('literal'),
|
||||
content: t.number,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
export type DatasetParams = t.TypeOf<typeof datasetParams>;
|
||||
112
frontend/packages/workflow/base/src/types/database.ts
Normal file
112
frontend/packages/workflow/base/src/types/database.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 ValueExpression, type ValueExpressionType } from './vo';
|
||||
import { type ViewVariableType } from './view-variable-type';
|
||||
import { type ValueExpressionDTO } from './dto';
|
||||
import { type ConditionOperator } from './condition';
|
||||
|
||||
export interface DatabaseField {
|
||||
id: number;
|
||||
name?: string;
|
||||
type?: ViewVariableType;
|
||||
required?: boolean;
|
||||
description?: string;
|
||||
isSystemField?: boolean;
|
||||
}
|
||||
|
||||
export interface WorkflowDatabase {
|
||||
id: string;
|
||||
fields?: DatabaseField[];
|
||||
iconUrl?: string;
|
||||
tableName?: string;
|
||||
tableDesc?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库配置字段
|
||||
*/
|
||||
export interface DatabaseSettingField {
|
||||
fieldID: number;
|
||||
fieldValue?: ValueExpression;
|
||||
}
|
||||
|
||||
export interface DatabaseSettingFieldIDDTO {
|
||||
name: 'fieldID';
|
||||
input: {
|
||||
type: 'string';
|
||||
value: {
|
||||
type: 'literal';
|
||||
content: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface DatabaseSettingFieldValueDTO {
|
||||
name: 'fieldValue';
|
||||
input?: ValueExpressionDTO;
|
||||
}
|
||||
|
||||
export type DatabaseSettingFieldDTO = [
|
||||
DatabaseSettingFieldIDDTO,
|
||||
DatabaseSettingFieldValueDTO | undefined,
|
||||
];
|
||||
|
||||
/**
|
||||
* 数据库条件
|
||||
*/
|
||||
export type DatabaseConditionOperator = ConditionOperator;
|
||||
export type DatabaseConditionLeft = string;
|
||||
export type DatabaseConditionRight = ValueExpression;
|
||||
export interface DatabaseCondition {
|
||||
left?: DatabaseConditionLeft;
|
||||
operator?: DatabaseConditionOperator;
|
||||
right?: DatabaseConditionRight;
|
||||
}
|
||||
|
||||
export interface DatabaseConditionLeftDTO {
|
||||
name: 'left';
|
||||
input: {
|
||||
type: 'string';
|
||||
value: {
|
||||
type: ValueExpressionType.LITERAL;
|
||||
content: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface DatabaseConditionOperatorDTO {
|
||||
// 对操作符的翻译前后端没有统一
|
||||
name: 'operation';
|
||||
input: {
|
||||
type: 'string';
|
||||
value: {
|
||||
type: ValueExpressionType.LITERAL;
|
||||
content: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface DatabaseConditionRightDTO {
|
||||
name: 'right';
|
||||
input?: ValueExpressionDTO;
|
||||
}
|
||||
|
||||
export type DatabaseConditionDTO = [
|
||||
DatabaseConditionLeftDTO | undefined,
|
||||
DatabaseConditionOperatorDTO | undefined,
|
||||
DatabaseConditionRightDTO | undefined,
|
||||
];
|
||||
332
frontend/packages/workflow/base/src/types/dto.ts
Normal file
332
frontend/packages/workflow/base/src/types/dto.ts
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* 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 ValueExpressionRawMeta } from './vo';
|
||||
import { type StandardNodeType } from './node-type';
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace DTODefine {
|
||||
export type LiteralExpressionContent =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| Array<unknown>;
|
||||
|
||||
export interface LiteralExpression {
|
||||
type: 'literal';
|
||||
content?: LiteralExpressionContent;
|
||||
rawMeta?: ValueExpressionRawMeta;
|
||||
}
|
||||
|
||||
export type RefExpressionContent =
|
||||
| {
|
||||
source: 'variable';
|
||||
blockID: undefined;
|
||||
name: string;
|
||||
}
|
||||
| {
|
||||
source: 'block-output';
|
||||
blockID: string;
|
||||
name: string;
|
||||
}
|
||||
| {
|
||||
source: `global_variable_${string}`;
|
||||
path: string[];
|
||||
blockID: string;
|
||||
name: string;
|
||||
};
|
||||
export interface RefExpression {
|
||||
type: 'ref';
|
||||
content?: RefExpressionContent;
|
||||
rawMeta?: ValueExpressionRawMeta;
|
||||
}
|
||||
|
||||
export interface ObjectRefExpression {
|
||||
type: 'object_ref';
|
||||
content?: unknown;
|
||||
rawMeta?: ValueExpressionRawMeta;
|
||||
}
|
||||
|
||||
// 当 schema 为 string 时表示原始的 FDL 类型export type ListVariableSchema = VariableTypeDef & Omit<VariableOption, 'name'>
|
||||
export type ObjectVariableSchema = InputVariableDTO[];
|
||||
export type ListVariableSchema = VariableTypeDef &
|
||||
Omit<VariableOption, 'name'>;
|
||||
export type VariableSchema = ListVariableSchema | ObjectVariableSchema;
|
||||
export type BasicVariableType =
|
||||
| 'string'
|
||||
| 'integer'
|
||||
| 'float'
|
||||
| 'boolean'
|
||||
| 'image'
|
||||
| 'unknown';
|
||||
export type ComplexVariableType = 'object' | 'list';
|
||||
export type VariableType = BasicVariableType | ComplexVariableType;
|
||||
|
||||
export interface BasicVarTypeDef {
|
||||
type: BasicVariableType;
|
||||
assistType?: AssistTypeDTO;
|
||||
}
|
||||
export interface ObjectVarTypeDef {
|
||||
type: VariableTypeDTO.object;
|
||||
schema?: ObjectVariableSchema;
|
||||
assistType?: AssistTypeDTO;
|
||||
}
|
||||
export interface ListVarTypeDef {
|
||||
type: VariableTypeDTO.list;
|
||||
schema: ListVariableSchema;
|
||||
assistType?: AssistTypeDTO;
|
||||
}
|
||||
|
||||
export type VariableTypeDef =
|
||||
| BasicVarTypeDef
|
||||
| ObjectVarTypeDef
|
||||
| ListVarTypeDef;
|
||||
interface VariableOption {
|
||||
name: string;
|
||||
label?: string;
|
||||
defaultValue?: LiteralExpressionContent;
|
||||
description?: string;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
export type InputVariableDTO = VariableOption & VariableTypeDef;
|
||||
}
|
||||
|
||||
/**
|
||||
* 后端定义的表达式格式
|
||||
* @example
|
||||
* - literal
|
||||
* {
|
||||
* type: 'string',
|
||||
* value: {
|
||||
* type: 'liteal',
|
||||
* content: '浙江'
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* - ref
|
||||
* // 普通引用类型
|
||||
* {
|
||||
* type: 'string', // 由引用的变量类型判断
|
||||
* value: {
|
||||
* type: 'ref',
|
||||
* content: {
|
||||
* source: 'block-output',
|
||||
* blockID: '1002',
|
||||
* name: 'result'
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* // list or object 引用类型
|
||||
* {
|
||||
* type: 'list', // 由引用的变量路径的最后一个值类型判断, 如果list.a.c, 则为c的格式
|
||||
* schema: { // 只有list和object有schema
|
||||
* type: 'object',
|
||||
* schema: [
|
||||
* { name: 'role', type: 'string' },
|
||||
* { name: 'content', type: 'string' }
|
||||
* ]
|
||||
* }
|
||||
* value: {
|
||||
* type: 'ref',
|
||||
* content: {
|
||||
* source: 'block-output',
|
||||
* blockID: '1002',
|
||||
* name: 'list.a.c' // 这里存的是引用的路径
|
||||
* },
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export interface ValueExpressionDTO {
|
||||
type?: string;
|
||||
assistType?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
schema?: any;
|
||||
value:
|
||||
| DTODefine.LiteralExpression
|
||||
| DTODefine.RefExpression
|
||||
| DTODefine.ObjectRefExpression;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ValueExpressionDTO {
|
||||
/**
|
||||
* 空引用
|
||||
*/
|
||||
export function createEmpty(): ValueExpressionDTO {
|
||||
return {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '',
|
||||
name: '',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
export interface InputValueDTO {
|
||||
name?: string;
|
||||
input: ValueExpressionDTO;
|
||||
schema?: InputValueDTO[];
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export enum VariableTypeDTO {
|
||||
object = 'object',
|
||||
list = 'list',
|
||||
string = 'string',
|
||||
integer = 'integer',
|
||||
float = 'float',
|
||||
boolean = 'boolean',
|
||||
image = 'image',
|
||||
time = 'time',
|
||||
}
|
||||
|
||||
export enum AssistTypeDTO {
|
||||
file = 1,
|
||||
image = 2,
|
||||
doc = 3,
|
||||
code = 4,
|
||||
ppt = 5,
|
||||
txt = 6,
|
||||
excel = 7,
|
||||
audio = 8,
|
||||
zip = 9,
|
||||
video = 10,
|
||||
svg = 11,
|
||||
voice = 12,
|
||||
time = 10000,
|
||||
}
|
||||
|
||||
/**
|
||||
* 后端的变量格式
|
||||
* @example
|
||||
* 1. simple
|
||||
* {
|
||||
* name: 'message',
|
||||
* type: 'string'
|
||||
* }
|
||||
* 2. object
|
||||
* {
|
||||
* name: 'someObj'
|
||||
* type: 'object',
|
||||
* schema: [
|
||||
* {
|
||||
* name: 'role',
|
||||
* type: 'string'
|
||||
* },
|
||||
* {
|
||||
* name: 'content',
|
||||
* type: 'string'
|
||||
* }
|
||||
* ]
|
||||
* }
|
||||
* 3. list<object>
|
||||
* {
|
||||
* name: 'history'
|
||||
* type: 'list',
|
||||
* schema: {
|
||||
* type: 'object',
|
||||
* schema: [
|
||||
* { name: 'role', type: 'string' },
|
||||
* { name: 'content', type: 'string' }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export interface VariableMetaDTO {
|
||||
/**
|
||||
* 变量类型
|
||||
*/
|
||||
type: VariableTypeDTO;
|
||||
/**
|
||||
* 辅助类型,如:string 类型的变量可以是 file 或者 image
|
||||
*/
|
||||
assistType?: AssistTypeDTO;
|
||||
/**
|
||||
* 变量名,在节点内不可重复
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* 变量数据结构,仅object 和 list 类型变量有
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
schema?: any; // BlockVariableDefine.ObjectVariableSchema | BlockVariableDefine.ListVariableSchema
|
||||
required?: boolean;
|
||||
description?: string;
|
||||
readonly?: boolean;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
defaultValue?: any;
|
||||
}
|
||||
|
||||
export interface BatchDTOInputList {
|
||||
name?: string;
|
||||
input: ValueExpressionDTO;
|
||||
// 初始化后存在,服务端数据不存在
|
||||
id?: string;
|
||||
}
|
||||
|
||||
export interface BatchDTO {
|
||||
batchSize: number;
|
||||
concurrentSize: number;
|
||||
inputLists: BatchDTOInputList[];
|
||||
}
|
||||
|
||||
export interface NodeDataDTO {
|
||||
inputs: {
|
||||
inputParameters?: InputValueDTO[];
|
||||
settingOnError?: { switch: boolean; dataOnErr: string };
|
||||
[key: string]: unknown;
|
||||
};
|
||||
nodeMeta: {
|
||||
description: string;
|
||||
icon: string;
|
||||
subTitle: string;
|
||||
title: string;
|
||||
mainColor: string;
|
||||
};
|
||||
outputs: VariableMetaDTO[];
|
||||
version?: string;
|
||||
}
|
||||
|
||||
export interface NodeDTO {
|
||||
id: string;
|
||||
type: StandardNodeType;
|
||||
meta?: {
|
||||
position: { x: number; y: number };
|
||||
[key: string]: unknown;
|
||||
};
|
||||
data: NodeDataDTO;
|
||||
edges?: {
|
||||
sourceNodeId: string;
|
||||
targetNodeId: string;
|
||||
sourcePortId: string;
|
||||
}[];
|
||||
blocks?: NodeDataDTO[];
|
||||
}
|
||||
|
||||
export interface InputTypeValueDTO {
|
||||
name: string;
|
||||
type: VariableTypeDTO;
|
||||
input: ValueExpressionDTO;
|
||||
}
|
||||
143
frontend/packages/workflow/base/src/types/index.ts
Normal file
143
frontend/packages/workflow/base/src/types/index.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 @coze-arch/no-batch-import-or-export */
|
||||
export {
|
||||
ViewVariableType,
|
||||
VARIABLE_TYPE_ALIAS_MAP,
|
||||
type InputVariable,
|
||||
FILE_TYPES,
|
||||
} from './view-variable-type';
|
||||
export {
|
||||
type RecursedParamDefinition,
|
||||
ParamValueType,
|
||||
} from './param-definition';
|
||||
export {
|
||||
ViewVariableTreeNode,
|
||||
type ViewVariableMeta,
|
||||
} from './view-variable-tree';
|
||||
export {
|
||||
type DTODefine,
|
||||
ValueExpressionDTO,
|
||||
type InputValueDTO,
|
||||
VariableTypeDTO,
|
||||
AssistTypeDTO,
|
||||
type VariableMetaDTO,
|
||||
type BatchDTOInputList,
|
||||
type BatchDTO,
|
||||
type NodeDTO,
|
||||
type NodeDataDTO,
|
||||
type InputTypeValueDTO,
|
||||
} from './dto';
|
||||
export { BlockInput } from './block-input-dto';
|
||||
export {
|
||||
type RefExpressionContent,
|
||||
ValueExpressionType,
|
||||
type LiteralExpression,
|
||||
type RefExpression,
|
||||
type ObjectRefExpression,
|
||||
ValueExpression,
|
||||
type InputValueVO,
|
||||
type OutputValueVO,
|
||||
BatchMode,
|
||||
type BatchVOInputList,
|
||||
type BatchVO,
|
||||
type InputTypeValueVO,
|
||||
} from './vo';
|
||||
|
||||
export {
|
||||
StandardNodeType,
|
||||
NODE_ORDER,
|
||||
CONVERSATION_NODES,
|
||||
MESSAGE_NODES,
|
||||
CONVERSATION_HISTORY_NODES,
|
||||
type BasicStandardNodeTypes,
|
||||
} from './node-type';
|
||||
export { type WorkflowJSON, type WorkflowNodeJSON } from './node';
|
||||
// !Notice data-set.ts 在用 io-ts 做运行时类型检查,禁止直接导出,避免 io-ts 被打到其它页面中
|
||||
// export { datasetParams, type DatasetParams } from './data-set';
|
||||
import { InputType } from '@coze-arch/bot-api/developer_api';
|
||||
|
||||
import {
|
||||
VARIABLE_TYPE_ALIAS_MAP,
|
||||
ViewVariableType,
|
||||
} from './view-variable-type';
|
||||
import { AssistTypeDTO } from './dto';
|
||||
|
||||
export { type ValueOf, type WithCustomStyle } from './utils';
|
||||
// 和后端定义撞了,注释
|
||||
export { type WorkflowInfo as FrontWorkflowInfo } from './workflow';
|
||||
|
||||
export {
|
||||
type WorkflowNodeRegistry,
|
||||
WorkflowNodeVariablesMeta,
|
||||
type NodeMeta,
|
||||
} from './registry';
|
||||
|
||||
/**
|
||||
* 参数类型展示文案
|
||||
* @tips workflow 编辑页请使用 {PARAM_TYPE_ALIAS_MAP}
|
||||
*/
|
||||
export const PARAM_TYPE_LABEL_MAP: Record<InputType, string> = {
|
||||
[InputType.String]: 'String',
|
||||
[InputType.Integer]: 'Integer',
|
||||
[InputType.Boolean]: 'Boolean',
|
||||
[InputType.Double]: 'Double',
|
||||
[InputType.List]: 'List',
|
||||
[InputType.Object]: 'Object',
|
||||
};
|
||||
|
||||
export const STRING_ASSIST_TYPE_LABEL_MAP = {
|
||||
[AssistTypeDTO.file]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.File],
|
||||
[AssistTypeDTO.image]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Image],
|
||||
[AssistTypeDTO.doc]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Doc],
|
||||
[AssistTypeDTO.code]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Code],
|
||||
[AssistTypeDTO.ppt]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Ppt],
|
||||
[AssistTypeDTO.txt]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Txt],
|
||||
[AssistTypeDTO.excel]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Excel],
|
||||
[AssistTypeDTO.audio]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Audio],
|
||||
[AssistTypeDTO.zip]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Zip],
|
||||
[AssistTypeDTO.video]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Video],
|
||||
[AssistTypeDTO.svg]: VARIABLE_TYPE_ALIAS_MAP[ViewVariableType.Svg],
|
||||
};
|
||||
|
||||
export enum WorkflowExecStatus {
|
||||
DEFAULT = 'default',
|
||||
/** 执行中 */
|
||||
EXECUTING = 'executing',
|
||||
/** 执行结束(此时依然有执行结束 banner,且工作流为 disable 状态) */
|
||||
DONE = 'done',
|
||||
}
|
||||
|
||||
export * from './llm';
|
||||
|
||||
export {
|
||||
type WorkflowDatabase,
|
||||
type DatabaseField,
|
||||
type DatabaseSettingField,
|
||||
type DatabaseSettingFieldDTO,
|
||||
type DatabaseCondition,
|
||||
type DatabaseConditionOperator,
|
||||
type DatabaseConditionLeft,
|
||||
type DatabaseConditionRight,
|
||||
type DatabaseConditionDTO,
|
||||
} from './database';
|
||||
|
||||
export {
|
||||
ConditionLogic,
|
||||
type ConditionOperator,
|
||||
ConditionLogicDTO,
|
||||
} from './condition';
|
||||
25
frontend/packages/workflow/base/src/types/llm.ts
Normal file
25
frontend/packages/workflow/base/src/types/llm.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// 跟后端约定好的协议,workflow 后端不感知。对应 api/bot/get_type_list 接口中 response.data.modal_list[*].model_params[*].default 的 key
|
||||
export enum GenerationDiversity {
|
||||
Customize = 'default_val',
|
||||
Creative = 'creative',
|
||||
Balance = 'balance',
|
||||
Precise = 'precise',
|
||||
}
|
||||
|
||||
export const RESPONSE_FORMAT_NAME = 'response_format';
|
||||
171
frontend/packages/workflow/base/src/types/node-type.ts
Normal file
171
frontend/packages/workflow/base/src/types/node-type.ts
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* 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 enum StandardNodeType {
|
||||
Start = '1',
|
||||
End = '2',
|
||||
LLM = '3',
|
||||
Api = '4',
|
||||
Code = '5',
|
||||
Dataset = '6',
|
||||
If = '8',
|
||||
SubWorkflow = '9',
|
||||
Variable = '11',
|
||||
Database = '12',
|
||||
Output = '13',
|
||||
Imageflow = '14',
|
||||
Text = '15',
|
||||
ImageGenerate = '16',
|
||||
ImageReference = '17',
|
||||
Question = '18',
|
||||
Break = '19',
|
||||
SetVariable = '20',
|
||||
Loop = '21',
|
||||
Intent = '22',
|
||||
// 对于新节点,可以使用文本的形式来编写 StandardNodeType,不再依赖 NodeTemplateType
|
||||
ImageCanvas = '23',
|
||||
SceneChat = '25',
|
||||
SceneVariable = '24',
|
||||
|
||||
/** 长期记忆,long term memory */
|
||||
LTM = '26',
|
||||
/** 数据库写入节点 */
|
||||
DatasetWrite = '27',
|
||||
Batch = '28',
|
||||
|
||||
Continue = '29',
|
||||
// 输入节点
|
||||
Input = '30',
|
||||
|
||||
Comment = '31',
|
||||
// 变量聚合
|
||||
VariableMerge = '32',
|
||||
// 查询消息列表
|
||||
QueryMessageList = '37',
|
||||
// 清空上下文节点
|
||||
ClearContext = '38',
|
||||
// 创建会话节点
|
||||
CreateConversation = '39',
|
||||
|
||||
// 触发器 CURD 4个节点
|
||||
// TriggerCreate = '33',
|
||||
// TriggerUpdate = '34',
|
||||
TriggerUpsert = '34',
|
||||
TriggerDelete = '35',
|
||||
TriggerRead = '36',
|
||||
|
||||
VariableAssign = '40',
|
||||
|
||||
// http 节点
|
||||
Http = '45',
|
||||
// 数据库 crud 节点
|
||||
|
||||
DatabaseUpdate = '42',
|
||||
DatabaseQuery = '43',
|
||||
DatabaseDelete = '44',
|
||||
DatabaseCreate = '46',
|
||||
|
||||
// 更新会话
|
||||
UpdateConversation = '51',
|
||||
|
||||
// 删除会话
|
||||
DeleteConversation = '52',
|
||||
|
||||
// 查询会话列表
|
||||
QueryConversationList = '53',
|
||||
|
||||
// 查询会话历史
|
||||
QueryConversationHistory = '54',
|
||||
|
||||
// 创建消息(某个会话)
|
||||
CreateMessage = '55',
|
||||
|
||||
// 更新消息(某个会话的某个消息)
|
||||
UpdateMessage = '56',
|
||||
|
||||
// 删除消息(某个会话的某个消息)
|
||||
DeleteMessage = '57',
|
||||
|
||||
JsonStringify = '58',
|
||||
JsonParser = '59',
|
||||
}
|
||||
|
||||
/**
|
||||
* 除了 Api、SubWorkflow、Imageflow 之外的基础节点类型
|
||||
*/
|
||||
export type BasicStandardNodeTypes = Exclude<
|
||||
StandardNodeType,
|
||||
| StandardNodeType.Api
|
||||
| StandardNodeType.Imageflow
|
||||
| StandardNodeType.SubWorkflow
|
||||
>;
|
||||
|
||||
/** 节点展示排序 */
|
||||
export const NODE_ORDER = {
|
||||
[StandardNodeType.Start]: 1,
|
||||
[StandardNodeType.End]: 2,
|
||||
[StandardNodeType.Api]: 3,
|
||||
[StandardNodeType.LLM]: 4,
|
||||
[StandardNodeType.Code]: 5,
|
||||
[StandardNodeType.Dataset]: 6,
|
||||
[StandardNodeType.SubWorkflow]: 7,
|
||||
[StandardNodeType.Imageflow]: 8,
|
||||
[StandardNodeType.If]: 9,
|
||||
[StandardNodeType.Loop]: 10,
|
||||
[StandardNodeType.Intent]: 11,
|
||||
[StandardNodeType.Text]: 12,
|
||||
[StandardNodeType.Output]: 13,
|
||||
[StandardNodeType.Question]: 14,
|
||||
[StandardNodeType.Variable]: 15,
|
||||
[StandardNodeType.Database]: 16,
|
||||
[StandardNodeType.LTM]: 17,
|
||||
[StandardNodeType.Batch]: 18,
|
||||
[StandardNodeType.Input]: 19,
|
||||
[StandardNodeType.SetVariable]: 20,
|
||||
[StandardNodeType.Break]: 21,
|
||||
[StandardNodeType.Continue]: 22,
|
||||
[StandardNodeType.SceneChat]: 23,
|
||||
[StandardNodeType.SceneVariable]: 24,
|
||||
// [StandardNodeType.TriggerCreate]: 25,
|
||||
[StandardNodeType.TriggerUpsert]: 26,
|
||||
[StandardNodeType.TriggerRead]: 27,
|
||||
[StandardNodeType.TriggerDelete]: 28,
|
||||
};
|
||||
|
||||
/** 会话类节点 */
|
||||
export const CONVERSATION_NODES = [
|
||||
StandardNodeType.CreateConversation,
|
||||
StandardNodeType.UpdateConversation,
|
||||
StandardNodeType.DeleteConversation,
|
||||
StandardNodeType.QueryConversationList,
|
||||
];
|
||||
|
||||
/** 消息类节点 */
|
||||
export const MESSAGE_NODES = [
|
||||
StandardNodeType.CreateMessage,
|
||||
StandardNodeType.UpdateMessage,
|
||||
StandardNodeType.DeleteMessage,
|
||||
StandardNodeType.QueryMessageList,
|
||||
];
|
||||
|
||||
/** 会话历史类节点 */
|
||||
export const CONVERSATION_HISTORY_NODES = [
|
||||
StandardNodeType.QueryConversationHistory,
|
||||
StandardNodeType.ClearContext,
|
||||
];
|
||||
41
frontend/packages/workflow/base/src/types/node.ts
Normal file
41
frontend/packages/workflow/base/src/types/node.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 { FlowNodeBaseType } from '@flowgram-adapter/free-layout-editor';
|
||||
import type {
|
||||
WorkflowEdgeJSON,
|
||||
WorkflowNodeMeta,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import type { StandardNodeType } from './node-type';
|
||||
|
||||
export interface WorkflowNodeJSON<T = Record<string, unknown>> {
|
||||
id: string;
|
||||
type: StandardNodeType | FlowNodeBaseType | string;
|
||||
meta?: WorkflowNodeMeta;
|
||||
data: T;
|
||||
version?: string;
|
||||
blocks?: WorkflowNodeJSON[];
|
||||
edges?: WorkflowEdgeJSON[];
|
||||
}
|
||||
|
||||
export interface WorkflowJSON {
|
||||
nodes: WorkflowNodeJSON[];
|
||||
edges: WorkflowEdgeJSON[];
|
||||
}
|
||||
|
||||
// 节点模版类型
|
||||
export { NodeTemplateType } from '@coze-arch/bot-api/workflow_api';
|
||||
@@ -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 ViewVariableType } from './view-variable-type';
|
||||
|
||||
/**
|
||||
* 参数定义
|
||||
*
|
||||
* 递归定义,包含了复杂类型的层级结构
|
||||
*/
|
||||
export interface RecursedParamDefinition {
|
||||
name?: string;
|
||||
/** Tree 组件要求每一个节点都有 key,而 key 不适合用名称(前后缀)等任何方式赋值,最终确定由接口转换层一次性提供随机 key */
|
||||
fieldRandomKey?: string;
|
||||
desc?: string;
|
||||
required?: boolean;
|
||||
type: ViewVariableType;
|
||||
children?: RecursedParamDefinition[];
|
||||
// region 参数值定义
|
||||
// 输入参数的值可以来自上游变量引用,也可以是用户输入的定值(复杂类型则只允许引用)
|
||||
// 如果是定值,传 fixedValue
|
||||
// 如果是引用,传 quotedValue
|
||||
isQuote?: ParamValueType;
|
||||
/** 参数定值 */
|
||||
fixedValue?: string;
|
||||
/** 参数引用 */
|
||||
quotedValue?: [nodeId: string, ...path: string[]]; // string[]
|
||||
// endregion
|
||||
}
|
||||
|
||||
export enum ParamValueType {
|
||||
QUOTE = 'quote',
|
||||
FIXED = 'fixed',
|
||||
}
|
||||
184
frontend/packages/workflow/base/src/types/registry.ts
Normal file
184
frontend/packages/workflow/base/src/types/registry.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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-namespace */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
import {
|
||||
type WorkflowNodeEntity,
|
||||
type WorkflowSubCanvas,
|
||||
type WorkflowNodeRegistry as WorkflowOriginNodeRegistry,
|
||||
type WorkflowNodeJSON,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import {
|
||||
type OutputValueVO,
|
||||
type InputValueVO,
|
||||
type ValueExpression,
|
||||
} from './vo';
|
||||
import { type VariableTagProps } from './view-variable-type';
|
||||
import { type StandardNodeType } from './node-type';
|
||||
|
||||
export interface WorkflowNodeVariablesMeta {
|
||||
/**
|
||||
* 输出变量路径, 默认 ['outputs']
|
||||
*/
|
||||
outputsPathList?: string[];
|
||||
/**
|
||||
* 输入变量路径,默认 ['inputs.inputParameters']
|
||||
*/
|
||||
inputsPathList?: string[];
|
||||
batchInputListPath?: string;
|
||||
}
|
||||
|
||||
export namespace WorkflowNodeVariablesMeta {
|
||||
export const DEFAULT: WorkflowNodeVariablesMeta = {
|
||||
outputsPathList: ['outputs'],
|
||||
inputsPathList: ['inputs.inputParameters'],
|
||||
batchInputListPath: 'inputs.batch.inputLists',
|
||||
};
|
||||
}
|
||||
|
||||
export interface NodeMeta<NodeTest = any> {
|
||||
isStart?: boolean;
|
||||
isNodeEnd?: boolean;
|
||||
deleteDisable?: boolean;
|
||||
copyDisable?: boolean; // 无法复制
|
||||
nodeDTOType: StandardNodeType;
|
||||
nodeMetaPath?: string;
|
||||
batchPath?: string;
|
||||
outputsPath?: string;
|
||||
inputParametersPath?: string;
|
||||
renderKey?: string;
|
||||
style?: any;
|
||||
errorStyle?: any;
|
||||
size?: { width: number; height: number }; // 初始化时候的默认大小
|
||||
defaultPorts?: Array<any>;
|
||||
useDynamicPort?: boolean;
|
||||
disableSideSheet?: boolean;
|
||||
subCanvas?: (node: WorkflowNodeEntity) => WorkflowSubCanvas | undefined;
|
||||
|
||||
/** 是否展示触发器 icon */
|
||||
showTrigger?: (props: { projectId?: string }) => boolean;
|
||||
/** 是否隐藏测试 */
|
||||
hideTest?: boolean;
|
||||
/** 获取卡片输入变量标签 */
|
||||
getInputVariableTag?: (
|
||||
/** 变量名称 */
|
||||
variableName: string | undefined,
|
||||
/** 输入值或变量 */
|
||||
input: ValueExpression,
|
||||
/** 附加参数 */
|
||||
extra?: {
|
||||
/**
|
||||
* 变量服务,从 '@coze-workflow/variable' 导入的 WorkflowVariableService
|
||||
* 这里不写上类型,是为了避免循环引用,另外 base 包引用 variable 包内容也不合理
|
||||
*/
|
||||
variableService: any;
|
||||
|
||||
/** 当前节点 */
|
||||
node: WorkflowNodeEntity;
|
||||
},
|
||||
) => VariableTagProps;
|
||||
|
||||
/** 是否支持单节点调试copilot生成表单参数 */
|
||||
enableCopilotGenerateTestNodeForm?: boolean;
|
||||
/**
|
||||
* 获取llm ids,所有需要用到大模型选择的的节点必须实现该方法
|
||||
* 因为用户可能存在账户升级或者模型下架,正常拉到的列表数据可能没有该id
|
||||
* 所以模型列表获取需要遍历所有节点拿到被用过的模型id
|
||||
* @param nodeJSON
|
||||
* @returns
|
||||
*/
|
||||
getLLMModelIdsByNodeJSON?: (
|
||||
nodeJSON: WorkflowNodeJSON,
|
||||
) => string[] | undefined | string | number | number[];
|
||||
/**
|
||||
* 节点 header 不可编辑 & 隐藏 ... 按钮
|
||||
*/
|
||||
headerReadonly?: boolean;
|
||||
headerReadonlyAllowDeleteOperation?: boolean;
|
||||
|
||||
/**
|
||||
* 节点帮助文档
|
||||
*/
|
||||
helpLink?: string | ((props: { apiName: string }) => string);
|
||||
/**
|
||||
* 节点测试相关数据
|
||||
*/
|
||||
test?: NodeTest;
|
||||
}
|
||||
|
||||
export interface WorkflowNodeRegistry<NodeTestMeta = any>
|
||||
extends WorkflowOriginNodeRegistry {
|
||||
variablesMeta?: WorkflowNodeVariablesMeta;
|
||||
meta: NodeMeta<NodeTestMeta>;
|
||||
|
||||
/**
|
||||
* 特化:根据节点获取输入 Parameters 的值,默认通过 /inputParameters 字段判断
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
getNodeInputParameters?: (node: FlowNodeEntity) => InputValueVO[] | undefined;
|
||||
|
||||
/**
|
||||
* 获取舞台节点右侧更多菜单的额外操作,目前在子流程节点和插件节点中使用
|
||||
* @returns
|
||||
*/
|
||||
getHeaderExtraOperation?: (
|
||||
formValues: any,
|
||||
node: FlowNodeEntity,
|
||||
) => React.ReactNode;
|
||||
|
||||
/**
|
||||
* 特化:根据节点获取输出 Outputs 的值,默认通过 /outputs 字段判断
|
||||
*/
|
||||
getNodeOutputs?: (node: FlowNodeEntity) => OutputValueVO[] | undefined;
|
||||
|
||||
/**
|
||||
* 节点提交前最后一次转化,这个方法在 `formMeta.transformOnSubmit` 之后执行
|
||||
* @param node 节点数据
|
||||
* @param 转化后的节点数据
|
||||
*/
|
||||
beforeNodeSubmit?: (node: WorkflowNodeJSON) => WorkflowNodeJSON;
|
||||
|
||||
/**
|
||||
* - 节点 Registry 初始化,可以在这里获取初始化数据,然后再进行表单渲染,注意此时 node 还没有创建
|
||||
* - 目前适用于 v2 表单引擎节点
|
||||
* @param nodeJSON 节点初始化数据(点击添加时,或者从后端获取时的节点数据)
|
||||
* @param context WorkflowPlaygroundContext,参见 packages/workflow/playground/src/workflow-playground-context.ts
|
||||
* @returns Promise<void>
|
||||
*/
|
||||
onInit?: (nodeJSON: WorkflowNodeJSON, context: any) => Promise<void>;
|
||||
|
||||
/**
|
||||
* - 是否有错误信息(从 service 中拿),如果有错误信息返回错误信息,否则返回空串或者 undefined
|
||||
* - 目前在 node-context-provider.tsx 中消费,监听当前节点错误信息,如果有,更新 FlowNodeErrorData 数据,从而触发节点渲染错误状态
|
||||
* - 目前适用于 v2 表单引擎节点
|
||||
* @param nodeJSON 节点初始化数据(点击添加时,或者从后端获取时的节点数据)
|
||||
* @param context WorkflowPlaygroundContext,参见 packages/workflow/playground/src/workflow-playground-context.ts
|
||||
*/
|
||||
checkError?: (nodeJSON: WorkflowNodeJSON, context: any) => string | undefined;
|
||||
|
||||
/**
|
||||
* - 节点销毁时调用,用于做一些清理工作,例如将相关节点的错误信息清空,回收资源
|
||||
* - 目前适用于 v2 表单引擎节点
|
||||
* @param node 节点
|
||||
* @param context workflow-playground-context
|
||||
* @returns
|
||||
*/
|
||||
onDispose?: (nodeJSON: WorkflowNodeJSON, context: any) => void;
|
||||
}
|
||||
24
frontend/packages/workflow/base/src/types/utils.ts
Normal file
24
frontend/packages/workflow/base/src/types/utils.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 ValueOf<T, K = keyof T> = K extends keyof T ? T[K] : never;
|
||||
|
||||
import { type CSSProperties } from 'react';
|
||||
|
||||
export type WithCustomStyle<T = object> = {
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
} & T;
|
||||
124
frontend/packages/workflow/base/src/types/view-variable-tree.ts
Normal file
124
frontend/packages/workflow/base/src/types/view-variable-tree.ts
Normal 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 ViewVariableType } from './view-variable-type';
|
||||
|
||||
export interface ViewVariableTreeNode {
|
||||
key: string;
|
||||
type: ViewVariableType;
|
||||
name: string;
|
||||
children?: ViewVariableTreeNode[];
|
||||
required?: boolean;
|
||||
description?: string;
|
||||
// 标识该条参数是内置参数,默认为 false
|
||||
isPreset?: boolean;
|
||||
// 标识该条参数是否启用,默认为 false
|
||||
enabled?: boolean;
|
||||
// 用户自定义节点名展示
|
||||
label?: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
defaultValue?: any;
|
||||
}
|
||||
|
||||
export interface ViewVariableMeta extends ViewVariableTreeNode {
|
||||
required?: boolean;
|
||||
description?: string;
|
||||
readonly?: boolean;
|
||||
mutable?: boolean; // 是否可以被 Set 节点的左值选中
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ViewVariableTreeNode {
|
||||
/**
|
||||
* 通过path 查询 子节点
|
||||
* @param node
|
||||
*/
|
||||
export function getVariableTreeNodeByPath(
|
||||
node: ViewVariableTreeNode,
|
||||
keyPath: string[],
|
||||
): ViewVariableTreeNode | undefined {
|
||||
keyPath = keyPath.slice();
|
||||
let currentNode: ViewVariableTreeNode | undefined = node;
|
||||
let key: string | undefined = keyPath.shift();
|
||||
while (key && currentNode) {
|
||||
key = keyPath.shift();
|
||||
if (key) {
|
||||
currentNode = (currentNode.children || []).find(
|
||||
child => child.key === key,
|
||||
) as ViewVariableTreeNode;
|
||||
if (!currentNode) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
/**
|
||||
* ['xxx', 'xxx'] -> 'a.b.c'
|
||||
* @param keyPath
|
||||
* @deprecated
|
||||
*/
|
||||
export function keyPathToNameString(
|
||||
node: ViewVariableTreeNode,
|
||||
keyPath: string[],
|
||||
): string {
|
||||
const result: string[] = [];
|
||||
keyPath = keyPath.slice();
|
||||
let currentNode: ViewVariableTreeNode | undefined = node;
|
||||
let key: string | undefined = keyPath.shift();
|
||||
while (key && currentNode) {
|
||||
result.push(currentNode.name);
|
||||
key = keyPath.shift();
|
||||
if (key) {
|
||||
currentNode = (currentNode.children || []).find(
|
||||
child => child.key === key,
|
||||
) as ViewVariableTreeNode;
|
||||
if (!currentNode) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.join('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* 'a.b.c' -> ['xxx', 'xxx']
|
||||
* @param nameStr
|
||||
* @deprecated
|
||||
*/
|
||||
export function nameStringToKeyPath(
|
||||
node: ViewVariableTreeNode,
|
||||
nameStr: string,
|
||||
): string[] {
|
||||
const result: string[] = [];
|
||||
const nameList = nameStr.split('.');
|
||||
let currentNode: ViewVariableTreeNode | undefined = node;
|
||||
let name = nameList.shift();
|
||||
while (name && currentNode) {
|
||||
result.push(currentNode.key);
|
||||
name = nameList.shift();
|
||||
if (name) {
|
||||
currentNode = (currentNode.children || []).find(
|
||||
child => child.name === name,
|
||||
) as ViewVariableTreeNode;
|
||||
if (!currentNode) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
273
frontend/packages/workflow/base/src/types/view-variable-type.ts
Normal file
273
frontend/packages/workflow/base/src/types/view-variable-type.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* 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 ReactNode } from 'react';
|
||||
|
||||
/**
|
||||
* 前端变量类型
|
||||
*/
|
||||
export enum ViewVariableType {
|
||||
String = 1,
|
||||
Integer,
|
||||
Boolean,
|
||||
Number,
|
||||
Object = 6,
|
||||
Image,
|
||||
File,
|
||||
Doc,
|
||||
Code,
|
||||
Ppt,
|
||||
Txt,
|
||||
Excel,
|
||||
Audio,
|
||||
Zip,
|
||||
Video,
|
||||
Svg,
|
||||
Voice,
|
||||
Time,
|
||||
// 上面是 api 中定义的 InputType。下面是整合后的。从 99 开始,避免和后端定义撞车
|
||||
ArrayString = 99,
|
||||
ArrayInteger,
|
||||
ArrayBoolean,
|
||||
ArrayNumber,
|
||||
ArrayObject,
|
||||
ArrayImage,
|
||||
ArrayFile,
|
||||
ArrayDoc,
|
||||
ArrayCode,
|
||||
ArrayPpt,
|
||||
ArrayTxt,
|
||||
ArrayExcel,
|
||||
ArrayAudio,
|
||||
ArrayZip,
|
||||
ArrayVideo,
|
||||
ArraySvg,
|
||||
ArrayVoice,
|
||||
ArrayTime,
|
||||
}
|
||||
/**
|
||||
* 使用 JSON 表示值的变量类型
|
||||
*/
|
||||
export const JSON_INPUT_TYPES = [
|
||||
ViewVariableType.Object,
|
||||
ViewVariableType.ArrayString,
|
||||
ViewVariableType.ArrayInteger,
|
||||
ViewVariableType.ArrayBoolean,
|
||||
ViewVariableType.ArrayNumber,
|
||||
ViewVariableType.ArrayObject,
|
||||
ViewVariableType.ArrayTime,
|
||||
];
|
||||
|
||||
export const FILE_TYPES = [
|
||||
ViewVariableType.File,
|
||||
ViewVariableType.Image,
|
||||
ViewVariableType.Doc,
|
||||
ViewVariableType.Code,
|
||||
ViewVariableType.Ppt,
|
||||
ViewVariableType.Txt,
|
||||
ViewVariableType.Excel,
|
||||
ViewVariableType.Audio,
|
||||
ViewVariableType.Zip,
|
||||
ViewVariableType.Video,
|
||||
ViewVariableType.Svg,
|
||||
ViewVariableType.Voice,
|
||||
|
||||
ViewVariableType.ArrayImage,
|
||||
ViewVariableType.ArrayFile,
|
||||
ViewVariableType.ArrayDoc,
|
||||
ViewVariableType.ArrayCode,
|
||||
ViewVariableType.ArrayPpt,
|
||||
ViewVariableType.ArrayTxt,
|
||||
ViewVariableType.ArrayExcel,
|
||||
ViewVariableType.ArrayAudio,
|
||||
ViewVariableType.ArrayZip,
|
||||
ViewVariableType.ArrayVideo,
|
||||
ViewVariableType.ArraySvg,
|
||||
ViewVariableType.ArrayVoice,
|
||||
];
|
||||
|
||||
// 基础类型,及其对应数组类型的配对
|
||||
export const BASE_ARRAY_PAIR: [ViewVariableType, ViewVariableType][] = [
|
||||
[ViewVariableType.String, ViewVariableType.ArrayString],
|
||||
[ViewVariableType.Integer, ViewVariableType.ArrayInteger],
|
||||
[ViewVariableType.Boolean, ViewVariableType.ArrayBoolean],
|
||||
[ViewVariableType.Number, ViewVariableType.ArrayNumber],
|
||||
[ViewVariableType.Object, ViewVariableType.ArrayObject],
|
||||
[ViewVariableType.Image, ViewVariableType.ArrayImage],
|
||||
[ViewVariableType.File, ViewVariableType.ArrayFile],
|
||||
[ViewVariableType.Doc, ViewVariableType.ArrayDoc],
|
||||
[ViewVariableType.Code, ViewVariableType.ArrayCode],
|
||||
[ViewVariableType.Ppt, ViewVariableType.ArrayPpt],
|
||||
[ViewVariableType.Txt, ViewVariableType.ArrayTxt],
|
||||
[ViewVariableType.Excel, ViewVariableType.ArrayExcel],
|
||||
[ViewVariableType.Audio, ViewVariableType.ArrayAudio],
|
||||
[ViewVariableType.Zip, ViewVariableType.ArrayZip],
|
||||
[ViewVariableType.Video, ViewVariableType.ArrayVideo],
|
||||
[ViewVariableType.Svg, ViewVariableType.ArraySvg],
|
||||
[ViewVariableType.Voice, ViewVariableType.ArrayVoice],
|
||||
[ViewVariableType.Time, ViewVariableType.ArrayTime],
|
||||
];
|
||||
|
||||
export const VARIABLE_TYPE_ALIAS_MAP: Record<ViewVariableType, string> = {
|
||||
[ViewVariableType.String]: 'String',
|
||||
[ViewVariableType.Integer]: 'Integer',
|
||||
[ViewVariableType.Boolean]: 'Boolean',
|
||||
[ViewVariableType.Number]: 'Number',
|
||||
[ViewVariableType.Object]: 'Object',
|
||||
[ViewVariableType.Image]: 'Image',
|
||||
[ViewVariableType.File]: 'File',
|
||||
[ViewVariableType.Doc]: 'Doc',
|
||||
[ViewVariableType.Code]: 'Code',
|
||||
[ViewVariableType.Ppt]: 'PPT',
|
||||
[ViewVariableType.Txt]: 'Txt',
|
||||
[ViewVariableType.Excel]: 'Excel',
|
||||
[ViewVariableType.Audio]: 'Audio',
|
||||
[ViewVariableType.Zip]: 'Zip',
|
||||
[ViewVariableType.Video]: 'Video',
|
||||
[ViewVariableType.Svg]: 'Svg',
|
||||
[ViewVariableType.Voice]: 'Voice',
|
||||
[ViewVariableType.Time]: 'Time',
|
||||
[ViewVariableType.ArrayString]: 'Array<String>',
|
||||
[ViewVariableType.ArrayInteger]: 'Array<Integer>',
|
||||
[ViewVariableType.ArrayBoolean]: 'Array<Boolean>',
|
||||
[ViewVariableType.ArrayNumber]: 'Array<Number>',
|
||||
[ViewVariableType.ArrayObject]: 'Array<Object>',
|
||||
[ViewVariableType.ArrayImage]: 'Array<Image>',
|
||||
[ViewVariableType.ArrayFile]: 'Array<File>',
|
||||
[ViewVariableType.ArrayDoc]: 'Array<Doc>',
|
||||
[ViewVariableType.ArrayCode]: 'Array<Code>',
|
||||
[ViewVariableType.ArrayPpt]: 'Array<PPT>',
|
||||
[ViewVariableType.ArrayTxt]: 'Array<Txt>',
|
||||
[ViewVariableType.ArrayExcel]: 'Array<Excel>',
|
||||
[ViewVariableType.ArrayAudio]: 'Array<Audio>',
|
||||
[ViewVariableType.ArrayZip]: 'Array<Zip>',
|
||||
[ViewVariableType.ArrayVideo]: 'Array<Video>',
|
||||
[ViewVariableType.ArraySvg]: 'Array<Svg>',
|
||||
[ViewVariableType.ArrayVoice]: 'Array<Voice>',
|
||||
[ViewVariableType.ArrayTime]: 'Array<Time>',
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ViewVariableType {
|
||||
export const LabelMap = VARIABLE_TYPE_ALIAS_MAP;
|
||||
export const ArrayTypes = BASE_ARRAY_PAIR.map(_pair => _pair[1]);
|
||||
|
||||
export function getLabel(type: ViewVariableType): string {
|
||||
return LabelMap[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有变量类型的补集
|
||||
* 该函数由GPT生成
|
||||
* @param inputTypes
|
||||
*/
|
||||
export function getComplement(inputTypes: ViewVariableType[]) {
|
||||
const allTypes: ViewVariableType[] = [
|
||||
...BASE_ARRAY_PAIR.map(_pair => _pair[0]),
|
||||
...BASE_ARRAY_PAIR.map(_pair => _pair[1]),
|
||||
];
|
||||
|
||||
return allTypes.filter(type => !inputTypes.includes(type));
|
||||
}
|
||||
|
||||
export function canDrilldown(type: ViewVariableType): boolean {
|
||||
return [ViewVariableType.Object, ViewVariableType.ArrayObject].includes(
|
||||
type,
|
||||
);
|
||||
}
|
||||
|
||||
export function isArrayType(type: ViewVariableType): boolean {
|
||||
const arrayTypes = BASE_ARRAY_PAIR.map(_pair => _pair[1]);
|
||||
return arrayTypes.includes(type);
|
||||
}
|
||||
|
||||
export function isFileType(type: ViewVariableType): boolean {
|
||||
return FILE_TYPES.includes(type);
|
||||
}
|
||||
|
||||
export function isVoiceType(type: ViewVariableType): boolean {
|
||||
return [ViewVariableType.Voice].includes(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 JSON 表示值的变量类型
|
||||
* @param type
|
||||
* @returns
|
||||
*/
|
||||
export function isJSONInputType(type: ViewVariableType): boolean {
|
||||
return JSON_INPUT_TYPES.includes(type);
|
||||
}
|
||||
|
||||
export function getArraySubType(type: ViewVariableType): ViewVariableType {
|
||||
const subType = BASE_ARRAY_PAIR.find(_pair => _pair[1] === type)?.[0];
|
||||
|
||||
if (!subType) {
|
||||
throw new Error('WorkflowVariableEntity Error: Unknown Variable Type');
|
||||
}
|
||||
|
||||
return subType;
|
||||
}
|
||||
|
||||
export function wrapToArrayType(type: ViewVariableType): ViewVariableType {
|
||||
const arrayType = BASE_ARRAY_PAIR.find(_pair => _pair[0] === type)?.[1];
|
||||
|
||||
if (!arrayType) {
|
||||
throw new Error('WorkflowVariableEntity Error: Unknown Variable Type');
|
||||
}
|
||||
|
||||
return arrayType;
|
||||
}
|
||||
|
||||
export function getAllArrayType(): ViewVariableType[] {
|
||||
const allTypes: ViewVariableType[] = [
|
||||
...BASE_ARRAY_PAIR.map(_pair => _pair[0]),
|
||||
...BASE_ARRAY_PAIR.map(_pair => _pair[1]),
|
||||
];
|
||||
|
||||
return allTypes.filter(isArrayType);
|
||||
}
|
||||
}
|
||||
|
||||
export interface InputVariable {
|
||||
name: string;
|
||||
/**
|
||||
* id 不一定有,非前后端约定,前端强加上的。风险:workflowSchema 结构化时可能会删掉此属性
|
||||
* 如需 id ,要在 nodes schema 中定义 :
|
||||
* getDefaultAppendValue: () => ({
|
||||
id: nanoid(),
|
||||
}),
|
||||
* 参考 packages/workflow/nodes/src/workflow-nodes/image-canvas/index.ts
|
||||
* 只能保证同一节点内唯一,只能保证同一节点内唯一,只能保证同一节点内唯一
|
||||
* 因为节点可以创建副本
|
||||
*/
|
||||
id?: string;
|
||||
type: ViewVariableType;
|
||||
/**
|
||||
* 索引
|
||||
*/
|
||||
index: number;
|
||||
}
|
||||
|
||||
/** 卡片上的变量展示标签 */
|
||||
export interface VariableTagProps {
|
||||
key?: string;
|
||||
/* 变量类型 */
|
||||
type?: ViewVariableType;
|
||||
/* 变量名,为空时会展示为 Undefined/未定义 */
|
||||
label?: ReactNode;
|
||||
/** 是否有效 */
|
||||
invalid?: boolean;
|
||||
}
|
||||
191
frontend/packages/workflow/base/src/types/vo.ts
Normal file
191
frontend/packages/workflow/base/src/types/vo.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 这个文件定义的是前端消费的数据, 用 VO (view object) 来表示
|
||||
*/
|
||||
import { isNil, isUndefined } from 'lodash-es';
|
||||
|
||||
import type { ViewVariableType } from './view-variable-type';
|
||||
|
||||
export interface RefExpressionContent {
|
||||
/**
|
||||
* 引用值是一个变量的 key 路径
|
||||
*/
|
||||
keyPath: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* 值表达式类型
|
||||
*/
|
||||
export enum ValueExpressionType {
|
||||
LITERAL = 'literal',
|
||||
REF = 'ref',
|
||||
OBJECT_REF = 'object_ref',
|
||||
}
|
||||
/**
|
||||
* 用来存储 ValueExpression 的原始数据供前端消费
|
||||
*/
|
||||
export interface ValueExpressionRawMeta {
|
||||
type?: ViewVariableType;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本表达式
|
||||
*/
|
||||
export interface LiteralExpression {
|
||||
type: ValueExpressionType.LITERAL;
|
||||
content?: string | number | boolean | Array<unknown>;
|
||||
rawMeta?: ValueExpressionRawMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象表达式
|
||||
*/
|
||||
export interface ObjectRefExpression {
|
||||
type: ValueExpressionType.OBJECT_REF;
|
||||
content?: unknown;
|
||||
rawMeta?: ValueExpressionRawMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* 引用变量
|
||||
*/
|
||||
export interface RefExpression {
|
||||
type: ValueExpressionType.REF;
|
||||
content?: RefExpressionContent;
|
||||
/**
|
||||
* rawMeta 记录了该 ref expression 的类型
|
||||
* 可能和所引用变量类型不同,此时会触发类型自动转换: [Workflow 类型自动转换]()
|
||||
*/
|
||||
rawMeta?: ValueExpressionRawMeta;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端输入值表达式
|
||||
*/
|
||||
export type ValueExpression =
|
||||
| RefExpression
|
||||
| LiteralExpression
|
||||
| ObjectRefExpression;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace ValueExpression {
|
||||
export function isRef(value: ValueExpression): value is RefExpression {
|
||||
return value.type === ValueExpressionType.REF;
|
||||
}
|
||||
export function isLiteral(
|
||||
value: ValueExpression,
|
||||
): value is LiteralExpression {
|
||||
return value.type === ValueExpressionType.LITERAL;
|
||||
}
|
||||
export function isObjectRef(
|
||||
value: ValueExpression,
|
||||
): value is ObjectRefExpression {
|
||||
return value?.type === ValueExpressionType.OBJECT_REF;
|
||||
}
|
||||
|
||||
export function isExpression(value?: ValueExpression): boolean {
|
||||
if (isUndefined(value)) {
|
||||
return false;
|
||||
}
|
||||
return isRef(value) || isLiteral(value);
|
||||
}
|
||||
|
||||
export function isEmpty(value: ValueExpression | undefined): boolean {
|
||||
if (value?.type === ValueExpressionType.OBJECT_REF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (value === null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 如果 value 不是对象或者函数,也就是原生类型,在插件自定义组件中会存在
|
||||
if (typeof value !== 'object' && typeof value !== 'function') {
|
||||
return isNil(value);
|
||||
}
|
||||
|
||||
// value.content 有多种类型,可能是 false
|
||||
if (value?.content === '' || isNil(value?.content)) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(value?.content)) {
|
||||
return value?.content.length === 0;
|
||||
}
|
||||
|
||||
if (value?.type === ValueExpressionType.REF) {
|
||||
return !(value?.content as RefExpressionContent)?.keyPath?.length;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端的value 输入值
|
||||
* {
|
||||
* name: '',
|
||||
* input: {
|
||||
* type: ValueExpressionType.REF,
|
||||
* content: {
|
||||
* keyPath: ['nodeId', 'out3']
|
||||
* }
|
||||
*
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export interface InputValueVO {
|
||||
name?: string;
|
||||
input: ValueExpression;
|
||||
children?: InputValueVO[];
|
||||
key?: string;
|
||||
}
|
||||
|
||||
export interface InputTypeValueVO {
|
||||
name: string;
|
||||
type: ViewVariableType;
|
||||
input: ValueExpression;
|
||||
}
|
||||
|
||||
export enum BatchMode {
|
||||
Single = 'single',
|
||||
Batch = 'batch',
|
||||
}
|
||||
|
||||
export interface BatchVOInputList {
|
||||
id: string;
|
||||
name: string;
|
||||
input: ValueExpression;
|
||||
}
|
||||
|
||||
export interface BatchVO {
|
||||
batchSize: number;
|
||||
concurrentSize: number;
|
||||
inputLists: BatchVOInputList[];
|
||||
}
|
||||
|
||||
export interface OutputValueVO {
|
||||
key: string;
|
||||
name: string;
|
||||
type: ViewVariableType;
|
||||
description?: string;
|
||||
readonly?: boolean;
|
||||
required?: boolean;
|
||||
children?: OutputValueVO[];
|
||||
}
|
||||
29
frontend/packages/workflow/base/src/types/workflow.ts
Normal file
29
frontend/packages/workflow/base/src/types/workflow.ts
Normal 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.
|
||||
*/
|
||||
|
||||
import {
|
||||
type Workflow,
|
||||
type WorkFlowDevStatus,
|
||||
type VCSCanvasData,
|
||||
type OperationInfo,
|
||||
type WorkFlowStatus,
|
||||
} from '@coze-arch/bot-api/workflow_api';
|
||||
|
||||
export type WorkflowInfo = Omit<Workflow, 'status'> & {
|
||||
status?: WorkFlowDevStatus | WorkFlowStatus;
|
||||
vcsData?: VCSCanvasData;
|
||||
operationInfo?: OperationInfo;
|
||||
};
|
||||
35
frontend/packages/workflow/base/src/utils/concat-test-id.ts
Normal file
35
frontend/packages/workflow/base/src/utils/concat-test-id.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { NODE_TEST_ID_PREFIX } from '../constants';
|
||||
|
||||
export const concatTestId = (...testIds: string[]) =>
|
||||
testIds.filter(id => !!id).join('.');
|
||||
|
||||
/**
|
||||
* 生成节点的测试id
|
||||
* @example concatNodeTestId(node, 'right-panel') => playground.node.100001.right-panel
|
||||
* @param node 节点
|
||||
* @param testIds 其它id
|
||||
* @returns
|
||||
*/
|
||||
export const concatNodeTestId = (node: FlowNodeEntity, ...testIds: string[]) =>
|
||||
concatTestId(
|
||||
node?.id ? concatTestId(NODE_TEST_ID_PREFIX, node.id) : '',
|
||||
...testIds,
|
||||
);
|
||||
121
frontend/packages/workflow/base/src/utils/form-helpers.ts
Normal file
121
frontend/packages/workflow/base/src/utils/form-helpers.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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 { cloneDeep } from 'lodash-es';
|
||||
import { isFormV2 } from '@flowgram-adapter/free-layout-editor';
|
||||
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
|
||||
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
/**
|
||||
* 找到以 pathEnds 为结尾的 FormItem,并获取它的值
|
||||
* @param node
|
||||
* @param pathEnds
|
||||
* @returns
|
||||
*/
|
||||
export function getFormValueByPathEnds<T = unknown>(
|
||||
node: FlowNodeEntity,
|
||||
pathEnds: string,
|
||||
): T | undefined {
|
||||
return isFormV2(node)
|
||||
? getFormValueByPathEndsV2<T>(node, pathEnds)
|
||||
: getFormValueByPathEndsV1<T>(node, pathEnds);
|
||||
}
|
||||
|
||||
function getFormValueByPathEndsV1<T = unknown>(
|
||||
node: FlowNodeEntity,
|
||||
pathEnds: string,
|
||||
): T | undefined {
|
||||
const form = node.getData(FlowNodeFormData).formModel;
|
||||
|
||||
const paths: string[] = [...form.formItemPathMap.keys()];
|
||||
const formPath = paths.find(path => path.endsWith(pathEnds));
|
||||
|
||||
if (!formPath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const formValue = cloneDeep<T>(form.getFormItemValueByPath(formPath));
|
||||
return formValue;
|
||||
}
|
||||
|
||||
function getFormValueByPathEndsV2<T = unknown>(
|
||||
node: FlowNodeEntity,
|
||||
pathEnds: string,
|
||||
): T | undefined {
|
||||
const form = node.getData(FlowNodeFormData).formModel;
|
||||
const data = form.getFormItemValueByPath('/');
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const value = findValueByPathEnds<T>(data, pathEnds);
|
||||
|
||||
return cloneDeep(value);
|
||||
}
|
||||
|
||||
const findValueByPathEnds = <T = unknown>(
|
||||
obj: unknown,
|
||||
pathEnds: string,
|
||||
currentPath = '',
|
||||
): T | undefined => {
|
||||
if (!obj) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// 检查当前路径是否以 pathEnds 结尾
|
||||
if (currentPath.endsWith(pathEnds)) {
|
||||
return obj as T;
|
||||
}
|
||||
|
||||
// 处理对象
|
||||
if (typeof obj === 'object' && !Array.isArray(obj)) {
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
const newPath = currentPath ? `${currentPath}/${key}` : `/${key}`;
|
||||
|
||||
if (newPath.endsWith(pathEnds)) {
|
||||
return obj[key] as T;
|
||||
}
|
||||
|
||||
// 递归查找子对象
|
||||
const result = findValueByPathEnds(obj[key], pathEnds, newPath);
|
||||
if (result !== undefined) {
|
||||
return result as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理数组
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
const newPath = `${currentPath}/${i}`;
|
||||
|
||||
if (newPath.endsWith(pathEnds)) {
|
||||
return obj[i] as T;
|
||||
}
|
||||
|
||||
// 递归查找数组元素
|
||||
const result = findValueByPathEnds(obj[i], pathEnds, newPath);
|
||||
if (result !== undefined) {
|
||||
return result as T;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
68
frontend/packages/workflow/base/src/utils/get-file-accept.ts
Normal file
68
frontend/packages/workflow/base/src/utils/get-file-accept.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 '../types/view-variable-type';
|
||||
|
||||
export const ACCEPT_MAP = {
|
||||
[ViewVariableType.Image]: ['image/*'],
|
||||
[ViewVariableType.Doc]: ['.docx', '.doc', '.pdf'],
|
||||
[ViewVariableType.Audio]: [
|
||||
'.mp3',
|
||||
'.wav',
|
||||
'.aac',
|
||||
'.flac',
|
||||
'.ogg',
|
||||
'.wma',
|
||||
'.alac',
|
||||
'.mid',
|
||||
'.midi',
|
||||
'.ac3',
|
||||
'.dsd',
|
||||
],
|
||||
[ViewVariableType.Excel]: ['.xls', '.xlsx', '.csv'],
|
||||
[ViewVariableType.Video]: ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv'],
|
||||
[ViewVariableType.Zip]: ['.zip', '.rar', '.7z', '.tar', '.gz', '.bz2'],
|
||||
[ViewVariableType.Code]: ['.py', '.java', '.c', '.cpp', '.js', '.css'],
|
||||
[ViewVariableType.Txt]: ['.txt'],
|
||||
[ViewVariableType.Ppt]: ['.ppt', '.pptx'],
|
||||
[ViewVariableType.Svg]: ['.svg'],
|
||||
};
|
||||
|
||||
export const getFileAccept = (
|
||||
inputType: ViewVariableType,
|
||||
availableFileTypes?: ViewVariableType[],
|
||||
) => {
|
||||
let accept: string;
|
||||
const itemType = ViewVariableType.isArrayType(inputType)
|
||||
? ViewVariableType.getArraySubType(inputType)
|
||||
: inputType;
|
||||
|
||||
if (itemType === ViewVariableType.File) {
|
||||
if (availableFileTypes?.length) {
|
||||
accept = availableFileTypes
|
||||
.map(type => ACCEPT_MAP[type]?.join(','))
|
||||
.join(',');
|
||||
} else {
|
||||
accept = Object.values(ACCEPT_MAP)
|
||||
.map(items => items.join(','))
|
||||
.join(',');
|
||||
}
|
||||
} else {
|
||||
accept = (ACCEPT_MAP[itemType] || []).join(',');
|
||||
}
|
||||
|
||||
return accept;
|
||||
};
|
||||
50
frontend/packages/workflow/base/src/utils/index.ts
Normal file
50
frontend/packages/workflow/base/src/utils/index.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 {
|
||||
SchemaExtractorParserName,
|
||||
SchemaExtractor,
|
||||
type SchemaExtractorConfig,
|
||||
type SchemaExtracted,
|
||||
type SchemaExtractorNodeConfig,
|
||||
type ParsedVariableMergeGroups,
|
||||
} from './schema-extractor';
|
||||
|
||||
export { concatTestId, concatNodeTestId } from './concat-test-id';
|
||||
export {
|
||||
type NodeResultExtracted,
|
||||
type CaseResultData,
|
||||
NodeResultExtractor,
|
||||
} from './node-result-extractor';
|
||||
|
||||
export { parseImagesFromOutputData } from './output-image-parser';
|
||||
|
||||
export { reporter, captureException } from './slardar-reporter';
|
||||
|
||||
export { getFormValueByPathEnds } from './form-helpers';
|
||||
|
||||
export { isGeneralWorkflow } from './is-general-workflow';
|
||||
|
||||
export { isPresetStartParams, isUserInputStartParams } from './start-params';
|
||||
|
||||
export {
|
||||
type TraverseValue,
|
||||
type TraverseNode,
|
||||
type TraverseContext,
|
||||
type TraverseHandler,
|
||||
traverse,
|
||||
} from './traverse';
|
||||
export { getFileAccept } from './get-file-accept';
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 { WorkflowMode } from '@coze-arch/bot-api/workflow_api';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param flowMode 是否广义上的 workflow,包含原来的 Workflow 和 Coze 2.0 新增的 Chatflow
|
||||
* @returns
|
||||
*/
|
||||
export const isGeneralWorkflow = (flowMode: WorkflowMode) =>
|
||||
flowMode === WorkflowMode.Workflow || flowMode === WorkflowMode.ChatFlow;
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 WorkflowJSON } from '../../types';
|
||||
import { type NodeResult } from '../../api';
|
||||
import {
|
||||
type NodeResultExtracted,
|
||||
type NodeResultExtractorParser,
|
||||
} from './type';
|
||||
import { defaultParser } from './parsers';
|
||||
export { type NodeResultExtracted, type CaseResultData } from './type';
|
||||
export class NodeResultExtractor {
|
||||
private readonly parser: NodeResultExtractorParser;
|
||||
public constructor(
|
||||
private readonly nodeResults: NodeResult[],
|
||||
private readonly workflowSchema: WorkflowJSON,
|
||||
) {
|
||||
this.parser = defaultParser;
|
||||
}
|
||||
|
||||
public extract(): NodeResultExtracted[] {
|
||||
return (
|
||||
this.nodeResults
|
||||
?.filter(Boolean)
|
||||
?.map(item => this.parser(item, this.workflowSchema)) || []
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable complexity */
|
||||
import { isString } from 'lodash-es';
|
||||
import { typeSafeJSONParse } from '@coze-arch/bot-utils';
|
||||
|
||||
import {
|
||||
type NodeResultExtractorParser,
|
||||
type NodeResultExtracted,
|
||||
type CaseResultData,
|
||||
} from '../type';
|
||||
import { parseImagesFromOutputData } from '../../output-image-parser';
|
||||
import { StandardNodeType, type WorkflowNodeJSON } from '../../../types';
|
||||
import { type NodeResult, TerminatePlanType } from '../../../api';
|
||||
|
||||
export const defaultParser: NodeResultExtractorParser = (
|
||||
nodeResult,
|
||||
workflowSchema,
|
||||
): NodeResultExtracted => {
|
||||
const { nodeId, isBatch, batch } = nodeResult;
|
||||
const node = workflowSchema.nodes.find(it => it.id === nodeId);
|
||||
const nodeType = node?.type as StandardNodeType;
|
||||
if (!isBatch) {
|
||||
return {
|
||||
nodeId,
|
||||
nodeType,
|
||||
isBatch,
|
||||
caseResult: [parseData(nodeResult, node)],
|
||||
};
|
||||
}
|
||||
const batchNodeResult = typeSafeJSONParse(batch) as NodeResult[];
|
||||
const caseResult = (batchNodeResult
|
||||
?.filter(Boolean)
|
||||
?.map(item => parseData(item, node, isBatch))
|
||||
?.filter(Boolean) || []) as CaseResultData[];
|
||||
return {
|
||||
nodeId,
|
||||
nodeType,
|
||||
isBatch,
|
||||
caseResult,
|
||||
};
|
||||
};
|
||||
|
||||
const parseData = (
|
||||
nodeResult: NodeResult,
|
||||
nodeSchema?: WorkflowNodeJSON,
|
||||
isBatch = false,
|
||||
): CaseResultData => {
|
||||
const { input, output, raw_output: rawOutput, extra, items } = nodeResult;
|
||||
|
||||
const dataList: CaseResultData['dataList'] = [];
|
||||
|
||||
const inputAsOutput = [
|
||||
StandardNodeType.End,
|
||||
StandardNodeType.Output,
|
||||
].includes(nodeSchema?.type as StandardNodeType);
|
||||
|
||||
if (isBatch) {
|
||||
dataList.push({
|
||||
title: '本次批处理变量',
|
||||
data: typeSafeJSONParse(items),
|
||||
});
|
||||
}
|
||||
if (!inputAsOutput) {
|
||||
dataList.push({
|
||||
title: '输入',
|
||||
data: typeSafeJSONParse(input) || input?.toString?.(),
|
||||
});
|
||||
}
|
||||
|
||||
const finalOutput = inputAsOutput ? input : output;
|
||||
const outputData =
|
||||
typeSafeJSONParse(finalOutput) || finalOutput?.toString?.();
|
||||
|
||||
const textHasRawout =
|
||||
nodeSchema?.type === StandardNodeType.Text &&
|
||||
isString(rawOutput) &&
|
||||
rawOutput?.length > 0;
|
||||
// 文本节点的 raw_out 不需要反序列化,一定是 string,badcase:用户拼接的 json 字符串如 '{}'、'123',反序列化后会变成object 和 number
|
||||
const rawOutputData = textHasRawout
|
||||
? rawOutput?.toString?.()
|
||||
: rawOutput
|
||||
? typeSafeJSONParse(rawOutput) || rawOutput?.toString?.()
|
||||
: undefined;
|
||||
|
||||
/** Code、Llm 节点需要展示 raw */
|
||||
const hasRawOutput =
|
||||
(Boolean(nodeSchema?.type) &&
|
||||
[
|
||||
StandardNodeType.Code,
|
||||
StandardNodeType.LLM,
|
||||
StandardNodeType.Question,
|
||||
].includes(nodeSchema?.type as StandardNodeType)) ||
|
||||
textHasRawout;
|
||||
// Start、Input 节点只展示输入
|
||||
const hasOutput =
|
||||
nodeSchema?.type !== StandardNodeType.Start &&
|
||||
nodeSchema?.type !== StandardNodeType.Input;
|
||||
if (hasOutput) {
|
||||
hasRawOutput &&
|
||||
rawOutputData &&
|
||||
dataList.push({
|
||||
title: '原始输出',
|
||||
data: rawOutputData,
|
||||
});
|
||||
const outputTitle = inputAsOutput
|
||||
? '输出变量'
|
||||
: rawOutputData
|
||||
? '最终输出'
|
||||
: '输出';
|
||||
outputData &&
|
||||
dataList.push({
|
||||
title: outputTitle,
|
||||
data: outputData,
|
||||
});
|
||||
}
|
||||
|
||||
if (nodeSchema?.type === StandardNodeType.End) {
|
||||
const isReturnText =
|
||||
(typeSafeJSONParse(extra) as any)?.response_extra?.terminal_plan ===
|
||||
TerminatePlanType.USESETTING;
|
||||
if (isReturnText) {
|
||||
dataList.push({
|
||||
title: '回答内容',
|
||||
data: typeSafeJSONParse(output) || output?.toString?.(),
|
||||
});
|
||||
}
|
||||
} else if (nodeSchema?.type === StandardNodeType.Output) {
|
||||
dataList.push({
|
||||
title: '回答内容',
|
||||
data: typeSafeJSONParse(output) || output?.toString?.(),
|
||||
});
|
||||
}
|
||||
|
||||
const outputJsonString = finalOutput ?? '';
|
||||
return {
|
||||
dataList,
|
||||
imgList: parseImagesFromOutputData({
|
||||
// batch data 的 output 下钻了一层,需要再包一层和 output 的 schema 保持一致
|
||||
outputData: isBatch
|
||||
? {
|
||||
outputList: [typeSafeJSONParse(outputJsonString)].filter(Boolean),
|
||||
}
|
||||
: typeSafeJSONParse(outputJsonString),
|
||||
nodeSchema,
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -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 { defaultParser } from './default-parser';
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 StandardNodeType, type WorkflowJSON } from '../../types';
|
||||
import { type NodeResult } from '../../api';
|
||||
|
||||
export interface CaseResultData {
|
||||
dataList?: Array<{ title: string; data: any }>;
|
||||
imgList?: string[];
|
||||
}
|
||||
|
||||
export interface NodeResultExtracted {
|
||||
nodeId?: string;
|
||||
nodeType?: StandardNodeType;
|
||||
isBatch?: boolean;
|
||||
caseResult?: CaseResultData[];
|
||||
}
|
||||
export type NodeResultExtractorParser = (
|
||||
nodeResult: NodeResult,
|
||||
workflowSchema: WorkflowJSON,
|
||||
) => NodeResultExtracted;
|
||||
120
frontend/packages/workflow/base/src/utils/output-image-parser.ts
Normal file
120
frontend/packages/workflow/base/src/utils/output-image-parser.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 { flatten, get } from 'lodash-es';
|
||||
import { type WorkflowNodeJSON } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { StandardNodeType, VariableTypeDTO, AssistTypeDTO } from '../types';
|
||||
|
||||
interface Schema {
|
||||
type: VariableTypeDTO;
|
||||
name: string;
|
||||
schema: Schema | Schema[];
|
||||
assistType: AssistTypeDTO;
|
||||
}
|
||||
|
||||
const isImageType = (schema: Schema) => {
|
||||
if (
|
||||
schema?.type === VariableTypeDTO.image ||
|
||||
(schema?.type === VariableTypeDTO.string &&
|
||||
[AssistTypeDTO.image, AssistTypeDTO.svg].includes(schema?.assistType))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function getImgList(data: unknown, schema: Schema): string[] {
|
||||
let imgList: string[] = [];
|
||||
|
||||
if (isImageType(schema) && typeof data === 'string') {
|
||||
imgList.push(data);
|
||||
}
|
||||
|
||||
if (schema?.type === VariableTypeDTO.list && Array.isArray(data)) {
|
||||
const imgListInItems = (data as unknown[]).map(item =>
|
||||
getImgList(item, schema.schema as Schema),
|
||||
);
|
||||
imgList = imgList.concat(...imgListInItems);
|
||||
}
|
||||
|
||||
if (schema?.type === VariableTypeDTO.object) {
|
||||
const imgListInObject = Object.entries(
|
||||
(data as Record<string, unknown>) || {},
|
||||
).map(([key, value]) =>
|
||||
getImgList(
|
||||
value,
|
||||
((schema.schema as Schema[]) || []).find(
|
||||
item => item.name === key,
|
||||
) as Schema,
|
||||
),
|
||||
);
|
||||
imgList = imgList.concat(...imgListInObject);
|
||||
}
|
||||
|
||||
return imgList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从节点 output data 中解析图片链接
|
||||
* @param outputData 节点输出数据 JSON 序列化后的字符串
|
||||
* @param nodeSchema 节点 schema
|
||||
* @param excludeNodeTypes 不解析该类型节点的图片链接
|
||||
*/
|
||||
export function parseImagesFromOutputData({
|
||||
outputData,
|
||||
nodeSchema,
|
||||
excludeNodeTypes = [],
|
||||
}: {
|
||||
outputData?: any;
|
||||
nodeSchema?: WorkflowNodeJSON;
|
||||
excludeNodeTypes?: StandardNodeType[];
|
||||
}): string[] {
|
||||
if (!nodeSchema || !outputData) {
|
||||
return [];
|
||||
}
|
||||
if (excludeNodeTypes.includes(nodeSchema?.type as StandardNodeType)) {
|
||||
return [];
|
||||
}
|
||||
let outputParameters: any[] = [];
|
||||
if (
|
||||
nodeSchema?.type === StandardNodeType.End ||
|
||||
nodeSchema?.type === StandardNodeType.Output
|
||||
) {
|
||||
outputParameters = (nodeSchema?.data?.inputs as any)?.inputParameters;
|
||||
} else {
|
||||
outputParameters = nodeSchema?.data?.outputs as any[];
|
||||
}
|
||||
if (!outputParameters) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const imgListInOutput = flatten(
|
||||
outputParameters.map(p =>
|
||||
getImgList(
|
||||
get(outputData, p?.name),
|
||||
p?.input
|
||||
? {
|
||||
...p.input,
|
||||
name: p.name,
|
||||
}
|
||||
: p,
|
||||
),
|
||||
),
|
||||
).filter(url => !!url);
|
||||
|
||||
return imgListInOutput;
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* 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 { describe, expect, it } from 'vitest';
|
||||
|
||||
import { StandardNodeType, type WorkflowJSON } from '../../../types';
|
||||
import {
|
||||
type SchemaExtracted,
|
||||
SchemaExtractor,
|
||||
type SchemaExtractorConfig,
|
||||
SchemaExtractorParserName,
|
||||
} from '..';
|
||||
|
||||
describe('SchemaExtractor', () => {
|
||||
it('should create instance', () => {
|
||||
const schema: WorkflowJSON = {
|
||||
nodes: [],
|
||||
edges: [],
|
||||
};
|
||||
const schemaExtractor = new SchemaExtractor(schema);
|
||||
expect(schemaExtractor).toBeInstanceOf(SchemaExtractor);
|
||||
});
|
||||
it('should create instance with empty or undefined schema', () => {
|
||||
const schemaExtractorEmpty = new SchemaExtractor(
|
||||
{} as unknown as WorkflowJSON,
|
||||
);
|
||||
expect(schemaExtractorEmpty).toBeInstanceOf(SchemaExtractor);
|
||||
const schemaExtractorUndefined = new SchemaExtractor(
|
||||
undefined as unknown as WorkflowJSON,
|
||||
);
|
||||
expect(schemaExtractorUndefined).toBeInstanceOf(SchemaExtractor);
|
||||
});
|
||||
it('should extract schema with config', () => {
|
||||
const schema: WorkflowJSON = {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
type: StandardNodeType.Api,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: StandardNodeType.LLM,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName2',
|
||||
},
|
||||
inputs: {
|
||||
llmParam: [
|
||||
{
|
||||
name: 'prompt',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: 'you should test {{here}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'systemPrompt',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: 'this is systemPrompt',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
prompt: 'you should test here',
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
const config: SchemaExtractorConfig = {
|
||||
[StandardNodeType.Api]: [
|
||||
{
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
],
|
||||
[StandardNodeType.LLM]: [
|
||||
{
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
name: 'llmParam',
|
||||
path: 'inputs.llmParam',
|
||||
parser: SchemaExtractorParserName.LLM_PARAM,
|
||||
},
|
||||
],
|
||||
};
|
||||
const schemaExtractor = new SchemaExtractor(schema);
|
||||
const extractedSchema: SchemaExtracted[] = schemaExtractor.extract(config);
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: StandardNodeType.Api,
|
||||
properties: {
|
||||
title: 'nodeName1',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '2',
|
||||
nodeType: StandardNodeType.LLM,
|
||||
properties: {
|
||||
title: 'nodeName2',
|
||||
llmParam: {
|
||||
prompt: 'you should test {{here}}',
|
||||
systemPrompt: 'this is systemPrompt',
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should use parser in the config', () => {
|
||||
const schema: WorkflowJSON = {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
type: StandardNodeType.Api,
|
||||
data: {
|
||||
content: {
|
||||
prefix: 'hello',
|
||||
suffix: 'world',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
const config: SchemaExtractorConfig = {
|
||||
[StandardNodeType.Api]: [
|
||||
{
|
||||
name: 'content',
|
||||
path: 'content',
|
||||
parser: (content: { prefix: string; suffix: string }) =>
|
||||
`${content.prefix},${content.suffix}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
const schemaExtractor = new SchemaExtractor(schema);
|
||||
const extractedSchema: SchemaExtracted[] = schemaExtractor.extract(config);
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: StandardNodeType.Api,
|
||||
properties: {
|
||||
content: 'hello,world!',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
it('should flat multi-layer schema', () => {
|
||||
const schema: WorkflowJSON = {
|
||||
nodes: [
|
||||
{
|
||||
id: '1',
|
||||
type: StandardNodeType.Loop,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName1',
|
||||
},
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: '1.1',
|
||||
type: StandardNodeType.Api,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName1.1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '1.2',
|
||||
type: StandardNodeType.Api,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName1.2',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '1.3',
|
||||
type: StandardNodeType.Api,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName1.3',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
sourceNodeID: '1.1',
|
||||
sourcePortID: 'output',
|
||||
targetNodeID: '1.2',
|
||||
targetPortID: 'input',
|
||||
},
|
||||
{
|
||||
sourceNodeID: '1.2',
|
||||
sourcePortID: 'output',
|
||||
targetNodeID: '1.3',
|
||||
targetPortID: 'input',
|
||||
},
|
||||
{
|
||||
sourceNodeID: '1.1',
|
||||
sourcePortID: 'output',
|
||||
targetNodeID: '1.3',
|
||||
targetPortID: 'input',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
type: StandardNodeType.Api,
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: 'nodeName2',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{
|
||||
sourceNodeID: '1',
|
||||
sourcePortID: 'output',
|
||||
targetNodeID: '2',
|
||||
targetPortID: 'input',
|
||||
},
|
||||
],
|
||||
};
|
||||
const config: SchemaExtractorConfig = {
|
||||
[StandardNodeType.Loop]: [
|
||||
{
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
],
|
||||
[StandardNodeType.Api]: [
|
||||
{
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
],
|
||||
};
|
||||
const schemaExtractor = new SchemaExtractor(schema);
|
||||
const extractedSchema: SchemaExtracted[] = schemaExtractor.extract(config);
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '1',
|
||||
nodeType: StandardNodeType.Loop,
|
||||
properties: {
|
||||
title: 'nodeName1',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '2',
|
||||
nodeType: StandardNodeType.Api,
|
||||
properties: {
|
||||
title: 'nodeName2',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '1.1',
|
||||
nodeType: StandardNodeType.Api,
|
||||
properties: {
|
||||
title: 'nodeName1.1',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '1.2',
|
||||
nodeType: StandardNodeType.Api,
|
||||
properties: {
|
||||
title: 'nodeName1.2',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '1.3',
|
||||
nodeType: StandardNodeType.Api,
|
||||
properties: {
|
||||
title: 'nodeName1.3',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractorParserName } from '../constant';
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor } from '..';
|
||||
|
||||
it('extract schema with dataset param parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '111943',
|
||||
type: '6',
|
||||
data: {
|
||||
inputs: {
|
||||
datasetParam: [
|
||||
{
|
||||
name: 'datasetList',
|
||||
input: {
|
||||
type: 'list',
|
||||
schema: { type: 'string' },
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: ['7330215302133268524', '7330215302133268524'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'topK',
|
||||
input: {
|
||||
type: 'integer',
|
||||
value: { type: 'literal', content: 6 },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'minScore',
|
||||
input: {
|
||||
type: 'number',
|
||||
value: { type: 'literal', content: 0.5 },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'strategy',
|
||||
input: {
|
||||
type: 'integer',
|
||||
value: { type: 'literal', content: 1 },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
// knowledge 知识库节点 6
|
||||
[StandardNodeType.Dataset]: [
|
||||
{
|
||||
// 对应知识库名称
|
||||
name: 'datasetParam',
|
||||
path: 'inputs.datasetParam',
|
||||
parser: SchemaExtractorParserName.DATASET_PARAM,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '111943',
|
||||
nodeType: '6',
|
||||
properties: {
|
||||
datasetParam: {
|
||||
datasetList: ['7330215302133268524', '7330215302133268524'],
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
import { expect, it } from 'vitest';
|
||||
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor } from '..';
|
||||
|
||||
it('extract schema with default parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '900001',
|
||||
type: '2',
|
||||
data: {
|
||||
inputs: {
|
||||
content: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: '{{output_a}} and {{output_b}}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
// end 结束节点 2
|
||||
[StandardNodeType.End]: [
|
||||
{
|
||||
// 对应输出指定内容
|
||||
name: 'content',
|
||||
path: 'inputs.content.value.content',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '900001',
|
||||
nodeType: '2',
|
||||
properties: { content: '{{output_a}} and {{output_b}}' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractorParserName } from '../constant';
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor } from '..';
|
||||
|
||||
it('extract schema with inputParameters parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '154650',
|
||||
type: '3',
|
||||
data: {
|
||||
inputs: {
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'input_a',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '190950',
|
||||
name: 'key0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'input_b',
|
||||
input: {
|
||||
type: 'list',
|
||||
schema: { type: 'string' },
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '154650',
|
||||
name: 'batch_a',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'const_c',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '1234' },
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
// llm 大模型节点 3
|
||||
[StandardNodeType.LLM]: [
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '154650',
|
||||
nodeType: '3',
|
||||
properties: {
|
||||
inputs: [
|
||||
{ name: 'input_a', value: 'key0', isImage: false },
|
||||
{ name: 'input_b', value: 'batch_a', isImage: false },
|
||||
{ name: 'const_c', value: '1234', isImage: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractorParserName } from '../constant';
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor } from '..';
|
||||
|
||||
it('extract schema with intents param parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '159306',
|
||||
type: '22',
|
||||
data: {
|
||||
inputs: {
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'query',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'BOT_USER_INPUT',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
llmParam: {
|
||||
modelType: 113,
|
||||
generationDiversity: 'balance',
|
||||
temperature: 0.5,
|
||||
topP: 1,
|
||||
frequencyPenalty: 0,
|
||||
presencePenalty: 0,
|
||||
maxTokens: 2048,
|
||||
responseFormat: 2,
|
||||
modelName: 'GPT-3.5',
|
||||
prompt: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: '{{query}}',
|
||||
},
|
||||
},
|
||||
systemPrompt: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'literal',
|
||||
content: '你好, {{query}}',
|
||||
},
|
||||
},
|
||||
enableChatHistory: false,
|
||||
},
|
||||
intents: [
|
||||
{
|
||||
name: '北京',
|
||||
},
|
||||
{
|
||||
name: '上海',
|
||||
},
|
||||
{
|
||||
name: '武汉',
|
||||
},
|
||||
{
|
||||
name: '深圳',
|
||||
},
|
||||
{
|
||||
name: '长沙2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
// end 结束节点 2
|
||||
[StandardNodeType.Intent]: [
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// intents
|
||||
name: 'intents',
|
||||
path: 'inputs.intents',
|
||||
parser: SchemaExtractorParserName.INTENTS,
|
||||
},
|
||||
{
|
||||
// system prompt
|
||||
name: 'systemPrompt',
|
||||
path: 'inputs.llmParam.systemPrompt.value.content',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '159306',
|
||||
nodeType: '22',
|
||||
properties: {
|
||||
inputs: [{ isImage: false, name: 'query', value: 'BOT_USER_INPUT' }],
|
||||
intents: { intent: '1. 北京 2. 上海 3. 武汉 4. 深圳 5. 长沙2' },
|
||||
systemPrompt: '你好, {{query}}',
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor, SchemaExtractorParserName } from '..';
|
||||
|
||||
it('extract schema with json string parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '176450',
|
||||
type: '25',
|
||||
data: {
|
||||
inputs: {
|
||||
Messages:
|
||||
'{"visibility":{"visibility":"3","user_settings":[{"biz_role_id":"7398508237160677420","role":"host","nickname":"host","role_type":1,"description":""},{"biz_role_id":"7402058670241185836","role":"juese2","nickname":"","role_type":3,"description":""},{"biz_role_id":"7405794345170763820","role":"bot1","nickname":"majiang","role_type":2,"description":"麻将高手"}]},"order":"1","contentMode":"1","messages":[{"biz_role_id":"7398508237160677420","role":"host","nickname":"host","role_type":1,"generate_mode":0,"content":"这是一条示例消息,点击可修改"},{"biz_role_id":"7402058670241185836","role":"juese2","nickname":"","role_type":3,"content":"","generate_mode":1}]}',
|
||||
Roles:
|
||||
'[{"biz_role_id":"7398508237160677420","role":"host","nickname":"host","role_type":1,"generate_mode":0},{"biz_role_id":"7402058670241185836","role":"juese2","nickname":"","role_type":3,"generate_mode":1}]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
// end 结束节点 2
|
||||
[StandardNodeType.SceneChat]: [
|
||||
{
|
||||
// 对应输出指定内容
|
||||
name: 'messages',
|
||||
path: 'inputs.Messages',
|
||||
parser: SchemaExtractorParserName.JSON_STRING_PARSER,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '176450',
|
||||
nodeType: '25',
|
||||
properties: {
|
||||
messages: {
|
||||
visibility: {
|
||||
visibility: '3',
|
||||
user_settings: [
|
||||
{
|
||||
biz_role_id: '7398508237160677420',
|
||||
role: 'host',
|
||||
nickname: 'host',
|
||||
role_type: 1,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
biz_role_id: '7402058670241185836',
|
||||
role: 'juese2',
|
||||
nickname: '',
|
||||
role_type: 3,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
biz_role_id: '7405794345170763820',
|
||||
role: 'bot1',
|
||||
nickname: 'majiang',
|
||||
role_type: 2,
|
||||
description: '麻将高手',
|
||||
},
|
||||
],
|
||||
},
|
||||
order: '1',
|
||||
contentMode: '1',
|
||||
messages: [
|
||||
{
|
||||
biz_role_id: '7398508237160677420',
|
||||
role: 'host',
|
||||
nickname: 'host',
|
||||
role_type: 1,
|
||||
generate_mode: 0,
|
||||
content: '这是一条示例消息,点击可修改',
|
||||
},
|
||||
{
|
||||
biz_role_id: '7402058670241185836',
|
||||
role: 'juese2',
|
||||
nickname: '',
|
||||
role_type: 3,
|
||||
content: '',
|
||||
generate_mode: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractorParserName } from '../constant';
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor } from '..';
|
||||
|
||||
it('extract schema with outputs parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '190950',
|
||||
type: '5',
|
||||
data: {
|
||||
outputs: [
|
||||
{ type: 'string', name: 'key0', description: 'test desc' },
|
||||
{ type: 'string', name: 'key1' },
|
||||
{ type: 'list', name: 'key2', schema: { type: 'string' } },
|
||||
{
|
||||
type: 'object',
|
||||
name: 'key3',
|
||||
schema: [{ type: 'string', name: 'key31' }],
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'key4',
|
||||
schema: {
|
||||
type: 'object',
|
||||
schema: [
|
||||
{ type: 'boolean', name: 'key41' },
|
||||
{ type: 'integer', name: 'key42' },
|
||||
{ type: 'float', name: 'key43' },
|
||||
{ type: 'list', name: 'key44', schema: { type: 'string' } },
|
||||
{
|
||||
type: 'object',
|
||||
name: 'key45',
|
||||
schema: [{ type: 'string', name: 'key451' }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
// code 代码节点 5
|
||||
[StandardNodeType.Code]: [
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '190950',
|
||||
nodeType: '5',
|
||||
properties: {
|
||||
outputs: [
|
||||
{ name: 'key0', description: 'test desc' },
|
||||
{ name: 'key1' },
|
||||
{ name: 'key2' },
|
||||
{ name: 'key3', children: [{ name: 'key31' }] },
|
||||
{
|
||||
name: 'key4',
|
||||
children: [
|
||||
{ name: 'key41' },
|
||||
{ name: 'key42' },
|
||||
{ name: 'key43' },
|
||||
{ name: 'key44' },
|
||||
{ name: 'key45', children: [{ name: 'key451' }] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractorParserName } from '../constant';
|
||||
import { StandardNodeType } from '../../../types';
|
||||
import { SchemaExtractor } from '..';
|
||||
|
||||
it('extract schema with variableAssign parser', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '184010',
|
||||
type: '21',
|
||||
data: {
|
||||
inputs: {
|
||||
inputParameters: [],
|
||||
variableParameters: [],
|
||||
},
|
||||
outputs: [],
|
||||
},
|
||||
blocks: [
|
||||
{
|
||||
id: '149710',
|
||||
type: '20',
|
||||
data: {
|
||||
inputs: {
|
||||
inputParameters: [
|
||||
{
|
||||
left: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '184010',
|
||||
name: 'var_str',
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '146923',
|
||||
name: 'new_str',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
left: {
|
||||
type: 'float',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '184010',
|
||||
name: 'var_num',
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
type: 'float',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '146923',
|
||||
name: 'new_num',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
left: {
|
||||
type: 'boolean',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '184010',
|
||||
name: 'var_bool',
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
type: 'boolean',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '146923',
|
||||
name: 'new_bool',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
[StandardNodeType.SetVariable]: [
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '149710',
|
||||
nodeType: '20',
|
||||
properties: {
|
||||
inputs: [
|
||||
{ name: 'var_str', value: 'new_str' },
|
||||
{ name: 'var_num', value: 'new_num' },
|
||||
{ name: 'var_bool', value: 'new_bool' },
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('variableAssign parser with empty inputParameters', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '149710',
|
||||
type: '20',
|
||||
data: {
|
||||
inputs: {
|
||||
inputParameters: undefined,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
[StandardNodeType.SetVariable]: [
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '149710',
|
||||
nodeType: '20',
|
||||
properties: {
|
||||
inputs: [],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('variableAssign parser with invalid schema', () => {
|
||||
const schemaExtractor = new SchemaExtractor({
|
||||
edges: [],
|
||||
nodes: [
|
||||
{
|
||||
id: '149710',
|
||||
type: '20',
|
||||
data: {
|
||||
inputs: {
|
||||
inputParameters: [
|
||||
{}, // INVALID
|
||||
{
|
||||
left: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: undefined, // INVALID
|
||||
},
|
||||
},
|
||||
right: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: 'new_str', // INVALID
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
left: {
|
||||
type: 'boolean',
|
||||
value: {}, // INVALID
|
||||
},
|
||||
right: {
|
||||
type: 'boolean',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '146923',
|
||||
name: 'new_bool',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
left: {
|
||||
type: 'boolean',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '184010',
|
||||
name: 'var_bool',
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
type: 'boolean',
|
||||
value: {}, // INVALID
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
const extractedSchema = schemaExtractor.extract({
|
||||
[StandardNodeType.SetVariable]: [
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(extractedSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '149710',
|
||||
nodeType: '20',
|
||||
properties: {
|
||||
inputs: [
|
||||
{
|
||||
name: '',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
value: 'new_str',
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
value: 'new_bool',
|
||||
},
|
||||
{
|
||||
name: 'var_bool',
|
||||
value: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @coze-arch/no-deep-relative-import */
|
||||
import { type SchemaExtractorConfig } from '../../type';
|
||||
import { SchemaExtractorParserName } from '../../constant';
|
||||
import { StandardNodeType } from '../../../../types';
|
||||
|
||||
export const imageflowExtractorConfig: SchemaExtractorConfig = {
|
||||
// api 节点 4
|
||||
[StandardNodeType.Api]: [
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* 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 const imageflowSchemaJSON = {
|
||||
nodes: [
|
||||
{
|
||||
id: '100001',
|
||||
type: '1',
|
||||
meta: { position: { x: 0, y: 0 } },
|
||||
data: {
|
||||
outputs: [
|
||||
{ type: 'string', name: 'ss', required: true },
|
||||
{ type: 'float', name: 'sss', required: true },
|
||||
],
|
||||
nodeMeta: {
|
||||
title: '开始',
|
||||
icon: 'icon-Start.png',
|
||||
description: '工作流的起始节点,用于设定启动工作流需要的信息',
|
||||
subTitle: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '900001',
|
||||
type: '2',
|
||||
meta: { position: { x: 305.67163461538473, y: 600.1374999999998 } },
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: '结束',
|
||||
icon: 'icon-End.png',
|
||||
description: '工作流的最终节点,用于返回工作流运行后的结果信息',
|
||||
subTitle: '',
|
||||
},
|
||||
inputs: {
|
||||
terminatePlan: 'returnVariables',
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'output',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'ss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'sss',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: 'ss' },
|
||||
},
|
||||
},
|
||||
],
|
||||
streamingOutput: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '164069',
|
||||
type: '4',
|
||||
meta: { position: { x: -455, y: 338.06874999999997 } },
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: '文生图',
|
||||
icon: 'icon_Text-to-Image-CN.png',
|
||||
isFromImageflow: true,
|
||||
description: '通过文字描述生成图片',
|
||||
},
|
||||
inputs: {
|
||||
apiParam: [
|
||||
{
|
||||
name: 'apiID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7352834806217981963' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'apiName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: 'text2image' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7352834694330794023' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '文生图' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginVersion',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'tips',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'outDocLink',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
],
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'prompt',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'ss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'ratio',
|
||||
input: {
|
||||
type: 'float',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'sss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'width',
|
||||
input: {
|
||||
type: 'float',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'sss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'ss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
outputs: [
|
||||
{ type: 'image', name: 'data', required: false },
|
||||
{ type: 'string', name: 'msg', required: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '164578',
|
||||
type: '4',
|
||||
meta: { position: { x: -543, y: 937.0687499999999 } },
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: '智能换脸',
|
||||
icon: 'icon_AI-FaceSwap.png',
|
||||
isFromImageflow: true,
|
||||
description: '为图片替换参考图的人脸',
|
||||
},
|
||||
inputs: {
|
||||
apiParam: [
|
||||
{
|
||||
name: 'apiID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7352888732107915305' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'apiName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: 'smartFaceChanging' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7352887570142969875' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '智能换脸' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginVersion',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'tips',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'outDocLink',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
],
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'reference_picture_url',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'ss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'skin',
|
||||
input: {
|
||||
type: 'image',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '164069',
|
||||
name: 'data',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'template_picture_url',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '164069',
|
||||
name: 'msg',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
outputs: [{ type: 'image', name: 'data', required: false }],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '146804',
|
||||
type: '4',
|
||||
meta: { position: { x: -17, y: 1182.06875 } },
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: '提示词优化',
|
||||
icon: 'icon_PromptOptimization.png',
|
||||
isFromImageflow: true,
|
||||
description: '智能优化图像提示词',
|
||||
},
|
||||
inputs: {
|
||||
apiParam: [
|
||||
{
|
||||
name: 'apiID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7360989981134864399' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'apiName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: 'sd_better_prompt' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7360989829062230050' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '提示词优化' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginVersion',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'tips',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'outDocLink',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
],
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'prompt',
|
||||
input: {
|
||||
type: 'image',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '140741',
|
||||
name: 'data',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
outputs: [{ type: 'string', name: 'data', required: false }],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '140741',
|
||||
type: '4',
|
||||
meta: { position: { x: -379.44467504743835, y: 1532.3798149905122 } },
|
||||
data: {
|
||||
nodeMeta: {
|
||||
title: '亮度',
|
||||
icon: 'icon_Brightness.png',
|
||||
isFromImageflow: true,
|
||||
description: '改变图片亮度',
|
||||
},
|
||||
inputs: {
|
||||
apiParam: [
|
||||
{
|
||||
name: 'apiID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7355822909170073600' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'apiName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: 'image_light' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginID',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '7355822909170057216' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginName',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '亮度' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'pluginVersion',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'tips',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'outDocLink',
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: '' },
|
||||
},
|
||||
},
|
||||
],
|
||||
inputParameters: [
|
||||
{
|
||||
name: 'bright',
|
||||
input: {
|
||||
type: 'float',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'sss',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'origin_url',
|
||||
input: {
|
||||
type: 'image',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '164069',
|
||||
name: 'data',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
outputs: [{ type: 'image', name: 'data', required: false }],
|
||||
},
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
{ sourceNodeID: '100001', targetNodeID: '164069' },
|
||||
{ sourceNodeID: '164069', targetNodeID: '164578' },
|
||||
{ sourceNodeID: '164578', targetNodeID: '140741' },
|
||||
{ sourceNodeID: '140741', targetNodeID: '146804' },
|
||||
{ sourceNodeID: '146804', targetNodeID: '900001' },
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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 SchemaExtractorConfig } from '../../type';
|
||||
import { SchemaExtractorParserName } from '../../constant';
|
||||
import { StandardNodeType } from '../../../../types';
|
||||
|
||||
export const workflowExtractorConfig: SchemaExtractorConfig = {
|
||||
// Start 开始节点 1
|
||||
[StandardNodeType.Start]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应 input name / description
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// End 结束节点 2
|
||||
[StandardNodeType.End]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应输出指定内容
|
||||
name: 'content',
|
||||
path: 'inputs.content.value.content',
|
||||
},
|
||||
],
|
||||
// LLM 大模型节点 3
|
||||
[StandardNodeType.LLM]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应batch value / batch description
|
||||
name: 'batch',
|
||||
path: 'inputs.batch.inputLists',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应提示词
|
||||
name: 'llmParam',
|
||||
path: 'inputs.llmParam',
|
||||
parser: SchemaExtractorParserName.LLM_PARAM,
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Plugin 节点 4
|
||||
[StandardNodeType.Api]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应batch value / batch description
|
||||
name: 'batch',
|
||||
path: 'inputs.batch.inputLists',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应input value / input description
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Code 代码节点 5
|
||||
[StandardNodeType.Code]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input value / input description
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应code内容
|
||||
name: 'code',
|
||||
path: 'inputs.code',
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Knowledge 知识库节点 6
|
||||
[StandardNodeType.Dataset]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应知识库名称
|
||||
name: 'datasetParam',
|
||||
path: 'inputs.datasetParam',
|
||||
parser: SchemaExtractorParserName.DATASET_PARAM,
|
||||
},
|
||||
],
|
||||
// If 判断节点 8
|
||||
[StandardNodeType.If]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'branches',
|
||||
path: 'inputs.branches',
|
||||
parser: SchemaExtractorParserName.DEFAULT,
|
||||
},
|
||||
],
|
||||
// Sub Workflow 工作流节点 9
|
||||
[StandardNodeType.SubWorkflow]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应batch value / batch description
|
||||
name: 'batch',
|
||||
path: 'inputs.batch.inputLists',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应input value / input description
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Variable 变量节点 11
|
||||
[StandardNodeType.Variable]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Database 数据库节点 12
|
||||
[StandardNodeType.Database]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// sql
|
||||
name: 'sql',
|
||||
path: 'inputs.sql',
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Message 消息节点 13
|
||||
[StandardNodeType.Output]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// content name
|
||||
name: 'content',
|
||||
path: 'inputs.content.value.content',
|
||||
},
|
||||
],
|
||||
// Sub Imageflow 图像流节点 14
|
||||
[StandardNodeType.Imageflow]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应batch value / batch description
|
||||
name: 'batch',
|
||||
path: 'inputs.batch.inputLists',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应input value / input description
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Text 文本处理节点 15
|
||||
[StandardNodeType.Text]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 拼接结果,以及拼接字符串
|
||||
name: 'concatResult',
|
||||
path: 'inputs.concatParams',
|
||||
parser: SchemaExtractorParserName.CONCAT_RESULT,
|
||||
},
|
||||
{
|
||||
// 自定义数组拼接符号
|
||||
name: 'arrayConcatChar',
|
||||
path: 'inputs.concatParams',
|
||||
parser: SchemaExtractorParserName.CUSTOM_ARRAY_CONCAT_CHAR,
|
||||
},
|
||||
{
|
||||
// 自定义分隔符
|
||||
name: 'splitChar',
|
||||
path: 'inputs.splitParams',
|
||||
parser: SchemaExtractorParserName.CUSTOM_SPLIT_CHAR,
|
||||
},
|
||||
],
|
||||
// Question 问题节点 18
|
||||
[StandardNodeType.Question]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// question 问题
|
||||
name: 'question',
|
||||
path: 'inputs.question',
|
||||
},
|
||||
{
|
||||
// answer_type 回答类型 option|text
|
||||
name: 'answerType',
|
||||
path: 'inputs.answer_type',
|
||||
},
|
||||
{
|
||||
// options
|
||||
name: 'options',
|
||||
path: 'inputs.options',
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Break 终止循环节点 19
|
||||
[StandardNodeType.Break]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
],
|
||||
// Set Variable 设置变量节点 20
|
||||
[StandardNodeType.SetVariable]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.VARIABLE_ASSIGN,
|
||||
},
|
||||
],
|
||||
// Loop 循环节点 21
|
||||
[StandardNodeType.Loop]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应variable name
|
||||
name: 'variables',
|
||||
path: 'inputs.variableParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应output name
|
||||
name: 'outputs',
|
||||
path: 'outputs',
|
||||
parser: SchemaExtractorParserName.OUTPUTS,
|
||||
},
|
||||
],
|
||||
// Intent 意图识别节点 22
|
||||
[StandardNodeType.Intent]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// intents
|
||||
name: 'intents',
|
||||
path: 'inputs.intents',
|
||||
parser: SchemaExtractorParserName.INTENTS,
|
||||
},
|
||||
{
|
||||
// system prompt
|
||||
name: 'systemPrompt',
|
||||
path: 'inputs.llmParam.systemPrompt.value.content',
|
||||
},
|
||||
],
|
||||
// Knowledge Write 知识库写入节点 27
|
||||
[StandardNodeType.DatasetWrite]: [
|
||||
{
|
||||
// 节点自定义名称
|
||||
name: 'title',
|
||||
path: 'nodeMeta.title',
|
||||
},
|
||||
{
|
||||
// 对应input name
|
||||
name: 'inputs',
|
||||
path: 'inputs.inputParameters',
|
||||
parser: SchemaExtractorParserName.INPUT_PARAMETERS,
|
||||
},
|
||||
{
|
||||
// 对应知识库名称
|
||||
name: 'datasetParam',
|
||||
path: 'inputs.datasetParam',
|
||||
parser: SchemaExtractorParserName.DATASET_PARAM,
|
||||
},
|
||||
],
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractor } from '..';
|
||||
import { imageflowSchemaJSON } from './resource/imageflow-schema';
|
||||
import { imageflowExtractorConfig } from './resource/imageflow-config';
|
||||
|
||||
it('extract imageflow schema', () => {
|
||||
const schemaExtractor = new SchemaExtractor(imageflowSchemaJSON);
|
||||
const extractedImageflowSchema = schemaExtractor.extract(
|
||||
imageflowExtractorConfig,
|
||||
);
|
||||
expect(extractedImageflowSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '164069',
|
||||
nodeType: '4',
|
||||
properties: {
|
||||
inputs: [
|
||||
{ name: 'prompt', value: 'ss', isImage: false },
|
||||
{ name: 'ratio', value: 'sss', isImage: false },
|
||||
{ name: 'width', value: 'sss', isImage: false },
|
||||
{ name: 'height', value: 'ss', isImage: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '164578',
|
||||
nodeType: '4',
|
||||
properties: {
|
||||
inputs: [
|
||||
{ name: 'reference_picture_url', value: 'ss', isImage: false },
|
||||
{ name: 'skin', value: 'data', isImage: false },
|
||||
{ name: 'template_picture_url', value: 'msg', isImage: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '146804',
|
||||
nodeType: '4',
|
||||
properties: {
|
||||
inputs: [{ name: 'prompt', value: 'data', isImage: false }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '140741',
|
||||
nodeType: '4',
|
||||
properties: {
|
||||
inputs: [
|
||||
{ name: 'bright', value: 'sss', isImage: false },
|
||||
{ name: 'origin_url', value: 'data', isImage: false },
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* 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 { expect, it } from 'vitest';
|
||||
|
||||
import { SchemaExtractor } from '..';
|
||||
import { workflowSchemaJSON } from './resource/workflow-schema';
|
||||
import { workflowExtractorConfig } from './resource/workflow-config';
|
||||
|
||||
it('extract workflow schema', () => {
|
||||
const schemaExtractor = new SchemaExtractor(workflowSchemaJSON);
|
||||
const extractedWorkflowSchema = schemaExtractor.extract(
|
||||
workflowExtractorConfig,
|
||||
);
|
||||
expect(extractedWorkflowSchema).toStrictEqual([
|
||||
{
|
||||
nodeId: '100001',
|
||||
nodeType: '1',
|
||||
properties: {
|
||||
title: '开始',
|
||||
outputs: [
|
||||
{ name: 'start_input_a', description: 'test desc' },
|
||||
{ name: 'start_input_b' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '900001',
|
||||
nodeType: '2',
|
||||
properties: {
|
||||
title: '结束',
|
||||
inputs: [
|
||||
{ name: 'output_a', value: 'outputList.output_b', isImage: false },
|
||||
],
|
||||
content: '{{output_a}} and {{output_b}}',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '154650',
|
||||
nodeType: '3',
|
||||
properties: {
|
||||
title: '大模型',
|
||||
batch: [
|
||||
{ name: 'batch_a', value: 'key2', isImage: false },
|
||||
{ name: 'batch_b', value: 'key4', isImage: false },
|
||||
],
|
||||
inputs: [
|
||||
{ name: 'input_a', value: 'key0', isImage: false },
|
||||
{ name: 'input_b', value: 'batch_a', isImage: false },
|
||||
{ name: 'const_c', value: '1234', isImage: false },
|
||||
],
|
||||
llmParam: {
|
||||
prompt: '{{input_a}} and {{input_b}}',
|
||||
systemPrompt: 'this is systemPrompt',
|
||||
},
|
||||
outputs: [
|
||||
{
|
||||
name: 'outputList',
|
||||
children: [
|
||||
{ name: 'output_a', description: 'desc output_a' },
|
||||
{ name: 'output_b' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '190950',
|
||||
nodeType: '5',
|
||||
properties: {
|
||||
title: '代码',
|
||||
inputs: [
|
||||
{ name: 'code_input_a', value: 'start_input_a', isImage: false },
|
||||
{ name: 'code_const_b', value: 'test const', isImage: false },
|
||||
],
|
||||
code: 'async function main({ params }: Args): Promise<Output> {\n return params; \n}',
|
||||
outputs: [
|
||||
{ name: 'key0' },
|
||||
{ name: 'key1' },
|
||||
{ name: 'key2' },
|
||||
{ name: 'key3', children: [{ name: 'key31' }] },
|
||||
{
|
||||
name: 'key4',
|
||||
children: [
|
||||
{ name: 'key41' },
|
||||
{ name: 'key42' },
|
||||
{ name: 'key43' },
|
||||
{ name: 'key44' },
|
||||
{ name: 'key45', children: [{ name: 'key451' }] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '111943',
|
||||
nodeType: '6',
|
||||
properties: {
|
||||
title: '知识库',
|
||||
inputs: [{ name: 'Query', value: 'start_input_b', isImage: false }],
|
||||
datasetParam: {
|
||||
datasetList: ['7330215302133268524', '7330215302133268524'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '183818',
|
||||
nodeType: '8',
|
||||
properties: {
|
||||
title: '选择器',
|
||||
branches: [
|
||||
{
|
||||
condition: {
|
||||
logic: 2,
|
||||
conditions: [
|
||||
{
|
||||
operator: 1,
|
||||
left: {
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'start_input_a',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '190950',
|
||||
name: 'key0',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operator: 2,
|
||||
left: {
|
||||
input: {
|
||||
type: 'integer',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '100001',
|
||||
name: 'start_input_b',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
input: {
|
||||
type: 'integer',
|
||||
value: { type: 'literal', content: '2' },
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
operator: 1,
|
||||
left: {
|
||||
input: {
|
||||
type: 'string',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '190950',
|
||||
name: 'key1',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
input: {
|
||||
type: 'string',
|
||||
value: { type: 'literal', content: 'constant_a' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '163608',
|
||||
nodeType: '11',
|
||||
properties: {
|
||||
title: '变量设置',
|
||||
inputs: [
|
||||
{ name: 'variable_a', value: 'start_input_a', isImage: false },
|
||||
],
|
||||
outputs: [{ name: 'isSuccess' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '150706',
|
||||
nodeType: '11',
|
||||
properties: {
|
||||
title: '变量获取',
|
||||
inputs: [{ name: 'Key', value: 'workflow_variable_a', isImage: false }],
|
||||
outputs: [{ name: 'bot_variable_b' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '193063',
|
||||
nodeType: '4',
|
||||
properties: {
|
||||
title: 'playOrRecommendMusic',
|
||||
batch: [
|
||||
{ name: 'item1', value: 'key2', isImage: false },
|
||||
{ name: 'item2', value: 'key4.key44', isImage: false },
|
||||
],
|
||||
inputs: [
|
||||
{ name: 'artist', value: 'item1', isImage: false },
|
||||
{ name: 'user_question', value: 'key0', isImage: false },
|
||||
{ name: 'song_name', value: 'start_input_b', isImage: false },
|
||||
{ name: 'description', value: 'start_input_a', isImage: false },
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
name: 'outputList',
|
||||
children: [
|
||||
{ name: 'response_for_model', description: 'response for model' },
|
||||
{ name: 'response_type', description: 'response type' },
|
||||
{ name: 'template_id', description: 'use card template 3' },
|
||||
{
|
||||
name: 'type_for_model',
|
||||
description: 'how to treat response, 2 means directly return',
|
||||
},
|
||||
{
|
||||
name: 'music_list',
|
||||
description: 'music data list, usually single item',
|
||||
children: [
|
||||
{ name: 'song_name', description: 'name of the music' },
|
||||
{
|
||||
name: 'start',
|
||||
description: 'the beginning of the material',
|
||||
},
|
||||
{
|
||||
name: 'source_from',
|
||||
description: 'id of the source, 1 is soda',
|
||||
},
|
||||
{ name: 'vid', description: 'vid of the video model' },
|
||||
{ name: 'video_auto_play', description: 'if auto play' },
|
||||
{
|
||||
name: 'source_id',
|
||||
description: 'the unique id from source',
|
||||
},
|
||||
{
|
||||
name: 'album_image',
|
||||
description: 'album image of the music',
|
||||
},
|
||||
{
|
||||
name: 'ref_score',
|
||||
description: 'confidence score that match user intention',
|
||||
},
|
||||
{
|
||||
name: 'video_model',
|
||||
description: 'video model to get material of the music',
|
||||
},
|
||||
{
|
||||
name: 'artist_name',
|
||||
description: 'artist name of the music',
|
||||
},
|
||||
{
|
||||
name: 'duration',
|
||||
description: 'play duration of the material',
|
||||
},
|
||||
{
|
||||
name: 'source_app_icon',
|
||||
description: 'icon of the music source',
|
||||
},
|
||||
{
|
||||
name: 'source_app_name',
|
||||
description: 'name of the music source',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'recommend_reason',
|
||||
description: 'if music is recommended, give reason',
|
||||
},
|
||||
{
|
||||
name: 'rel_score',
|
||||
description: 'confidence score of found music',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '139426',
|
||||
nodeType: '9',
|
||||
properties: {
|
||||
title: 'test_ref',
|
||||
batch: [{ name: 'item1', value: 'key2', isImage: false }],
|
||||
inputs: [{ name: 'input_a', value: 'item1', isImage: false }],
|
||||
outputs: [{ name: 'outputList', children: [{ name: 'output_a' }] }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '122146',
|
||||
nodeType: '11',
|
||||
properties: {
|
||||
title: '变量',
|
||||
inputs: [{ name: 'arr_str', value: 'arr_str', isImage: false }],
|
||||
outputs: [{ name: 'isSuccess' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '124687',
|
||||
nodeType: '11',
|
||||
properties: {
|
||||
title: '变量_1',
|
||||
inputs: [{ name: 'Key', value: 'sss', isImage: false }],
|
||||
outputs: [{ name: 'dddd' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '184010',
|
||||
nodeType: '21',
|
||||
properties: {
|
||||
title: '循环',
|
||||
inputs: [{ name: 'arr_str', value: 'arr_str', isImage: false }],
|
||||
variables: [
|
||||
{ name: 'var_str', value: 'str', isImage: false },
|
||||
{ name: 'var_num', value: 'num', isImage: false },
|
||||
{ name: 'var_bool', value: 'bool', isImage: false },
|
||||
],
|
||||
outputs: [{ name: 'output_list' }],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '149710',
|
||||
nodeType: '20',
|
||||
properties: {
|
||||
title: '循环变量',
|
||||
inputs: [
|
||||
{ name: 'var_str', value: 'new_str' },
|
||||
{ name: 'var_num', value: 'new_num' },
|
||||
{ name: 'var_bool', value: 'new_bool' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '185397',
|
||||
nodeType: '13',
|
||||
properties: {
|
||||
title: '消息',
|
||||
inputs: [
|
||||
{ name: 'str', value: 'var_str', isImage: false },
|
||||
{ name: 'num', value: 'var_num', isImage: false },
|
||||
{ name: 'bool', value: 'var_bool', isImage: false },
|
||||
],
|
||||
content: 'str: {{str}}\nnum: {{num}}\nbool: {{bool}}',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '146923',
|
||||
nodeType: '5',
|
||||
properties: {
|
||||
title: '代码',
|
||||
inputs: [
|
||||
{ name: 'var_str', value: 'var_str', isImage: false },
|
||||
{ name: 'var_num', value: 'var_num', isImage: false },
|
||||
{ name: 'var_bool', value: 'var_bool', isImage: false },
|
||||
],
|
||||
code: 'async function main({ params }: Args): Promise<Output> {\n return {\n "new_str": params.var_str + \'✅\',\n "new_num": params.var_num + 1,\n "new_bool": !params.var_bool,\n };\n}',
|
||||
outputs: [
|
||||
{ name: 'new_str' },
|
||||
{ name: 'new_num' },
|
||||
{ name: 'new_bool' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '123896',
|
||||
nodeType: '15',
|
||||
properties: {
|
||||
title: '文本处理',
|
||||
concatResult: 'str: {{String1}}\nnum: {{String2}}\nbool: {{String3}}',
|
||||
arrayConcatChar: '',
|
||||
splitChar: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '179778',
|
||||
nodeType: '8',
|
||||
properties: {
|
||||
title: '选择器',
|
||||
branches: [
|
||||
{
|
||||
condition: {
|
||||
logic: 2,
|
||||
conditions: [
|
||||
{
|
||||
operator: 1,
|
||||
left: {
|
||||
input: {
|
||||
type: 'boolean',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '184010',
|
||||
name: 'var_bool',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
input: {
|
||||
type: 'boolean',
|
||||
value: { type: 'literal', content: 'true' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
nodeId: '177333',
|
||||
nodeType: '8',
|
||||
properties: {
|
||||
title: '选择器_1',
|
||||
branches: [
|
||||
{
|
||||
condition: {
|
||||
logic: 2,
|
||||
conditions: [
|
||||
{
|
||||
operator: 13,
|
||||
left: {
|
||||
input: {
|
||||
type: 'float',
|
||||
value: {
|
||||
type: 'ref',
|
||||
content: {
|
||||
source: 'block-output',
|
||||
blockID: '184010',
|
||||
name: 'var_num',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
right: {
|
||||
input: {
|
||||
type: 'integer',
|
||||
value: { type: 'literal', content: '5' },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{ nodeId: '194199', nodeType: '19', properties: { title: '终止循环' } },
|
||||
]);
|
||||
});
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
export enum SchemaExtractorParserName {
|
||||
DEFAULT = 'default',
|
||||
INPUT_PARAMETERS = 'inputParameters',
|
||||
OUTPUTS = 'outputs',
|
||||
DATASET_PARAM = 'datasetParam',
|
||||
LLM_PARAM = 'llmParam',
|
||||
INTENTS = 'intents',
|
||||
CONCAT_RESULT = 'concatResult',
|
||||
CUSTOM_ARRAY_CONCAT_CHAR = 'customArrayConcatChar',
|
||||
CUSTOM_SPLIT_CHAR = 'customSplitChar',
|
||||
REF_INPUT_PARAMETER = 'refInputParameter',
|
||||
VARIABLE_ASSIGN = 'variableAssign',
|
||||
JSON_STRING_PARSER = 'jsonStringParser',
|
||||
IMAGE_REFERENCE_PARSER = 'imageReferenceParser',
|
||||
EXPRESSION_PARSER = 'expressionParser',
|
||||
VARIABLE_MERGE_GROUPS_PARSER = 'variableMergeGroupsParser',
|
||||
DB_FIELDS_PARSER = 'dbFieldsParser',
|
||||
DB_CONDITIONS_PARSER = 'dbConditionsParser',
|
||||
}
|
||||
|
||||
export const SYSTEM_DELIMITERS = [
|
||||
'\n',
|
||||
'\t',
|
||||
'.',
|
||||
'。',
|
||||
',',
|
||||
',',
|
||||
';',
|
||||
';',
|
||||
' ',
|
||||
];
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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, cloneDeep } from 'lodash-es';
|
||||
import type { WorkflowEdgeJSON } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import type {
|
||||
StandardNodeType,
|
||||
WorkflowJSON,
|
||||
WorkflowNodeJSON,
|
||||
} from '../../types';
|
||||
import type {
|
||||
SchemaExtracted,
|
||||
SchemaExtractorConfig,
|
||||
SchemaExtractorNodeConfig,
|
||||
SchemaExtractorParser,
|
||||
} from './type';
|
||||
import { schemaExtractorParsers } from './parsers';
|
||||
import { SchemaExtractorParserName } from './constant';
|
||||
export { SchemaExtractorParserName } from './constant';
|
||||
|
||||
export type {
|
||||
SchemaExtractorConfig,
|
||||
SchemaExtracted,
|
||||
SchemaExtractorNodeConfig,
|
||||
ParsedVariableMergeGroups,
|
||||
} from './type';
|
||||
|
||||
export class SchemaExtractor {
|
||||
private readonly schema: WorkflowJSON;
|
||||
private readonly parser: Record<
|
||||
SchemaExtractorParserName,
|
||||
SchemaExtractorParser
|
||||
>;
|
||||
constructor(schema: WorkflowJSON) {
|
||||
this.schema = this.flatSchema(cloneDeep(schema));
|
||||
this.parser = schemaExtractorParsers;
|
||||
}
|
||||
public extract(config: SchemaExtractorConfig): SchemaExtracted[] {
|
||||
this.bindParser(config);
|
||||
// 1. 遍历schema中node数组,对每个node做处理
|
||||
return this.schema.nodes
|
||||
.map((node: WorkflowNodeJSON): SchemaExtracted | null => {
|
||||
// 2. 获取节点对应的配置
|
||||
const nodeConfigs: SchemaExtractorNodeConfig[] = config[node.type];
|
||||
if (!nodeConfigs) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
nodeId: node.id,
|
||||
nodeType: node.type as StandardNodeType,
|
||||
properties: this.extractNode(nodeConfigs, node.data),
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as SchemaExtracted[];
|
||||
}
|
||||
private extractNode(
|
||||
nodeConfigs: SchemaExtractorNodeConfig[],
|
||||
nodeData: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
return nodeConfigs.reduce(
|
||||
(
|
||||
extractedConfig: Record<string, unknown>,
|
||||
nodeConfig: SchemaExtractorNodeConfig,
|
||||
): Record<string, unknown> => {
|
||||
// 3. 根据节点配置路径获取属性值
|
||||
const rawData: unknown = this.extractProperties(
|
||||
nodeData,
|
||||
nodeConfig.path,
|
||||
);
|
||||
if (nodeConfig.parser && typeof nodeConfig.parser === 'function') {
|
||||
// 4. 使用解析器对属性值进行转换
|
||||
extractedConfig[nodeConfig.name] = nodeConfig.parser(rawData);
|
||||
}
|
||||
return extractedConfig;
|
||||
},
|
||||
{},
|
||||
);
|
||||
}
|
||||
private extractProperties(properties: Record<string, unknown>, path: string) {
|
||||
return get(properties, path);
|
||||
}
|
||||
private bindParser(config: SchemaExtractorConfig) {
|
||||
Object.entries(config).forEach(([nodeType, nodeConfigs]) => {
|
||||
nodeConfigs.forEach(nodeConfig => {
|
||||
if (!nodeConfig.parser) {
|
||||
nodeConfig.parser = SchemaExtractorParserName.DEFAULT;
|
||||
}
|
||||
if (typeof nodeConfig.parser === 'string') {
|
||||
nodeConfig.parser = this.parser[nodeConfig.parser];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
private getEdgeID(edge: WorkflowEdgeJSON): string {
|
||||
const from = edge.sourceNodeID;
|
||||
const to = edge.targetNodeID;
|
||||
const fromPort = edge.sourcePortID;
|
||||
const toPort = edge.targetPortID;
|
||||
return `${from}_${fromPort || ''}-${to || ''}_${toPort || ''}`;
|
||||
}
|
||||
private flatSchema(
|
||||
json: WorkflowJSON = { nodes: [], edges: [] },
|
||||
): WorkflowJSON {
|
||||
const rootNodes = json.nodes ?? [];
|
||||
const rootEdges = json.edges ?? [];
|
||||
|
||||
const flattenNodeJSONs: WorkflowNodeJSON[] = [...rootNodes];
|
||||
const flattenEdgeJSONs: WorkflowEdgeJSON[] = [...rootEdges];
|
||||
|
||||
// 如需支持多层结构,以下部分改为递归
|
||||
rootNodes.forEach(nodeJSON => {
|
||||
const { blocks, edges } = nodeJSON;
|
||||
if (blocks) {
|
||||
flattenNodeJSONs.push(...blocks);
|
||||
const blockIDs: string[] = [];
|
||||
blocks.forEach(block => {
|
||||
blockIDs.push(block.id);
|
||||
});
|
||||
delete nodeJSON.blocks;
|
||||
}
|
||||
if (edges) {
|
||||
flattenEdgeJSONs.push(...edges);
|
||||
const edgeIDs: string[] = [];
|
||||
edges.forEach(edge => {
|
||||
const edgeID = this.getEdgeID(edge);
|
||||
edgeIDs.push(edgeID);
|
||||
});
|
||||
delete nodeJSON.edges;
|
||||
}
|
||||
});
|
||||
|
||||
const flattenSchema: WorkflowJSON = {
|
||||
nodes: flattenNodeJSONs,
|
||||
edges: flattenEdgeJSONs,
|
||||
};
|
||||
|
||||
return flattenSchema;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 { type SchemaExtractorConcatResultParser } from '../type';
|
||||
|
||||
export const concatResultParser: SchemaExtractorConcatResultParser =
|
||||
concatParams => {
|
||||
const concatResult = (concatParams || []).find(
|
||||
v => v.name === 'concatResult',
|
||||
);
|
||||
return get(concatResult, 'input.value.content', '') as string;
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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 { type SchemaExtractorArrayConcatCharParser } from '../type';
|
||||
import { SYSTEM_DELIMITERS } from '../constant';
|
||||
|
||||
export const arrayConcatCharParser: SchemaExtractorArrayConcatCharParser =
|
||||
concatParams => {
|
||||
const allArrayItemConcatChars = (concatParams || []).find(
|
||||
v => v.name === 'allArrayItemConcatChars',
|
||||
);
|
||||
|
||||
let customConcatChars = '';
|
||||
if (allArrayItemConcatChars) {
|
||||
const list = get(allArrayItemConcatChars, 'input.value.content', []) as {
|
||||
value: string;
|
||||
}[];
|
||||
|
||||
const customItems = list.filter(
|
||||
v => !SYSTEM_DELIMITERS.includes(v.value),
|
||||
);
|
||||
customConcatChars = customItems.map(v => v.value).join(', ');
|
||||
}
|
||||
|
||||
return customConcatChars;
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 { type SchemaExtractorSplitCharParser } from '../type';
|
||||
import { SYSTEM_DELIMITERS } from '../constant';
|
||||
|
||||
export const splitCharParser: SchemaExtractorSplitCharParser = splitParams => {
|
||||
const allDelimiters = (splitParams || []).find(
|
||||
v => v.name === 'allDelimiters',
|
||||
);
|
||||
|
||||
let customDelimiters = '';
|
||||
if (allDelimiters) {
|
||||
const list = get(allDelimiters, 'input.value.content', []) as {
|
||||
value: string;
|
||||
}[];
|
||||
const customItems = list.filter(v => !SYSTEM_DELIMITERS.includes(v.value));
|
||||
customDelimiters = customItems.map(v => v.value).join(', ');
|
||||
}
|
||||
|
||||
return customDelimiters;
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 { type SchemaExtractorDatasetParamParser } from '../type';
|
||||
|
||||
export const datasetParamParser: SchemaExtractorDatasetParamParser =
|
||||
datasetParam => {
|
||||
const datasetListItem = datasetParam.find(
|
||||
param => param.name === 'datasetList',
|
||||
);
|
||||
const datasetList = get(datasetListItem, 'input.value.content');
|
||||
if (!datasetList || !Array.isArray(datasetList)) {
|
||||
return {
|
||||
datasetList: [],
|
||||
};
|
||||
}
|
||||
return {
|
||||
datasetList,
|
||||
};
|
||||
};
|
||||
@@ -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 { type SchemaExtractorDbConditionsParser } from '../type';
|
||||
import { inputParametersParser } from './input-parameters';
|
||||
export const dbConditionsParser: SchemaExtractorDbConditionsParser =
|
||||
conditionList =>
|
||||
conditionList
|
||||
?.flatMap(conditions => inputParametersParser(conditions || []))
|
||||
?.filter(Boolean) as ReturnType<SchemaExtractorDbConditionsParser>;
|
||||
@@ -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 { parseExpression } from '../utils';
|
||||
import { type SchemaExtractorDbFieldsParser } from '../type';
|
||||
export const dbFieldsParser: SchemaExtractorDbFieldsParser = dbFields =>
|
||||
dbFields
|
||||
?.map(([fieldID, fieldValue]) => {
|
||||
const parsedFieldID = parseExpression(fieldID?.input);
|
||||
const parsedFieldValue = parseExpression(fieldValue?.input);
|
||||
if (!parsedFieldValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
name: parsedFieldID?.value,
|
||||
value: parsedFieldValue?.value,
|
||||
isImage: parsedFieldValue?.isImage,
|
||||
};
|
||||
})
|
||||
?.filter(Boolean) as ReturnType<SchemaExtractorDbFieldsParser>;
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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 { parseExpression } from '../utils';
|
||||
import { type SchemaExtractorExpressionParser } from '../type';
|
||||
import type { ValueExpressionDTO } from '../../../types';
|
||||
export const expressionParser: SchemaExtractorExpressionParser = expression => {
|
||||
const expressions = ([] as ValueExpressionDTO[])
|
||||
.concat(expression)
|
||||
.filter(Boolean);
|
||||
return expressions
|
||||
.map(parseExpression)
|
||||
.filter(Boolean) as ReturnType<SchemaExtractorExpressionParser>;
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 { SchemaExtractorImageReferenceParser } from '../type';
|
||||
import { inputParametersParser } from './input-parameters';
|
||||
export const imageReferenceParser: SchemaExtractorImageReferenceParser =
|
||||
references => {
|
||||
if (!Array.isArray(references)) {
|
||||
return [];
|
||||
}
|
||||
return inputParametersParser(
|
||||
references.map(ref => ({
|
||||
name: '-',
|
||||
input: ref.url,
|
||||
})),
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 SchemaExtractorParser } from '../type';
|
||||
import { SchemaExtractorParserName } from '../constant';
|
||||
import { variableMergeGroupsParser } from './variable-merge-groups-parser';
|
||||
import { variableAssignParser } from './variable-assign';
|
||||
import { refInputParametersParser } from './ref-input-parameters';
|
||||
import { outputsParser } from './output';
|
||||
import { llmParamParser } from './llm-param';
|
||||
import { jsonStringParser } from './json-string-parser';
|
||||
import { intentsParser } from './intents';
|
||||
import { inputParametersParser } from './input-parameters';
|
||||
import { imageReferenceParser } from './image-reference';
|
||||
import { expressionParser } from './expression-parser';
|
||||
import { dbFieldsParser } from './db-fields';
|
||||
import { dbConditionsParser } from './db-conditions';
|
||||
import { datasetParamParser } from './dataset-param';
|
||||
import { splitCharParser } from './custom-split-char';
|
||||
import { arrayConcatCharParser } from './custom-array-concat-char';
|
||||
import { concatResultParser } from './concat-result';
|
||||
|
||||
export const schemaExtractorParsers: Record<
|
||||
SchemaExtractorParserName,
|
||||
SchemaExtractorParser
|
||||
> = {
|
||||
[SchemaExtractorParserName.DEFAULT]: t => t,
|
||||
[SchemaExtractorParserName.INPUT_PARAMETERS]: inputParametersParser,
|
||||
[SchemaExtractorParserName.OUTPUTS]: outputsParser,
|
||||
[SchemaExtractorParserName.DATASET_PARAM]: datasetParamParser,
|
||||
[SchemaExtractorParserName.LLM_PARAM]: llmParamParser,
|
||||
[SchemaExtractorParserName.INTENTS]: intentsParser,
|
||||
[SchemaExtractorParserName.CONCAT_RESULT]: concatResultParser,
|
||||
[SchemaExtractorParserName.CUSTOM_ARRAY_CONCAT_CHAR]: arrayConcatCharParser,
|
||||
[SchemaExtractorParserName.CUSTOM_SPLIT_CHAR]: splitCharParser,
|
||||
[SchemaExtractorParserName.REF_INPUT_PARAMETER]: refInputParametersParser,
|
||||
[SchemaExtractorParserName.VARIABLE_ASSIGN]: variableAssignParser,
|
||||
[SchemaExtractorParserName.JSON_STRING_PARSER]: jsonStringParser,
|
||||
[SchemaExtractorParserName.IMAGE_REFERENCE_PARSER]: imageReferenceParser,
|
||||
[SchemaExtractorParserName.EXPRESSION_PARSER]: expressionParser,
|
||||
[SchemaExtractorParserName.VARIABLE_MERGE_GROUPS_PARSER]:
|
||||
variableMergeGroupsParser,
|
||||
[SchemaExtractorParserName.DB_FIELDS_PARSER]: dbFieldsParser,
|
||||
[SchemaExtractorParserName.DB_CONDITIONS_PARSER]: dbConditionsParser,
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 { parseExpression } from '../utils';
|
||||
import { type SchemaExtractorInputParametersParser } from '../type';
|
||||
import type { InputValueDTO } from '../../../types';
|
||||
export const inputParametersParser: SchemaExtractorInputParametersParser =
|
||||
inputParameters => {
|
||||
let parameters: InputValueDTO[] = [];
|
||||
if (!Array.isArray(inputParameters)) {
|
||||
if (typeof inputParameters === 'object') {
|
||||
Object.keys(inputParameters || {}).forEach(key => {
|
||||
parameters.push({
|
||||
name: key,
|
||||
input: inputParameters[key],
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
parameters = inputParameters;
|
||||
}
|
||||
|
||||
return parameters
|
||||
.map(inputParameter => {
|
||||
const expression = get(inputParameter, 'input');
|
||||
const parsedExpression = parseExpression(expression);
|
||||
if (!parsedExpression) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
name: inputParameter.name,
|
||||
...parsedExpression,
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as ReturnType<SchemaExtractorInputParametersParser>;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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 SchemaExtractorIntentsParamParser } from '../type';
|
||||
|
||||
export const intentsParser: SchemaExtractorIntentsParamParser = intents => ({
|
||||
intent: intents.map((item, idx) => `${idx + 1}. ${item.name}`).join(' '),
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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 SchemaExtractorJSONStringParser } from '../type';
|
||||
|
||||
export const jsonStringParser: SchemaExtractorJSONStringParser = (
|
||||
jsonString: string,
|
||||
) => JSON.parse(jsonString || '{}') as object | object[] | undefined;
|
||||
@@ -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 { get } from 'lodash-es';
|
||||
|
||||
import { type SchemaExtractorLLMParamParser } from '../type';
|
||||
|
||||
export const llmParamParser: SchemaExtractorLLMParamParser = llmParam => {
|
||||
const promptItem = llmParam.find(param => param.name === 'prompt');
|
||||
const prompt = (get(promptItem, 'input.value.content') as string) || '';
|
||||
const systemPromptItem = llmParam.find(
|
||||
param => param.name === 'systemPrompt',
|
||||
);
|
||||
const systemPrompt =
|
||||
(get(systemPromptItem, 'input.value.content') as string) || '';
|
||||
return {
|
||||
systemPrompt,
|
||||
prompt,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 { isWorkflowImageTypeURL } from '../utils';
|
||||
import { type SchemaExtractorOutputsParser } from '../type';
|
||||
import { AssistTypeDTO, VariableTypeDTO } from '../../../types/dto';
|
||||
export const outputsParser: SchemaExtractorOutputsParser = outputs => {
|
||||
// 判断是否为数组
|
||||
if (!Array.isArray(outputs)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return outputs.map(output => {
|
||||
const parsed: {
|
||||
name: string;
|
||||
description?: string;
|
||||
children?: ReturnType<SchemaExtractorOutputsParser>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value?: any;
|
||||
isImage?: boolean;
|
||||
// 默认值里包含图片时,图片信息单独放到这里
|
||||
images?: string[];
|
||||
} = {
|
||||
name: output.name || '',
|
||||
};
|
||||
if (output.description) {
|
||||
parsed.description = output.description;
|
||||
}
|
||||
if (output.type === 'object' && Array.isArray(output.schema)) {
|
||||
parsed.children = outputsParser(output.schema);
|
||||
}
|
||||
if (output.type === 'list' && Array.isArray(output.schema?.schema)) {
|
||||
parsed.children = outputsParser(output.schema.schema);
|
||||
}
|
||||
// Start 节点默认值放到 value 上
|
||||
if (output.defaultValue) {
|
||||
parsed.value = output.defaultValue;
|
||||
|
||||
// string、file、image、svg
|
||||
if (
|
||||
(output.type === 'string' &&
|
||||
isWorkflowImageTypeURL(output.defaultValue)) ||
|
||||
[AssistTypeDTO.image, AssistTypeDTO.svg].includes(
|
||||
output.assistType as AssistTypeDTO,
|
||||
)
|
||||
) {
|
||||
parsed.images = [String(output.defaultValue)];
|
||||
} else if (output.type === VariableTypeDTO.list) {
|
||||
// Array<Image> | Array<Svg>
|
||||
if (
|
||||
[AssistTypeDTO.image, AssistTypeDTO.svg].includes(
|
||||
output.schema?.assistType,
|
||||
)
|
||||
) {
|
||||
try {
|
||||
const list = JSON.parse(output.defaultValue) as string[];
|
||||
Array.isArray(list) &&
|
||||
(parsed.images = list.map(item => String(item)));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
// Array<File>
|
||||
} else if (output.schema?.assistType === AssistTypeDTO.file) {
|
||||
try {
|
||||
const list = JSON.parse(output.defaultValue) as string[];
|
||||
Array.isArray(list) &&
|
||||
(parsed.images = list
|
||||
.map(item => String(item))
|
||||
.filter(item => isWorkflowImageTypeURL(item)));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
parsed.isImage = (parsed.images?.length ?? 0) > 0;
|
||||
}
|
||||
return parsed;
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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, isPlainObject } from 'lodash-es';
|
||||
|
||||
import { isWorkflowImageTypeURL } from '../utils';
|
||||
import { type SchemaExtractorReferencesParser } from '../type';
|
||||
|
||||
interface Item {
|
||||
name: string;
|
||||
value: string;
|
||||
isImage: boolean;
|
||||
}
|
||||
|
||||
interface ReferenceValue {
|
||||
type: string;
|
||||
value: {
|
||||
content: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const refInputParametersParser: SchemaExtractorReferencesParser =
|
||||
references => {
|
||||
const results: Item[] = [];
|
||||
for (const refObject of references) {
|
||||
const keys = Object.keys(refObject);
|
||||
for (const itemName of keys) {
|
||||
const itemValue = refObject[itemName];
|
||||
|
||||
if (
|
||||
isPlainObject(itemValue) &&
|
||||
(itemValue as ReferenceValue)?.type === 'string'
|
||||
) {
|
||||
const content = get(itemValue as ReferenceValue, 'value.content');
|
||||
results.push({
|
||||
name: itemName,
|
||||
value: content,
|
||||
isImage: isWorkflowImageTypeURL(content),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 SchemaExtractorVariableAssignParser } from '../type';
|
||||
import { type ValueExpressionDTO, type DTODefine } from '../../../types';
|
||||
|
||||
const getValueExpressionName = (
|
||||
valueExpression: ValueExpressionDTO,
|
||||
): string | undefined => {
|
||||
const content = valueExpression?.value?.content as
|
||||
| DTODefine.RefExpressionContent
|
||||
| string;
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
if (typeof content === 'string') {
|
||||
return content;
|
||||
} else if (typeof content === 'object') {
|
||||
if (content.source === 'block-output' && typeof content.name === 'string') {
|
||||
return content.name;
|
||||
} else if (
|
||||
typeof content.source === 'string' &&
|
||||
content.source.startsWith('global_variable')
|
||||
) {
|
||||
return (
|
||||
content as {
|
||||
source: `global_variable_${string}`;
|
||||
path: string[];
|
||||
blockID: string;
|
||||
name: string;
|
||||
}
|
||||
)?.path?.join('.');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const variableAssignParser: SchemaExtractorVariableAssignParser =
|
||||
variableAssigns => {
|
||||
if (!Array.isArray(variableAssigns)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return variableAssigns
|
||||
.map(variableAssign => {
|
||||
const leftContent = getValueExpressionName(variableAssign.left);
|
||||
const rightContent = getValueExpressionName(variableAssign.right);
|
||||
// 变量赋值节点的右值字段
|
||||
const inputContent = variableAssign.input
|
||||
? getValueExpressionName(variableAssign.input)
|
||||
: null;
|
||||
return {
|
||||
name: leftContent ?? '',
|
||||
value: rightContent ?? inputContent ?? '',
|
||||
};
|
||||
})
|
||||
.filter(Boolean) as ReturnType<SchemaExtractorVariableAssignParser>;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 SchemaExtractorVariableMergeGroupsParser } from '../type';
|
||||
import { expressionParser } from './expression-parser';
|
||||
export const variableMergeGroupsParser: SchemaExtractorVariableMergeGroupsParser =
|
||||
mergeGroups =>
|
||||
mergeGroups.map(group => ({
|
||||
groupName: group.name,
|
||||
variables: expressionParser(group.variables),
|
||||
}));
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 { DatasetParams } from '../../types/data-set';
|
||||
import type {
|
||||
InputValueDTO,
|
||||
VariableMetaDTO,
|
||||
StandardNodeType,
|
||||
ValueExpressionDTO,
|
||||
} from '../../types';
|
||||
import type { SchemaExtractorParserName } from './constant';
|
||||
|
||||
export type SchemaExtractorConfig = Partial<
|
||||
Record<StandardNodeType, SchemaExtractorNodeConfig[]>
|
||||
>;
|
||||
|
||||
export interface SchemaExtractorNodeConfig {
|
||||
name: string;
|
||||
/** lodash.get 入参格式 */
|
||||
path: string;
|
||||
parser?: SchemaExtractorParserName | Function;
|
||||
displayName?: string;
|
||||
}
|
||||
|
||||
export interface SchemaExtracted {
|
||||
nodeId: string;
|
||||
nodeType: StandardNodeType;
|
||||
properties: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type SchemaExtractorParser<
|
||||
T extends SchemaExtractorParserName = SchemaExtractorParserName,
|
||||
> = {
|
||||
[SchemaExtractorParserName.DEFAULT]: SchemaExtractorDefaultParser;
|
||||
[SchemaExtractorParserName.INPUT_PARAMETERS]: SchemaExtractorInputParametersParser;
|
||||
[SchemaExtractorParserName.OUTPUTS]: SchemaExtractorOutputsParser;
|
||||
[SchemaExtractorParserName.DATASET_PARAM]: SchemaExtractorDatasetParamParser;
|
||||
[SchemaExtractorParserName.LLM_PARAM]: SchemaExtractorLLMParamParser;
|
||||
[SchemaExtractorParserName.INTENTS]: SchemaExtractorIntentsParamParser;
|
||||
[SchemaExtractorParserName.CONCAT_RESULT]: SchemaExtractorConcatResultParser;
|
||||
[SchemaExtractorParserName.CUSTOM_ARRAY_CONCAT_CHAR]: SchemaExtractorArrayConcatCharParser;
|
||||
[SchemaExtractorParserName.CUSTOM_SPLIT_CHAR]: SchemaExtractorSplitCharParser;
|
||||
[SchemaExtractorParserName.REF_INPUT_PARAMETER]: SchemaExtractorReferencesParser;
|
||||
[SchemaExtractorParserName.VARIABLE_ASSIGN]: SchemaExtractorVariableAssignParser;
|
||||
[SchemaExtractorParserName.JSON_STRING_PARSER]: SchemaExtractorJSONStringParser;
|
||||
[SchemaExtractorParserName.IMAGE_REFERENCE_PARSER]: SchemaExtractorImageReferenceParser;
|
||||
[SchemaExtractorParserName.EXPRESSION_PARSER]: SchemaExtractorExpressionParser;
|
||||
[SchemaExtractorParserName.VARIABLE_MERGE_GROUPS_PARSER]: SchemaExtractorVariableMergeGroupsParser;
|
||||
[SchemaExtractorParserName.DB_FIELDS_PARSER]: SchemaExtractorDbFieldsParser;
|
||||
[SchemaExtractorParserName.DB_CONDITIONS_PARSER]: SchemaExtractorDbConditionsParser;
|
||||
}[T];
|
||||
|
||||
export type SchemaExtractorDefaultParser = (arg: unknown) => unknown;
|
||||
export interface ParsedExpression {
|
||||
name: string;
|
||||
value: string;
|
||||
isImage: boolean;
|
||||
}
|
||||
|
||||
export type SchemaExtractorInputParametersParser = (
|
||||
inputParameters: InputValueDTO[] | Record<string, InputValueDTO['input']>,
|
||||
) => ParsedExpression[];
|
||||
|
||||
export type SchemaExtractorDbFieldsParser = (
|
||||
inputParameters: Array<[InputValueDTO, InputValueDTO]>,
|
||||
) => ParsedExpression[];
|
||||
|
||||
export type SchemaExtractorDbConditionsParser = (
|
||||
inputParameters: Array<InputValueDTO[]>,
|
||||
) => ParsedExpression[];
|
||||
|
||||
export type SchemaExtractorReferencesParser = (
|
||||
reference: Record<string, unknown>[],
|
||||
) => ParsedExpression[];
|
||||
|
||||
export type SchemaExtractorOutputsParser = (outputs: VariableMetaDTO[]) => {
|
||||
name: string;
|
||||
description?: string;
|
||||
children?: ReturnType<SchemaExtractorOutputsParser>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
value?: any;
|
||||
isImage?: boolean;
|
||||
// 默认值里包含图片时,图片信息单独放到这里
|
||||
images?: string[];
|
||||
}[];
|
||||
|
||||
export type SchemaExtractorDatasetParamParser = (outputs: DatasetParams) => {
|
||||
datasetList: string[];
|
||||
};
|
||||
|
||||
export type SchemaExtractorLLMParamParser = (llmParam: InputValueDTO[]) => {
|
||||
prompt: string;
|
||||
systemPrompt: string;
|
||||
};
|
||||
|
||||
export type SchemaExtractorIntentsParamParser = (
|
||||
intents: { name: string }[],
|
||||
) => {
|
||||
intent: string;
|
||||
};
|
||||
|
||||
export type SchemaExtractorConcatResultParser = (
|
||||
concatParams: InputValueDTO[],
|
||||
) => string;
|
||||
|
||||
export type SchemaExtractorArrayConcatCharParser = (
|
||||
concatParams: InputValueDTO[],
|
||||
) => string;
|
||||
|
||||
export type SchemaExtractorSplitCharParser = (
|
||||
concatParams: InputValueDTO[],
|
||||
) => string;
|
||||
|
||||
export type SchemaExtractorVariableAssignParser = (
|
||||
variableAssigns: {
|
||||
left: ValueExpressionDTO;
|
||||
right: ValueExpressionDTO;
|
||||
input?: ValueExpressionDTO;
|
||||
}[],
|
||||
) => {
|
||||
name: string;
|
||||
value: string;
|
||||
}[];
|
||||
|
||||
export type SchemaExtractorJSONStringParser = (
|
||||
jsonString: string,
|
||||
) => object | object[] | undefined;
|
||||
|
||||
type ImageReferences = Array<{
|
||||
url: ValueExpressionDTO;
|
||||
}>;
|
||||
|
||||
export type SchemaExtractorImageReferenceParser = (
|
||||
references: ImageReferences,
|
||||
) => ParsedExpression[];
|
||||
|
||||
export type SchemaExtractorExpressionParser = (
|
||||
expression: ValueExpressionDTO[] | ValueExpressionDTO,
|
||||
) => ParsedExpression[];
|
||||
|
||||
export interface VariableMergeGroupType {
|
||||
name: string;
|
||||
variables: ValueExpressionDTO[];
|
||||
}
|
||||
|
||||
export interface ParsedVariableMergeGroups {
|
||||
groupName: string;
|
||||
variables: ParsedExpression[];
|
||||
}
|
||||
|
||||
export type SchemaExtractorVariableMergeGroupsParser = (
|
||||
mergeGroups: VariableMergeGroupType[],
|
||||
) => ParsedVariableMergeGroups[];
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 { type ValueExpressionDTO } from '../../types';
|
||||
|
||||
// 是否我们自己上传生成的url, 这是个临时方案,等修复 schema 中的 type:string -> type:image 后,删掉此逻辑
|
||||
export function isWorkflowImageTypeURL(str: string): boolean {
|
||||
// base64 加工
|
||||
const hostWhiteList = [
|
||||
'cC1ib3Qtd29ya2Zsb3ctc2lnbi5ieXRlZGFuY2UubmV0',
|
||||
'cC1ib3Qtd29ya2Zsb3cuYnl0ZWQub3Jn',
|
||||
'cC1ib3Qtd29ya2Zsb3cuYnl0ZWRhbmNlLm5ldA==',
|
||||
'cDI2LWJvdC13b3JrZmxvdy1zaWduLmJ5dGVpbWcuY29t',
|
||||
'cDMtYm90LXdvcmtmbG93LXNpZ24uYnl0ZWltZy5jb20=',
|
||||
'cDktYm90LXdvcmtmbG93LXNpZ24uYnl0ZWltZy5jb20=',
|
||||
];
|
||||
const suffixWhiteList = ['image', 'jpg', 'jpeg', 'png'];
|
||||
|
||||
let urlObj;
|
||||
|
||||
try {
|
||||
urlObj = new URL(str);
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const suffix =
|
||||
urlObj.searchParams?.get('x-wf-file_name')?.split('.')?.pop() ||
|
||||
urlObj.pathname.split('.').pop();
|
||||
|
||||
if (!suffixWhiteList.includes(suffix)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!hostWhiteList.includes(btoa(urlObj.hostname ?? ''))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export const parseExpression = (expression?: ValueExpressionDTO) => {
|
||||
if (!expression) {
|
||||
return null;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const content = get(expression, 'value.content') as any;
|
||||
if (!content) {
|
||||
return null;
|
||||
} else if (typeof content === 'string') {
|
||||
return {
|
||||
value: content,
|
||||
isImage: isWorkflowImageTypeURL(content),
|
||||
};
|
||||
} else if (content.source === 'block-output' && typeof content.name) {
|
||||
return {
|
||||
value: content.name,
|
||||
isImage: false,
|
||||
};
|
||||
} else if (
|
||||
typeof content.source === 'string' &&
|
||||
content.source.startsWith('global_variable')
|
||||
) {
|
||||
return {
|
||||
value: content?.path?.join('.'),
|
||||
isImage: false,
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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 { reporter as infraReporter } from '@coze-arch/logger';
|
||||
const namespace = 'workflow';
|
||||
|
||||
/**
|
||||
* 流程使用的 slardar 上报实例
|
||||
*/
|
||||
export const reporter = infraReporter.createReporterWithPreset({
|
||||
namespace,
|
||||
});
|
||||
|
||||
/**
|
||||
* 异常捕获,会被当js error上报
|
||||
* @param exception
|
||||
* @param importErrorInfo
|
||||
*/
|
||||
export function captureException(exception: Error) {
|
||||
infraReporter.slardarInstance?.('captureException', exception, {
|
||||
isErrorBoundary: 'false',
|
||||
namespace,
|
||||
});
|
||||
}
|
||||
30
frontend/packages/workflow/base/src/utils/start-params.ts
Normal file
30
frontend/packages/workflow/base/src/utils/start-params.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 { BOT_USER_INPUT, CONVERSATION_NAME, USER_INPUT } from '../constants';
|
||||
/**
|
||||
* 是否预设的开始节点的输入参数
|
||||
*/
|
||||
export const isPresetStartParams = (name?: string): boolean =>
|
||||
[BOT_USER_INPUT, USER_INPUT, CONVERSATION_NAME].includes(name ?? '');
|
||||
|
||||
/**
|
||||
* Start 节点参数是 BOT 聊天时用户的输入内容
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
export const isUserInputStartParams = (name?: string): boolean =>
|
||||
[BOT_USER_INPUT, USER_INPUT].includes(name ?? '');
|
||||
183
frontend/packages/workflow/base/src/utils/traverse.ts
Normal file
183
frontend/packages/workflow/base/src/utils/traverse.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* 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 security/detect-object-injection -- no-need */
|
||||
/* eslint-disable @typescript-eslint/no-namespace -- no-need */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- no-need
|
||||
export type TraverseValue = any;
|
||||
export interface TraverseNode {
|
||||
value: TraverseValue;
|
||||
container?: TraverseValue;
|
||||
parent?: TraverseNode;
|
||||
key?: string;
|
||||
index?: number;
|
||||
}
|
||||
|
||||
export interface TraverseContext {
|
||||
node: TraverseNode;
|
||||
setValue: (value: TraverseValue) => void;
|
||||
getParents: () => TraverseNode[];
|
||||
getPath: () => Array<string | number>;
|
||||
getStringifyPath: () => string;
|
||||
deleteSelf: () => void;
|
||||
}
|
||||
|
||||
export type TraverseHandler = (context: TraverseContext) => void;
|
||||
|
||||
/**
|
||||
* 深度遍历对象,对每个值做处理
|
||||
* @param value 遍历对象
|
||||
* @param handle 处理函数
|
||||
*/
|
||||
export const traverse = <T extends TraverseValue = TraverseValue>(
|
||||
value: T,
|
||||
handler: TraverseHandler | TraverseHandler[],
|
||||
): T => {
|
||||
const traverseHandler: TraverseHandler = Array.isArray(handler)
|
||||
? (context: TraverseContext) => {
|
||||
handler.forEach(handlerFn => handlerFn(context));
|
||||
}
|
||||
: handler;
|
||||
TraverseUtils.traverseNodes({ value }, traverseHandler);
|
||||
return value;
|
||||
};
|
||||
|
||||
namespace TraverseUtils {
|
||||
/**
|
||||
* 深度遍历对象,对每个值做处理
|
||||
* @param node 遍历节点
|
||||
* @param handle 处理函数
|
||||
*/
|
||||
export const traverseNodes = (
|
||||
node: TraverseNode,
|
||||
handle: TraverseHandler,
|
||||
): void => {
|
||||
const { value } = node;
|
||||
if (!value) {
|
||||
// 异常处理
|
||||
return;
|
||||
}
|
||||
if (Object.prototype.toString.call(value) === '[object Object]') {
|
||||
// 对象,遍历对象的每个属性
|
||||
Object.entries(value).forEach(([key, item]) =>
|
||||
traverseNodes(
|
||||
{
|
||||
value: item,
|
||||
container: value,
|
||||
key,
|
||||
parent: node,
|
||||
},
|
||||
handle,
|
||||
),
|
||||
);
|
||||
} else if (Array.isArray(value)) {
|
||||
// 数组,遍历数组的每个元素
|
||||
// 从数组的末尾开始遍历,这样即使中途移除了某个元素,也不会影响到未处理的元素的索引
|
||||
for (let index = value.length - 1; index >= 0; index--) {
|
||||
const item: string = value[index];
|
||||
traverseNodes(
|
||||
{
|
||||
value: item,
|
||||
container: value,
|
||||
index,
|
||||
parent: node,
|
||||
},
|
||||
handle,
|
||||
);
|
||||
}
|
||||
}
|
||||
const context: TraverseContext = createContext({ node });
|
||||
handle(context);
|
||||
};
|
||||
|
||||
const createContext = ({
|
||||
node,
|
||||
}: {
|
||||
node: TraverseNode;
|
||||
}): TraverseContext => ({
|
||||
node,
|
||||
setValue: (value: unknown) => setValue(node, value),
|
||||
getParents: () => getParents(node),
|
||||
getPath: () => getPath(node),
|
||||
getStringifyPath: () => getStringifyPath(node),
|
||||
deleteSelf: () => deleteSelf(node),
|
||||
});
|
||||
|
||||
const setValue = (node: TraverseNode, value: unknown) => {
|
||||
// 设置值函数
|
||||
// 引用类型,需要借助父元素修改值
|
||||
// 由于是递归遍历,所以需要根据node来判断是给对象的哪个属性赋值,还是给数组的哪个元素赋值
|
||||
if (!value || !node) {
|
||||
return;
|
||||
}
|
||||
node.value = value;
|
||||
// 从上级作用域node中取出container,key,index
|
||||
const { container, key, index } = node;
|
||||
if (key && container) {
|
||||
container[key] = value;
|
||||
} else if (typeof index === 'number') {
|
||||
container[index] = value;
|
||||
}
|
||||
};
|
||||
|
||||
const getParents = (node: TraverseNode): TraverseNode[] => {
|
||||
const parents: TraverseNode[] = [];
|
||||
let currentNode: TraverseNode | undefined = node;
|
||||
while (currentNode) {
|
||||
parents.unshift(currentNode);
|
||||
currentNode = currentNode.parent;
|
||||
}
|
||||
return parents;
|
||||
};
|
||||
|
||||
const getPath = (node: TraverseNode): Array<string | number> => {
|
||||
const path: Array<string | number> = [];
|
||||
const parents = getParents(node);
|
||||
parents.forEach(parent => {
|
||||
if (parent.key) {
|
||||
path.unshift(parent.key);
|
||||
} else if (parent.index) {
|
||||
path.unshift(parent.index);
|
||||
}
|
||||
});
|
||||
return path;
|
||||
};
|
||||
|
||||
const getStringifyPath = (node: TraverseNode): string => {
|
||||
const path = getPath(node);
|
||||
return path.reduce((stringifyPath: string, pathItem: string | number) => {
|
||||
if (typeof pathItem === 'string') {
|
||||
const re = /\W/g;
|
||||
if (re.test(pathItem)) {
|
||||
// 包含特殊字符
|
||||
return `${stringifyPath}["${pathItem}"]`;
|
||||
}
|
||||
return `${stringifyPath}.${pathItem}`;
|
||||
} else {
|
||||
return `${stringifyPath}[${pathItem}]`;
|
||||
}
|
||||
}, '');
|
||||
};
|
||||
|
||||
const deleteSelf = (node: TraverseNode): void => {
|
||||
const { container, key, index } = node;
|
||||
if (key && container) {
|
||||
delete container[key];
|
||||
} else if (typeof index === 'number') {
|
||||
container.splice(index, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user