feat(usage-logs): 新增缓存成本字段并更新相关逻辑
This commit is contained in:
1 parent
9b2ac4e45f
commit
3b7f39a171
4 files changed
+35
-15
No files matched your search
@@ -469,8 +469,8 @@ export function UsageLogs() {
|
||||
<td className="px-2 py-3">
|
||||
{/* <div className="font-medium">{formatCompactNumber(item.totalTokens)} tokens</div> */}
|
||||
<div className="text-xs text-muted-foreground">
|
||||
输入 {formatCompactNumber(item.promptTokens)} / 输出{" "}
|
||||
{formatCompactNumber(item.completionTokens)}
|
||||
{formatCompactNumber(item.promptTokens)} / {formatCompactNumber(item.completionTokens)} /{" "}
|
||||
{formatCompactNumber(item.cachedTokens)}
|
||||
</div>
|
||||
<div className="mt-1 text-xs text-muted-foreground">
|
||||
{formatCurrency(item.totalCost, displayDecimals)}
|
||||
@@ -631,6 +631,7 @@ export function UsageLogs() {
|
||||
<div className="grid gap-x-8 border-t border-border/60 pt-2 md:grid-cols-2">
|
||||
<div className="divide-y divide-border/60">
|
||||
<DetailRow label="总成本" value={formatCurrency(selectedItem.totalCost, displayDecimals)} />
|
||||
<DetailRow label="缓存成本" value={formatCurrency(selectedItem.cacheCost, displayDecimals)} />
|
||||
<DetailRow label="延迟" value={formatDuration(selectedItem.latencyMs)} />
|
||||
<DetailRow label="总 Tokens" value={formatCompactNumber(selectedItem.totalTokens)} />
|
||||
</div>
|
||||
|
||||
@@ -177,6 +177,7 @@ export interface AnalyticsEventItem {
|
||||
cachedTokens: number
|
||||
totalTokens: number
|
||||
totalCost: number
|
||||
cacheCost: number
|
||||
latencyMs: number
|
||||
retryCount: number
|
||||
upstreamStatus: number
|
||||
|
||||
+22
-9
@@ -131,6 +131,7 @@ const DOUBLE_FIELDS = {
|
||||
upstreamStatus: "double8",
|
||||
successFlag: "double9",
|
||||
billingScale: "double10",
|
||||
cacheCost: "double11",
|
||||
} as const;
|
||||
|
||||
const BREAKDOWN_FIELDS: Record<AnalyticsBreakdownDimension, string> = {
|
||||
@@ -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<Record<string, unknown>>(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<Record<string, unknown>>(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<Record<string, unknown>>(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<Record<string, unknown>>(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<Record<string, unknown>>(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),
|
||||
|
||||
@@ -27,6 +27,7 @@ export type UsageLogContext = {
|
||||
|
||||
type UsageCostResult = {
|
||||
totalCost: number;
|
||||
cacheCost: number;
|
||||
}
|
||||
|
||||
type FailureLogParams = {
|
||||
@@ -188,7 +189,7 @@ export const buildUsageRequestMetadata = (c: Context<HonoCustomType>) => {
|
||||
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 || ""
|
||||
|
||||
Reference in new issue
Block a user