chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -23,5 +23,5 @@ export enum FileItemStatus {
|
||||
Wait = 'wait',
|
||||
}
|
||||
|
||||
// 支持预览的图片类型
|
||||
// Image types that support preview
|
||||
export const PREVIEW_IMAGE_TYPE = ['jpg', 'jpeg', 'png', 'webp', 'svg'];
|
||||
|
||||
@@ -119,7 +119,7 @@ export const getIconByExtension = (extension: string) => {
|
||||
return fileIcon;
|
||||
};
|
||||
|
||||
/** 获取文件名后缀 */
|
||||
/** Get filename suffix */
|
||||
export function getFileExtension(name?: string) {
|
||||
if (!name) {
|
||||
return '';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 解析运行结果的组件
|
||||
* Components that parse run results
|
||||
*/
|
||||
export { LogDetail } from './log-detail';
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* 批处理的分页器
|
||||
* Batch pager
|
||||
*/
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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?.(
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -68,7 +68,7 @@ export interface TestsetSelectAPI {
|
||||
|
||||
const DEBOUNCE_DELAY = 200;
|
||||
|
||||
/** option key, 更新 name、incompatible、input时都要重新渲染 */
|
||||
/** 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;
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** 固定的 field name */
|
||||
/** Fixed field name */
|
||||
export enum FieldName {
|
||||
Node = '_node',
|
||||
Batch = '_batch',
|
||||
|
||||
@@ -21,7 +21,7 @@ export {
|
||||
} from './condition';
|
||||
|
||||
/**
|
||||
* test form 相关常量
|
||||
* Test form correlation constants
|
||||
*/
|
||||
export { FieldName } from './form';
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -153,7 +153,7 @@ const translateField = (temp: any) => {
|
||||
|
||||
return {
|
||||
title: temp.name,
|
||||
// 一期固定为 string
|
||||
// Fixed as string
|
||||
type: 'string',
|
||||
'x-decorator-props': {
|
||||
tag: temp.type,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
width: 100%;
|
||||
user-select: text;
|
||||
|
||||
/** 高度限制 */
|
||||
/** height limit */
|
||||
max-height: 272px;
|
||||
min-height: 24px;
|
||||
overflow-y: scroll;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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']}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_content、LogObjSpecialKey
|
||||
* 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];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/** Code、Llm 节点需要展示 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 {
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -38,7 +38,7 @@ export interface QuestionFormState {
|
||||
}
|
||||
|
||||
export interface QuestionFormAction {
|
||||
/** 更新状态 */
|
||||
/** update status */
|
||||
patch: (next: Partial<QuestionFormState>) => void;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,11 +18,11 @@ export const TestRunService = Symbol('TestRunService');
|
||||
|
||||
export interface TestRunService {
|
||||
/**
|
||||
* 停止试运行
|
||||
* Stop practicing running
|
||||
*/
|
||||
pauseTestRun: () => void;
|
||||
/**
|
||||
* 继续试运行
|
||||
* Keep practicing running
|
||||
*/
|
||||
continueTestRun: () => void;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[],
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> => {
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user