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

@@ -64,7 +64,7 @@ export const defaultLabelStyle: LabelStyle = {
export const defaultLabelText: LabelText = (datum, element, params) =>
`${datum.start}-${datum.end}`;
// xScale的padding(解决hover后rect边框被截断问题)
// padding of xScale (solve the problem of rect border being truncated after hover)
export const scrollbarMargin = 10;
export const datazoomHeight = 20;
export const datazoomDecimals = 0;

View File

@@ -126,7 +126,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
[_globalLabelStyle],
);
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- 计算需要
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- calculation required
const topOffset = datazoomHeight + datazoomPaddingBottom + 8;
const globalStyle: GlobalStyle = useMemo(
@@ -142,7 +142,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
return rowCount * rowHeight;
}, [flamethreadData]);
// 此参数含义: 可视窗口Height / 火焰图Height
// Meaning of this parameter: Visual Window Height/Flame Map Height
const yScaleRangeFactor = useMemo(() => {
const rowCount = uniqWith(
flamethreadData,
@@ -182,7 +182,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
const tickData = scale.tickData(visibleColumnCount);
const dx =
tickData.length > 1
? // eslint-disable-next-line @typescript-eslint/no-magic-numbers -- 计算需要
? // eslint-disable-next-line @typescript-eslint/no-magic-numbers -- calculation required
(range[1] - range[0]) / (tickData.length - 1) / 2
: 0;
@@ -197,7 +197,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
style: { dx: -dx },
formatMethod: (_value: string) => {
const value = Number(_value);
// 特化逻辑: 隐藏0刻度
// Specialized logic: hide 0 ticks
if (dx > 0 && value === 0) {
return '';
}
@@ -213,7 +213,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
{
type: GrammarMarkType.component,
componentType: ComponentEnum.grid,
tickCount: visibleColumnCount, // vgrammer库的类型写的不严谨实际是可用的
tickCount: visibleColumnCount, // The types of the vgrammer library are not strictly written, but are actually usable
scale: 'xScale',
gridType: 'line',
gridShape: 'line',
@@ -270,7 +270,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
lineWidth,
lineDash,
visible: true,
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- 尺寸计算,无须处理
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- size calculation, no processing required
distance: lineWidth / 2,
};
},
@@ -290,7 +290,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
lineWidth,
lineDash,
visible: true,
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- 尺寸计算,无须定义常量
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- size calculation, no need to define constants
distance: lineWidth / 2,
};
},
@@ -311,7 +311,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
lineWidth,
lineDash,
visible: true,
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- 尺寸计算,无须定义常量
// eslint-disable-next-line @typescript-eslint/no-magic-numbers -- size calculation, no need to define constants
distance: lineWidth / 2,
};
},
@@ -337,7 +337,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
},
encode: {
update: {
pickable: false, // vgrammer库的类型写的不严谨
pickable: false, // The type of vgrammer library is not strictly written
text: labelText ?? defaultLabelText,
fill: (datum, element, params) => datum?.labelStyle.fill,
},
@@ -542,7 +542,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
[selectedKey],
);
// 创建/更新view
// Create/update view
useLayoutEffect(() => {
const initializeYScale = (view: IView) => {
const yScale = view?.getScaleById('yScale');
@@ -556,12 +556,12 @@ export const Flamethread: FC<FlamethreadProps> = props => {
const registerEvent = (view: IView) => {
const rectElm = view?.getMarkById('rect');
// rect点击事件
// Rect click event
rectElm?.addEventListener('click', ((event, element) => {
onClick?.(event, element);
}) as InteractionEventHandler);
// rect hover高亮
// Rect hover highlight
view?.interaction('element-highlight', {
selector: 'rect',
highlightState: 'hover2',
@@ -581,7 +581,7 @@ export const Flamethread: FC<FlamethreadProps> = props => {
});
}
// rect hover显示tooltip
// Rect hover tooltip
if (tooltip) {
view?.interaction('tooltip', {
selector: 'rect',

View File

@@ -51,7 +51,7 @@ export interface RectNode {
end: number;
rectStyle?: RectStyle;
labelStyle?: Pick<LabelStyle, 'fill'>;
// 其他字段,会透传
// Other fields will be passed through
extra?: unknown;
}

View File

@@ -125,7 +125,7 @@ export const CommonEdge = (props: EdgeProps<EdgeData>) => {
const topologyEdgeStatus = getTopologyItemStatus(data?.tailDynamicSpanNode);
// vertical类型线段布局时采用节点位置进行定位从而使线段起点和终点定位在节点开始位置
// When the vertical type line segment is laid out, the node position is used for positioning, so that the start and end points of the line segment are positioned at the beginning of the node
const adaptedSourceX = isVerticalEdge(sourcePosition)
? data?.layoutInfo?.customSourceX ?? sourceX
: sourceX;

View File

@@ -45,7 +45,7 @@ export const CommonNode = (props: NodeProps<NodeData>) => {
data: { name, icon, layoutDirection, dynamicSpanNode },
} = props;
// 特化逻辑动态tracing中没有workflow_start节点topo中workflow_start节点默认高亮
// Specialization logic: There are no workflow_start nodes in dynamic tracing, workflow_start nodes in topo are highlighted by default
const topologyNodeStatus =
Number(type) === SpanCategory.WorkflowStart
? TopologyEdgeStatus.DYNAMIC

View File

@@ -111,11 +111,11 @@ export const useGenerateTopology = (
const [topologicalData, setTopologicalData] = useState<TopologicalData>();
// 静态topo接口原始数据及计算数据缓存
// Static topo interface original data source and computing data cache
const staticTopologyDataRef = useRef<Record<string, StaticTopologyDataCache>>(
{},
);
// 某个span最近的上游可查询到topo的span节点缓存
// The nearest upstream of a span can be queried to the span node cache of topo
const nearestTopologyRootSpanMapRef = useRef<DynamicNodeMap>({});
const resetStatus = () => {
@@ -136,7 +136,7 @@ export const useGenerateTopology = (
const traceTree = buildTraceTree(spanData, false);
return extractOriginDynamicNodeMap(traceTree, botId || entityId || '');
} else {
// TraceId类型暂不实现
// The TraceId type is not yet implemented
return undefined;
}
};
@@ -151,7 +151,7 @@ export const useGenerateTopology = (
setLoading(true);
/**
* Step 1
* 获取动态tracing tree数据找到当前选中节点
* Get dynamic tracing tree data and find the currently selected node
*/
const originDynamicData = getOriginDynamicData();
const currentSelectedSpanNode =
@@ -163,7 +163,7 @@ export const useGenerateTopology = (
}
const getNearestTopologyRootSpanNode = () => {
// 命中缓存
// hit cache
if (nearestTopologyRootSpanMapRef.current[selectedSpanId]) {
return nearestTopologyRootSpanMapRef.current[selectedSpanId];
}
@@ -178,7 +178,7 @@ export const useGenerateTopology = (
/**
* Step 2
* 从当前节点开始向上找到最近的可绘制topo的节点并请求得到静态原始topo数据
* Find the nearest drawable topo node starting from the current node and request static raw topo data
*/
const nearestTopologyRootSpanNode = getNearestTopologyRootSpanNode() as
| CSpanAttrInvokeAgent
@@ -193,7 +193,7 @@ export const useGenerateTopology = (
const { type } = nearestTopologyRootSpanNode;
// 当前支持InvokeAgentWorkflow类型分别取bot和workflow的id以及version
// Currently supports InvokeAgent and Workflow types, taking the id and version of bot and workflow respectively
const getStaticTopologyMetaData = (): Partial<
Pick<GetTopoInfoReq, 'resource_id' | 'version'>
> => {
@@ -237,7 +237,7 @@ export const useGenerateTopology = (
] as StaticTopologyDataCache | undefined;
const topoInfo =
// 优先从缓存读取
// Read from cache first
staticTopologyDataCache?.topoInfoMap ??
(await fetchStaticTopologyData(processedGetTopoInfoReq));
@@ -248,16 +248,16 @@ export const useGenerateTopology = (
/**
* Step 3
* 过滤出当前静态topo节点中需要展示动态调用链路的节点
* Filter out the nodes in the current static topo node that need to display the dynamic call link
*/
const topoMetaInfo =
// 优先从缓存读取
// Read from cache first
staticTopologyDataCache?.topoMetaInfo ??
generateTopologyMetaInfo(topoInfo);
const upstreamNodeMap = staticTopologyDataCache?.upstreamNodeMap ?? {};
// 判断当前节点自身是否有topo信息即当前所需要展示的topo的根节点
// Determine whether the current node itself has topo information (that is, the root node of the topo that needs to be displayed at present)
const isSelectedNodeTopologyRoot =
currentSelectedSpanNode.id === nearestTopologyRootSpanNode.id;
@@ -266,8 +266,8 @@ export const useGenerateTopology = (
getNodeResourceId(currentSelectedSpanNode, botId || entityId || '')
];
// 如果当前节点为topo根节点那么展示所有动态节点信息
// 否则过滤出当前节点在静态topo中的所有上游节点只对这些上游节点进行展示
// If the current node is the topo root node, then all dynamic node information is displayed.
// Otherwise, filter out all upstream nodes of the current node in the static topo and display only those upstream nodes
const currentDynamicNodeMap = isSelectedNodeTopologyRoot
? originDynamicData.dynamicNodeMap
: filterObjectByKeys(
@@ -288,7 +288,7 @@ export const useGenerateTopology = (
/**
* Step 4
* 补齐动态节点信息 & 布局信息到静态topo
* Complement dynamic node information & layout information to static topo
*/
const originalTopologicalData = completeDynamicTopologyInfo(
topoInfo,
@@ -301,7 +301,7 @@ export const useGenerateTopology = (
layoutDirection,
);
// 存入缓存
// cache
staticTopologyDataRef.current[staticTopologyDataMapKey] = {
topoInfoMap: topoInfo,
topoMetaInfo,
@@ -318,7 +318,7 @@ export const useGenerateTopology = (
}, [botId, entityId, spaceId, dataSource, selectedSpanId]);
useEffect(
// 销毁时清空缓存
// Clear cache on destruction
() => () => {
staticTopologyDataRef.current = {
topoInfoMap: {},

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import ReactFlow, { ReactFlowProvider } from 'reactflow';
import { useMemo } from 'react';
@@ -31,15 +31,15 @@ import s from './index.module.less';
const TopologyFlowContent = (props: TopologyFlowProps) => {
const { style, className, renderHeader, ...restProps } = props;
// 计算topo数据
// Calculate topo data
const [loading, topologicalData] = useGenerateTopology({
...restProps,
});
// 每次topo数据变更后计算topo布局信息
// After each topo data change, calculate the topo layout information
const [topologyFlowDomRef] = useLayoutTopology(topologicalData);
// 渲染外部自定义header实现带有业务语义
// Rendering external custom header implementations (with business semantics)
const topologyHeader = useMemo(() => {
if (!renderHeader || !topologicalData) {
return null;
@@ -65,7 +65,7 @@ const TopologyFlowContent = (props: TopologyFlowProps) => {
{topologyHeader}
<div className={s['topology-flow-container-flow']}>
<ReactFlow
// @ts-expect-error 使用number类型枚举SpanType作为自定义type可忽略报错
// @ts-expect-error Use the number type to enumerate SpanType as a custom type, the error can be ignored
nodes={topologicalData.nodes}
edges={topologicalData.edges}
nodeTypes={CUSTOM_NODES}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type Node, type Edge } from 'reactflow';
import { type CSSProperties } from 'react';
@@ -52,7 +52,7 @@ export interface UseGenerateTopologyHookData {
selectedSpanId?: string;
}
// @ts-expect-error 使用number类型枚举SpanType作为自定义type可忽略报错
// @ts-expect-error Use the number type to enumerate SpanType as a custom type, the error can be ignored
export type TopologicalNode = Node<NodeData, SpanCategory>;
export type TopologicalEdge = Edge<EdgeData>;

View File

@@ -92,10 +92,10 @@ export const filterObjectByKeys = <T extends object>(
}, {} as T);
/**
* 静态topo数据中resource_id根据span类型的不同和不同的字段映射
* @param spanNode 当前span
* Static topo data resource_id different and different data field mapping according to span type
* @param spanNode current span
* @param entityId
* @returns 与静态topo所映射的id
* @Returns the id mapped to the static topo
*/
export const getNodeResourceId = (
spanNode: SpanNode,
@@ -103,7 +103,7 @@ export const getNodeResourceId = (
): string => {
const { category, id } = spanNode;
const workflowNodeId =
// 兼容合并后的Batch节点
// Compatible with merged Batch Nodes
(spanNode as CSPanBatch).workflow_node_id ??
(spanNode as CSpanAttrCondition).extra?.workflow_node_id;
if (workflowNodeId) {
@@ -133,7 +133,7 @@ export const generateStaticTopologyDataMapKey = (
return `${space_id}-${resource_id}-${version}-${env}-${resource_type}`;
};
// 单 agent 下 trace 里将去除 SpanType.InvokeAgent 节点,topology root 可能为 SpanType.UserInput
// The SpanType. InvokeAgent node will be removed from the trace under a single agent, and the topology root may be SpanType. UserInput.
export const getTopologyAgentRootType = () => {
const FLAGS = getFlags();
return FLAGS['bot.devops.use_user_input_as_agent']
@@ -142,7 +142,7 @@ export const getTopologyAgentRootType = () => {
};
export const isTopologyRootSpan = (span: CSpan) => {
// 只有基础工作流展示拓扑
// Only the base workflow shows the topology
if (span.type === SpanType.Workflow) {
return (span as CSpanAttrWorkflow).extra?.workflow_schema_type === 1;
}
@@ -160,7 +160,7 @@ export const extractOriginDynamicNodeMap = (
while (queue.length > 0) {
const currentNode = queue.shift() as SpanNode;
// 过滤掉broken节点
// Filter out broken nodes
if (currentNode.id === rootBreakSpanId) {
continue;
}
@@ -204,9 +204,9 @@ function findUserInputRootNode(dynamicNodeMap: DynamicNodeMap) {
}
/**
* 将动态节点填充给对应的静态节点和连线,补全其动态运行信息
* Fill the dynamic nodes to the corresponding static nodes and connections to complete their dynamic operation information
* @param staticTopoInfo
* @param dynamicNodeMap 当前所需要展示的动态节点map
* @Param dynamicNodeMap The dynamic node map that needs to be displayed at present
* @param layoutDirection
* @returns
*/
@@ -247,7 +247,7 @@ export const completeDynamicTopologyInfo = (
let dynamicSpanNode = dynamicNodeMap[resource_id] as SpanNode | undefined;
// 特化逻辑单agent场景下节点信息将不包含agent节点需要使用userInput替换
// Specialized logic: In a single agent scenario, the node information will not include the agent node, and it needs to be replaced with userInput.
if (
typedResourceKind === SpanCategory.Agent &&
!dynamicSpanNode &&
@@ -287,7 +287,7 @@ export const completeDynamicTopologyInfo = (
const { edge_id = '', source_node_id = '', target_node_id = '' } = item;
const sourceNodeInfo = nodeInfoMap[source_node_id];
// 特化逻辑动态tracing中没有workflow_start节点默认将其下游的动态节点填入
// Specialized logic: there is no workflow_start node in dynamic tracing, the dynamic node downstream is filled in by default
const isWorkflowStartNode =
Number(sourceNodeInfo?.node?.resource_kind) ===
SpanCategory.WorkflowStart;
@@ -297,7 +297,7 @@ export const completeDynamicTopologyInfo = (
const targetNode: SpanNode | undefined =
dynamicNodeMap[nodeInfoMap[target_node_id]?.resourceId];
// 特化逻辑单agent场景下节点信息将不包含agent节点需要使用userInput替换
// Specialized logic: In a single agent scenario, the node information will not include the agent node, and it needs to be replaced with userInput.
if (
sourceNodeInfo.node.resource_kind === SpanCategory.Agent &&
!sourceNode &&
@@ -364,7 +364,7 @@ const getStaticSpanTitle = (category: SpanCategory, name: string) => {
};
/**
* 进行对原始topo数据的布局和样式处理
* Layout and style processing of raw topo data
* @param originTopologicalData
* @param layoutDirection
* @returns
@@ -399,7 +399,7 @@ export const getLayoutedMeta = (
Dagre.layout(graphInstance);
// 采集节点的定位信息用于vertical类型的连线绘制时进行定位
// Acquire the positioning information of the node for positioning when drawing vertical types of connections
const nodeXAxisMap: Record<string, number> = {};
const layoutNodes: TopologicalNode[] = nodes.map(node => {
@@ -444,9 +444,9 @@ export const getTopologyItemStatus = (spanNode?: SpanNode) => {
};
/**
* 使用静态topo数据生成记录每个节点的上游节点的graph以及id map
* Generate graphs and id maps of upstream nodes for each node using static topo data
* @param topoInfo
* @returns graph和map的meta信息
* @Returns meta information for graphs and maps
*/
export const generateTopologyMetaInfo = (topoInfo: TopoInfo): TopoMetaInfo => {
const { nodes = [], edges = [] } = topoInfo;
@@ -471,11 +471,11 @@ export const generateTopologyMetaInfo = (topoInfo: TopoInfo): TopoMetaInfo => {
};
/**
* 查询静态topo图中某个节点的所有上游节点使用DP降低复杂度
* @param selectedNodeId 当前span id
* @param topoGraph 存有记录每个节点的上游节点的graph
* @param upstreamNodeMap 存有记录某个节点所有上游节点的map
* @returns 上游所有节点的id list
* Query all upstream nodes of a node in a static topo graph, using DP to reduce complexity
* @param selectedNodeId Current span id
* @Param topoGraph holds a graph that records the upstream nodes of each node
* @Param upstreamNodeMap holds a map that records all upstream nodes of a node
* @Returns the id list of all nodes upstream
*/
export const getAllUpstreamTopologyNodeIds = (
selectedNodeId: string,

View File

@@ -56,7 +56,7 @@ const TraceFlamethread: FC<TraceFlamethreadProps> = props => {
onClick,
} = props;
// 初始化flamethreadData
// Initialize flamethreadData
useEffect(() => {
if (dataType === DataSourceTypeEnum.SpanData && spanData) {
if (spanData?.length === 0 && flamethreadData.length === 0) {

View File

@@ -68,18 +68,18 @@ const genRectNode = (info: {
};
export const spanData2flamethreadData = (spanData: CSpan[]): RectNode[] => {
// 1. 根据spans组装call trees
// 1. According to spans, assemble call trees
const callTrees = buildCallTrees(spanData);
// 2. 生成tartSpan
// 2. Generate tartSpan
const startSpan: SpanNode = getRootSpan(callTrees, false);
// 3. 获取 break节点(非start的根节点都是breakSpan)
// 3. Get the break node (all non-start root nodes are breakSpan)
const breakSpans: SpanNode[] = getBreakSpans(callTrees, false);
let rstSpans: SpanNode[] = [];
// 前序搜索,确保父节点在前
// Preorder search to ensure that the parent node is in front
const walk = (spans: SpanNode[]) => {
rstSpans = rstSpans.concat(spans);
spans.forEach(span => {
@@ -93,13 +93,13 @@ export const spanData2flamethreadData = (spanData: CSpan[]): RectNode[] => {
}
walk(breakSpans);
// 过滤掉不显示的span节点
// Filter out undisplayed span nodes
rstSpans = rstSpans.filter(span => isVisibleSpan(span));
// start_time稳定排序
// Sort by start_time
const sortedSpans = sortBy(rstSpans, o => o.start_time);
// 添加跟节点
// Add follow node
sortedSpans.unshift(startSpan);
const rectNodes: RectNode[] = [];

View File

@@ -90,7 +90,7 @@ const TraceTree: FC<TraceTreeProps> = props => {
newWindow: true,
});
};
// 初始化flamethreadData
// Initialize flamethreadData
useEffect(() => {
if (dataType === DataSourceTypeEnum.SpanData && spanData) {
if (spanData?.length === 0 && treeData === undefined) {

View File

@@ -38,8 +38,8 @@ export type TraceTreeProps = {
export interface SpanDetail {
isCozeWorkflowNode: boolean;
workflowLevel: number; // workflow 层级
workflowVersion?: string; // 父节点透传给子节点
workflowLevel: number; // Workflow Hierarchy
workflowVersion?: string; // Parent node passes through to sub-node
}
export interface WorkflowJumpParams {

View File

@@ -99,7 +99,7 @@ const genTitleRender = ({
}
const config = spanCategoryConfig[category];
// 虚拟的break的根节点
// Virtual broken root node
if (span.id === rootBreakSpanId) {
return (
<div
@@ -181,7 +181,7 @@ export const spanData2treeData = (
span,
},
};
// breakSpan节点
// breakSpan node
if (span.id === rootBreakSpanId) {
treeNode = {
...treeNode,
@@ -202,7 +202,7 @@ export const getSpanInfoMap = (root: SpanNode) => {
const spanInfoMap: Record<string, SpanDetail | undefined> = {};
const bfs = (node: SpanNode) => {
// coze workflow 设置 isCozeWorkflowNode
// Coze workflow settings isCozeWorkflowNode
if (
node.type === SpanType.Workflow &&
getSpanProp(node, 'workflow_schema_type') === 1
@@ -218,7 +218,7 @@ export const getSpanInfoMap = (root: SpanNode) => {
: undefined,
};
} else {
// coze workflow 的子节点设置 isCozeWorkflowNode
// Coze workflow sub-node settings isCozeWorkflowNode
const { isCozeWorkflowNode, workflowLevel = 0 } =
spanInfoMap[node.parent_id] || {};
@@ -234,7 +234,7 @@ export const getSpanInfoMap = (root: SpanNode) => {
}
}
// 递归
// recursion
for (const childNode of node.children || []) {
bfs(childNode);
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
/* eslint-disable @typescript-eslint/no-magic-numbers -- 本组件中会有很多位置计算的数字,无须处理*/
/* eslint-disable @typescript-eslint/no-magic-numbers -- there will be many numbers calculated in this component, no need to deal with them*/
import { type FC, useCallback, useMemo, useState } from 'react';
import { isFunction, mergeWith } from 'lodash-es';
@@ -72,8 +72,8 @@ const Tree: FC<TreeProps> = ({
);
/**
* 使得指定的selectKey的Line置于顶层。
* 通过调整line顺序来实现z-index效果key${selectKey}的line在最上层
* Causes the Line of the specified selectKey to be placed at the top level.
* By adjusting the line order, the z-index effect is achieved: the line with the key ${selectKey} is at the top
*/
const adjustLineOrder = useCallback(
(lines: Line[]): Line[] => {
@@ -90,7 +90,7 @@ const Tree: FC<TreeProps> = ({
}
}
// 支持根据zIndex控制高度
// Support controlling height according to zIndex
lines.sort((lineA, lineB) => {
const zIndexA = lineA.endNode.zIndex ?? -1;
const zIndexB = lineB.endNode.zIndex ?? -1;
@@ -141,7 +141,7 @@ const Tree: FC<TreeProps> = ({
);
/**
* 根据line信息生成svg path。 colNo, rowNum都从0开始
* Generate svg path from line information. colNo, rowNum all start from 0
*/
const genSvgPath = useCallback(
(line: Line): string => {
@@ -155,41 +155,41 @@ const Tree: FC<TreeProps> = ({
const { lineRadius = 0, lineGap = 0 } = normalLineStyle;
const nodeHeight = nodeBoxHeight + verticalInterval;
// 起始点
// starting point
const startX = startColNo * indent + offsetX;
const startY =
startRowNo * nodeHeight + (nodeBoxHeight + verticalInterval / 2);
if (startColNo === endColNo) {
// 竖线的长度
// The length of the vertical line
const lineASize =
(endRowNo - startRowNo - 1) * nodeHeight + verticalInterval;
// 移动到起始点
// Move to the starting point
const moveToStartPoint = `M ${startX} ${startY + lineGap}`;
// 竖线
// vertical line
const lineA = `L ${startX} ${startY + lineASize}`;
return `${moveToStartPoint} ${lineA}`;
} else {
// 竖线的长度
// The length of the vertical line
const lineASize =
(endRowNo - startRowNo - 1) * nodeHeight +
verticalInterval / 2 +
nodeHeight / 2 -
lineRadius;
// 横线的长度
// The length of the horizontal line
const lineBSize =
(endColNo - startColNo) * indent - offsetX - lineRadius;
// 结束点的坐标
// Coordinates of the end point
const endX = startX + lineBSize + lineRadius;
const endY = startY + lineASize + lineRadius;
// 移动到起始点
// Move to the starting point
const moveToStartPoint = `M ${startX} ${startY + lineGap}`;
// 竖线
// vertical line
const lineA = `L ${startX} ${startY + lineASize}`;
// 二次贝塞尔曲线
// Quadratic Bézier Curve
const qbc = `Q ${startX} ${endY} ${startX + lineRadius} ${endY}`;
// 横线
// horizontal line
const lineB = `L ${endX - lineGap} ${endY}`;
return `${moveToStartPoint} ${lineA} ${qbc} ${lineB}`;
}

View File

@@ -20,8 +20,8 @@ export type LineAttrs = Pick<
SVGAttributes<unknown>,
'stroke' | 'strokeDasharray' | 'strokeWidth'
> & {
lineRadius?: number; // line圆角半径 注意:这个数值不要大于 indent/2
lineGap?: number; // line距离boxgap
lineRadius?: number; // Line fillet radius, note: this value should not be greater than indent/2
lineGap?: number; // Line distance box gap
};
export interface LineStyle {
@@ -34,13 +34,13 @@ export interface LineStyle {
export interface TreeNode {
key: string;
title: ReactNode | ((nodeData: TreeNodeExtra) => ReactNode);
selectEnabled?: boolean; // 默认值 true
indentDisabled?: boolean; // 关闭缩进。 仅针对如下场景生效:子节点中的最后一个节点
lineStyle?: LineStyle; // 当指定了此属性时会覆盖全局的lineStyle
selectEnabled?: boolean; // Default value true
indentDisabled?: boolean; // Turn off indentation. Only works for the following scenarios: the last node in the sub-node
lineStyle?: LineStyle; // When this property is specified, the global lineStyle is overridden.
children?: TreeNode[];
linePath?: PathEnum[];
zIndex?: number;
// 其他字段,会透传
// Other fields will be passed through
extra?: unknown;
}
@@ -53,12 +53,12 @@ export enum PathEnum {
export type TreeNodeExtra = Omit<TreeNode, 'children'> & {
colNo: number;
rowNo: number;
unindented: boolean; // 相对于父节点,是否未缩进
selected: boolean; // 是否被选中
hover: boolean; // 是否hover
unindented: boolean; // Is it unindented relative to the parent node?
selected: boolean; // Is it selected?
hover: boolean; // Whether to hover
};
// 拉平后的TreeNode信息
// Flattened TreeNode information
export type TreeNodeFlatten = Omit<TreeNodeExtra, 'selected' | 'hover'>;
export interface Line {
@@ -67,9 +67,9 @@ export interface Line {
}
export interface GlobalStyle {
indent?: number; // 父节点和子节点的缩进距离
verticalInterval?: number; // node节点的垂直间距
nodeBoxHeight?: number; // node-box节点的高度
indent?: number; // Indent distance of parent and child nodes
verticalInterval?: number; // Vertical spacing of nodes
nodeBoxHeight?: number; // The height of the node-box node
offsetX?: number;
}
@@ -83,7 +83,7 @@ export interface TreeProps {
selectedKey?: string;
hoverKey?: string;
disableDefaultHover?: boolean;
indentDisabled?: boolean; // 关闭缩进。 仅针对如下场景生效:最后一个节点
indentDisabled?: boolean; // Turn off indentation. Only works for the following scenarios: the last node
lineStyle?: LineStyle;
globalStyle?: GlobalStyle;
className?: string;

View File

@@ -19,14 +19,14 @@ import { omit } from 'lodash-es';
import { type TreeNodeFlatten, type TreeNode, type Line } from './typing';
/**
* 基于TreeData生成:
* Generated based on TreeData:
*
* @param treeData tree原始数据
* @param options.indentDisabled 是否取消缩进。仅针对下述场景有效:异常节点+最后一个节点
* @param treeData tree original data source
* @Param options.indentDisabled Whether to unindent. Valid only for the following scenarios: exception node + last node
*
* @returns
* 1. nodes, 拉平后的node节点信息
* 2. lines, 用于将node进行连接
* 1. nodes, node information after leveling
* 2. lines, used to connect nodes
*/
export const flattenTreeData = (
treeData: TreeNode,
@@ -45,7 +45,7 @@ export const flattenTreeData = (
...omit(node, ['children']),
colNo: nodeColNo,
rowNo: nodes.length,
unindented: fatherNodeFlatten?.colNo === nodeColNo, // 未缩进
unindented: fatherNodeFlatten?.colNo === nodeColNo, // Unindented
};
nodes.push(nodeFlatten);
if (fatherNodeFlatten !== undefined) {
@@ -59,7 +59,7 @@ export const flattenTreeData = (
const childNodes = node.children;
childNodes.forEach((childNode, index) => {
// 取消缩进。 生效场景:异常节点+最后一个节点
// Cancel indentation. Effective scene: exception node + last node
const indentDisabled =
childNode.indentDisabled ?? options.indentDisabled;
if (indentDisabled && childNodes.length - 1 === index) {

View File

@@ -120,7 +120,7 @@ export const spanTypeConfigMap: SpanTypeConfigMap = {
label: I18n.t('analytic_query_subtype_value_knowledge'),
},
[SpanType.Chain]: {},
// 特定业务
// specific business
[SpanType.Hook]: {
label: I18n.t('analytics_query_invoke', {
name: 'Hook',

View File

@@ -40,13 +40,13 @@ import {
type CSpanAttrUserInput,
} from '../typings/cspan';
// 对根节点追加traceAdvanceInfo信息
// Append traceAdvanceInfo to the root node
const appendTraceAdvanceInfo = (
spans: CSpan[],
traceAdvanceInfo?: Omit<TraceAdvanceInfo, 'trace_id'>,
): CSpan[] =>
spans.map(span => {
// 修改根节点的状态。 根节点的tokensstatus以服务端获取的为准
// Modify the state of the root node. The tokens and status of the root node are subject to the server level
if (
span.type === SpanType.UserInput ||
span.type === SpanType.UserInputV2
@@ -100,7 +100,7 @@ const appendSpans = (spans: CSpan[], callTrees: SpanNode[]) => {
span.type === SpanType.UserInput ||
span.type === SpanType.UserInputV2
) {
// 根节点input_tokens_sumoutput_tokens_sum的数值以服务端获取为准,不做计算
// The values input_tokens_sum and output_tokens_sum of the root node are subject to server level acquisition and are not calculated
return span;
} else {
return {
@@ -112,7 +112,7 @@ const appendSpans = (spans: CSpan[], callTrees: SpanNode[]) => {
});
};
// 获取CSpan节点的tokens信息
// Get the tokens information of the CSpan node
const getCSpanTokens = (
span: CSpan,
): {
@@ -138,7 +138,7 @@ const getCSpanTokens = (
output_tokens: outputTokensRst,
};
} else {
// SingleSpan节点
// SingleSpan Node
return {
input_tokens: (getSpanProp(span, 'input_tokens') as number) ?? 0,
output_tokens: (getSpanProp(span, 'output_tokens') as number) ?? 0,
@@ -146,7 +146,7 @@ const getCSpanTokens = (
}
};
// 追加invokeAgentInfodialog_round和model字段
// Append invokeAgentInfo's dialog_round and model fields
const appendRootSpan = (info: { rootSpan: CSpan; spans: CSpan[] }): CTrace => {
const { rootSpan, spans } = info;
const rstSpan: CTrace = rootSpan;
@@ -181,14 +181,14 @@ interface UseSpanTransformReturn {
spans: CSpan[];
}
// start节点不存在时生成虚拟start节点
// When the start node does not exist, generate a virtual start node
export const appendVirtualStart = (spans: CSpan[]): CSpan[] => {
const startSpans = spans.filter(rootSpan => {
const { category } = rootSpan;
return category === SpanCategory.Start;
});
// 生成虚拟span
// Generate virtual spans
if (startSpans.length > 0) {
return spans;
} else {
@@ -204,27 +204,27 @@ export const useSpanTransform = (
const rst = useMemo(() => {
let spans = spans2CSpans(orgSpans, spanCategoryMeta);
// 追加虚拟span
// Append virtual span
spans = appendVirtualStart(spans);
// 追加traceAdvanceInfo信息
// append traceAdvanceInfo
spans = appendTraceAdvanceInfo(spans, traceAdvanceInfo);
// 根据spans组装call trees
// According to spans, assembling called trees
let callTrees = buildCallTrees(spans);
// 根节点超过 1 个,需要按 message id 过滤
// If there is more than 1 root node, it needs to be filtered by message id.
if (callTrees.length > 1 && messageId) {
callTrees = callTrees.filter(
root =>
!('extra' in root) ||
(root.extra && !('message_id' in root.extra)) ||
// 存在 message_id 的情况下,过滤 id 匹配的节点
// In the presence of message_id, filter the nodes with matching IDs
root.extra?.message_id === messageId,
);
}
const rootSpan = getRootSpan(callTrees, false);
// rootSpan的根节点调整: 追加invokeAgent信息
// Root node adjustment of rootSpan: add invokeAgent information
const rootSpanRst = appendRootSpan({
rootSpan,
spans,
@@ -237,10 +237,10 @@ export const useSpanTransform = (
return root.children?.some(subRoot => visit(targetId, subRoot)) ?? false;
};
// 过滤掉不在rootSpan中的节点
// Filter out nodes not in rootSpan
spans = spans.filter(span => visit(span.id, rootSpan));
// 对spans节点进行调整: spans中workflow节点tokens累加计算
// Adjust the spans node: the accumulation calculation of tokens in the workflow node in spans
const spansRst = appendSpans(spans, callTrees);
return {
rootSpan: rootSpanRst,

View File

@@ -23,13 +23,13 @@ export {
} from './components/flamethread';
export { default as Tree, type MouseEventParams } from './components/tree';
export { useSpanTransform } from './hooks/use-span-transform';
// Tree和Flamethread的参数类型
// Parameter types for Tree and FlamethRead
export { DataSourceTypeEnum } from './typings/graph';
export {
// useSpanTransform相关类型
// useSpanTransform related types
type SpanCategoryMeta,
// useSpanTransform 生成的定制span
// useSpanTransform generated custom spans
type CSpan,
type CTrace,
type CSpanSingle,

View File

@@ -62,11 +62,11 @@ type CSpanCommonProp = Pick<
Span,
'trace_id' | 'id' | 'parent_id' | 'name' | 'type' | 'status'
> & {
start_time: number; // 默认为Int64用起来不方便
latency: number; // 默认为Int64用起来不方便
category?: SpanCategory; // 加载Meta失败时才为空
input_tokens_sum?: number; // 扩展字段,用于存储子节点的input_tokens之和
output_tokens_sum?: number; // 扩展字段,用于存储子节点的output_tokens之和
start_time: number; // The default is Int64, which is inconvenient to use
latency: number; // The default is Int64, which is inconvenient to use
category?: SpanCategory; // Empty only when Meta fails to load
input_tokens_sum?: number; // Extended field to store the sum of sub-node input_tokens
output_tokens_sum?: number; // Extended field to store the sum of sub-node output_tokens
};
type GenCSpan<T> = CSpanCommonProp & {

View File

@@ -22,8 +22,8 @@ export enum DataSourceTypeEnum {
}
export interface DataSource {
// 取值为traceId时组件会根据traceId查询SpanData
// When the value is traceId, the component queries SpanData based on traceId.
type: DataSourceTypeEnum;
spanData?: CSpan[]; // type为spanData时特有字段
traceId?: string; // type为traceId时特有字段
spanData?: CSpan[]; // When type is spanData, unique fields
traceId?: string; // When type is traceId, unique field
}

View File

@@ -36,7 +36,7 @@ export type SpanNode = CSpan & {
export const getSpanDataByTraceId = (traceId: string): CSpan[] => [];
// 获取tree的跟节点
// Get the heel node of the tree
export const buildCallTrees = (
spans: CSpan[],
splitBatchSpan = true,
@@ -49,7 +49,7 @@ export const buildCallTrees = (
spans.forEach(span => {
const curSpan = { ...span, children: [] };
// Batch节点
// Batch Node
if ('spans' in span && splitBatchSpan) {
span.spans.forEach(subSpan => {
map[subSpan.id] = curSpan;
@@ -88,7 +88,7 @@ export const getRootSpan = (spans: SpanNode[], needBuildTrees = true) => {
}
});
// 无start的场景: 虚拟一个startSpan供多方使用火焰图树状图详情图以确保一致多个startSpans则取第一个
// Scenarios without start: virtual one StartSpan (for multiple parties to use, flame map, tree map, detail map to ensure consistency); multiple StartSpans, take the first one
return startSpans.length > 0 ? startSpans[0] : genVirtualStart(rootSpans);
};
@@ -150,7 +150,7 @@ export const getStatusLabel = (
return spanStatusConfigMap[status]?.label ?? '';
};
// start节点不存在时生成虚拟start节点
// When the start node does not exist, generate a virtual start node
const getRootBreakSpan = (breakSpans: SpanNode[]): SpanNode => ({
id: rootBreakSpanId,
parent_id: '',
@@ -163,7 +163,7 @@ const getRootBreakSpan = (breakSpans: SpanNode[]): SpanNode => ({
children: breakSpans,
});
// 根据switchAgent/restartAgent建立父子关系
// Create a parent-child relationship according to switchAgent/reStartAgent
const handleAgent = (spans: SpanNode[]): SpanNode[] => {
const getAgent = (startAt: number, agents: SpanNode[]) => {
const len = agents.length;
@@ -213,7 +213,7 @@ const handleAgent = (spans: SpanNode[]): SpanNode[] => {
return [...rstSpans, ...agentSpans];
};
// 只有特殊的节点类型可以作为根节点
// Only special node types can be used as root nodes
const isTreeRootSpanType = (type: SpanType) =>
[
SpanType.InvokeAgent,
@@ -222,7 +222,7 @@ const isTreeRootSpanType = (type: SpanType) =>
SpanType.LLMCall,
SpanType.WorkflowLLMCall,
SpanType.WorkflowLLMBatchCall,
// BlockWise的都放在这里
// All BlockWise's are put here
SpanType.BWStart,
SpanType.BWEnd,
SpanType.BWBatch,
@@ -234,11 +234,11 @@ const isTreeRootSpanType = (type: SpanType) =>
SpanType.BWVariable,
SpanType.BWCallFlow,
SpanType.BWConnector,
// 新增类型都支持层级
// New types all support hierarchies
SpanType.Hook,
].includes(type);
// 依据调用树构建TraceTree
// Build TraceTree based on call tree
const callTree2TraceTree = (rootSpan: SpanNode): SpanNode => {
const rstSpans: SpanNode[] = [];
const walk = (span: SpanNode) => {
@@ -248,10 +248,10 @@ const callTree2TraceTree = (rootSpan: SpanNode): SpanNode => {
rstSpans.push(callTree2TraceTree(subSpan));
} else {
if (isVisibleSpan(subSpan)) {
// 当前节点加入到 rootSpan.children
// The current node is added to rootSpan.children
rstSpans.push(omit(subSpan, 'children'));
}
// 递归子节点(当前节点)。 注意:隐藏的节点类型,也要递归的。 当前节点隐藏,其子节点有可能是显示的
// Recursive sub-node (current node). Note: The type of hidden node should also be recursive. The current node is hidden, and its sub-node may be displayed
walk(subSpan);
}
});
@@ -265,25 +265,25 @@ const callTree2TraceTree = (rootSpan: SpanNode): SpanNode => {
};
export const buildTraceTree = (spans: SpanNode[], splitBatchSpan?: boolean) => {
// 1. 根据spans组装call trees
// 1. According to spans, assemble call trees
const callTrees = buildCallTrees(spans, splitBatchSpan);
// 2. 生成startSpan
// 2. Generate the gastSpan
const startSpan: SpanNode = getRootSpan(callTrees, false);
// 3. 获取 break节点(非start的根节点都是breakSpan)
// 3. Get the break node (all non-start root nodes are breakSpan)
const breakSpans: SpanNode[] = getBreakSpans(callTrees, false);
// 4. 根据调用tree生成PRD中的Tree(即PRD中的Tree)
// 4. According to the call tree, generate the Tree in PRD (ie, the Tree in PRD)
const treeStartSpan = callTree2TraceTree(startSpan);
if (breakSpans.length > 0) {
// 5. 将所有breakSpans挂载到rootBreakSpan节点下
// 5. Mount all breakSpans under the rootBreakSpan node
const breakSpan: SpanNode = getRootBreakSpan(breakSpans);
// 6. 根据调用tree生成TraceTree
// 6. Generate TraceTree according to the call tree
const treeBreakSpan = callTree2TraceTree(breakSpan);
// 7. treeBreakSpan挂在到treeStartSpan
// 7. Hang treeBreakSpan under treeStartSpan
treeStartSpan.children = treeStartSpan.children ?? [];
treeStartSpan.children.push(treeBreakSpan);
treeBreakSpan.parent = treeStartSpan;

View File

@@ -86,7 +86,7 @@ const getStatusForBatch = (spans: CSpanSingleForBatch[]): SpanStatus => {
return isSuccess ? SpanStatus.Success : SpanStatus.Error;
};
// spans直接聚合,生成batchSpan
// Spans are directly polymerized to generate batchSpan.
const genBatchSpan = function (
spans: CSpanSingleForBatch[],
spanCategoryMap?: SpanCategoryMap,
@@ -94,13 +94,13 @@ const genBatchSpan = function (
if (spans.length === 0) {
return undefined;
}
// 合法性检查
// legality check
const taskTotal = spans[0].extra?.task_total;
const spans0 = spans.filter(curSpan => {
const curTaskTotal = curSpan.extra?.task_total;
return curTaskTotal !== taskTotal;
});
// taskTotal不全部相等,数据不合法
// taskTotal is not all equal, the data is invalid
if (spans0.length > 0) {
return undefined;
}
@@ -122,7 +122,7 @@ const aggregationBatchSpan = function (
) {
const batchSpans: CSPanBatch[] = [];
// 根据 workflowNodeId + type对span进行归类
// Sorting spans by workflowNodeId + type
const map: {
[key: string]: CSpanSingleForBatch[];
} = {};
@@ -136,14 +136,14 @@ const aggregationBatchSpan = function (
map[type + workflowNodeId].push(span);
});
// 进一步根据时间+序号进行归类,生成CSpanBatch
// Further classify according to time + serial number to generate CSpanBatch
Object.keys(map).forEach(key => {
const workflowSpans = map[key];
// 排序:时间
// Sort by: Time
workflowSpans.sort(compareByStartAt);
// 根据task_index进行聚合
// Aggregate according to task_index
let curTaskIndexs: number[] = [];
let curSpans: CSpanSingleForBatch[] = [];
workflowSpans.forEach(span => {
@@ -153,7 +153,7 @@ const aggregationBatchSpan = function (
}
if (curTaskIndexs.includes(taskIndex)) {
// 序号存在了,则新开启一组
// If the serial number exists, open a new set.
const batchSpan = genBatchSpan(curSpans, spanCategoryMap);
if (batchSpan) {
batchSpans.push(batchSpan);
@@ -175,11 +175,11 @@ const aggregationBatchSpan = function (
};
/**
* 处理原始Span将额外节点字段统一到extra
* Process raw Span (unify extra node fields to extra)
* @param span Span
* @returns CSpanSingle
*/
// eslint-disable-next-line complexity -- 参数过多
// eslint-disable-next-line complexity -- too many parameters
export const span2CSpan = function (
span: Span,
spanCategoryMap?: SpanCategoryMap,
@@ -260,7 +260,7 @@ export const spans2CSpans = function (
? genSpanCategoryMap(spanCategoryMeta)
: undefined;
// 根据span.id进行去重
// Deduplicate according to span.id
const uniqSpans = uniqBy(spans, 'id');
// Span -> CSpanSingle

View File

@@ -69,7 +69,7 @@ export const getTokens = (
span.type === SpanType.UserInputV2 ||
span.type === SpanType.Workflow
) {
// SingleSpan节点 - Workflow
// SingleSpan Node - Workflow
const inputTokens = getSpanProp(span, 'input_tokens_sum') as number;
const outputTokens = getSpanProp(span, 'output_tokens_sum') as number;
@@ -78,7 +78,7 @@ export const getTokens = (
output_tokens: outputTokens,
};
} else {
// SingleSpan节点 - 非workflow节点
// SingleSpan Node - Non-workflow Node
const inputTokens = getSpanProp(span, 'input_tokens') as number;
const outputTokens = getSpanProp(span, 'output_tokens') as number;

View File

@@ -165,7 +165,7 @@ const getFieldInput = (span: CSpan): FieldItem => ({
const getStreamOutput = (span: CSpan): FieldItem => {
const value = getSpanProp(span, 'streaming_output') as StreamingOutputStatus;
// key starling key
// Key to starling key
return {
key: I18n.t('query_stream_output'),
value: streamingOutputStatusConfigMap[value]?.label,

View File

@@ -195,7 +195,7 @@ export const SideDebugPanel = (props: SideDebugPanelProps) => {
if (!spanCategory) {
await handleFetchTracesMetaInfo();
}
// 从某条消息进入
// Enter from a message
if (entranceMessageLogId) {
try {
const spans = await handleFetchQueryDetail(entranceMessageLogId);
@@ -232,7 +232,7 @@ export const SideDebugPanel = (props: SideDebugPanelProps) => {
setLoading(false);
}
}
//直接进入
//Direct entry
else {
try {
const spans = await handleFetchQuery();

View File

@@ -15,26 +15,26 @@
*/
/**
* 支持筛选的query时间范围
* Support for filtering query time ranges
*/
export const DATE_FILTERING_DAYS_NUMBER = 7;
export const FILTERING_OPTION_ALL = 'ALL';
/**
* query每次加载条数
* Query the number of pieces loaded per time
*/
export const FILTERING_LIMIT = 30;
export const TRACES_ADVANCE_INFO_TIME_BUFFER = 1000;
export const TIME_MINUTE = 60;
/**
* query拉取默认偏移量
* Query Pull Default Offset
*/
export const INITIAL_OFFSET = '0';
export const EMPTY_TEXT = '-';
/**
* query拉取防抖时间
* Query Pull anti-shake time
*/
export const QUERY_FILTER_DEBOUNCE_TIME = 300;
/**
* 调试台位置信息localStorage key
* Debug station location information localStorage key
*/
export const DEBUG_PANEL_LAYOUT_KEY = 'coze_debug_panel_layout_config';

View File

@@ -31,7 +31,7 @@ export type UseDebugPanelLayoutConfig = () => [
];
/**
* 获取和修改存储在localStorage中的调试台布局数据
* Get and modify debug bench layout data stored in localStorage
* @returns UseDebugPanelLayoutConfig
*/
export const useDebugPanelLayoutConfig: UseDebugPanelLayoutConfig = () => {

View File

@@ -36,35 +36,35 @@ interface DebugPanelStore {
isPanelShow: boolean;
basicInfo: BasicInfo;
/**
* 当前选中的Query LogID
* The currently selected Query LogID
*/
entranceMessageLogId?: string;
/**
* 日期筛选结果
* date filter results
*/
targetDateId?: QueryFilterItemId;
/**
* 状态筛选结果
* status filter results
*/
targetExecuteStatusId?: QueryFilterItemId;
/**
* 当前选中的Trace节点信息
* Trace node information currently selected
*/
targetOverallSpanInfo?: TargetOverallSpanInfo;
/**
* 当前计算后的Trace列表
* Current Calculated Trace List
*/
enhancedOverallSpans: CSpan[];
/**
* 某条Trace下Span节点列表
* List of Spans under a Trace
*/
orgDetailSpans?: Span[];
/**
* 额外Span类型信息服务端提供
* Additional Span type information (server level provided)
*/
spanCategory?: SpanCategory;
/**
* 当前选中的Span节点信息
* Information about the currently selected Span node
*/
targetDetailSpan?: CSpan;
curBatchPage?: number;

View File

@@ -31,7 +31,7 @@ dayjs.extend(utc);
const jsonBig = JSONBig({ storeAsString: true });
/**
* 转换时间戳为当前格式化当前时区时间
* Convert timestamp to current format current time zone
* @param timestamp string | number
* @returns UTCTimeInfo
*/
@@ -62,7 +62,7 @@ export const getPastWeekDates = (): string[] => {
};
/**
* 从格式化时间提取其当前对应的开始/结束时间戳
* Extract its current corresponding start/end timestamp from the format time
* @param formattedDate QueryFilterItemId
* @returns DailyTime
*/

View File

@@ -43,7 +43,7 @@ export const getSpanProp = (span: CSpan, key: string) => {
};
/**
* 加强原始Span信息注入服务端采集的tokenstatus等信息)
* Enhance the original Span information (inject token, status, etc. collected at the server level)
* @param originSpans Span[]
* @param traceAdvanceInfo TraceAdvanceInfo[]
* @returns CSpan[]

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
// 目前拿不到文件的 size 信息 文件过大会导致浏览器卡顿 PDF 暂时不需要注册进去 之后放开
// At present, the size information of the file cannot be obtained. If the file is too large, the browser card PDF does not need to be registered for the time being, and then it will be released.
import { JsonPreviewBasePlugin } from '../base';
import OverlayAPI from '../../common/overlay';
import PdfPreviewContent from './preview';

View File

@@ -13,11 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @coze-arch/max-line-per-function -- 迁移代码 */
/* eslint-disable @typescript-eslint/no-explicit-any -- 迁移代码 */
/* eslint-disable max-lines -- 迁移代码 */
/* eslint-disable max-lines-per-function -- 迁移代码 */
/* eslint-disable @coze-arch/max-line-per-function -- migrating code */
/* eslint-disable @typescript-eslint/no-explicit-any -- migrate code */
/* eslint-disable max-lines -- migrate code */
/* eslint-disable max-lines-per-function -- migrating code */
import {
useState,
@@ -171,7 +171,7 @@ const MockSetSelectComp = (
const bizCtx: BizCtx = {
...bizSceneCtx,
connectorUID: uid,
connectorID: CONNECTOR_ID, // 业务线为Coze
connectorID: CONNECTOR_ID, // Business line for Coze
};
const { jump } = usePageJumpService();
@@ -457,7 +457,7 @@ const MockSetSelectComp = (
content={getTooltipInfo()}
visible={disabled && focused}
position="left"
style={{ display: disabled ? 'block' : 'none' }} // visible disabled不生效
style={{ display: disabled ? 'block' : 'none' }} // Visible disabled not effective
>
<div
style={style}

View File

@@ -72,7 +72,7 @@ export const MockSetDeleteModal = ({
} = mockSetInfo || {};
const [mockSetRefCount, setMockSetRefCount] = useState(-1);
// space信息
// Space information
const spaceType = useSpaceStore(s => s.space.space_type);
const isPersonal = spaceType === SpaceType.Personal;

View File

@@ -13,8 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable @coze-arch/max-line-per-function -- 迁移代码 */
/* eslint-disable @coze-arch/max-line-per-function -- migrating code */
import { useRef } from 'react';
import { I18n } from '@coze-arch/i18n';
@@ -87,10 +87,10 @@ export const MockSetEditModal = ({
const [FLAGS] = useFlags();
// 根据是否传入 id 判断是否为创建场景
// Determine whether to create a scene based on whether to pass in the id
const isCreate = !initialInfo.id;
// space信息
// Space information
const spaceType = useSpaceStore(s => s.space.space_type);
const isPersonal = spaceType === SpaceType.Personal;
@@ -216,7 +216,7 @@ export const MockSetEditModal = ({
>
{({ formState }) => (
<>
{/* mockSet名称 */}
{/* mockSet name */}
{disabled ? (
<Form.Slot
label={{
@@ -244,7 +244,7 @@ export const MockSetEditModal = ({
rules={mockSetInfoRules.name}
/>
)}
{/* mockSet描述 */}
{/* mockSet description */}
{disabled ? (
<Form.Slot
label={{
@@ -273,8 +273,8 @@ export const MockSetEditModal = ({
}}
/>
)}
{/* 二期支持autoGenerate*/}
{/* 社区版暂不支持该功能 */}
{/* Phase II supports autoGenerate*/}
{/* The community edition does not support this function for the time being */}
{isCreate && FLAGS['bot.devops.mockset_auto_generate'] ? (
<>
<Form.Checkbox

View File

@@ -24,7 +24,7 @@ export const REAL_DATA_MOCKSET = {
name: I18n.t('real_data'),
};
// 初始化仅有real_data
// Initialization only real_data
export const MOCK_OPTION_LIST = [REAL_DATA_MOCKSET];
export const POLLING_INTERVAL = 10000;
@@ -48,7 +48,7 @@ export const mockSetInfoRules: {
message: I18n.t('create_plugin_modal_nameerror'),
}
: {
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, //
pattern: /^[\w\s\u4e00-\u9fa5]+$/u, // Increased domestic support for Chinese
message: I18n.t('create_plugin_modal_nameerror_cn'),
},
],

View File

@@ -13,9 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* eslint-disable max-lines-per-function -- 代码迁移 */
/* eslint-disable @coze-arch/max-line-per-function -- 代码迁移 */
/* eslint-disable max-lines-per-function -- code migration */
/* eslint-disable @coze-arch/max-line-per-function -- code migration */
import { useEffect, useRef } from 'react';
import { nanoid } from 'nanoid';
@@ -149,7 +149,7 @@ export const useInitialGetEnabledMockSet = ({
}
};
// 取消
// cancel
const cancel = () => {
pollingTurnRef.current = undefined;
cancelReq.current?.();

View File

@@ -37,7 +37,7 @@ interface AutoFillButtonProps {
onAutoFill?: (schemas: NodeFormSchema[]) => void;
}
/** AI生成节点数据的按钮 */
/** AI button to generate node data */
export function AutoFillButton({
className,
style,
@@ -79,7 +79,7 @@ export function AutoFillButton({
abortRef.current?.abort();
};
// 社区版暂不支持该功能
// The community edition does not support this function for the time being
if (!FLAGS['bot.devops.testset_auto_gen'] || !(IS_OVERSEA || IS_BOE)) {
return null;
}

View File

@@ -28,7 +28,7 @@ interface FormLabelProps {
style?: CSSProperties;
}
// 内置的FormLabel样式不支持 typeLabel所以简单自定义
// The built-in FormLabel style does not support typeLabel, so it is easy to customize
export function FormLabel({
label,
typeLabel,

View File

@@ -70,7 +70,7 @@ interface TestsetEditSideSheetProps extends TestsetEditState {
onClose?: () => void;
onSuccess?: (testset?: TestsetData) => void;
onCancel?: () => void;
/** 是否为多人协作模式 */
/** Is it a multiplayer collaboration mode? */
isExpertMode?: boolean;
}
@@ -78,10 +78,10 @@ const TESTSET_NAME_FIELD = '__TESTSET_NAME__';
const TESTSET_DESC_FIELD = '__TESTSET_DESC__';
/**
* 特化逻辑:表单项赋默认值
* - Boolean类型:`false` 因为undefined的表现上和false一样容易引发用户误解
* - Object类型:`{}`
* - Array类型: `[]`
* Specialized logic: list entries are assigned default values
* - Boolean type: 'false' because undefined behaves the same as false, which is easy to cause user misunderstandings
* - Object type: '{}'
* - Array type: '[]'
*/
function assignDefaultValue(ipt: FormItemSchema) {
if (!isNil(ipt.value)) {
@@ -140,7 +140,7 @@ export function TestsetEditSideSheet({
const remoteSchemas = toNodeFormSchemas(res.schemaJson);
if (localSchemas.length) {
// 编辑模式比对本地和远程schema并尝试赋值
// Edit schema: compare local and remote schemas and try to assign values
const localSchemaMap: Record<string, FormItemSchema | undefined> = {};
traverseNodeFormSchemas(
localSchemas,
@@ -156,7 +156,7 @@ export function TestsetEditSideSheet({
}
});
} else {
// 创建模式:赋默认值
// Creation mode: assigns default values
traverseNodeFormSchemas(remoteSchemas, (schema, ipt) => {
assignDefaultValue(ipt);
});
@@ -178,7 +178,7 @@ export function TestsetEditSideSheet({
});
}, [visible, testset]);
// 给节点表单设置值
// Set a value for the node form
useEffect(() => {
if (typeof nodeSchemas === 'undefined') {
return;
@@ -260,7 +260,7 @@ export function TestsetEditSideSheet({
}
};
// 提交表单
// Submit Form
const onSubmit = async () => {
setSubmitting(true);
try {
@@ -278,7 +278,7 @@ export function TestsetEditSideSheet({
ipt.value = val;
}
// 清除 object/array的空值,包括空字符串
// Clears null values of objects/arrays, including empty strings
if (
!val &&
(ipt.type === FormItemSchemaType.LIST ||
@@ -287,7 +287,7 @@ export function TestsetEditSideSheet({
ipt.value = undefined;
}
// bool 类型 需要将枚举转为布尔值
// Bool type, you need to convert the enumeration to a boolean
if (ipt.type === FormItemSchemaType.BOOLEAN) {
ipt.value = transBoolSelect2Bool(ipt.value as ValuesForBoolSelect);
}
@@ -330,7 +330,7 @@ export function TestsetEditSideSheet({
});
testsetFormApi.current?.setValues(formValues);
// 设置值之后再校验一次
// Check again after setting the value
testsetFormApi.current?.validate(validateFields);
};

View File

@@ -41,7 +41,7 @@ import s from './node-form-section.module.less';
interface NodeFormSectionProps {
schema: NodeFormSchema;
/** AI生成中 */
/** AI is being generated */
autoGenerating?: boolean;
className?: string;
style?: CSSProperties;
@@ -49,7 +49,7 @@ interface NodeFormSectionProps {
const { Section, InputNumber } = Form;
/** 整数类型表单精度 */
/** integer type form precision */
const INTEGER_PRECISION = 0.1;
export function NodeFormSection({
@@ -62,7 +62,7 @@ export function NodeFormSection({
const renderSectionTitle = () => {
let sectionName = schema.component_name;
// 目前只有start和variable两种节点
// Currently only two nodes are start and variable
switch (schema.component_type) {
case ComponentType.CozeStartNode:
sectionName = I18n.t('workflow_testset_start_node');

View File

@@ -28,7 +28,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

@@ -33,7 +33,7 @@ import {
} from '../../types';
let ajv: Ajv | undefined;
/** jsonStr转为节点表单schema简单的`JSON.parse` */
/** jsonStr converts to a node form schema (simply'JSON.parse ') */
export function toNodeFormSchemas(jsonStr?: string): NodeFormSchema[] {
if (!jsonStr) {
return [];
@@ -48,7 +48,7 @@ export function toNodeFormSchemas(jsonStr?: string): NodeFormSchema[] {
}
}
/** 空值判断,null/undefined/NaN */
/** Null value judgment, null/undefined/NaN */
export function isNil(val: unknown) {
return (
typeof val === 'undefined' ||
@@ -61,7 +61,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 function isSameType(t1?: string, t2?: string) {
if (typeof t1 === 'undefined' || typeof t2 === 'undefined') {
return false;
@@ -70,7 +70,7 @@ export function isSameType(t1?: string, t2?: string) {
return isNumberType(t1) ? isNumberType(t2) : t1 === t2;
}
/** 两层for遍历schema (经常需要遍历,单独抽一个的函数) */
/** Two layers for traversing schema (often need to traverse, draw a single function) */
export function traverseNodeFormSchemas(
schemas: NodeFormSchema[],
cb: (s: NodeFormSchema, ip: FormItemSchema) => any,
@@ -83,9 +83,9 @@ export function traverseNodeFormSchemas(
}
/**
* 校验名称格式(参考插件名称)
* - 海外:仅支持输入字母、数字、下划线或空格
* - 国内:仅支持输入中文、字母、数字、下划线或空格
* 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,
@@ -109,19 +109,19 @@ 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;
}
/**
* 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,
@@ -143,12 +143,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) {
@@ -156,7 +156,7 @@ export function getTestsetNameRules({
return;
}
// 检查重复
// Check for duplicates
try {
const { isPass } = await debuggerApi.CheckCaseDuplicate({
bizCtx,
@@ -179,9 +179,9 @@ export function getTestsetNameRules({
}
/**
* 表单label
* - bot选择你需要的Bot
* - 其他:字段名
* Form label
* - bot: choose the bot you need
* - Other: field names
*/
export function getLabel(formSchema: FormItemSchema) {
return formSchema.type === FormItemSchemaType.BOT
@@ -207,7 +207,7 @@ function getSubType(type: string) {
}
}
/** 类型标签 */
/** type label */
export function getTypeLabel(formSchema: FormItemSchema) {
switch (formSchema.type) {
case FormItemSchemaType.STRING:
@@ -230,8 +230,8 @@ export function getTypeLabel(formSchema: FormItemSchema) {
/**
* placeholder
* - bot:请选择bot
* - 其他xx必填
* - bot: Please select bot
* - Other: xx required
*/
export function getPlaceholder({ name, type }: FormItemSchema) {
if (type === FormItemSchemaType.BOT) {
@@ -245,7 +245,7 @@ export function getPlaceholder({ name, type }: FormItemSchema) {
});
}
/** 字段在表单中的唯一字段名 */
/** The unique field name of the field in the form */
export function getSubFieldName(
formSchema: NodeFormSchema,
itemSchema: FormItemSchema,
@@ -322,8 +322,8 @@ function validateByJsonSchema(val: any, jsonSchema: any) {
}
/**
* 自定义表单的额外参数
* 目前只对array和object表单加jsonSchema校验
* Customize the form's additional parameters
* Currently only jsonSchema validation is applied to array and object forms
*/
export function getCustomProps(formItemSchema: FormItemSchema) {
switch (formItemSchema.type) {
@@ -368,7 +368,7 @@ export enum ValuesForBoolSelect {
UNDEFINED = 'undefined',
}
/** 布尔类型选项 */
/** Boolean Type Options */
export const optionsForBoolSelect = [
{
value: ValuesForBoolSelect.TRUE,

View File

@@ -42,7 +42,7 @@
}
.semi-select-option-list {
/* stylelint-disable-next-line declaration-no-important -- semi-select-option-listmax-height写在了style上所以要important覆盖 */
/* stylelint-disable-next-line declaration-no-important -- semi-select-option-list max-height is written on style, so override important */
max-height: 208px !important;
&::-webkit-scrollbar-track {

View File

@@ -43,12 +43,12 @@ import {
import s from './index.module.less';
export interface TestsetSelectProps {
/** 当前testset */
/** Current testset */
testset: TestsetData | undefined;
placeholder?: string;
/** 是否有workflow编辑权限也挂放在外层的 TestsetManageProvider上组件上的editable优先级更高 */
/** Whether there is workflow editing permission, it is also hung on the outer TestsetManageProvider, and the editable priority on the component is higher */
editable?: boolean;
/** 编辑面板mask */
/** Edit panel mask */
editSideSheetMask?: boolean;
onSelect: (v?: TestsetData) => void;
className?: string;
@@ -57,7 +57,7 @@ export interface TestsetSelectProps {
const DEBOUNCE_DELAY = 200;
/** option key, 更新 nameincompatibleinput时都要重新渲染 */
/** Option key, re-render when updating name, incompatible, input */
function getOptionKey({ caseBase, schemaIncompatible }: TestsetData) {
return `${caseBase?.caseID}_${caseBase?.name}_${caseBase?.input}_${
schemaIncompatible ? 0 : 1
@@ -65,12 +65,12 @@ function getOptionKey({ caseBase, schemaIncompatible }: TestsetData) {
}
/**
* Testset下拉选择组件
* 需配合`TestsetManageProvider`一起使用
* TestSet drop-down selection component
* Should be used with TestsetManageProvider
* @example
* ``` tsx
* <TestsetManageProvider
* // 一些必填参数 bizCtx bizComponentSubject editable formRenders
* //Some required parameters bizCtx bizComponentSubject editable formRendersitable formRenders
* >
* <TestsetSideSheet visible={visible} onClose={() => setVisible(false)} />
* </TestsetManageProvider>
@@ -107,7 +107,7 @@ export function TestsetSelect({
{},
);
// 首次加载
// first load
useEffect(() => {
(async () => {
setPending(true);
@@ -153,7 +153,7 @@ export function TestsetSelect({
closeTestsetEdit();
};
// 选中Testset
// Select Testset
const onSelectTestset = (val: SelectProps['value']) => {
if (typeof val !== 'string' || editRef.current) {
return;
@@ -163,7 +163,7 @@ export function TestsetSelect({
op => op.caseBase?.caseID === val,
);
// 不兼容的不可选中
// Incompatible unselectable
if (!selectedTestset || selectedTestset.schemaIncompatible) {
return;
}
@@ -220,11 +220,11 @@ export function TestsetSelect({
}
};
// 自定义选中选项
// Custom selected options
const renderSelectedItem: RenderSingleSelectedItemFn = () =>
testset ? <SelectedTestsetOptionItem data={testset} /> : null;
// testset为空的时候不展示下拉选项对大部分来说可能不需要看到这个下拉
// When the testset is empty, the drop-down option is not displayed (for most people, you may not need to see this drop-down).
if (pending) {
return null;
}
@@ -258,8 +258,8 @@ export function TestsetSelect({
{optionsData.list.map(data => (
<Select.Option
value={data.caseBase?.caseID}
// disabledoption编辑/删除唤起其他浮层后select不会自动失焦
// 用样式模拟disabled并修改onSelect选中不兼容testset的逻辑
// Disabled option Edit/Delete to select without auto-out of focus after evoking other floating layers
// Simulate disabled with styles and modify the logic of onSelect selecting incompatible testsets
className={cls(data.schemaIncompatible && s['incompatible-option'])}
key={getOptionKey(data)}
>

View File

@@ -32,7 +32,7 @@ import s from './testset-option-item.module.less';
interface TestsetOptionItemProps {
className?: string;
data: TestsetData;
/** 有编辑权限 */
/** Have editing permission */
editable?: boolean;
onEdit?: (data: TestsetData) => void;
onDelete?: (data: TestsetData) => void;
@@ -40,7 +40,7 @@ interface TestsetOptionItemProps {
const { Text } = Typography;
/** 多行文本展示优化 */
/** multi-line text display optimization */
const MULTILINE_TOOLTIP_STYLE: CSSProperties = { wordBreak: 'break-word' };
export function TestsetOptionItem({
@@ -54,7 +54,7 @@ export function TestsetOptionItem({
const testsetName = data.caseBase?.name ?? '-';
const onOptionClick = (evt: MouseEvent<HTMLDivElement>) => {
// 非兼容时需要阻止冒泡
// Bubbling needs to be prevented when incompatible
if (incompatible) {
evt.preventDefault();
evt.stopPropagation();
@@ -123,7 +123,7 @@ export function TestsetOptionItem({
);
}
/** 选中的回填项 */
/** Selected backfill */
export function SelectedTestsetOptionItem({
data,
className,

View File

@@ -52,7 +52,7 @@ export interface TestsetSideSheetProps {
visible: boolean;
editable?: boolean;
onClose: () => void;
/** 是否为多人协作模式 */
/** Is it a multiplayer collaboration mode? */
isExpertMode?: boolean;
}
@@ -89,19 +89,19 @@ interface TestsetQueryResult {
const DEFAULT_PAGE_SIZE = 30;
/**
* Testset管理侧边面板
* 需配合`TestsetManageProvider`一起使用
* Testset Management Side Panel
* Should be used with TestsetManageProvider
*
* @example
* ``` tsx
* <TestsetManageProvider
* // 一些必填参数 bizCtx bizComponentSubject editable formRenders
* //Some required parameters bizCtx bizComponentSubject editable formRendersitable formRenders
* >
* <TestsetSideSheet visible={visible} onClose={() => setVisible(false)} />
* </TestsetManageProvider>
* ```
*/
// eslint-disable-next-line @coze-arch/max-line-per-function -- 大组件>150行只超了不到5行哈
// eslint-disable-next-line @coze-arch/max-line-per-function -- large components > 150 lines, only less than 5 lines
export function TestsetSideSheet({
visible,
onClose,
@@ -142,7 +142,7 @@ export function TestsetSideSheet({
if (visible) {
patchTestsetResp({ list: [] });
reloadTestsetList();
// 检查schema
// Check schema
checkSchema();
}
}, [visible]);
@@ -240,7 +240,7 @@ export function TestsetSideSheet({
return (
<>
{/* Testset管理侧边面板 */}
{/* Testset Management Side Panel */}
<SideSheet
className={s.sidesheet}
title={
@@ -267,7 +267,7 @@ export function TestsetSideSheet({
<AutoLoadMore noMore={noMore} loadingMore={loadingMore} />
</div>
</SideSheet>
{/* Testset创建/编辑侧边面板 */}
{/* Testset Create/Edit Side Panel */}
<TestsetEditSideSheet
{...testsetEditState}
mask={false}

View File

@@ -39,9 +39,9 @@ import s from './testset-list-item.module.less';
interface TestsetListItemProps {
data: TestsetData;
onEdit?: (data: TestsetData) => void;
/** 点击了删除 */
/** I clicked delete. */
onClickDelete?: () => void;
/** 确认删除 */
/** Confirm deletion */
onDelete?: (data: TestsetData) => Promise<void>;
}

View File

@@ -15,8 +15,8 @@
*/
export enum TestsetManageEventName {
/** 创建测试集成功 */
/** Created test set successfully */
CREATE_TESTSET_SUCCESS = 'create_testset_success',
/** 点击AI生成节点入参 */
/** Click AI to generate node imported parameters */
AIGC_PARAMS_CLICK = 'aigc_params_click',
}

View File

@@ -37,7 +37,7 @@ export enum SchemaError {
INVALID = 'invalid',
}
/** 变量命名校验规则(对齐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]*$/;
@@ -112,21 +112,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;
}
@@ -143,7 +143,7 @@ function validateSchema(json?: string) {
try {
const schemas = JSON.parse(json) as NodeFormSchema[];
// schema为空 or start节点的inputs为空
// Schema is empty or start node inputs are empty
const isEmpty =
schemas.length === 0 ||
(schemas[0].component_type === ComponentType.CozeStartNode &&
@@ -166,7 +166,7 @@ function validateSchema(json?: string) {
}
}
/** 检查workflow节点表单是否为空(schema为空 or start节点的inputs为空) */
/** Checks if the workflow node form is empty (schema is empty or start node inputs are empty) */
export function useCheckSchema() {
const { bizComponentSubject, bizCtx } = useTestsetManageStore(store => store);
const [schemaError, setSchemaError] = useState(SchemaError.OK);

View File

@@ -26,11 +26,11 @@ import { type TestsetManageEventName } from './events';
export interface TestsetManageState {
bizCtx?: BizCtx;
bizComponentSubject?: ComponentSubject;
/** 编辑权限 */
/** edit permission */
editable?: boolean;
/** 表单渲染组件 */
/** form rendering component */
formRenders?: Partial<Record<FormItemSchemaType, NodeFormItem>>;
/** 埋点事件上报 */
/** Event tracking event reporting */
reportEvent?: (
name: TestsetManageEventName,
params?: Record<string, unknown>,
@@ -38,7 +38,7 @@ export interface TestsetManageState {
}
export interface TestsetManageAction {
/** 更新状态 */
/** update status */
patch: (s: Partial<TestsetManageState>) => void;
}

View File

@@ -47,13 +47,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;
}