feat: logs cache field (#2920)
* feat: logs cache field * feat: logs cache field * feat: logs cache field
This commit is contained in:
@@ -286,6 +286,44 @@ function renderModelName(record, copyText, t) {
|
||||
}
|
||||
}
|
||||
|
||||
function toTokenNumber(value) {
|
||||
const parsed = Number(value);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function formatTokenCount(value) {
|
||||
return toTokenNumber(value).toLocaleString();
|
||||
}
|
||||
|
||||
function getPromptCacheSummary(other) {
|
||||
if (!other || typeof other !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const cacheReadTokens = toTokenNumber(other.cache_tokens);
|
||||
const cacheCreationTokens = toTokenNumber(other.cache_creation_tokens);
|
||||
const cacheCreationTokens5m = toTokenNumber(other.cache_creation_tokens_5m);
|
||||
const cacheCreationTokens1h = toTokenNumber(other.cache_creation_tokens_1h);
|
||||
|
||||
const hasSplitCacheCreation =
|
||||
cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0;
|
||||
const cacheWriteTokens = hasSplitCacheCreation
|
||||
? cacheCreationTokens5m + cacheCreationTokens1h
|
||||
: cacheCreationTokens;
|
||||
|
||||
if (cacheReadTokens <= 0 && cacheWriteTokens <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
cacheReadTokens,
|
||||
cacheWriteTokens,
|
||||
};
|
||||
}
|
||||
|
||||
export const getLogsColumns = ({
|
||||
t,
|
||||
COLUMN_KEYS,
|
||||
@@ -524,11 +562,56 @@ export const getLogsColumns = ({
|
||||
},
|
||||
{
|
||||
key: COLUMN_KEYS.PROMPT,
|
||||
title: t('输入'),
|
||||
title: (
|
||||
<div className='flex items-center gap-1'>
|
||||
{t('输入')}
|
||||
<Tooltip
|
||||
content={t(
|
||||
'根据 Anthropic 协定,/v1/messages 的输入 tokens 仅统计非缓存输入,不包含缓存读取与缓存写入 tokens。',
|
||||
)}
|
||||
>
|
||||
<IconHelpCircle className='text-gray-400 cursor-help' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
),
|
||||
dataIndex: 'prompt_tokens',
|
||||
render: (text, record, index) => {
|
||||
const other = getLogOther(record.other);
|
||||
const cacheSummary = getPromptCacheSummary(other);
|
||||
const hasCacheRead = (cacheSummary?.cacheReadTokens || 0) > 0;
|
||||
const hasCacheWrite = (cacheSummary?.cacheWriteTokens || 0) > 0;
|
||||
let cacheText = '';
|
||||
if (hasCacheRead && hasCacheWrite) {
|
||||
cacheText = `${t('缓存读')} ${formatTokenCount(cacheSummary.cacheReadTokens)} · ${t('写')} ${formatTokenCount(cacheSummary.cacheWriteTokens)}`;
|
||||
} else if (hasCacheRead) {
|
||||
cacheText = `${t('缓存读')} ${formatTokenCount(cacheSummary.cacheReadTokens)}`;
|
||||
} else if (hasCacheWrite) {
|
||||
cacheText = `${t('缓存写')} ${formatTokenCount(cacheSummary.cacheWriteTokens)}`;
|
||||
}
|
||||
|
||||
return record.type === 0 || record.type === 2 || record.type === 5 ? (
|
||||
<>{<span> {text} </span>}</>
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'flex-start',
|
||||
lineHeight: 1.2,
|
||||
}}
|
||||
>
|
||||
<span>{text}</span>
|
||||
{cacheText ? (
|
||||
<span
|
||||
style={{
|
||||
marginTop: 2,
|
||||
fontSize: 11,
|
||||
color: 'var(--semi-color-text-2)',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{cacheText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user