chore: replace all cn comments of fe to en version by volc api (#320)

This commit is contained in:
tecvan
2025-07-31 10:32:15 +08:00
committed by GitHub
parent 716ec0cba8
commit 71f6245a01
2960 changed files with 15545 additions and 15545 deletions

View File

@@ -23,5 +23,5 @@ export enum FileItemStatus {
Wait = 'wait',
}
// 支持预览的图片类型
// Image types that support preview
export const PREVIEW_IMAGE_TYPE = ['jpg', 'jpeg', 'png', 'webp', 'svg'];

View File

@@ -119,7 +119,7 @@ export const getIconByExtension = (extension: string) => {
return fileIcon;
};
/** 获取文件名后缀 */
/** Get filename suffix */
export function getFileExtension(name?: string) {
if (!name) {
return '';

View File

@@ -155,13 +155,13 @@ export const FileUpload: React.FC<FileUploadProps> = props => {
const handleChange = useMemoizedFn(val => onChange?.(val));
// 当fileList更新时触发onChange
// When the fileList is updated, onChange is triggered.
useUpdateEffect(() => {
const newVal = getSubmitValue();
handleChange?.(newVal);
}, [fileList]);
// 当表单值更新时,同步到fileList
// When the form value is updated, sync to fileList
useEffect(() => {
const val = getSubmitValue();
if (val !== value) {

View File

@@ -17,13 +17,13 @@
import type { FileItemStatus } from '../file-icon';
export interface FileItem extends File {
// 唯一标识
// unique identifier
uid?: string;
// 文件地址
// File address
url?: string;
// 上传进度
// upload progress
percent?: number;
// 校验信息
// verification information
validateMessage?: string;
status?: FileItemStatus;
[key: string]: any;

View File

@@ -99,10 +99,10 @@ export const useUpload = (props?: UploadConfig) => {
throw new CustomError('normal_error', 'no uri');
}
// 上传完成,清空超时计时器
// Upload complete, clear timeout timer
clearTimeout(progressTimer);
// 加签uri获得url
// Add uri and get the url.
const { url } = await workflowApi.SignImageURL(
{
uri,

View File

@@ -30,9 +30,9 @@ interface UploadValidateRule {
}
/**
* 格式化文件大小
* @param bytes 文件大小
* @param decimals 小数位数, 默认 2 位
* Format file size
* @param bytes file size
* @Param decimals, default 2 digits
* @example
* formatBytes(1024); // 1KB
* formatBytes('1024'); // 1KB
@@ -50,7 +50,7 @@ export function formatBytes(bytes: number, decimals = 2) {
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))}${sizes[i]}`;
}
/** 文件大小校验 */
/** file size verification */
export const sizeValidate = (
size: number,
maxSize: number = MAX_FILE_SIZE,
@@ -71,7 +71,7 @@ export interface ImageSizeRule {
}
/**
* 获取图片的宽高
* Get the width and height of the image
*/
export async function getImageSize(
file: FileItem,
@@ -93,7 +93,7 @@ export async function getImageSize(
});
}
/** 图像宽高校验 */
/** image width check */
// eslint-disable-next-line complexity
export const imageSizeValidate = async (
file: FileItem,
@@ -101,7 +101,7 @@ export const imageSizeValidate = async (
): Promise<string | undefined> => {
const { maxWidth, minWidth, maxHeight, minHeight, aspectRatio } = rule || {};
// 未定义时不校验
// No validation when undefined
if (isNil(maxWidth || minWidth || maxHeight || minHeight || aspectRatio)) {
return;
}
@@ -145,7 +145,7 @@ export const acceptValidate = (fileName: string, accept?: string) => {
const fileExtension = getFileExtension(fileName);
const mimeType = mime.lookup(fileExtension);
// image/* 匹配所有的图片类型
// Image/* matches all image types
if (acceptList.includes('image/*') && mimeType?.startsWith?.('image/')) {
return undefined;
}

View File

@@ -15,7 +15,7 @@
*/
/**
* test run test form 布局的 FormItem
* Test run test form layout FormItem
*/
import React, { type FC, type ReactNode, type PropsWithChildren } from 'react';

View File

@@ -33,7 +33,7 @@ import css from './full-input.module.less';
export interface InnerFullInputProps {
value?: string;
disabled?: boolean;
/** 是否可以展开,默认 true */
/** Whether it can be expanded, the default is true. */
expand?: boolean;
className?: string;
onChange: (v?: string) => void;
@@ -66,7 +66,7 @@ const InnerFullInputAdapter: React.FC<FullInputProps> = ({
const content = editorRef.current.getContent();
const { markdown } = delta2md(content.deltas[0], content.deltas);
/**
* change 可能来自用户输入或者初始化,做一下 diff 来保证性能
* Changes may come from user input or initialization, do a diff to ensure performance
*/
if (markdown !== innerValueRef.current) {
innerValueRef.current = markdown;

View File

@@ -36,11 +36,11 @@ export interface InputNumberV2Props {
onChange?: (v?: string) => void;
onBlur?: () => void;
onFocus?: () => void;
/** 整型 */
/** integer */
int?: boolean;
}
/** 是否是合法的数字字符串 */
/** Is it a legal numeric string? */
function isValidNumber(str: string) {
try {
const value = new BigNumber(str);
@@ -73,18 +73,18 @@ export const InputNumberV2Adapter: React.FC<InputNumberV2Props> = ({
const handleBlur = () => {
if (props.value === '' || props.value === undefined) {
/** 失焦时若值为空,则同时清空验证值 */
/** If the value is empty when out of focus, the verification value is also cleared */
verifiedRef.current = undefined;
if (props.value === '') {
// 如果是空字符串需要主动转换为 undefined
// If it is an empty string, it needs to be actively converted to undefined.
onChange?.(undefined);
}
} else {
/** 失焦时若值不为空,则需要验证值的合法性 */
/** If the value is not empty when out of focus, you need to verify the legitimacy of the value */
/**
* 1. 若值本身合法,则对值做格式化
* 2. 若值不合法,则采纳最近一次的合法值
* 3. 若都没有,则返回 undefined
* 1. If the value itself is legal, format the value
* 2. If the value is not legal, the most recent legal value is adopted
* 3. If none, return undefined
*/
let next: undefined | string;
const nextBig = normalizeNumber(props.value) || verifiedRef.current;
@@ -116,7 +116,7 @@ export const InputNumberV2Adapter: React.FC<InputNumberV2Props> = ({
onChange?.(next);
};
/** 当值发生变化,需要把值同步到合法数字 */
/** When the value changes, you need to synchronize the value to a legal number */
useEffect(() => {
if (props.value === '' || props.value === undefined) {
verifiedRef.current = undefined;

View File

@@ -22,7 +22,7 @@ export { FormPanelLayout } from './form-panel';
export { TraceIconButton, BaseTestButton } from './test-button';
export { ResizablePanel } from './resizable-panel';
export { BasePanel } from './resizable-panel/base-panel';
// 禁止直接导出 form-engine 避免 formily 包被打到首屏
// Prohibit direct export of form-engine to avoid formily packages being hit to the first screen
// export { FormCore } from './form-engine';
export { NodeEventInfo } from './node-event-info';

View File

@@ -37,7 +37,7 @@ export default function useGetCurrentResult({
const isNodeLogNeedAsync = true;
const { batch, isBatch } = result || {};
// 反序列化获取所有遍历数组
// Deserialize to get all iterated arrays
const batchData: NodeResult[] = useMemo(() => {
if (!isBatch) {
return [];
@@ -49,13 +49,13 @@ export default function useGetCurrentResult({
}
return {
...i,
/** batch 数据里面不包含该标记,手动增加 */
/** The tag is not included in the batch data, and it is added manually. */
isBatch: true,
};
});
}, [isBatch, batch]);
// 当前执行日志(同步获取完整日志)
// Current execution log (get full log synchronously)
const current: NodeResult | undefined = useMemo(() => {
if (!isBatch) {
return result;

View File

@@ -15,6 +15,6 @@
*/
/**
* 解析运行结果的组件
* Components that parse run results
*/
export { LogDetail } from './log-detail';

View File

@@ -52,9 +52,9 @@ export const LogDetail: React.FC<LogDetailProps> = ({
onOpenWorkflowLink,
}) => {
const { isBatch, nodeId } = result;
/** 从 0 开始 */
/** Start from 0 */
const [paging, setPaging] = useState(0);
/** 只看错误 */
/** Just look at the error. */
const [onlyShowError, setOnlyShowError] = useState(false);
const { current, batchData } = useGetCurrentResult({
@@ -73,14 +73,14 @@ export const LogDetail: React.FC<LogDetailProps> = ({
const { modal, open } = useMarkdownModal();
// 当分页数据发生变化,重新选中第一项
// When the paging data changes, re-select the first item
useLayoutEffect(() => {
setPaging(0);
}, [batchData]);
return (
<div className={css['log-detail']}>
{/* 分页 */}
{/* paging */}
{isBatch ? (
<LogDetailPagination
paging={paging}

View File

@@ -17,7 +17,7 @@
import { type NodeResult } from '@coze-workflow/base';
/**
* log images 业务逻辑太重了,本期暂不抽
* Log images business logic is too heavy, this period will not draw
*/
export type LogImages = React.FC<{
testRunResult: NodeResult;

View File

@@ -15,7 +15,7 @@
*/
/**
* 批处理的分页器
* Batch pager
*/
import React, { useMemo } from 'react';

View File

@@ -37,17 +37,17 @@ import s from './more-selector.module.less';
interface MoreSelectorProps {
/**
* 运行结果数组
* Run result array
*/
data: (NodeResult | null)[];
/** 当前选择索引 */
/** current selection index */
paging: number;
fixedCount: number;
/** placeholder */
placeholder: string;
/** 选择索引变更事件 */
/** Select index change event */
onChange: (p: number) => void;
}

View File

@@ -39,14 +39,14 @@ export const PageSelector: React.FC<PageSelectorProps> = ({
data,
onChange,
}) => {
// 固定展示的条目,最大为 10 条,不到 10 条按实际展示
// Fixed display items, the maximum is 10, less than 10 items are displayed according to the actual display
const fixedItems = useMemo(
() => data.slice(0, fixedCount),
[fixedCount, data],
);
const moreItems = useMemo(() => data.slice(fixedCount), [data]);
// 是否需要通过下拉框展示更多
// Do you need to show more through the drop-down box?
const hasMore = useMemo(() => data.length > fixedCount, [data, fixedCount]);
return (

View File

@@ -28,19 +28,19 @@ import styles from './base-panel.module.less';
interface BasePanelProps {
className?: string;
/**
* 面板头,不传不渲染
* Panel header, no pass and no render
*/
header?: React.ReactNode;
/**
* 面板脚,不传不渲染
* Panel foot, do not pass and do not render
*/
footer?: React.ReactNode;
/**
* 默认初始高度,不支持响应式
* Default initial height, does not support responsive
*/
height?: number;
/**
* 是否可拖拽改变高度
* Can you drag and drop to change the height?
*/
resizable?:
| boolean
@@ -49,7 +49,7 @@ interface BasePanelProps {
max?: number;
};
/**
* 点击关闭事件,仅当渲染面板头时可能触发
* Click to close the event, which may only be triggered when rendering the panel header
*/
onClose?: () => void;
}

View File

@@ -56,7 +56,7 @@ export interface ResizablePanelRef {
const MIN_HEIGHT = 156;
/**
* TODO: 这里的核心伸缩能力后面想换成 semi Resizable,这里先临时写一些逻辑适配
* TODO: The core telescopic ability here wants to be replaced by semi Resizable later. Here are some temporary logical adaptations.
*/
export const ResizablePanel = forwardRef<
ResizablePanelRef,
@@ -95,7 +95,7 @@ export const ResizablePanel = forwardRef<
const handleMouseMove = useCallback(
e => {
if (isResizing.current) {
const newHeight = startHeight.current - (e.clientY - startY.current); // 计算新的高度
const newHeight = startHeight.current - (e.clientY - startY.current); // Calculate the new height
setHeight(newHeight > MIN_HEIGHT ? newHeight : MIN_HEIGHT);
}
},
@@ -103,17 +103,17 @@ export const ResizablePanel = forwardRef<
);
const handleMouseUp = useCallback(() => {
isResizing.current = false;
document.removeEventListener('mousemove', handleMouseMove); // 取消监听
document.removeEventListener('mouseup', handleMouseUp); // 取消监听
document.removeEventListener('mousemove', handleMouseMove); // Cancel listening
document.removeEventListener('mouseup', handleMouseUp); // Cancel listening
}, [handleMouseMove]);
const handleMouseDown = useCallback(
e => {
isResizing.current = true;
startY.current = e.clientY; // 记录鼠标开始拖拽时的 Y 轴坐标
startY.current = e.clientY; // Record the Y-axis coordinates when the mouse starts dragging
startHeight.current = innerRef.current?.offsetHeight || 0;
document.addEventListener('mousemove', handleMouseMove); // 监听鼠标移动事件
document.addEventListener('mouseup', handleMouseUp); // 监听鼠标抬起事件
document.addEventListener('mousemove', handleMouseMove); // Monitor mouse movement events
document.addEventListener('mouseup', handleMouseUp); // Monitor mouse lift events
},
[handleMouseMove, handleMouseUp],
);

View File

@@ -72,7 +72,7 @@
.resizable-panel-slide {
position: absolute;
bottom: 0;
right: -100%; /* 初始位置在容器的右外侧 */
right: -100%; /* The initial position is on the right outside of the container */
overflow: hidden;

View File

@@ -25,26 +25,26 @@ interface Config {
}
/**
* 目前仅支持高度可变
* Currently only highly variable is supported
*/
export const useResize = (config: Config) => {
const [dragging, setDragging] = useState(false);
const [height, setHeight] = useState(config.default);
const ref = useRef<HTMLDivElement>(null);
/**
* 拖拽过程中
* Dragging process
*/
const resizing = useRef(false);
/**
* y 轴变化
* Y-axis variation
*/
const startY = useRef(0);
/** 开始位置 */
/** starting position */
const start = useRef(0);
const handleMouseMove = useMemoizedFn(e => {
if (resizing.current) {
const newHeight = start.current - (e.clientY - startY.current); // 计算新的高度
const newHeight = start.current - (e.clientY - startY.current); // Calculate the new height
if (config.max && newHeight > config.max) {
setHeight(config.max);
} else if (config.min && newHeight < config.min) {
@@ -57,17 +57,17 @@ export const useResize = (config: Config) => {
const handleMouseUp = useCallback(() => {
resizing.current = false;
setDragging(false);
document.removeEventListener('mousemove', handleMouseMove); // 取消监听
document.removeEventListener('mouseup', handleMouseUp); // 取消监听
document.removeEventListener('mousemove', handleMouseMove); // Cancel listening
document.removeEventListener('mouseup', handleMouseUp); // Cancel listening
}, [handleMouseMove]);
const handleMouseDown = useMemoizedFn(e => {
resizing.current = true;
setDragging(true);
startY.current = e.clientY; // 记录鼠标开始拖拽时的 Y 轴坐标
startY.current = e.clientY; // Record the Y-axis coordinates when the mouse starts dragging
start.current = ref.current?.offsetHeight || 0;
document.addEventListener('mousemove', handleMouseMove); // 监听鼠标移动事件
document.addEventListener('mouseup', handleMouseUp); // 监听鼠标抬起事件
document.addEventListener('mousemove', handleMouseMove); // Monitor mouse movement events
document.addEventListener('mouseup', handleMouseUp); // Monitor mouse lift events
});
return {

View File

@@ -132,7 +132,7 @@ export const ChatFlowTestsetEditForm: React.FC<TestsetEditFormProps> = ({
ipt.value = val;
}
// 清除 object/array的空值,包括空字符串
// Clears null values of objects/arrays, including empty strings
if (
!val &&
(ipt.type === FormItemSchemaType.LIST ||
@@ -141,7 +141,7 @@ export const ChatFlowTestsetEditForm: React.FC<TestsetEditFormProps> = ({
ipt.value = undefined;
}
// bool 类型 需要将枚举转为布尔值
// Bool type, you need to convert the enumeration to a boolean
if (ipt.type === FormItemSchemaType.BOOLEAN) {
ipt.value = transTestsetBoolSelect2Bool(
ipt.value as TestsetFormValuesForBoolSelect,
@@ -184,7 +184,7 @@ export const ChatFlowTestsetEditForm: React.FC<TestsetEditFormProps> = ({
// });
// formRef.current?.setValues(formValues);
// // 设置值之后再校验一次
// //Check again after setting the value
// formRef.current?.validate(validateFields);
// },
// [formRef],

View File

@@ -48,10 +48,10 @@ import {
import styles from './edit-form-section.module.less';
/** 整数类型表单精度 */
/** integer type form precision */
const INTEGER_PRECISION = 0.1;
/** bot field name 固定值 */
/** Bot field name fixed value */
const BOT_FIELD_NAME = `${TESTSET_BOT_NAME}_${ComponentType.CozeVariableBot}`;
interface EditFormSectionTitleProps {
@@ -62,7 +62,7 @@ const EditFormSectionTitle: React.FC<EditFormSectionTitleProps> = ({
schema,
}) => {
const title = useMemo(() => {
// 目前只有start和variable两种节点
// Currently only two nodes are start and variable
switch (schema.component_type) {
case ComponentType.CozeStartNode:
return I18n.t('workflow_testset_start_node');
@@ -255,8 +255,8 @@ export const EditFormSection: React.FC<EditFormSectionProps> = ({
const botFieldValue = useFieldState(BOT_FIELD_NAME);
// 判断是否选择到了应用,只有选中应用才会回显对话组件。
// 应用内直接默认选中应用,可以回显对话组件。
// Determine whether the application is selected, and only the selected application will echo the dialog component.
// The application is directly selected by default in the application, and the dialog component can be echoed.
const isBotSelectProject =
(botFieldValue?.value?.id &&
botFieldValue?.value?.type === IntelligenceType.Project) ||
@@ -272,7 +272,7 @@ export const EditFormSection: React.FC<EditFormSectionProps> = ({
if (projectId && i.type === FormItemSchemaType.BOT) {
return false;
}
// 对话组件只会在应用内存在
// The dialog component will only exist within the app
if (!isBotSelectProject && i.type === FormItemSchemaType.CHAT) {
return false;
}

View File

@@ -149,7 +149,7 @@ export const TestsetEditForm: React.FC<TestsetEditFormProps> = ({ data }) => {
ipt.value = val;
}
// 清除 object/array的空值,包括空字符串
// Clears null values of objects/arrays, including empty strings
if (
!val &&
(ipt.type === FormItemSchemaType.LIST ||
@@ -158,7 +158,7 @@ export const TestsetEditForm: React.FC<TestsetEditFormProps> = ({ data }) => {
ipt.value = undefined;
}
// bool 类型 需要将枚举转为布尔值
// Bool type, you need to convert the enumeration to a boolean
if (ipt.type === FormItemSchemaType.BOOLEAN) {
ipt.value = transTestsetBoolSelect2Bool(
ipt.value as TestsetFormValuesForBoolSelect,
@@ -201,7 +201,7 @@ export const TestsetEditForm: React.FC<TestsetEditFormProps> = ({ data }) => {
});
formRef.current?.setValues(formValues);
// 设置值之后再校验一次
// Check again after setting the value
formRef.current?.validate(validateFields);
},
[formRef],

View File

@@ -31,7 +31,7 @@ function count(val: unknown) {
return val ? `${val}`.length : 0;
}
/** 需要后缀 & blur trim,扩展下原始的input */
/** The suffix & blur trim is required to expand the original input */
function InnerInput(props: InputProps) {
const onBlur = (evt: FocusEvent<HTMLInputElement>) => {
props.onChange?.(

View File

@@ -46,7 +46,7 @@ export const useEditFormSchemas = (testset?: CaseDataDetail | null) => {
[]) as NodeFormSchema[];
if (localSchemas.length) {
// 编辑模式比对本地和远程schema并尝试赋值
// Edit schema: compare local and remote schemas and try to assign values
const localSchemaMap: Record<string, FormItemSchema | undefined> = {};
traverseTestsetNodeFormSchemas(
localSchemas,
@@ -66,7 +66,7 @@ export const useEditFormSchemas = (testset?: CaseDataDetail | null) => {
}
});
} else {
// 创建模式:赋默认值
// Creation mode: assigns default values
traverseTestsetNodeFormSchemas(remoteSchemas, (schema, ipt) => {
assignTestsetFormDefaultValue(ipt);
});

View File

@@ -39,12 +39,12 @@ export interface TestsetManageState {
projectId?: string;
/**
* 校验缓存
* check cache
*/
validateCache: ValidateSchemaResult;
/**
* 编辑面板状态
* Edit Panel Status
*/
editPanelVisible: boolean;
editData: CaseDataDetail | null;
@@ -52,28 +52,28 @@ export interface TestsetManageState {
editPanelCloseState: boolean;
/**
* 自动填充状态
* autofill status
*/
generating: boolean;
/**
* 自定义渲染组件,暂时从外部传入,后续不要了
* Custom rendering component, temporarily passed in from the outside, no more in the future
*/
formRenders: Partial<Record<FormItemSchemaType, NodeFormItem>>;
}
export interface TestsetManageAction {
/** 更新状态 */
/** update status */
patch: (s: Partial<TestsetManageState>) => void;
/**
* 校验 schema
* Validation schema
*/
validateSchema: () => Promise<ValidateSchemaResult>;
/**
* 打开编辑面板
* Open the editing panel
*/
openEditPanel: (data?: CaseDataDetail) => void;
/**
* 关闭编辑面板
* Close the edit panel
*/
closeEditPanel: () => void;
@@ -164,7 +164,7 @@ export const TestsetManageProvider: React.FC<
formRenders,
children,
}) => {
// 只初始化一次
// Initialize only once
const storeRef = useRef<TestsetManageStore>(
createTestsetManageState({
bizCtx: {

View File

@@ -68,7 +68,7 @@ export interface TestsetSelectAPI {
const DEBOUNCE_DELAY = 200;
/** option key, 更新 nameincompatibleinput时都要重新渲染 */
/** Option key, re-render when updating name, incompatible, input */
function getOptionKey({ caseBase, schemaIncompatible }: CaseDataDetail) {
return `${caseBase?.caseID}_${caseBase?.name}_${caseBase?.input}_${
schemaIncompatible ? 0 : 1
@@ -139,7 +139,7 @@ export const TestsetSelect = forwardRef<TestsetSelectAPI, TestsetSelectProps>(
}
const selectedTestset = optionsCacheRef.current.get(val);
// 不兼容的不可选中
// Incompatible unselectable
if (!selectedTestset || selectedTestset.schemaIncompatible) {
return;
}

View File

@@ -37,9 +37,9 @@ export function useTestsetOptions() {
const [loadingMore, setLoadingMore] = useState(false);
const [optionsData, setOptionsData] = useState<OptionsData>({ list: [] });
// options 实时
// Options in real time
const optionsDataRef = useRef(optionsData);
// options 缓存
// Options cache
const optionsCacheRef = useRef(new Map<Int64, CaseDataDetail>());
const setOptionsDataWithCache = useCallback(

View File

@@ -97,7 +97,7 @@ export const falseValue = {
[ConditionType.False]: () => I18n.t('workflow_detail_condition_select_false'),
};
// 等于、不等于、长度大于、长度大于等于、长度小于、长度小于等于、包含、不包含、为空、不为空
// equal to, not equal to, length greater than, length greater than or equal to, length less than, length less than or equal to, contain, do not contain, empty, not empty
export const stringConditionValueMap = merge(
{},
equalValue,
@@ -112,7 +112,7 @@ export const stringConditionValueMap = merge(
notEmptyValue,
);
// 等于、不等于、大于、大于等于、小于、小于等于、为空、不为空
// equal to, not equal to, greater than, greater than or equal to, less than, less than or equal to, empty, not empty
export const intConditionValueMap = merge(
{},
equalValue,
@@ -125,7 +125,7 @@ export const intConditionValueMap = merge(
smallerEqualValue,
);
// 等于、不等于、为True、为False、为空、不为空
// Equal to, not equal to, True, False, Null, Not Null
export const booleanConditionValueMap = merge(
{},
equalValue,
@@ -136,7 +136,7 @@ export const booleanConditionValueMap = merge(
falseValue,
);
// 等于、不等于、大于等于、小于等于、大于、小于、为空、不为空
// equal to, not equal to, greater than or equal to, less than or equal to, greater than, less than, empty, not empty
export const numberConditionValueMap = merge(
{},
equalValue,
@@ -149,7 +149,7 @@ export const numberConditionValueMap = merge(
notEmptyValue,
);
// 包含、不包含、为空、不为空
// Include, do not contain, empty, not empty
export const objectConditionValueMap = merge(
{},
includeKeyValue,
@@ -158,7 +158,7 @@ export const objectConditionValueMap = merge(
notEmptyValue,
);
// 长度大于、长度大于等于、长度小于、长度小于等于、包含、不包含、为空、不为空
// Length greater than, length greater than or equal to, length less than, length less than or equal to, contain, do not contain, empty, not empty
export const arrayConditionValueMap = merge(
{},
lengthBiggerValue,
@@ -171,7 +171,7 @@ export const arrayConditionValueMap = merge(
notEmptyValue,
);
// 所有的值的集合
// The set of all values
export const totalConditionValueMap = merge(
{},
equalValue,

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/** 固定的 field name */
/** Fixed field name */
export enum FieldName {
Node = '_node',
Batch = '_batch',

View File

@@ -21,7 +21,7 @@ export {
} from './condition';
/**
* test form 相关常量
* Test form correlation constants
*/
export { FieldName } from './form';

View File

@@ -15,11 +15,11 @@
*/
/**
* testset 列表分页大小
* TestSet List Page Size
*/
export const TESTSET_PAGE_SIZE = 10;
/** test set connector ID 是一个固定的字符串 */
/** Test set connector ID is a fixed string */
export const TESTSET_CONNECTOR_ID = '10000';
export enum FormItemSchemaType {
@@ -41,7 +41,7 @@ export enum TestsetFormValuesForBoolSelect {
UNDEFINED = 'undefined',
}
/** 布尔类型选项 */
/** Boolean Type Options */
export const TESTSET_FORM_BOOLEAN_SELECT_OPTIONS = [
{
value: TestsetFormValuesForBoolSelect.TRUE,

View File

@@ -25,7 +25,7 @@ export const useSync = (inputEvent: NodeEvent | undefined) => {
const testRunService = useTestRunService();
const eventSync = useMemoizedFn((event: NodeEvent | undefined) => {
// 结束
// end
if (!event) {
testRunService.continueTestRun();
return;

View File

@@ -153,7 +153,7 @@ const translateField = (temp: any) => {
return {
title: temp.name,
// 一期固定为 string
// Fixed as string
type: 'string',
'x-decorator-props': {
tag: temp.type,

View File

@@ -14,13 +14,13 @@
* limitations under the License.
*/
/** 预置的特殊的 key */
/** Preset special key */
export enum LogObjSpecialKey {
Error = '$error',
Warning = '$warning',
}
/** log 中 value 的显示样式类型 */
/** Display style type of value in log */
export enum LogValueStyleType {
Default,
Number,

View File

@@ -22,7 +22,7 @@ import { shallow } from 'zustand/shallow';
import { type StoreApi } from 'zustand';
export interface DataViewerState {
// 折叠展开的状态
// Folded and unfolded state
expand: Record<string, boolean> | null;
setExpand: (key: string, v: boolean) => void;
}

View File

@@ -29,7 +29,7 @@ export const DataViewerProvider: React.FC<
> = ({ children, fields }) => {
const store = useMemo(() => createDataViewerStore(), []);
// 根只有一项且其可以下钻时,默认展开它
// When the root has only one item and it can be drilled down, it is expanded by default
useEffect(() => {
if (
store.getState().expand === null &&

View File

@@ -5,7 +5,7 @@
width: 100%;
user-select: text;
/** 高度限制 */
/** height limit */
max-height: 272px;
min-height: 24px;
overflow-y: scroll;

View File

@@ -27,7 +27,7 @@ import { DataViewerProvider } from './context';
import css from './data-viewer.module.less';
export interface DataViewerProps {
/** 支持对象或者纯文本渲染 */
/** Supports object or plain text rendering */
data: JsonValueType;
mdPreview?: boolean;
className?: string;

View File

@@ -30,7 +30,7 @@ export const useValue = (value: Field['value']) => {
type: LogValueStyleType.Default,
};
} else if (isObject(value)) {
// 大数字返回数字类型,值用字符串
// Large number Returns the numeric type, and the value is a string.
if (isBigNumber(value)) {
return {
value: bigNumberToString(value),

View File

@@ -32,10 +32,10 @@ import styles from './json-field.module.less';
const SPACE_WIDTH = 14;
/* JSON 类型数据渲染 */
/* JSON type data rendering */
const FieldValue: React.FC<{
value: Field['value'];
/** 是否是 markdown 格式 */
/** Is it in markdown format? */
isMarkdown?: boolean;
onMarkdownPreview?: () => void;
}> = ({ value, isMarkdown, onMarkdownPreview }) => {
@@ -52,7 +52,7 @@ const FieldValue: React.FC<{
>
{current}
</span>
{/* 预览 */}
{/* preview */}
{isMarkdown ? (
<Button
className={styles['value-button']}

View File

@@ -15,21 +15,21 @@
*/
/*******************************************************************************
* log 相关的类型
* Log related types
*/
/** 线可能存在的几种状态 */
/** The possible states of a line */
export enum LineStatus {
/** 完全隐藏,最后一个父属性嵌套的子属性同列将不会有线 */
/** Completely hidden, the last parent attribute nested child attribute will not be wired in the same column */
Hidden,
/** 完全显示,仅出现在属性相邻的线 */
/** Full display, appearing only on adjacent lines of properties */
Visible,
/** 半显示,非相邻的线 */
/** Semi-display, non-adjacent lines */
Half,
/** 最后属性的相邻线 */
/** Adjacent line of last property */
Last,
}
/** JsonViewer 中的 value 可能值 */
/** Possible values in JsonViewer */
export type JsonValueType =
| string
| null
@@ -39,12 +39,12 @@ export type JsonValueType =
| undefined;
export interface Field {
/** 使用数组而不是 'a.b.c' 是因为可能存在 key='a.b' 会产生错误嵌套 */
/** The use of arrays instead of'a.b.c 'is due to the possibility that key =' a.b 'will generate false nesting */
path: string[];
lines: LineStatus[];
/** 这里 value 可能是任意值,这里是不完全枚举 */
/** Here value can be any value, here is an incomplete enumeration */
value: JsonValueType;
children: Field[];
/** 是否是可下钻的对象(包含数组) */
/** Whether it is a drillable object (containing an array) */
isObj: boolean;
}

View File

@@ -17,7 +17,7 @@
import BigNumber from 'bignumber.js';
/**
* 是不是大数字
* Is it a big number?
* @param value
* @returns
*/
@@ -26,7 +26,7 @@ export function isBigNumber(value: unknown): value is BigNumber {
}
/**
* 大数字转字符串
* Large number to string
* @param value
* @returns
*/

View File

@@ -20,29 +20,29 @@ import { LineStatus, type JsonValueType, type Field } from '../types';
import { isBigNumber } from './big-number';
/**
* 通过父元素的线条状态推到子元素的线条状态
* Push through the line state of the parent element to the line state of the child element
*/
const getLineByParent2Child = (pLine: LineStatus): LineStatus => {
switch (pLine) {
/** 表示父节点也是从父父节点下钻而来,此处的子节点只需要把线延续下去即可 */
/** It means that the parent node is also drilled from the parent node, and the sub-node here only needs to continue the line. */
case LineStatus.Visible:
return LineStatus.Half;
/** 表示父节点是父父节点的最后一个节点,子节点无需再延续,渲染空白即可 */
/** Indicates that the parent node is the last node of the parent node, and the sub-node does not need to continue, just render blank. */
case LineStatus.Last:
return LineStatus.Hidden;
/** 其他的情况完全继承父节点的线 */
/** Other cases fully inherit the line of the parent node */
default:
return pLine;
}
};
/**
* object 解析成可以循环渲染的 fields
* 1. object 非复杂类型,则返回长度为 1 的 fields 只渲染一项
* 2. object = {},则返回长度为 0 的 fields渲染层需要做好兜底
* Parse objects into fields that can be cycled
* 1. If object is not a complex type, fields of length 1 are returned to render only one item
* 2. If object = {}, fields of length 0 are returned, and the rendering layer needs to be well covered
*/
const generateFields = (object: JsonValueType): Field[] => {
/** object 非复杂类型 */
/** If the object is not a complex type */
if (!isObject(object) || isBigNumber(object)) {
return [
{
@@ -55,17 +55,17 @@ const generateFields = (object: JsonValueType): Field[] => {
];
}
/** 递归计算时缓存一下计算好的线,没别的意义,降低一些时间复杂度 */
/** Cache the calculated line during recursive calculation, which is meaningless and reduces some time complexity */
const lineMap = new Map<string[], LineStatus[]>();
/** 递归解析 object fields */
/** Recursive parsing of object as fields */
const dfs = ($object: object, $parentPath: string[] = []): Field[] => {
// 如果不是对象,直接返回空数组,兜底异常情况
// If it is not an object, return an empty array directly to cover the exception
if (!isObject($object)) {
return [];
}
// 如果是大数字,直接返回空数组
// If it is a large number, return an empty array directly
if (isBigNumber($object)) {
return [];
}
@@ -79,12 +79,12 @@ const generateFields = (object: JsonValueType): Field[] => {
const path = $parentPath.concat(key);
const last = idx === keys.length - 1;
/**
* 根据父节点的线推导子节点的线
* Derive the sub-node's line from the parent's line
*/
const lines = parentLines
.map<LineStatus>(getLineByParent2Child)
/**
* 最后拼接上子节点自己的线,最后一个节点和普通的节点有样式区分
* Finally, splice the sub-node's own line, and the last node is distinguished from the ordinary node by style.
*/
.concat(last ? LineStatus.Last : LineStatus.Visible);
lineMap.set(path, lines);

View File

@@ -14,7 +14,7 @@
.image-item {
max-height: 280px;
aspect-ratio: 1 / 1; /* 宽高比为1:1 */
aspect-ratio: 1 / 1; /* The aspect ratio is 1:1. */
display: flex;
align-items: center;
justify-content: center;

View File

@@ -21,43 +21,43 @@ import { type LogValueType } from '../../../types';
const REASONING_CONTENT_NAME = 'reasoning_content';
interface isDifferentOutputArgs {
/** 节点输出 */
/** Node output */
nodeOutput: LogValueType;
/** 原始输出 */
/** raw output */
rawOutput: LogValueType;
/** 是否是大模型节点 */
/** Is it a large model node? */
isLLM?: boolean;
}
/**
* 通用的节点输出判异
* General Node Output Decision
*/
const isDifferentCommonOutput = (args: isDifferentOutputArgs) => {
const { nodeOutput, rawOutput } = args;
/**
* case1: rawOutput === undefined
* 可以无须比较,直接返回假
* You can directly return to the fake without comparison.
*/
if (isUndefined(rawOutput)) {
return false;
}
const nodeOutputType = typeof nodeOutput;
const rawOutputType = typeof rawOutput;
/** case2: 两者类型不同 */
/** Case2: The two types are different */
if (nodeOutputType !== rawOutputType) {
return true;
}
/** case4: 深度比较 */
/** Case4: Depth comparison */
return !isEqual(nodeOutput, rawOutput);
};
/**
* 大模型节点特有的判断逻辑
* The Unique Judgment Logic of Large Model Nodes
*/
const isDifferentLLMOutput = (args: isDifferentOutputArgs) => {
const { nodeOutput } = args;
/** 如果节点输出是对象,则去除系统字段 */
/** If the node output is an object, remove the system field */
const readNodeOutput = isObject(nodeOutput)
? omit(nodeOutput, [REASONING_CONTENT_NAME])
: nodeOutput;
@@ -65,21 +65,21 @@ const isDifferentLLMOutput = (args: isDifferentOutputArgs) => {
...args,
nodeOutput: readNodeOutput,
});
/** 常规判断已经判同,则直接返回 */
/** If the conventional judgment has been approved, it will return directly. */
if (!isDiffCommon) {
return isDiffCommon;
}
/** 如果不是节点输出不是对象,直接判异 */
/** If it is not a node, the output is not an object, and the direct judgment is */
if (!isObject(readNodeOutput)) {
return true;
}
const arr = Object.entries(readNodeOutput);
/** 如果排除系统字段,仍然超过多个字段,则无须进一步比较,直接判异 */
/** If the system field is excluded and there are still more than one field, there is no need for further comparison and a direct judgment is made */
if (arr.length !== 1) {
return true;
}
/** 用唯一的值与节点输出做异同判断 */
/** Use unique values to judge similarities and differences with node outputs */
return isDifferentCommonOutput({
...args,
nodeOutput: arr[0][1],
@@ -87,22 +87,22 @@ const isDifferentLLMOutput = (args: isDifferentOutputArgs) => {
};
/**
* 精细的判断节点输出和原始输出是否相同
* Fine determination of whether the node output and the original output are the same
*/
export const isDifferentOutput = (
args: isDifferentOutputArgs,
): [boolean, any] => {
/**
* nodeOutput 可能值:
* Possible nodeOutput values:
* 1. undefined
* 2. string
* 3. 包涵一个自定义字段、reasoning_contentLogObjSpecialKey
* 4. 包涵多个自定义字段
* rawOutput 可能值:
* 3. Includes a custom field, reasoning_content, LogObjSpecialKey
* 4. Include multiple custom fields
* rawOutput possible values:
* 1. undefined
* 2. string
* 4. 任意对象
* 5. 任意数组
* 4. Any object
* 5. Arbitrary array
*/
try {
const { isLLM } = args;
@@ -111,7 +111,7 @@ export const isDifferentOutput = (
: isDifferentCommonOutput(args);
return [result, undefined];
} catch (err) {
/** 该函数会深入解析日志结构,不排除出现异常的可能性,出现异常则判异, */
/** This function will deeply analyze the log structure and do not rule out the possibility of abnormalities. */
return [true, err];
}
};

View File

@@ -82,7 +82,7 @@ const updateOutputWithNewType = (
};
export const useSyncOutput = (outputPath: string, node: FlowNodeEntity) => {
// TODO: 改到 effects 中实现 ,依赖节点引擎支持自定义事件触发 effects
// TODO: Changed to effects, relying on the node engine to support custom event triggering effects
const updateOutput = (output: ParsedOutput[]) => {
if (outputPath) {
const formModel =

View File

@@ -32,7 +32,7 @@ const LLM_TEXT = {
const DEFAULT_TEXT = {
tabLabel: I18n.t('workflow_detail_testrun_panel_raw_output'),
};
/** 一些特化节点的文案 */
/** Copywriting for some specialized nodes */
const TEXT = {
Code: CODE_TEXT,
LLM: LLM_TEXT,
@@ -46,7 +46,7 @@ export enum TabValue {
export const useOutputLog = (log: OutputLog) => {
const [tab, setTab] = useState(TabValue.Output);
const reporter = useTestRunReporterService();
/** 是否渲染原始输出 */
/** Whether to render the original output */
const showRawOutput = useMemo(() => {
const [result, err] = isDifferentOutput({
nodeOutput: log.data,

View File

@@ -30,7 +30,7 @@ interface NodeStatusBarProps {
hasExecuteResult?: boolean;
needAuth?: boolean;
/**
* 是否包含会话处理
* Is session handling included?
*/
hasConversation?: boolean;
onAuth?: () => void;
@@ -73,9 +73,9 @@ export const NodeStatusBar: React.FC<
return (
<div
className={styles['node-status-bar']}
// 必须要禁止 down 冒泡,防止判定圈选和 node hover不支持多边形
// It is necessary to disable down bubbling to prevent judgment circling and node hovering (polygons are not supported)
onMouseDown={e => e.stopPropagation()}
// 其他事件统一走点击事件,且也需要阻止冒泡
// Other events uniformly go to the click event, and it is also necessary to prevent bubbling.
onClick={handleToggleShowDetail}
>
<div

View File

@@ -88,7 +88,7 @@ export const falseValue = {
[ConditionType.False]: () => I18n.t('workflow_detail_condition_select_false'),
};
// 等于、不等于、长度大于、长度大于等于、长度小于、长度小于等于、包含、不包含、为空、不为空
// equal to, not equal to, length greater than, length greater than or equal to, length less than, length less than or equal to, contain, do not contain, empty, not empty
export const stringConditionValueMap = merge(
{},
equalValue,
@@ -103,7 +103,7 @@ export const stringConditionValueMap = merge(
notEmptyValue,
);
// 等于、不等于、大于、大于等于、小于、小于等于、为空、不为空
// equal to, not equal to, greater than, greater than or equal to, less than, less than or equal to, empty, not empty
export const intConditionValueMap = merge(
{},
equalValue,
@@ -116,7 +116,7 @@ export const intConditionValueMap = merge(
smallerEqualValue,
);
// 等于、不等于、为True、为False、为空、不为空
// Equal to, not equal to, True, False, Null, Not Null
export const booleanConditionValueMap = merge(
{},
equalValue,
@@ -127,7 +127,7 @@ export const booleanConditionValueMap = merge(
falseValue,
);
// 等于、不等于、大于等于、小于等于、大于、小于、为空、不为空
// equal to, not equal to, greater than or equal to, less than or equal to, greater than, less than, empty, not empty
export const numberConditionValueMap = merge(
{},
equalValue,
@@ -140,7 +140,7 @@ export const numberConditionValueMap = merge(
notEmptyValue,
);
// 包含、不包含、为空、不为空
// Include, do not contain, empty, not empty
export const objectConditionValueMap = merge(
{},
includeValue,
@@ -149,7 +149,7 @@ export const objectConditionValueMap = merge(
notEmptyValue,
);
// 长度大于、长度大于等于、长度小于、长度小于等于、包含、不包含、为空、不为空
// Length greater than, length greater than or equal to, length less than, length less than or equal to, contain, do not contain, empty, not empty
export const arrayConditionValueMap = merge(
{},
lengthBiggerValue,
@@ -162,7 +162,7 @@ export const arrayConditionValueMap = merge(
notEmptyValue,
);
// 所有的值的集合
// The set of all values
export const totalConditionValueMap = merge(
{},
equalValue,

View File

@@ -20,21 +20,21 @@ export {
logicTextMap,
} from './condition';
/** 日志类型 */
/** log type */
export enum LogType {
/** 输入 */
/** input */
Input,
/** 输出 */
/** output */
Output,
/** 批处理数据 */
/** batch data */
Batch,
/** Condition */
Condition,
/** 大模型推理过程 */
/** Large model reasoning process */
Reasoning,
/** 大模型Function过程 */
/** Large Model Function Process */
FunctionCall,
/** 子流程跳转连接 */
/** Subprocess jump connection */
WorkflowLink,
}

View File

@@ -18,7 +18,7 @@ import { type WorkflowLinkLogData } from '@/types';
import { type LogType } from './constants';
/** log 中的 value 可能值 */
/** Possible values in the log */
export type LogValueType =
| string
| null
@@ -27,7 +27,7 @@ export type LogValueType =
| boolean
| undefined;
/** 通常的日志结构 */
/** Normal log structure */
export interface BaseLog {
label: string;
data: LogValueType;
@@ -37,7 +37,7 @@ export interface BaseLog {
}
/**
* condition 的值
* Value of condition
*/
export interface ConditionData {
leftData: LogValueType;
@@ -51,7 +51,7 @@ export interface ConditionGroup {
logicData: string;
}
/** condition 的日志结构 */
/** Log structure for conditions */
export interface ConditionLog {
conditions: ConditionGroup[];
type: LogType.Condition;
@@ -72,14 +72,14 @@ export interface FunctionCallLog {
}
/**
* 输出的日志结构
* Output log structure
*/
export interface OutputLog {
label: string;
data: LogValueType;
copyTooltip?: string;
type: LogType.Output;
/** 节点类型 */
/** Node type */
nodeType: string;
mockInfo?: {
isHit: boolean;

View File

@@ -24,21 +24,21 @@ import {
} from '../types';
import { LogType } from '../constants';
/** 是否是输出日志 */
/** Is it an output log? */
export const isOutputLog = (log: Log): log is OutputLog =>
log.type === LogType.Output;
/** 是否是 condition 输入 */
/** Is it condition input? */
export const isConditionLog = (log: Log): log is ConditionLog =>
log.type === LogType.Condition;
/** 是否是大模型推理日志 */
/** Is it a large model inference log? */
export const isReasoningLog = (log: Log): log is BaseLog =>
log.type === LogType.Reasoning;
export const isFunctionCallLog = (log: Log): log is FunctionCallLog =>
log.type === LogType.FunctionCall;
/** 是否是子流程跳转连接 */
/** Is it a subprocess jump connection? */
export const isWorkflowLinkLog = (log: Log): log is WorkflowLinkLog =>
log.type === LogType.WorkflowLink;

View File

@@ -75,20 +75,20 @@ const generateBatchData = (result: NodeResult): Log => {
const generateInput = (logs: Log[], result: NodeResult) => {
const { NodeType: type } = result;
/** input 不可能是 string所以使用 {} 兜底,异常情况直接展示空即可 */
/** The input cannot be a string, so use {} to cover the bottom, and the exception can be directly displayed empty. */
const inputData = (typeSafeJSONParse(result.input, {
emptyValue: result.input,
enableBigInt,
}) || {}) as LogValueType;
/** step 1.1: condition 节点单独处理 */
/** Step 1.1: condition node handled separately */
if (type === 'If') {
const normalizeConditions = normalizeConditionInputData(inputData);
const conditions = normalizeConditions.map(branch => ({
conditions: branch.conditions.map(condition => {
/** 右值不一定存在 */
/** An rvalue doesn't necessarily exist */
const { left, right = {}, operator } = condition;
const operatorFn = totalConditionValueMap[operator];
/** 后端的 operator 枚举值通过 i18n 转化为文本 */
/** Back-end operator enumeration values are converted to text via i18n */
const operatorData = isFunction(operatorFn) ? operatorFn() : operator;
const leftData = left?.key ? { [left?.key]: left?.value } : left?.value;
const rightData =
@@ -108,7 +108,7 @@ const generateInput = (logs: Log[], result: NodeResult) => {
}));
logs.push({ conditions, type: LogType.Condition });
} else {
/** end、Message 节点的 label 不同,输入即输出 */
/** Different labels of end and message nodes, input is output */
const isOutputNode = type === 'End' || type === 'Message';
const label = isOutputNode
? I18n.t('workflow_detail_end_output')
@@ -142,26 +142,26 @@ const generateOutput = (
const { hitStatus: mockHitStatus, mockSetName } =
(typeSafeJSONParse(mockHitInfo) as any) || {};
/**
* case1: output 解析成功,可能是对象或者字符串
* case2: output 解析失败,可能是字符串
* case3: output 为空值,兜底展示空对象
* Case1: output parsed successfully, may be an object or string
* Case2: output parsing failed, possibly string
* Case3: output is null, the empty object is displayed at the bottom
*/
let outputData =
typeSafeJSONParse(result.output, { enableBigInt }) || result.output || {};
/** step 2.1: 处理 rawOutput */
/** Step 2.1: Handling rawOutput */
/**
* case1: output 解析成功,可能是对象或者字符串
* case2: output 解析失败,可能是字符串,由于是原始输出空值也视为有意义
* Case1: output parsed successfully, may be an object or string
* Case2: output parsing failed, it may be a string, because it is the original output null value, it is also considered meaningful
*/
const rawData =
typeSafeJSONParse(result.raw_output, { enableBigInt }) || result.raw_output;
/** CodeLlm 节点需要展示 raw */
/** Code, Llm nodes need to display raw */
const textHasRawout = type === 'Text' && hasStringOutput(rawData);
const hasRawOutput =
(type && ['Code', 'LLM', 'Question'].includes(type)) || textHasRawout;
/** step 2.2: 处理 errorInfo */
/** Step 2.2: Handling errorInfo */
if (errorInfo) {
const errorData = {
[errorLevel === 'Error'
@@ -169,9 +169,9 @@ const generateOutput = (
: LogObjSpecialKey.Warning]: errorInfo,
};
/**
* 错误放到 output 中展示,output 的展示优先级最高
* output 为对象则直接 assign error
* 否则 output 需要被赋值到 output 字段并和 error 组成一个对象
* Errors are displayed in output, and output has the highest priority
* If the output is an object, directly assign error
* Otherwise, output needs to be assigned to the output field and formed into an object with error
*/
outputData = isObject(outputData)
? {
@@ -269,19 +269,19 @@ export const generateLog = (
const logs: Log[] = [];
/** step 0: 处理 batch data */
/** Step 0: Processing batch data */
if (isBatch) {
logs.push(generateBatchData(result));
}
/** step 1: 处理 input */
/** Step 1: Process the input */
generateInput(logs, result);
/** step 2: 处理 output */
/** Step 2: Process the output */
generateOutput(logs, result, node);
/** step 3: 对于子工作流节点,生成额外的跳转链接 */
/** Step 3: For child workflow nodes, generate additional jump links */
generateExternalLink(logs, result, node);
return {

View File

@@ -19,10 +19,10 @@ import remarkParse from 'remark-parse';
import { isString } from 'lodash-es';
/**
* 是否符合渲染为 markdown
* 1. ast > 1
* 2. ast = 1 且类型不为普通段落
* 2. ast = 1 且类型为普通段落,但段落中超过两个或者仅有一项但不为 text
* Is it consistent with rendering as markdown?
* 1. ast > 1 or
* 2. ast = 1 and the type is not a normal paragraph
* 2. ast = 1 and the type is normal paragraph, but there are more than two paragraphs or only one item in the paragraph but not text
*/
export const isPreviewMarkdown = (str: unknown) => {
if (!isString(str)) {

View File

@@ -194,7 +194,7 @@ export function parseFunctionCall(
const type = get(output, 'msg_type');
const skills = getNodeSkills(node);
// 知识库类型
// Knowledge base type
if (type === 'knowledge_recall') {
const data: FunctionCallKnowledgeOutput = JSON.parse(
get(output, 'data', '{}') as string,
@@ -202,12 +202,12 @@ export function parseFunctionCall(
return getDatasetLogItem(data, skills);
}
// 插件类型
// plugin type
if (input?.plugin_type === LLMNodeDataSkillType.Plugin) {
return getPluginLogItem(input, output, skills);
}
// Workflow类型
// Workflow Type
if (input?.plugin_type === LLMNodeDataSkillType.Workflow) {
return getWorkflowLogItem(input, output, skills);
}

View File

@@ -34,7 +34,7 @@ interface NodeItemProps {
onClick: (p: ProblemItem) => void;
}
// 避免节点删除后丢失icon、title信息
// Avoid losing icon and title information after node deletion
const useMetaMemo = (nodeId: string) => {
const [nodeMeta, setNodeMeta] = useState<CommonNodeData>();
const playground = usePlayground();

View File

@@ -32,14 +32,14 @@ const generateErrors2Problems = (errors: ValidationState['errors']) => {
const nodeErrors = list.filter(i => i.errorType === 'node');
const lineErrors = list.filter(i => i.errorType === 'line');
// 处理节点错误
// Handling node errors
const nodeLevelErrors = nodeErrors.filter(
item => item.errorLevel === 'error',
);
const nodeLevelWarnings = nodeErrors.filter(
item => item.errorLevel === 'warning',
);
// errors 优先,其次才显示 warning
// Errors first, warnings second
const nodeCurrentErrors = nodeLevelErrors.length
? nodeLevelErrors
: nodeLevelWarnings;
@@ -55,7 +55,7 @@ const generateErrors2Problems = (errors: ValidationState['errors']) => {
nodeProblems.push(nodeProblem);
}
// 处理线错误
// processing line error
if (lineErrors.length) {
lineProblems.push(...lineErrors);
}

View File

@@ -24,14 +24,14 @@ export type WorkflowProblem = WorkflowValidateError & {
};
export interface ProblemItem {
// 错误描述
// error description
errorInfo: string;
// 错误等级
// error level
errorLevel: FeedbackStatus;
// 错误类型: 节点 / 连线
// Error Type: Node/Connection
errorType: 'node' | 'line';
// 节点id
// Node ID
nodeId: string;
// 若为连线错误,还需要目标节点来确认这条连线
// In the case of a connection error, the target node is also required to confirm the connection
targetNodeId?: string;
}

View File

@@ -30,7 +30,7 @@ export const MessageList = () => {
const ref = useRef<HTMLDivElement>(null);
// 自动滚动到最后一条消息
// Automatically scroll to the last message
useEffect(() => {
const lastChild = ref.current?.lastElementChild;
lastChild?.scrollIntoView();

View File

@@ -33,7 +33,7 @@ export const useQuestionForm = (questionEvent: NodeEvent | undefined) => {
patch: store.patch,
}));
const eventSync = useMemoizedFn((event: NodeEvent | undefined) => {
// 结束
// end
if (!event) {
testRunService.continueTestRun();
return;

View File

@@ -38,7 +38,7 @@ export interface QuestionFormState {
}
export interface QuestionFormAction {
/** 更新状态 */
/** update status */
patch: (next: Partial<QuestionFormState>) => void;
}

View File

@@ -37,7 +37,7 @@ export const useSendMessage = () => {
const testRunService = useTestRunService();
const send = useMemoizedFn(async (text: string) => {
// 前端先填入回答
// Fill in the answer first
const temp: ReceivedMessage = {
content: text,
type: MessageType.Answer,

View File

@@ -26,12 +26,12 @@ import {
type Listener = (e: WorkflowContentChangeEvent) => void;
/**
* 监听 document content 变动的 hook
* A hook to monitor changes in document content
*/
export const useDocumentContentChange = (
/** 监听器 */
/** Listener */
listener: Listener,
/** 监听类型,默认监听所有 */
/** Listen type, listen to all by default */
listenType?: WorkflowContentChangeType,
) => {
const workflowDocument = useService<WorkflowDocument>(WorkflowDocument);

View File

@@ -57,7 +57,7 @@ export { QuestionForm } from './features/question';
// export { InputForm } from './features/input';
/** trace */
// 禁止直接导出 trace避免 visactor 包被打到首屏
// It is forbidden to export traces directly to avoid the visactor package being hit on the first screen
// export {
// TraceListPanel,
// TraceDetailPanel,

View File

@@ -22,7 +22,7 @@ export type FormDataType = any;
export const TestFormService = Symbol('TestFormService');
export interface TestFormService {
/** 表单缓存值 */
/** form cache value */
cacheValues: Map<string, FormDataType>;
getCacheValues: (id: string) => null | FormDataType;

View File

@@ -20,18 +20,18 @@ export enum EventName {
TryStart = 'workflow_testrun_sdk_try_start',
RunEnd = 'workflow_testrun_sdk_run_end',
/*****************************************************************************
* form 相关
* Form related
*/
/** 表单 schema 计算 */
/** Form schema calculation */
FormSchemaGen = 'workflow_testrun_sdk_form_schema_gen',
/** 表单 UI 模式 */
/** Form UI Pattern */
FormRunUIMode = 'workflow_testrun_sdk_form_run_ui_mode',
/** 表单数据填充 */
/** form data filling */
FormGenDataOrigin = 'workflow_testrun_sdk_form_gen_data_origin',
/** log 相关 */
/** log related */
LogOutputDifference = 'workflow_testrun_sdk_log_output_difference',
LogOutputMarkdown = 'workflow_testrun_sdk_log_output_markdown',
/** trace 相关 */
/** Trace correlation */
TraceOpen = 'workflow_testrun_sdk_trace_open',
}
@@ -87,14 +87,14 @@ export type PickReporterParams<
> = Pick<Params[T], X>;
export interface TestRunReporterService {
/** 日志原始输出异同解析 */
/** Log original output similarities and differences analysis */
logRawOutputDifference: (
params: Pick<
Params[EventName.LogOutputDifference],
'error_msg' | 'log_node_type' | 'is_difference'
>,
) => void;
/** 日志 Markdown 解析 */
/** log Markdown parsing */
logOutputMarkdown: (
params: PickReporterParams<EventName.LogOutputMarkdown, 'action_type'>,
) => void;

View File

@@ -18,11 +18,11 @@ export const TestRunService = Symbol('TestRunService');
export interface TestRunService {
/**
* 停止试运行
* Stop practicing running
*/
pauseTestRun: () => void;
/**
* 继续试运行
* Keep practicing running
*/
continueTestRun: () => void;
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/** log 中的 value 可能值 */
/** Possible values in the log */
export type LogValueType =
| string
| null
@@ -28,7 +28,7 @@ interface MockInfo {
mockSetName?: string;
}
/** 通常的日志结构 */
/** Normal log structure */
export interface BaseLog {
label: string;
source: LogValueType;
@@ -37,7 +37,7 @@ export interface BaseLog {
mockInfo?: MockInfo;
type: 'input' | 'output' | 'raw_output' | 'batch';
}
/** condition 的日志结构 */
/** Log structure for conditions */
export interface ConditionLog {
conditions: Array<{
conditions: {
@@ -51,7 +51,7 @@ export interface ConditionLog {
}>;
}
/** 嵌套的日志结构 */
/** Nested log structure */
export interface TreeLog {
label: string;
children: (BaseLog | ConditionLog)[];
@@ -59,7 +59,7 @@ export interface TreeLog {
export type Log = BaseLog | ConditionLog | TreeLog;
/** 格式化之后的 condition log */
/** Formatted condition log */
export interface ConditionFormatLog {
leftData: LogValueType;
rightData: LogValueType;

View File

@@ -32,13 +32,13 @@ export type ObjectFieldSchema = {
}[];
export interface FormItemSchema {
// 扩展为枚举
// Expand to enumeration
type: string;
name: string;
description?: string;
required?: boolean;
value?: string | number | boolean;
/** object/array复杂类型有schema定义 */
/** Object/array complex types have schema definitions */
schema?: ArrayFieldSchema | ObjectFieldSchema;
}

View File

@@ -38,7 +38,7 @@ export const typeSafeJSONParse = (
}
return JSON.parse(String(v));
} catch (e) {
// 日志解析
// log parsing
if (options?.needReport) {
reporter.errorEvent({
error: e as Error,

View File

@@ -16,7 +16,7 @@
import { isBoolean, isNumber } from 'lodash-es';
// 将表单值转换为testrun接口协议格式
// Convert form values to testrun interface protocol format
export const stringifyValue = (
values: any,
stringifyKeys?: string[],

View File

@@ -86,8 +86,8 @@ function workflowJsonToJsonSchema(workflowJson: any) {
}
/**
* 自定义表单的额外参数
* 目前只对array和object表单加jsonSchema校验
* Customize the form's additional parameters
* Currently only jsonSchema validation is applied to array and object forms
*/
export function getTestsetFormItemCustomProps(
formItemSchema: FormItemSchema,

View File

@@ -21,8 +21,8 @@ import { FormItemSchemaType } from '../../constants';
/**
* placeholder
* - bot:请选择bot
* - 其他xx必填
* - bot: Please select bot
* - Other: xx required
*/
export function getTestsetFormItemPlaceholder({ name, type }: FormItemSchema) {
if (type === FormItemSchemaType.BOT) {

View File

@@ -30,7 +30,7 @@ const getTestDataByTestset = (testsetData?: CaseDataDetail) => {
let nodeData: Record<string, unknown> | undefined;
dataArray.forEach(data => {
/** 特殊虚拟节点 */
/** Special Virtual Node */
if (data?.component_type === ComponentType.CozeVariableBot) {
botData = data.inputs?.[0]?.value;
} else if (data?.component_type === ComponentType.CozeVariableChat) {

View File

@@ -27,16 +27,16 @@ interface GetTestsetNameRulesProps {
bizCtx?: BizCtx;
/** bizComponentSubject */
bizComponentSubject?: ComponentSubject;
/** 原始值 */
/** raw value */
originVal?: string;
/** 是否为海外(海外不允许输入中文 与PluginName校验规则对齐 */
/** Whether it is overseas (overseas is not allowed to enter Chinese, it is aligned with the PluginName verification rule) */
isOversea?: boolean;
}
/**
* 校验名称格式(参考插件名称)
* - 海外:仅支持输入字母、数字、下划线或空格
* - 国内:仅支持输入中文、字母、数字、下划线或空格
* Verification name format (refer to plug-in name)
* - Overseas: Only support entering letters, numbers, underscores or spaces
* - Domestic: Only supports entering Chinese, letters, numbers, underscores or spaces
*/
function validateNamePattern(
name: string,
@@ -56,12 +56,12 @@ function validateNamePattern(
}
/**
* Testset名称表单校验规则
* TestSet Name Form Validation Rules
*
* @param param.bizCtx - bizCtx
* @param param.bizComponentSubject - bizComponentSubject
* @param param.originVal - 原始值
* @param param.isOversea - 是否为海外(海外不允许输入中文 与PluginName校验规则对齐
* @Param param.originVal - original value
* @Param param.isOverseas - whether it is overseas (overseas is not allowed to enter Chinese, it is aligned with the PluginName verification rule)
*/
export function getTestsetNameRules({
bizCtx,
@@ -83,12 +83,12 @@ export function getTestsetNameRules({
return;
}
// 编辑模式下,名称与原名相同时跳过
// In edit mode, skip when the name is the same as the original name
if (originVal && value === originVal) {
return;
}
// 中文、字母等等等等
// Chinese, letters, etc., etc
const formatMsg = validateNamePattern(value, isOversea);
if (formatMsg) {
@@ -96,7 +96,7 @@ export function getTestsetNameRules({
return;
}
// 检查重复
// Check for duplicates
try {
const { isPass } = await debuggerApi.CheckCaseDuplicate({
bizCtx,

View File

@@ -20,7 +20,7 @@ function isNumberType(t: string) {
return t === FormItemSchemaType.NUMBER || t === FormItemSchemaType.FLOAT;
}
/** 判断类型一致,**特化:**`number`和`float`视为同一类型 */
/** Determine that the type is consistent, ** specialization: ** 'number' and'float 'are regarded as the same type */
export const isTestsetFormSameFieldType = (t1?: string, t2?: string) => {
if (typeof t1 === 'undefined' || typeof t2 === 'undefined') {
return false;

View File

@@ -31,7 +31,7 @@ import type {
} from '../../types';
import { FormItemSchemaType } from '../../constants';
/** 变量命名校验规则(对齐workflow得参数名校验) */
/** Variable name verification rules (parameter name verification for aligned workflow) */
const PARAM_NAME_VALIDATION_RULE =
/^(?!.*\b(true|false|and|AND|or|OR|not|NOT|null|nil|If|Switch)\b)[a-zA-Z_][a-zA-Z_$0-9]*$/;
@@ -106,21 +106,21 @@ function checkArrayOrObjectField(field: FormItemSchema) {
}
function checkNodeFormSchema(schema: NodeFormSchema) {
// 节点参数为空
// Node parameter is empty
if (!schema.inputs.length) {
return false;
}
const nameSet = new Set<string>();
for (const ipt of schema.inputs) {
// 名称非法 or 重复
// Name illegal or duplicate
if (!validateParamName(ipt.name) || nameSet.has(ipt.name)) {
return false;
}
nameSet.add(ipt.name);
// 单独检测复杂类型
// Detect complex types individually
if (!checkArrayOrObjectField(ipt)) {
return false;
}
@@ -137,7 +137,7 @@ function validate(json?: string): ValidateSchemaResult {
try {
const schemas = JSON.parse(json) as NodeFormSchema[];
// schema为空 or start节点的inputs为空
// Schema is empty or start node inputs are empty
if (
schemas.length === 0 ||
(schemas[0].component_type === ComponentType.CozeStartNode &&
@@ -164,7 +164,7 @@ interface ValidateSchemaOptions {
bizComponentSubject?: ComponentSubject;
}
/** 检查workflow节点表单是否为空(schema为空 or start节点的inputs为空) */
/** Checks if the workflow node form is empty (schema is empty or start node inputs are empty) */
export const validateTestsetSchema = async (
options: ValidateSchemaOptions,
): Promise<ValidateSchemaResult> => {

View File

@@ -19,7 +19,7 @@ import { nanoid } from 'nanoid';
export type ExtraType = Record<string, any>;
/**
* cache 类型
* cache type
*/
export interface CacheMapType {
start: number;
@@ -37,7 +37,7 @@ export class Tracker {
const value = {
start,
extra,
// 如果已经存在,则永久禁用该事件上报
// If it already exists, permanently disable the event reporting
disabled: !!prev,
};
this.cache.set(key, value);
@@ -49,7 +49,7 @@ export class Tracker {
return null;
}
const duration = performance.now() - prev.start;
// 成功上报重置状态,等待下一次上报
// Successfully report the reset status and wait for the next report.
this.cache.delete(key);
return { ...prev, duration };
}