chore: replace all cn comments of fe to en version by volc api (#320)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -51,7 +51,7 @@ export interface RectNode {
|
||||
end: number;
|
||||
rectStyle?: RectStyle;
|
||||
labelStyle?: Pick<LabelStyle, 'fill'>;
|
||||
// 其他字段,会透传
|
||||
// Other fields will be passed through
|
||||
extra?: unknown;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
// 当前支持InvokeAgent和Workflow类型,分别取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: {},
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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[] = [];
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}`;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ export type LineAttrs = Pick<
|
||||
SVGAttributes<unknown>,
|
||||
'stroke' | 'strokeDasharray' | 'strokeWidth'
|
||||
> & {
|
||||
lineRadius?: number; // line圆角半径 注意:这个数值不要大于 indent/2
|
||||
lineGap?: number; // line距离box的gap
|
||||
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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 => {
|
||||
// 修改根节点的状态。 根节点的tokens和status以服务端获取的为准
|
||||
// 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_sum和output_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 = (
|
||||
}
|
||||
};
|
||||
|
||||
// 追加invokeAgentInfo的dialog_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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 & {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user