feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type ReactNode } from 'react';
import {
type CSpanSingle,
type CSPanBatch,
type CSpan,
getTokens,
getSpanProp,
type FieldItem,
fieldItemHandlers,
} from '@coze-devops/common-modules/query-trace';
import { checkIsBatchBasicCSpan } from '@coze-devops/common-modules/query-trace';
import { I18n, type I18nKeysNoOptionsType } from '@coze-arch/i18n';
import { Tooltip } from '@coze-arch/bot-semi';
import { SPAN_STATUS_CONFIG_MAP } from '../consts/span';
import { EMPTY_TEXT } from '../consts';
import { formatTime } from '.';
const getLatencyFirst = (_span: CSpan) => {
if (_span === undefined) {
return undefined;
}
if (checkIsBatchBasicCSpan(_span)) {
const span = _span as CSPanBatch;
let startTimeFirstResp = Number.POSITIVE_INFINITY;
span.spans.forEach(subSpan => {
if (
subSpan.extra !== undefined &&
'start_time_first_resp' in subSpan.extra &&
subSpan.extra?.start_time_first_resp !== '0'
) {
startTimeFirstResp = Math.min(
startTimeFirstResp,
Number(subSpan.extra?.start_time_first_resp),
);
}
});
if (startTimeFirstResp === Number.POSITIVE_INFINITY) {
return undefined;
}
return startTimeFirstResp - span.start_time;
} else {
const span = _span as CSpanSingle;
if (
span.extra !== undefined &&
'start_time_first_resp' in span.extra &&
span.extra?.start_time_first_resp !== '0'
) {
return Number(span?.extra?.start_time_first_resp) - span.start_time;
} else {
return undefined;
}
}
};
const getFieldStatus = (span: CSpan): FieldItem => {
const { status } = span;
const { label } = SPAN_STATUS_CONFIG_MAP[status] ?? {};
return {
key: I18n.t('analytic_query_status'),
value: label ? I18n.t(label as I18nKeysNoOptionsType) : undefined,
};
};
const getFieldLatencyFirst = (span: CSpan): FieldItem => {
const latencyFirst = getLatencyFirst(span);
return {
key: I18n.t('analytic_query_latencyfirst'),
value: latencyFirst !== undefined ? `${latencyFirst}ms` : EMPTY_TEXT,
};
};
const getFieldFirstResponseTime = (span: CSpan): FieldItem => {
const startTimeFirstResp = getSpanProp(span, 'start_time_first_resp');
return {
key: I18n.t('analytic_query_firstrestime'),
value:
!startTimeFirstResp || startTimeFirstResp === '0'
? '-'
: formatTime(Number(startTimeFirstResp)),
};
};
const getFieldTokens = (span: CSpan): FieldItem => {
const genValueRender = (
inputTokens?: number,
outputTokens?: number,
): ReactNode => {
if (inputTokens !== undefined && outputTokens !== undefined) {
return (
<Tooltip
content={
<article>
<div className="whitespace-nowrap">
Input Tokens: {inputTokens}
</div>
<div className="whitespace-nowrap">
Output Tokens: {outputTokens}
</div>
</article>
}
position="bottom"
>
<div style={{ fontSize: 12 }}>{inputTokens + outputTokens}</div>
</Tooltip>
);
} else {
return EMPTY_TEXT;
}
};
const { input_tokens: inputTokens, output_tokens: outputTokens } =
getTokens(span);
return {
key: I18n.t('analytic_query_tokens'),
value: genValueRender(inputTokens, outputTokens),
};
};
const getFieldLogId = (span: CSpan): FieldItem => ({
key: I18n.t('analytic_query_logid'),
value: getSpanProp(span, 'log_id') as string,
});
export const fieldHandlers = {
...fieldItemHandlers,
status: getFieldStatus,
latency_first: getFieldLatencyFirst,
first_response_time: getFieldFirstResponseTime,
tokens: getFieldTokens,
log_id: getFieldLogId,
};
export type FieldType = keyof typeof fieldHandlers;

View File

