From 3b7f39a1717ac29ec00a4e1fb0e815d01c4d602f Mon Sep 17 00:00:00 2001 From: Jeff Gu Date: Tue, 14 Apr 2026 20:16:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(usage-logs):=20=E6=96=B0=E5=A2=9E=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=88=90=E6=9C=AC=E5=AD=97=E6=AE=B5=E5=B9=B6=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=9B=B8=E5=85=B3=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/UsageLogs.tsx | 5 +++-- frontend/src/types/index.ts | 1 + src/analytics/query.ts | 31 ++++++++++++++++++++++--------- src/analytics/usage-logger.ts | 13 +++++++++---- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/frontend/src/pages/UsageLogs.tsx b/frontend/src/pages/UsageLogs.tsx index b2221a4..00bf591 100644 --- a/frontend/src/pages/UsageLogs.tsx +++ b/frontend/src/pages/UsageLogs.tsx @@ -469,8 +469,8 @@ export function UsageLogs() { {/*
{formatCompactNumber(item.totalTokens)} tokens
*/}
- 输入 {formatCompactNumber(item.promptTokens)} / 输出{" "} - {formatCompactNumber(item.completionTokens)} + {formatCompactNumber(item.promptTokens)} / {formatCompactNumber(item.completionTokens)} /{" "} + {formatCompactNumber(item.cachedTokens)}
{formatCurrency(item.totalCost, displayDecimals)} @@ -631,6 +631,7 @@ export function UsageLogs() {
+
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 0b19b99..ca9931a 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -177,6 +177,7 @@ export interface AnalyticsEventItem { cachedTokens: number totalTokens: number totalCost: number + cacheCost: number latencyMs: number retryCount: number upstreamStatus: number diff --git a/src/analytics/query.ts b/src/analytics/query.ts index cee156f..0e3bc02 100644 --- a/src/analytics/query.ts +++ b/src/analytics/query.ts @@ -131,6 +131,7 @@ const DOUBLE_FIELDS = { upstreamStatus: "double8", successFlag: "double9", billingScale: "double10", + cacheCost: "double11", } as const; const BREAKDOWN_FIELDS: Record = { @@ -197,6 +198,7 @@ const USAGE_LOG_NUMERIC_COLUMNS = [ DOUBLE_FIELDS.upstreamStatus, DOUBLE_FIELDS.successFlag, DOUBLE_FIELDS.billingScale, + DOUBLE_FIELDS.cacheCost, ]; const ALL_PROBED_COLUMNS = [ @@ -538,16 +540,19 @@ const buildDoubleSelect = ( : `0 AS ${alias}`; }; -const buildNormalizedTotalCostExpression = (support: DatasetColumnSupport): string => { - if (!isColumnAvailable(support, DOUBLE_FIELDS.totalCost)) { +const buildNormalizedCostExpression = ( + support: DatasetColumnSupport, + costField: string +): string => { + if (!isColumnAvailable(support, costField)) { return "0"; } if (!isColumnAvailable(support, DOUBLE_FIELDS.billingScale)) { - return `${DOUBLE_FIELDS.totalCost} * ${LEGACY_TO_RAW_FACTOR}`; + return `${costField} * ${LEGACY_TO_RAW_FACTOR}`; } - return `if(${DOUBLE_FIELDS.billingScale} > 0, ${DOUBLE_FIELDS.totalCost} * (${BILLING_RAW_SCALE} / ${DOUBLE_FIELDS.billingScale}), ${DOUBLE_FIELDS.totalCost} * ${LEGACY_TO_RAW_FACTOR})`; + return `if(${DOUBLE_FIELDS.billingScale} > 0, ${costField} * (${BILLING_RAW_SCALE} / ${DOUBLE_FIELDS.billingScale}), ${costField} * ${LEGACY_TO_RAW_FACTOR})`; }; const hasAnyLegacyLogSchema = (support: DatasetColumnSupport): boolean => { @@ -624,7 +629,7 @@ export const queryUsageOverview = async ( const { range, config } = getRangeConfig(requestedRange); const dataset = getDatasetName(c); const columnSupport = await getDatasetColumnSupport(c, dataset, buildRangeWhereClause(config)); - const normalizedTotalCostExpression = buildNormalizedTotalCostExpression(columnSupport); + const normalizedTotalCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.totalCost); const rows = await runAnalyticsQuery>(c, ` SELECT sum(_sample_interval) AS requests, @@ -666,7 +671,7 @@ export const queryUsageTrend = async ( const dataset = getDatasetName(c); const trendWindow = buildTrendWindow(range, config); const columnSupport = await getDatasetColumnSupport(c, dataset, trendWindow.whereClause); - const normalizedTotalCostExpression = buildNormalizedTotalCostExpression(columnSupport); + const normalizedTotalCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.totalCost); const rows = await runAnalyticsQuery>(c, ` SELECT toUnixTimestamp(${buildBucketClause(config)}) AS bucket_ts, @@ -716,7 +721,7 @@ export const queryUsageBreakdown = async ( const dataset = getDatasetName(c); const dimensionField = BREAKDOWN_FIELDS[dimension]; const columnSupport = await getDatasetColumnSupport(c, dataset, buildRangeWhereClause(config)); - const normalizedTotalCostExpression = buildNormalizedTotalCostExpression(columnSupport); + const normalizedTotalCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.totalCost); const rows = await runAnalyticsQuery>(c, ` SELECT ${dimensionField} AS label, @@ -785,6 +790,8 @@ WHERE ${buildRangeWhereClause(config)} const columnSupport = await getDatasetColumnSupport(c, dataset, buildRangeWhereClause(config)); const compatibilityWarning = getUsageLogCompatibilityWarning(columnSupport); + const normalizedTotalCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.totalCost); + const normalizedCacheCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.cacheCost); const rows = await runAnalyticsQuery>(c, ` SELECT timestamp, @@ -812,7 +819,8 @@ SELECT ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.completionTokens, "completion_tokens")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.cachedTokens, "cached_tokens")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.totalTokens, "total_tokens")}, - ${buildNormalizedTotalCostExpression(columnSupport)} AS total_cost, + ${normalizedTotalCostExpression} AS total_cost, + ${normalizedCacheCostExpression} AS cache_cost, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.latencyMs, "latency_ms")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.retryCount, "retry_count")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.upstreamStatus, "upstream_status")} @@ -853,6 +861,7 @@ LIMIT ${limit} cachedTokens: toNumber(row.cached_tokens), totalTokens: toNumber(row.total_tokens), totalCost: toNumber(row.total_cost), + cacheCost: toNumber(row.cache_cost), latencyMs: toNumber(row.latency_ms), retryCount: toNumber(row.retry_count), upstreamStatus: toNumber(row.upstream_status), @@ -958,6 +967,8 @@ WHERE ${whereClause} }; } + const normalizedTotalCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.totalCost); + const normalizedCacheCostExpression = buildNormalizedCostExpression(columnSupport, DOUBLE_FIELDS.cacheCost); const rows = await runAnalyticsQuery>(c, ` SELECT timestamp, @@ -985,7 +996,8 @@ SELECT ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.completionTokens, "completion_tokens")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.cachedTokens, "cached_tokens")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.totalTokens, "total_tokens")}, - ${buildNormalizedTotalCostExpression(columnSupport)} AS total_cost, + ${normalizedTotalCostExpression} AS total_cost, + ${normalizedCacheCostExpression} AS cache_cost, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.latencyMs, "latency_ms")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.retryCount, "retry_count")}, ${buildDoubleSelect(columnSupport, DOUBLE_FIELDS.upstreamStatus, "upstream_status")} @@ -1038,6 +1050,7 @@ OFFSET ${offset} cachedTokens: toNumber(row.cached_tokens), totalTokens: toNumber(row.total_tokens), totalCost: toNumber(row.total_cost), + cacheCost: toNumber(row.cache_cost), latencyMs: toNumber(row.latency_ms), retryCount: toNumber(row.retry_count), upstreamStatus: toNumber(row.upstream_status), diff --git a/src/analytics/usage-logger.ts b/src/analytics/usage-logger.ts index fec8252..a172ec4 100644 --- a/src/analytics/usage-logger.ts +++ b/src/analytics/usage-logger.ts @@ -27,6 +27,7 @@ export type UsageLogContext = { type UsageCostResult = { totalCost: number; + cacheCost: number; } type FailureLogParams = { @@ -188,7 +189,7 @@ export const buildUsageRequestMetadata = (c: Context) => { const buildCommonPoint = ( context: UsageLogContext, usage: Usage, - totalCost: number, + costResult: UsageCostResult, result: "success" | "failure", errorCode: string, errorSummary: string @@ -230,12 +231,13 @@ const buildCommonPoint = ( completionTokens, cachedTokens, totalTokens, - safeNumber(totalCost), + safeNumber(costResult.totalCost), Math.max(0, Date.now() - context.startedAt), safeNumber(context.trackingState.retryCount), upstreamStatus, result === "success" ? 1 : 0, BILLING_RAW_SCALE, + safeNumber(costResult.cacheCost), ], }; }; @@ -246,7 +248,7 @@ export const writeUsageSuccessEvent = ( usage: Usage, costResult: UsageCostResult ) => { - writeDataPoint(c, buildCommonPoint(context, usage, costResult.totalCost, "success", "", "")); + writeDataPoint(c, buildCommonPoint(context, usage, costResult, "success", "", "")); }; export const writeUsageFailureEvent = ( @@ -259,7 +261,10 @@ export const writeUsageFailureEvent = ( buildCommonPoint( context, {}, - 0, + { + totalCost: 0, + cacheCost: 0, + }, "failure", params.errorCode, params.errorSummary || context.trackingState.errorSummary || ""