export function convertToTraceInfo()

in spring-ai-alibaba-studio/ui/src/utils/trace_util.ts [52:164]


export function convertToTraceInfo(data: any): TraceInfo | null {
    const spans = data.scopeSpans.flatMap((scopeSpan: any) => scopeSpan.spans);

    if (spans.length < 2) return null;

    const spanMap = new Map<string, TraceDetail>();

    let totalInputTokens = 0;
    let totalOutputTokens = 0;
    let totalTokens = 0;
    let model = '';
    let input = '';
    let output = '';

    // First pass: Create TraceDetail for each span and calculate usage details
    spans.forEach((span: any) => {
        const attributes = span.attributes.reduce((acc: any, attr: any) => {
            acc[attr.key] = attr.value.stringValue;
            return acc;
        }, {});

        const prompt = extractAttributeValue(span, 'gen_ai.content.prompt') || '';
        const completion = extractAttributeValue(span, 'gen_ai.content.completion') || '';

        // Aggregate usage details
        if (attributes['gen_ai.usage.input_tokens']) {
            totalInputTokens += parseInt(attributes['gen_ai.usage.input_tokens'], 10);
        }
        if (attributes['gen_ai.usage.output_tokens']) {
            totalOutputTokens += parseInt(attributes['gen_ai.usage.output_tokens'], 10);
        }
        if (attributes['gen_ai.usage.total_tokens']) {
            totalTokens += parseInt(attributes['gen_ai.usage.total_tokens'], 10);
        }
        if (attributes['gen_ai.request.model']) {
            model = attributes['gen_ai.request.model'];
        }
        if (attributes['gen_ai.operation.name'] == 'chat') {
            model = attributes['gen_ai.request.model'];
            span.events.forEach(event => {
                const eventsAttributes = event.attributes.reduce((acc: any, attr: any) => {
                    acc[attr.key] = attr.value.arrayValue.values.map((value: any) => {
                        return { input: value.stringValue };
                    });
                    return acc;
                }, {});
                if (eventsAttributes['gen_ai.prompt']) {
                    input = eventsAttributes['gen_ai.prompt'];
                }
                if (eventsAttributes['gen_ai.completion']) {
                    output = eventsAttributes['gen_ai.completion'];
                }
            });
        }
        const costTime = span.endTimeUnixNano - span.startTimeUnixNano;
        spanMap.set(span.spanId, {
            title: `${span.name}   ${(costTime / 1e9).toFixed(2)}s`,
            input: prompt,
            key: span.spanId,
            output: completion,
            attributes: attributes,
            costTime: costTime,
            detail: {}, // Can include more info as needed
            children: [],
        });
    });

    // Second pass: Organize children
    spans.forEach((span: any) => {
        if (span.parentSpanId && spanMap.has(span.parentSpanId)) {
            const parentDetail = spanMap.get(span.parentSpanId);
            if (parentDetail) {
                parentDetail.children.push(spanMap.get(span.spanId)!);
            }
        }
    });

    const rootSpan = spans.find((span: any) => !span.parentSpanId);

    if (!rootSpan) return null;

    const rootAttributes = rootSpan.attributes.reduce((acc: any, attr: any) => {
        acc[attr.key] = attr.value.stringValue;
        return acc;
    }, {});

    return {
        id: rootSpan.traceId,
        latencyMilliseconds: calculateLatency(rootSpan.startTimeUnixNano, rootSpan.endTimeUnixNano).toString(),
        model: model,
        promptTokens: totalInputTokens,
        completionTokens: totalOutputTokens.toString(),
        totalTokens: totalTokens.toString(),
        tags: [], // Can be populated as needed
        calculatedTotalCost: '0.0', // Placeholder for actual calculation
        calculatedInputCost: '0.0', // Placeholder for actual calculation
        calculatedOutputCost: '0.0', // Placeholder for actual calculation
        usageDetails: {
            input: totalInputTokens.toString(),
            output: totalOutputTokens.toString(),
            total: totalTokens.toString(),
        },
        input: input,
        output: output,
        timestamp: rootSpan.endTimeUnixNano,
        costDetails: {
            input: '0.0', // Placeholder for actual calculation
            output: '0.0', // Placeholder for actual calculation
            total: '0.0', // Placeholder for actual calculation
        },
        traceDetail: spanMap.get(rootSpan.spanId)!,
    };
}