feat: manually mirror opencoze's code from bytedance

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

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { nanoid } from 'nanoid';
import { ViewVariableType } from '@coze-workflow/base';
import {
OutputTree,
type OutputTreeProps,
} from '@/form-extensions/components/output-tree';
import { useField, withField } from '@/form';
import { ChatHistoryRound } from '@/components/chat-history-round';
const DEFAULT_VALUE = [
{
key: nanoid(),
name: 'chatHistory',
type: ViewVariableType.ArrayObject,
children: [
{
key: nanoid(),
name: 'role',
type: ViewVariableType.String,
},
{
key: nanoid(),
name: 'content',
type: ViewVariableType.String,
},
],
},
] as OutputTreeProps['value'];
export const HistoryRoundField = withField(
({ showLine }: { showLine: boolean }) => {
const { value, onChange, readonly } = useField<number>();
return (
<div className="relative">
<OutputTree
id="chat-history"
readonly
value={DEFAULT_VALUE}
defaultCollapse
// eslint-disable-next-line @typescript-eslint/no-empty-function
onChange={() => {}}
withDescription={false}
withRequired={false}
noCard
/>
{showLine ? (
<div className="h-px -mt-[3px] mb-[14px] bg-[#FFF]" />
) : null}
<ChatHistoryRound
value={value}
readonly={readonly}
onChange={w => {
onChange(Number(w));
}}
/>
</div>
);
},
);

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { useNodeTestId } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { Switch, Tooltip } from '@coze-arch/coze-design';
import { useField, withField } from '@/form';
export const HistorySwitchField = withField(() => {
const { name, value, onChange, readonly } = useField<boolean>();
const { getNodeSetterId } = useNodeTestId();
return (
<Tooltip content={I18n.t('wf_chatflow_125')} position="right">
<div className="flex items-center gap-1">
<div className={'text-[12px]'}>{I18n.t('wf_chatflow_124')}</div>
<Switch
size="mini"
checked={value}
data-testid={getNodeSetterId(name)}
onChange={checked => {
onChange?.(checked);
}}
disabled={readonly}
/>
</div>
</Tooltip>
);
});

View File

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

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React, { type PropsWithChildren } from 'react';
import {
useNodeTestId,
type InputValueVO,
type ViewVariableType,
} from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { getFlags } from '@coze-arch/bot-flags';
import { ValueExpressionInputField } from '@/node-registries/common/fields';
import { useGlobalState } from '@/hooks';
import {
Section,
useFieldArray,
ColumnTitles,
FieldArrayList,
FieldArrayItem,
withFieldArray,
useWatch,
} from '@/form';
import { COLUMNS } from '../constants';
import { HistorySwitchField } from './history-switch-field';
import { HistoryRoundField } from './history-round-field';
interface InputsProps {
inputType?: ViewVariableType;
disabledTypes?: ViewVariableType[];
}
export const Inputs = withFieldArray(
({ inputType, disabledTypes }: InputsProps & PropsWithChildren) => {
const { name: fieldName, value } = useFieldArray<InputValueVO>();
const safeValue = value || [];
const { getNodeSetterId } = useNodeTestId();
const { isChatflow } = useGlobalState();
const enableChatHistory = useWatch<boolean>(
'inputs.historySetting.enableChatHistory',
);
const FLAGS = getFlags();
return (
<Section
title={I18n.t('workflow_detail_node_parameter_input')}
tooltip={I18n.t('ltm_240826_01')}
testId={getNodeSetterId(fieldName)}
actions={[
// 社区版暂不支持该功能
isChatflow && FLAGS['bot.automation.ltm_enhance'] ? (
<HistorySwitchField name="inputs.historySetting.enableChatHistory" />
) : null,
]}
>
<ColumnTitles columns={COLUMNS} />
<FieldArrayList>
{safeValue?.map(({ name }, index) => (
<FieldArrayItem hiddenRemove>
<ValueExpressionInputField
key={index}
label={name}
required
inputType={inputType}
disabledTypes={disabledTypes}
name={`${fieldName}.${index}.input`}
/>
</FieldArrayItem>
))}
</FieldArrayList>
{isChatflow ? (
<div className="mt-[4px]">
{enableChatHistory ? (
<HistoryRoundField
name="inputs.historySetting.chatHistoryRound"
showLine
/>
) : null}
</div>
) : null}
</Section>
);
},
);

