From 9285ebe012e538bcf559fb0df40a47aa382eb29d Mon Sep 17 00:00:00 2001 From: guke Date: Mon, 13 Jul 2026 10:53:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(comparison-records):=20=E6=AF=94=E4=BB=B7?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=B1=95=E7=A4=BA=20LLM=20=E5=AE=9E=E9=99=85?= =?UTF-8?q?=E6=88=90=E6=9C=AC(=E5=88=97=E8=A1=A8=E5=88=97=20+=20=E8=AF=A6?= =?UTF-8?q?=E6=83=85=20+=20=E4=BB=B7=E6=A0=BC=E5=BF=AB=E7=85=A7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 列表「成本」列优先显示后端冻结的实际成本(当时价/绿色),无则回退前端估算(灰色) - 详情抽屉「LLM 成本」:有 llm_cost_yuan 显示实际值 +「实际·当时价」标,无则回退估算; 另展示所用单价快照 JSON - types.ts:ComparisonRecordListItem 加 llm_cost_yuan(详情继承) - 顺带:InputNumber addonAfter → suffix(消 antd 弃用警告);详情 llm_calls 遍历对 input_messages 加空值防御(残缺数据不再整页白屏) Co-Authored-By: Claude Opus 4.8 --- src/app/(main)/comparison-records/page.tsx | 40 +++++++++++++++++----- src/lib/types.ts | 3 ++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/src/app/(main)/comparison-records/page.tsx b/src/app/(main)/comparison-records/page.tsx index 3062d13..b365460 100644 --- a/src/app/(main)/comparison-records/page.tsx +++ b/src/app/(main)/comparison-records/page.tsx @@ -215,7 +215,16 @@ export default function ComparisonRecordsPage() { title: '成本', key: 'cost', width: 90, - render: (_, r) => fmtCost(calcCost(r.input_tokens, r.output_tokens, pricePerMTok)), + // 有后端冻结的实际成本(当时价)就用它;否则回退老的前端估算(需顶部填 LLM 单价)。 + render: (_, r) => { + if (r.llm_cost_yuan != null) { + return {fmtCost(r.llm_cost_yuan)}; + } + const est = calcCost(r.input_tokens, r.output_tokens, pricePerMTok); + return est == null + ? '-' + : {fmtCost(est)}; + }, }, { title: '机型/ROM', @@ -292,7 +301,7 @@ export default function ComparisonRecordsPage() { min={0} step={0.1} style={{ width: 210 }} - addonAfter="元/百万token" + suffix="元/百万token" /> @@ -331,12 +340,27 @@ export default function ComparisonRecordsPage() { ? '-' : `${detail.input_tokens ?? 0} / ${detail.output_tokens ?? 0} / ${(detail.input_tokens ?? 0) + (detail.output_tokens ?? 0)}`} - - {fmtCost(calcCost(detail.input_tokens, detail.output_tokens, pricePerMTok))} - {pricePerMTok == null && (detail.input_tokens != null || detail.output_tokens != null) - ? (顶部填 LLM 单价后显示) - : null} + + {detail.llm_cost_yuan != null ? ( + <> + {fmtCost(detail.llm_cost_yuan)} + 实际·当时价 + + ) : ( + <> + {fmtCost(calcCost(detail.input_tokens, detail.output_tokens, pricePerMTok))} + 估算 + {pricePerMTok == null && (detail.input_tokens != null || detail.output_tokens != null) + ? (顶部填 LLM 单价后显示) + : null} + + )} + {detail.llm_price_snapshot ? ( + +
{JSON.stringify(detail.llm_price_snapshot, null, 2)}
+
+ ) : null} {detail.source_platform_name || '-'}({cents(detail.source_price_cents)}) {detail.best_platform_name || '-'}({cents(detail.best_price_cents)}) {cents(detail.saved_amount_cents)}{detail.is_source_best ? '(源平台最便宜)' : ''} @@ -432,7 +456,7 @@ export default function ComparisonRecordsPage() { ), children: (
- {c.input_messages.map((m, j) => ( + {(c.input_messages ?? []).map((m, j) => (
{m.role}
{pretty(m.content)}
diff --git a/src/lib/types.ts b/src/lib/types.ts index e853258..cfd158d 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -354,6 +354,7 @@ export interface ComparisonRecordListItem { retry_count: number | null; input_tokens: number | null; output_tokens: number | null; + llm_cost_yuan: number | null; // 后端按「当时价」冻结的实际成本(元);旧记录 null → 「成本」列回退估算 device_model: string | null; rom_vendor: string | null; rom_name: string | null; @@ -396,6 +397,8 @@ export interface ComparisonRecordDetail extends ComparisonRecordListItem { latitude: number | null; llm_calls: LlmCall[] | null; raw_payload: Record | null; + // 算成本所用单价快照 {mode, prices:{model:{...}}}(llm_cost_yuan 继承自列表项)。 + llm_price_snapshot: Record | null; } export interface AdRevenueImpression {