@@ -0,0 +1,135 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import JSONBig from 'json-bigint';
import utc from 'dayjs/plugin/utc';
import dayjs from 'dayjs';
import { type ob_query_trace } from '@coze-arch/bot-api/ob_query_api';
import { type QueryFilterItemId, type UTCTimeInfo } from '../typings';
import {
DATE_FILTERING_DAYS_NUMBER,
FILTERING_OPTION_ALL,
TIME_MINUTE,
} from '../consts';
dayjs.extend(utc);
const jsonBig = JSONBig({ storeAsString: true });
/**
* 转换时间戳为当前格式化当前时区时间
* @param timestamp string | number
* @returns UTCTimeInfo
*/
export const getTimeInCurrentTimeZone = (
timestamp: string | number,
): UTCTimeInfo => {
const utcDate = dayjs.utc(timestamp);
const localDate = utcDate.local();
const offset = localDate.utcOffset();
const offsetString = `UTC${offset >= 0 ? '+' : '-'}${Math.abs(
offset / TIME_MINUTE,
)}`;
const dateString = localDate.format('MM-DD HH:mm');
return {
timeOffsetString: offsetString,
dateString,
};
};
export const getPastWeekDates = (): string[] => {
const today = dayjs();
const dateList: string[] = [];
for (let i = 0; i < DATE_FILTERING_DAYS_NUMBER; i++) {
const pastDay = today.subtract(i, 'day');
dateList.push(pastDay.format('YYYY-MM-DD'));
}
return dateList;
};
/**
* 从格式化时间提取其当前对应的开始/结束时间戳
* @param formattedDate QueryFilterItemId
* @returns DailyTime
*/
export const getDailyTimestampByDate = (
formattedDate?: QueryFilterItemId,
): Pick<ob_query_trace.ListDebugQueriesRequest, 'startAtMS' | 'endAtMS'> => {
if (formattedDate === FILTERING_OPTION_ALL) {
const today = dayjs();
return {
startAtMS: today
.subtract(DATE_FILTERING_DAYS_NUMBER - 1, 'day')
.startOf('day')
.valueOf()
.toString(),
endAtMS: today.endOf('day').valueOf().toString(),
};
} else {
const date = dayjs(formattedDate);
return {
startAtMS: date.startOf('day').valueOf().toString(),
endAtMS: date.endOf('day').valueOf().toString(),
};
}
};
export const textWithFallback = (text?: string | number) =>
text && text !== '' ? text : '-';
export const formatTime = (timestamp?: number | string) =>
dayjs(timestamp).format('YYYY-MM-DD HH:mm:ss');
export const isJsonString = (str: string) => {
try {
const jsonData = JSON.parse(str);
if (Object.prototype.toString.call(jsonData) !== '[object Object]') {
return false;
}
} catch (error) {
return false;
}
return true;
};
export const isDebugShowJsonString = (str: string) => {
try {
const jsonData = JSON.parse(str);
if (
Object.prototype.toString.call(jsonData) !== '[object Object]' &&
Object.prototype.toString.call(jsonData) !== '[object Array]'
) {
return false;
}
} catch (error) {
return false;
}
return true;
};
export const jsonParseWithBigNumber = (jsonString: string) =>
JSON.parse(JSON.stringify(jsonBig.parse(jsonString)));
export const jsonParse = (
jsonString: string,
): Record<string, unknown> | string | unknown[] => {
if (isDebugShowJsonString(jsonString)) {
return jsonParseWithBigNumber(jsonString);
} else {
return jsonString;
}
};

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { span2CSpan } from '@coze-devops/common-modules/query-trace';
import {
checkIsBatchBasicCSpan,
type CSPanBatch,
type CSpan,
type CSpanSingle,
} from '@coze-devops/common-modules/query-trace';
import {
type Span,
type TraceAdvanceInfo,
} from '@coze-arch/bot-api/ob_query_api';
export const getSpanProp = (span: CSpan, key: string) => {
if (checkIsBatchBasicCSpan(span)) {
const batchSpan = span as CSPanBatch;
return (
batchSpan[key as keyof CSPanBatch] ??
batchSpan.spans[0]?.extra?.[key as keyof CSPanBatch['spans'][0]['extra']]
);
} else {
const singleSpan = span as CSpanSingle;
return (
singleSpan[key as keyof CSpanSingle] ??
singleSpan.extra?.[key as keyof CSpanSingle['extra']]
);
}
};
/**
* 加强原始Span信息注入服务端采集的token、status等信息
* @param originSpans Span[]
* @param traceAdvanceInfo TraceAdvanceInfo[]
* @returns CSpan[]
*/
export const enhanceOriginalSpan = (
originSpans: Span[],
traceAdvanceInfo: TraceAdvanceInfo[],
): CSpan[] => {
const traceAdvanceInfoMap: Record<string, TraceAdvanceInfo> =
traceAdvanceInfo.reduce<Record<string, TraceAdvanceInfo>>((pre, cur) => {
pre[cur.trace_id] = cur;
return pre;
}, {});
const traceCSpans = originSpans.map(item => span2CSpan(item));
const enhancedOverallSpans: CSpan[] = traceCSpans.map(item => {
const {
tokens: { input, output },
status,
} = traceAdvanceInfoMap[item.trace_id];
return {
...item,
status,
input_tokens_sum: input,
output_tokens_sum: output,
};
});
return enhancedOverallSpans;
};

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2025 coze-dev Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
export function windowOpen({ url, target }: { url: string; target?: string }) {
const element = document.createElement('a');
element.target = target || '_blank';
element.href = url;
element.click();
}