feat(comparison-records): 比价记录展示 LLM 实际成本(列表列 + 详情 + 价格快照)
- 列表「成本」列优先显示后端冻结的实际成本(当时价/绿色),无则回退前端估算(灰色) - 详情抽屉「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 <noreply@anthropic.com>
This commit is contained in:
@@ -215,7 +215,16 @@ export default function ComparisonRecordsPage() {
|
|||||||
title: '成本',
|
title: '成本',
|
||||||
key: 'cost',
|
key: 'cost',
|
||||||
width: 90,
|
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',
|
title: '机型/ROM',
|
||||||
@@ -292,7 +301,7 @@ export default function ComparisonRecordsPage() {
|
|||||||
min={0}
|
min={0}
|
||||||
step={0.1}
|
step={0.1}
|
||||||
style={{ width: 210 }}
|
style={{ width: 210 }}
|
||||||
addonAfter="元/百万token"
|
suffix="元/百万token"
|
||||||
/>
|
/>
|
||||||
</Space>
|
</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)}`}
|
: `${detail.input_tokens ?? 0} / ${detail.output_tokens ?? 0} / ${(detail.input_tokens ?? 0) + (detail.output_tokens ?? 0)}`}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label="估算成本">
|
<Descriptions.Item label="LLM 成本">
|
||||||
{fmtCost(calcCost(detail.input_tokens, detail.output_tokens, pricePerMTok))}
|
{detail.llm_cost_yuan != null ? (
|
||||||
{pricePerMTok == null && (detail.input_tokens != null || detail.output_tokens != null)
|
<>
|
||||||
? <span style={{ color: '#999', fontSize: 12 }}>(顶部填 LLM 单价后显示)</span>
|
{fmtCost(detail.llm_cost_yuan)}
|
||||||
: null}
|
<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>
|
</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.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="最优">{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>
|
<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: (
|
children: (
|
||||||
<div>
|
<div>
|
||||||
{c.input_messages.map((m, j) => (
|
{(c.input_messages ?? []).map((m, j) => (
|
||||||
<div key={j} style={{ marginBottom: 8 }}>
|
<div key={j} style={{ marginBottom: 8 }}>
|
||||||
<Tag>{m.role}</Tag>
|
<Tag>{m.role}</Tag>
|
||||||
<pre style={preStyle}>{pretty(m.content)}</pre>
|
<pre style={preStyle}>{pretty(m.content)}</pre>
|
||||||
|
|||||||
@@ -354,6 +354,7 @@ export interface ComparisonRecordListItem {
|
|||||||
retry_count: number | null;
|
retry_count: number | null;
|
||||||
input_tokens: number | null;
|
input_tokens: number | null;
|
||||||
output_tokens: number | null;
|
output_tokens: number | null;
|
||||||
|
llm_cost_yuan: number | null; // 后端按「当时价」冻结的实际成本(元);旧记录 null → 「成本」列回退估算
|
||||||
device_model: string | null;
|
device_model: string | null;
|
||||||
rom_vendor: string | null;
|
rom_vendor: string | null;
|
||||||
rom_name: string | null;
|
rom_name: string | null;
|
||||||
@@ -396,6 +397,8 @@ export interface ComparisonRecordDetail extends ComparisonRecordListItem {
|
|||||||
latitude: number | null;
|
latitude: number | null;
|
||||||
llm_calls: LlmCall[] | null;
|
llm_calls: LlmCall[] | null;
|
||||||
raw_payload: Record<string, unknown> | null;
|
raw_payload: Record<string, unknown> | null;
|
||||||
|
// 算成本所用单价快照 {mode, prices:{model:{...}}}(llm_cost_yuan 继承自列表项)。
|
||||||
|
llm_price_snapshot: Record<string, unknown> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AdRevenueImpression {
|
export interface AdRevenueImpression {
|
||||||
|
|||||||
Reference in New Issue
Block a user