View File

@@ -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 { nanoid } from 'nanoid';
import { ViewVariableType } from '@coze-workflow/variable';
import { I18n } from '@coze-arch/i18n';
// 入参路径,试运行等功能依赖该路径提取参数
export const INPUT_PATH = 'inputs.inputParameters';
// 定义固定出参
export const OUTPUTS = [
{
key: nanoid(),
name: 'outputList',
type: ViewVariableType.ArrayObject,
children: [
{
key: nanoid(),
name: 'output',
type: ViewVariableType.String,
},
{
key: nanoid(),
name: 'date',
type: ViewVariableType.String,
},
],
},
];
export const COLUMNS = [
{
label: I18n.t('workflow_detail_node_parameter_name'),
style: { width: 148 },
},
{ label: I18n.t('workflow_detail_end_output_value') },
];

View File

@@ -0,0 +1,113 @@
/*
* 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 {
ValueExpressionType,
type LiteralExpression,
ViewVariableType,
} from '@coze-workflow/base';
import { getFlags } from '@coze-arch/bot-flags';
import { type FormData, type DTODataWhenInit } from './types';
import { OUTPUTS } from './constants';
const HISTORY_FIELDS = ['enableChatHistory', 'chatHistoryRound'];
const DEFAULT_VALUE = [
{ name: 'Query', input: { type: ValueExpressionType.REF } },
];
/**
* 节点后端数据 -> 前端表单数据
*/
export const transformOnInit = (value: DTODataWhenInit): FormData => {
// 拖入节点value 为 null
// 创建副本value.inputs.inputParameters 数组为空
const inputParameters = value?.inputs?.inputParameters ?? [];
const inputParametersWithNoHistoryFields = inputParameters.filter(
v => !HISTORY_FIELDS.includes(v.name || ''),
);
const historySwitchItem = inputParameters.find(
v => v.name === 'enableChatHistory',
)?.input as LiteralExpression;
const historyRoundItem = inputParameters.find(
v => v.name === 'chatHistoryRound',
)?.input as LiteralExpression;
return {
nodeMeta: value?.nodeMeta,
inputs: {
inputParameters:
inputParametersWithNoHistoryFields.length === 0
? DEFAULT_VALUE
: inputParametersWithNoHistoryFields,
// 历史会话设置
historySetting: {
// 是否开启历史会话
enableChatHistory: Boolean(historySwitchItem?.content),
// 历史会话轮数,默认为 3
chatHistoryRound: (historyRoundItem?.content as number) ?? 3,
},
},
outputs: value?.outputs ?? OUTPUTS,
};
};
/**
* 前端表单数据 -> 节点后端数据
* @param value
* @returns
*/
export const transformOnSubmit = (value: FormData): DTODataWhenInit => {
const { inputParameters, historySetting } = value.inputs;
if (getFlags()['bot.automation.ltm_enhance']) {
inputParameters.push({
name: 'enableChatHistory',
input: {
content: historySetting.enableChatHistory,
type: ValueExpressionType.LITERAL,
rawMeta: {
type: ViewVariableType.Boolean,
},
},
});
inputParameters.push({
name: 'chatHistoryRound',
input: {
content: historySetting.chatHistoryRound,
type: ValueExpressionType.LITERAL,
rawMeta: {
type: ViewVariableType.Integer,
},
},
});
}
const result = {
nodeMeta: value?.nodeMeta,
inputs: {
inputParameters,
},
outputs: value?.outputs,
};
return result;
};

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
ValidateTrigger,
type FormMetaV2,
} from '@flowgram-adapter/free-layout-editor';
import { nodeMetaValidate } from '@/nodes-v2/materials/node-meta-validate';
import { createValueExpressionInputValidate } from '@/node-registries/common/validators';
import {
fireNodeTitleChange,
provideNodeOutputVariablesEffect,
} from '@/node-registries/common/effects';
import { type FormData } from './types';
import { FormRender } from './form';
import { transformOnInit, transformOnSubmit } from './data-transformer';
export const LTM_FORM_META: FormMetaV2<FormData> = {
// 节点表单渲染
render: () => <FormRender />,
// 验证触发时机
validateTrigger: ValidateTrigger.onChange,
// 验证规则
validate: {
nodeMeta: nodeMetaValidate,
// 必填
'inputs.inputParameters.0.input': createValueExpressionInputValidate({
required: true,
}),
},
// 副作用管理
effect: {
nodeMeta: fireNodeTitleChange,
outputs: provideNodeOutputVariablesEffect,
},
// 节点后端数据 -> 前端表单数据
formatOnInit: transformOnInit,
// 前端表单数据 -> 节点后端数据
formatOnSubmit: transformOnSubmit,
};

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ViewVariableType } from '@coze-workflow/base';
import { I18n } from '@coze-arch/i18n';
import { NodeConfigForm } from '@/node-registries/common/components';
import { OutputsField } from '../common/fields';
import { INPUT_PATH } from './constants';
import { Inputs } from './components';
export const FormRender = () => (
<NodeConfigForm>
<div className="relative">
<Inputs
name={INPUT_PATH}
inputType={ViewVariableType.String}
disabledTypes={ViewVariableType.getComplement([
ViewVariableType.String,
])}
defaultValue={[{ name: 'Query' }]}
/>
</div>
<OutputsField
title={I18n.t('workflow_detail_node_output')}
tooltip={I18n.t('ltm_240826_02')}
id="ltm-node-outputs"
name="outputs"
topLevelReadonly={true}
customReadonly
/>
</NodeConfigForm>
);

