feat(comparison-records): 比价记录展示 LLM 实际成本(列表列 + 详情 + 价格快照) #48

Merged
guke merged 4 commits from feat/llm-token-cost into main 2026-07-14 14:38:39 +08:00
2 changed files with 35 additions and 8 deletions
Showing only changes of commit 9285ebe012 - Show all commits
+32 -8
View File
@@ -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 <span title="实际成本(按调用时单价冻结)" style={{ color: '#389e0d' }}>{fmtCost(r.llm_cost_yuan)}</span>;
}
const est = calcCost(r.input_tokens, r.output_tokens, pricePerMTok);
return est == null
? '-'
: <span title="估算(顶部 LLM 单价 × token" style={{ color: '#999' }}>{fmtCost(est)}</span>;
},
},
{
title: '机型/ROM',
@@ -292,7 +301,7 @@ export default function ComparisonRecordsPage() {
min={0}
step={0.1}
style={{ width: 210 }}
addonAfter="元/百万token"
suffix="元/百万token"
/>
</Space>
@@ -331,12 +340,27 @@ export default function ComparisonRecordsPage() {
? '-'
: `${detail.input_tokens ?? 0} / ${detail.output_tokens ?? 0} / ${(detail.input_tokens ?? 0) + (detail.output_tokens ?? 0)}`}
</Descriptions.Item>
<Descriptions.Item label="估算成本">
{fmtCost(calcCost(detail.input_tokens, detail.output_tokens, pricePerMTok))}
{pricePerMTok == null && (detail.input_tokens != null || detail.output_tokens != null)
? <span style={{ color: '#999', fontSize: 12 }}> LLM </span>
: null}
<Descriptions.Item label="LLM 成本">
{detail.llm_cost_yuan != null ? (
<>
{fmtCost(detail.llm_cost_yuan)}
<Tag color="green" style={{ marginLeft: 6 }}>·</Tag>
</>
) : (
<>
{fmtCost(calcCost(detail.input_tokens, detail.output_tokens, pricePerMTok))}
<span style={{ color: '#999', fontSize: 12, marginLeft: 6 }}></span>
{pricePerMTok == null && (detail.input_tokens != null || detail.output_tokens != null)
? <span style={{ color: '#999', fontSize: 12 }}> LLM </span>
: null}
</>
)}
</Descriptions.Item>
{detail.llm_price_snapshot ? (
<Descriptions.Item label="价格快照" span={2}>
<pre style={preStyle}>{JSON.stringify(detail.llm_price_snapshot, null, 2)}</pre>
</Descriptions.Item>
) : null}
<Descriptions.Item label="源平台">{detail.source_platform_name || '-'}{cents(detail.source_price_cents)}</Descriptions.Item>
<Descriptions.Item label="最优">{detail.best_platform_name || '-'}{cents(detail.best_price_cents)}</Descriptions.Item>
<Descriptions.Item label="省" span={2}>{cents(detail.saved_amount_cents)}{detail.is_source_best ? '(源平台最便宜)' : ''}</Descriptions.Item>
@@ -432,7 +456,7 @@ export default function ComparisonRecordsPage() {
),
children: (
<div>
{c.input_messages.map((m, j) => (
{(c.input_messages ?? []).map((m, j) => (
<div key={j} style={{ marginBottom: 8 }}>
<Tag>{m.role}</Tag>
<pre style={preStyle}>{pretty(m.content)}</pre>
+3
View File
@@ -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<string, unknown> | null;
// 算成本所用单价快照 {mode, prices:{model:{...}}}(llm_cost_yuan 继承自列表项)。
llm_price_snapshot: Record<string, unknown> | null;
}
export interface AdRevenueImpression {