Compare commits

..

1 Commits

Author SHA1 Message Date
guke 83ddb41a49 feat(coupon-data): 点位成功率改为按券成功率的算术平均(前端从按券明细上卷)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 18:45:44 +08:00
3 changed files with 11 additions and 47 deletions
+8 -41
View File
@@ -38,15 +38,6 @@ const fmtCost = (c: number | null) => (c == null ? '-' : `¥${c.toFixed(4)}`);
const fmtTok = (inTok: number | null, outTok: number | null) =>
inTok == null && outTok == null ? '-' : `${inTok ?? 0}/${outTok ?? 0}`;
// LLM 价格快照只展示 prices 里各模型的单价(元/每百万 token),忽略 mode/_source 等元字段。
function llmPriceRows(snapshot: Record<string, unknown> | null): string[] {
const prices = snapshot?.prices;
if (!prices || typeof prices !== 'object') return [];
return Object.entries(prices as Record<string, { input_per_1m?: number; output_per_1m?: number }>).map(
([model, p]) => `${model} 输入 ${p?.input_per_1m ?? '-'}元/每百万tokens,输出${p?.output_per_1m ?? '-'}元/每百万tokens`,
);
}
// LLM message content 常是 json.dumps 的卡片列表;能 parse 成 JSON 就缩进展开,否则原样。
function pretty(s: string | null): string {
if (!s) return '';
@@ -224,16 +215,7 @@ export default function ComparisonRecordsPage() {
title: '成本',
key: 'cost',
width: 90,
// 有后端冻结的实际成本(当时价)就用它;否则回退老的前端估算(需顶部填 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>;
},
render: (_, r) => fmtCost(calcCost(r.input_tokens, r.output_tokens, pricePerMTok)),
},
{
title: '机型/ROM',
@@ -310,7 +292,7 @@ export default function ComparisonRecordsPage() {
min={0}
step={0.1}
style={{ width: 210 }}
suffix="元/百万token"
addonAfter="元/百万token"
/>
</Space>
@@ -349,27 +331,12 @@ 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="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 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>
{detail.llm_price_snapshot ? (
<Descriptions.Item label="LLM价格快照" span={2}>
{llmPriceRows(detail.llm_price_snapshot).map((r) => <div key={r}>{r}</div>)}
</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>
@@ -465,7 +432,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 -3
View File
@@ -11,7 +11,7 @@ interface ConfigItem {
key: string;
label: string;
group: string;
type: string; // int / int_list / dict_str_int / bool / json
type: string; // int / int_list / dict_str_int / bool
help: string | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
default: any;
@@ -24,7 +24,7 @@ interface ConfigItem {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function toEdit(item: ConfigItem): any {
if (item.type === 'int_list') return (item.value as number[]).join(', ');
if (item.type === 'dict_str_int' || item.type === 'json') return JSON.stringify(item.value);
if (item.type === 'dict_str_int') return JSON.stringify(item.value);
return item.value;
}
@@ -37,7 +37,7 @@ function fromEdit(type: string, raw: any): any {
.split(',')
.map((s) => parseInt(s.trim(), 10));
}
if (type === 'dict_str_int' || type === 'json') return JSON.parse(raw);
if (type === 'dict_str_int') return JSON.parse(raw);
return raw;
}
-3
View File
@@ -354,7 +354,6 @@ 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;
@@ -397,8 +396,6 @@ 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 {