View File

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

View File

@@ -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 { InputParameters, Outputs } from '../common/components';
export function LtmContent() {
return (
<>
<InputParameters />
<Outputs />
</>
);
}

View File

@@ -0,0 +1,44 @@
/*
* 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 {
DEFAULT_NODE_META_PATH,
DEFAULT_OUTPUTS_PATH,
} from '@coze-workflow/nodes';
import {
StandardNodeType,
type WorkflowNodeRegistry,
} from '@coze-workflow/base';
import { type NodeTestMeta } from '@/test-run-kit';
import { test } from './node-test';
import { LTM_FORM_META } from './form-meta';
import { INPUT_PATH } from './constants';
export const LTM_NODE_REGISTRY: WorkflowNodeRegistry<NodeTestMeta> = {
type: StandardNodeType.LTM,
meta: {
nodeDTOType: StandardNodeType.LTM,
size: { width: 360, height: 130.7 },
nodeMetaPath: DEFAULT_NODE_META_PATH,
outputsPath: DEFAULT_OUTPUTS_PATH,
inputParametersPath: INPUT_PATH, // 入参路径,试运行等功能依赖该路径提取参数
test,
helpLink: '/open/docs/guides/ltm_node',
},
formMeta: LTM_FORM_META,
};

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { I18n } from '@coze-arch/i18n';
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import {
generateParametersToProperties,
generateEnvToRelatedContextProperties,
} from '@/test-run-kit';
import { type NodeTestMeta } from '@/test-run-kit';
export const test: NodeTestMeta = {
generateRelatedContext(_, context) {
const { isInProject } = context;
if (isInProject) {
return {};
}
return generateEnvToRelatedContextProperties({
isNeedBot: true,
hasLTMNode: true,
disableProject: true,
disableProjectTooltip: I18n.t('wf_chatflow_142'),
});
},
generateFormInputProperties(node) {
const formData = node
.getData(FlowNodeFormData)
.formModel.getFormItemValueByPath('/');
const inputParameters = formData?.inputs.inputParameters;
return generateParametersToProperties(inputParameters, { node });
},
};

View File

@@ -0,0 +1,49 @@
/*
* 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 ViewVariableMeta,
type InputValueVO,
type InputValueDTO,
type VariableMetaDTO,
} from '@coze-workflow/base';
import type { NodeMeta } from '@/typing';
export interface FormData {
nodeMeta: NodeMeta;
inputs: {
inputParameters: InputValueVO[];
historySetting: {
enableChatHistory: boolean;
chatHistoryRound: number;
};
};
outputs: ViewVariableMeta[];
}
export interface DTOData<
InputType = InputValueDTO,
OutputType = VariableMetaDTO,
> {
nodeMeta: NodeMeta;
inputs: {
inputParameters?: InputType[];
};
outputs: OutputType[];
}
export type DTODataWhenInit = DTOData<InputValueVO, ViewVariableMeta>;