feat(report): expose headline evidence details
This commit is contained in:
@@ -205,29 +205,37 @@ type ActionItem struct {
|
||||
}
|
||||
|
||||
type HeadlineItem struct {
|
||||
Label string
|
||||
Title string
|
||||
Summary string
|
||||
Baseline string
|
||||
TrustLabel string
|
||||
Tone string
|
||||
Label string
|
||||
Title string
|
||||
Summary string
|
||||
Baseline string
|
||||
TrustLabel string
|
||||
SourceKindLabel string
|
||||
PrimarySource string
|
||||
UpdatedAt string
|
||||
EvidenceDetail string
|
||||
Tone string
|
||||
}
|
||||
|
||||
type ModelEvent struct {
|
||||
EventType string
|
||||
ModelName string
|
||||
ProviderName string
|
||||
OperatorName string
|
||||
TrustLabel string
|
||||
Baseline string
|
||||
Summary string
|
||||
Currency string
|
||||
OldInputPrice float64
|
||||
NewInputPrice float64
|
||||
OldOutputPrice float64
|
||||
NewOutputPrice float64
|
||||
PriceChangePct float64
|
||||
Priority int
|
||||
EventType string
|
||||
ModelName string
|
||||
ProviderName string
|
||||
OperatorName string
|
||||
TrustLabel string
|
||||
SourceKindLabel string
|
||||
PrimarySource string
|
||||
UpdatedAt string
|
||||
EvidenceDetail string
|
||||
Baseline string
|
||||
Summary string
|
||||
Currency string
|
||||
OldInputPrice float64
|
||||
NewInputPrice float64
|
||||
OldOutputPrice float64
|
||||
NewOutputPrice float64
|
||||
PriceChangePct float64
|
||||
Priority int
|
||||
}
|
||||
|
||||
type Recommendation struct {
|
||||
@@ -805,7 +813,8 @@ func loadNewModelEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
COALESCE(lp.output_price_per_mtok, 0) AS output_price,
|
||||
COALESCE(lp.is_free, false) AS is_free,
|
||||
COALESCE(m.context_length, 0) AS context_length,
|
||||
COALESCE(mp.country, 'unknown') AS provider_country
|
||||
COALESCE(mp.country, 'unknown') AS provider_country,
|
||||
m.created_at
|
||||
FROM models m
|
||||
LEFT JOIN model_provider mp ON m.provider_id = mp.id
|
||||
LEFT JOIN latest_prices lp ON lp.model_id = m.id AND lp.rn = 1
|
||||
@@ -832,6 +841,7 @@ func loadNewModelEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
isFree bool
|
||||
contextLength int
|
||||
providerCountry string
|
||||
createdAt time.Time
|
||||
)
|
||||
if err := rows.Scan(
|
||||
&modelName,
|
||||
@@ -844,6 +854,7 @@ func loadNewModelEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
&isFree,
|
||||
&contextLength,
|
||||
&providerCountry,
|
||||
&createdAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -869,17 +880,21 @@ func loadNewModelEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
}
|
||||
|
||||
events = append(events, ModelEvent{
|
||||
EventType: "new_model",
|
||||
ModelName: modelName,
|
||||
ProviderName: providerName,
|
||||
OperatorName: operatorName,
|
||||
TrustLabel: buildTrustLabel(model),
|
||||
Baseline: "首次出现",
|
||||
Summary: summary,
|
||||
Currency: currency,
|
||||
NewInputPrice: inputPrice,
|
||||
EventType: "new_model",
|
||||
ModelName: modelName,
|
||||
ProviderName: providerName,
|
||||
OperatorName: operatorName,
|
||||
TrustLabel: buildTrustLabel(model),
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: buildPrimarySource("region_pricing", operatorName),
|
||||
UpdatedAt: createdAt.Format("2006-01-02 15:04"),
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
Baseline: "首次出现",
|
||||
Summary: summary,
|
||||
Currency: currency,
|
||||
NewInputPrice: inputPrice,
|
||||
NewOutputPrice: outputPrice,
|
||||
Priority: 85 + minInt(contextLength/(1024*128), 10),
|
||||
Priority: 85 + minInt(contextLength/(1024*128), 10),
|
||||
})
|
||||
}
|
||||
return events, rows.Err()
|
||||
@@ -909,7 +924,8 @@ func loadPriceChangeEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
COALESCE(ph.new_input_price, 0),
|
||||
COALESCE(ph.old_output_price, 0),
|
||||
COALESCE(ph.new_output_price, 0),
|
||||
COALESCE(mp.country, 'unknown') AS provider_country
|
||||
COALESCE(mp.country, 'unknown') AS provider_country,
|
||||
ph.changed_at
|
||||
FROM pricing_history ph
|
||||
JOIN models m ON ph.model_id = m.id
|
||||
LEFT JOIN model_provider mp ON m.provider_id = mp.id
|
||||
@@ -936,6 +952,7 @@ func loadPriceChangeEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
oldOutputPrice float64
|
||||
newOutputPrice float64
|
||||
providerCountry string
|
||||
changedAt time.Time
|
||||
)
|
||||
if err := rows.Scan(
|
||||
&modelName,
|
||||
@@ -948,6 +965,7 @@ func loadPriceChangeEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
&oldOutputPrice,
|
||||
&newOutputPrice,
|
||||
&providerCountry,
|
||||
&changedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -967,32 +985,32 @@ func loadPriceChangeEvents(db *sql.DB, date string) ([]ModelEvent, error) {
|
||||
}
|
||||
|
||||
eventType := "price_increase"
|
||||
label := "价格上调"
|
||||
summary := "价格上调已足以影响默认成本,需要确认备用模型。"
|
||||
if changePct < 0 {
|
||||
eventType = "price_cut"
|
||||
label = "价格下调"
|
||||
summary = "价格下降已足以影响默认选型,值得重新评估同类模型。"
|
||||
}
|
||||
|
||||
events = append(events, ModelEvent{
|
||||
EventType: eventType,
|
||||
ModelName: modelName,
|
||||
ProviderName: providerName,
|
||||
OperatorName: operatorName,
|
||||
TrustLabel: buildTrustLabel(model),
|
||||
Baseline: fmt.Sprintf("较昨日 %+.0f%%", changePct),
|
||||
Summary: summary,
|
||||
Currency: currency,
|
||||
OldInputPrice: oldInputPrice,
|
||||
NewInputPrice: newInputPrice,
|
||||
OldOutputPrice: oldOutputPrice,
|
||||
NewOutputPrice: newOutputPrice,
|
||||
PriceChangePct: changePct,
|
||||
Priority: 70 + minInt(int(abs(changePct)), 25),
|
||||
EventType: eventType,
|
||||
ModelName: modelName,
|
||||
ProviderName: providerName,
|
||||
OperatorName: operatorName,
|
||||
TrustLabel: buildTrustLabel(model),
|
||||
SourceKindLabel: "价格快照",
|
||||
PrimarySource: "pricing_history",
|
||||
UpdatedAt: changedAt.Format("2006-01-02 15:04"),
|
||||
EvidenceDetail: buildPriceEvidenceDetail(changePct, oldInputPrice, newInputPrice, currency),
|
||||
Baseline: fmt.Sprintf("较昨日 %+.0f%%", changePct),
|
||||
Summary: summary,
|
||||
Currency: currency,
|
||||
OldInputPrice: oldInputPrice,
|
||||
NewInputPrice: newInputPrice,
|
||||
OldOutputPrice: oldOutputPrice,
|
||||
NewOutputPrice: newOutputPrice,
|
||||
PriceChangePct: changePct,
|
||||
Priority: 70 + minInt(int(abs(changePct)), 25),
|
||||
})
|
||||
|
||||
_ = label
|
||||
}
|
||||
return events, rows.Err()
|
||||
}
|
||||
@@ -1089,15 +1107,19 @@ func enrichModelEvents(r *ReportV3) []ModelEvent {
|
||||
}
|
||||
existing[key] = struct{}{}
|
||||
events = append(events, ModelEvent{
|
||||
EventType: "free_highlight",
|
||||
ModelName: model.Name,
|
||||
ProviderName: model.ProviderName,
|
||||
OperatorName: model.OperatorName,
|
||||
TrustLabel: buildTrustLabel(model),
|
||||
Baseline: "今日快照",
|
||||
Summary: buildModelEvidence(model),
|
||||
Currency: model.Currency,
|
||||
Priority: priority,
|
||||
EventType: "free_highlight",
|
||||
ModelName: model.Name,
|
||||
ProviderName: model.ProviderName,
|
||||
OperatorName: model.OperatorName,
|
||||
TrustLabel: buildTrustLabel(model),
|
||||
SourceKindLabel: "免费策略快照",
|
||||
PrimarySource: buildPrimarySource("free_snapshot", model.OperatorName),
|
||||
UpdatedAt: formatEventUpdatedAt(r.GeneratedAt, r.Date),
|
||||
EvidenceDetail: buildFreeEvidenceDetail(model),
|
||||
Baseline: "今日快照",
|
||||
Summary: buildModelEvidence(model),
|
||||
Currency: model.Currency,
|
||||
Priority: priority,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1311,11 +1333,15 @@ func buildHeadlineItemsFromEvents(events []ModelEvent) []HeadlineItem {
|
||||
|
||||
func headlineItemFromModelEvent(event ModelEvent) HeadlineItem {
|
||||
item := HeadlineItem{
|
||||
Title: event.ModelName,
|
||||
Summary: event.Summary,
|
||||
Baseline: event.Baseline,
|
||||
TrustLabel: event.TrustLabel,
|
||||
Tone: "neutral",
|
||||
Title: event.ModelName,
|
||||
Summary: event.Summary,
|
||||
Baseline: event.Baseline,
|
||||
TrustLabel: event.TrustLabel,
|
||||
SourceKindLabel: event.SourceKindLabel,
|
||||
PrimarySource: event.PrimarySource,
|
||||
UpdatedAt: event.UpdatedAt,
|
||||
EvidenceDetail: event.EvidenceDetail,
|
||||
Tone: "neutral",
|
||||
}
|
||||
|
||||
switch event.EventType {
|
||||
@@ -1343,6 +1369,58 @@ func headlineItemFromModelEvent(event ModelEvent) HeadlineItem {
|
||||
return item
|
||||
}
|
||||
|
||||
func buildPrimarySource(sourceKind, operatorName string) string {
|
||||
switch sourceKind {
|
||||
case "region_pricing":
|
||||
if operatorName == "" {
|
||||
return "region_pricing"
|
||||
}
|
||||
return operatorName + " / region_pricing"
|
||||
case "free_snapshot":
|
||||
if operatorName == "" {
|
||||
return "free snapshot"
|
||||
}
|
||||
return operatorName + " / free snapshot"
|
||||
default:
|
||||
return sourceKind
|
||||
}
|
||||
}
|
||||
|
||||
func buildPriceEvidenceDetail(changePct, oldPrice, newPrice float64, currency string) string {
|
||||
direction := "上涨"
|
||||
if changePct < 0 {
|
||||
direction = "下降"
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"pricing_history 记录到输入价格由 %s 调整为 %s,较昨日%s %.0f%%",
|
||||
formatPrice(oldPrice, currency),
|
||||
formatPrice(newPrice, currency),
|
||||
direction,
|
||||
abs(changePct),
|
||||
)
|
||||
}
|
||||
|
||||
func buildFreeEvidenceDetail(model ModelInfo) string {
|
||||
switch classifyFreeSource(model) {
|
||||
case "官方免费":
|
||||
return fmt.Sprintf("%s 当前快照显示为官方免费入口", model.OperatorName)
|
||||
case "聚合免费":
|
||||
return fmt.Sprintf("%s 当前快照显示为聚合免费入口", model.OperatorName)
|
||||
default:
|
||||
return fmt.Sprintf("%s 当前快照显示免费,但来源仍待确认", model.OperatorName)
|
||||
}
|
||||
}
|
||||
|
||||
func formatEventUpdatedAt(value, fallbackDate string) string {
|
||||
if strings.TrimSpace(value) != "" {
|
||||
return value
|
||||
}
|
||||
if fallbackDate != "" {
|
||||
return fallbackDate + " 00:00"
|
||||
}
|
||||
return "-"
|
||||
}
|
||||
|
||||
func buildActionItems(r *ReportV3) []ActionItem {
|
||||
var actions []ActionItem
|
||||
|
||||
@@ -1670,7 +1748,19 @@ func generateMarkdownV3(r *ReportV3, path string) error {
|
||||
for _, item := range r.HeadlineItems {
|
||||
fmt.Fprintf(f, "### %s · %s\n\n", item.Label, item.Title)
|
||||
fmt.Fprintf(f, "- 影响: %s\n", item.Summary)
|
||||
if item.SourceKindLabel != "" {
|
||||
fmt.Fprintf(f, "- 事件来源: %s\n", item.SourceKindLabel)
|
||||
}
|
||||
if item.PrimarySource != "" {
|
||||
fmt.Fprintf(f, "- 主来源: %s\n", item.PrimarySource)
|
||||
}
|
||||
if item.UpdatedAt != "" {
|
||||
fmt.Fprintf(f, "- 更新时间: %s\n", item.UpdatedAt)
|
||||
}
|
||||
fmt.Fprintf(f, "- 基线: %s\n", item.Baseline)
|
||||
if item.EvidenceDetail != "" {
|
||||
fmt.Fprintf(f, "- 判定依据: %s\n", item.EvidenceDetail)
|
||||
}
|
||||
fmt.Fprintf(f, "- 可信度: %s\n\n", item.TrustLabel)
|
||||
}
|
||||
|
||||
@@ -1984,11 +2074,26 @@ body {
|
||||
font-weight: 700;
|
||||
}
|
||||
.trust-line,
|
||||
.baseline-line {
|
||||
.baseline-line,
|
||||
.source-line {
|
||||
margin-top: 8px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
.evidence-block {
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed var(--line);
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
}
|
||||
.evidence-item {
|
||||
font-size: 0.92rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
.evidence-item strong {
|
||||
color: var(--ink);
|
||||
}
|
||||
.scene-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@@ -2167,8 +2272,14 @@ th {
|
||||
<div class="card-kicker">{{.Label}}</div>
|
||||
<div class="card-title">{{.Title}}</div>
|
||||
<div class="card-summary">{{.Summary}}</div>
|
||||
{{if .SourceKindLabel}}<div class="source-line">事件来源:{{.SourceKindLabel}}</div>{{end}}
|
||||
<div class="baseline-line">基线:{{.Baseline}}</div>
|
||||
<div class="trust-line">可信度:{{.TrustLabel}}</div>
|
||||
<div class="evidence-block">
|
||||
{{if .PrimarySource}}<div class="evidence-item"><strong>主来源</strong>:{{.PrimarySource}}</div>{{end}}
|
||||
{{if .UpdatedAt}}<div class="evidence-item"><strong>更新时间</strong>:{{.UpdatedAt}}</div>{{end}}
|
||||
{{if .EvidenceDetail}}<div class="evidence-item"><strong>判定依据</strong>:{{.EvidenceDetail}}</div>{{end}}
|
||||
</div>
|
||||
</article>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
@@ -184,25 +184,33 @@ func TestDecorateReportV1BuildsHotDaySummary(t *testing.T) {
|
||||
report := sampleReportForV1()
|
||||
report.ModelEvents = []ModelEvent{
|
||||
{
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
Priority: 95,
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: "OpenRouter / region_pricing",
|
||||
UpdatedAt: "2026-05-13 09:30",
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
Priority: 95,
|
||||
},
|
||||
{
|
||||
EventType: "price_cut",
|
||||
ModelName: "qwen-vl-max",
|
||||
ProviderName: "Alibaba",
|
||||
OperatorName: "DashScope",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 -18%",
|
||||
Summary: "价格下降已足以影响视觉模型默认选择。",
|
||||
PriceChangePct: -18,
|
||||
Priority: 90,
|
||||
EventType: "price_cut",
|
||||
ModelName: "qwen-vl-max",
|
||||
ProviderName: "Alibaba",
|
||||
OperatorName: "DashScope",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 -18%",
|
||||
Summary: "价格下降已足以影响视觉模型默认选择。",
|
||||
SourceKindLabel: "价格快照",
|
||||
PrimarySource: "pricing_history",
|
||||
UpdatedAt: "2026-05-13 10:00",
|
||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 18%",
|
||||
PriceChangePct: -18,
|
||||
Priority: 90,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -277,6 +285,22 @@ func TestGenerateMarkdownV3IncludesTencentSubscriptionSection(t *testing.T) {
|
||||
ModelPreview: "hy3-preview",
|
||||
},
|
||||
}
|
||||
report.ModelEvents = []ModelEvent{
|
||||
{
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: "OpenRouter / region_pricing",
|
||||
UpdatedAt: "2026-05-13 09:30",
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
Priority: 95,
|
||||
},
|
||||
}
|
||||
decorateReportV1(report)
|
||||
|
||||
if err := generateMarkdownV3(report, path); err != nil {
|
||||
@@ -295,6 +319,9 @@ func TestGenerateMarkdownV3IncludesTencentSubscriptionSection(t *testing.T) {
|
||||
"## 今日变化",
|
||||
"## 场景推荐",
|
||||
"## 完整数据附录",
|
||||
"主来源: OpenRouter / region_pricing",
|
||||
"更新时间: 2026-05-13 09:30",
|
||||
"判定依据: models.created_at = 今日,且已存在最新价格快照",
|
||||
"## 💳 腾讯云套餐订阅价",
|
||||
"通用 Token Plan Lite",
|
||||
"Hy Token Plan Max",
|
||||
@@ -313,14 +340,18 @@ func TestGenerateHTMLV3IncludesTencentSubscriptionSection(t *testing.T) {
|
||||
report := sampleReportForV1()
|
||||
report.ModelEvents = []ModelEvent{
|
||||
{
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
Priority: 95,
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: "OpenRouter / region_pricing",
|
||||
UpdatedAt: "2026-05-13 09:30",
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
Priority: 95,
|
||||
},
|
||||
}
|
||||
report.TencentSubscriptionPlans = []SubscriptionPlanInfo{
|
||||
@@ -354,6 +385,10 @@ func TestGenerateHTMLV3IncludesTencentSubscriptionSection(t *testing.T) {
|
||||
"今日头条",
|
||||
"DeepSeek-V4-Flash",
|
||||
"首次出现",
|
||||
"主来源",
|
||||
"更新时间",
|
||||
"判定依据",
|
||||
"模型快照",
|
||||
"场景推荐",
|
||||
"完整数据附录",
|
||||
"官方免费",
|
||||
@@ -371,25 +406,33 @@ func TestBuildHeadlineItemsUsesModelEvents(t *testing.T) {
|
||||
report := sampleReportForV1()
|
||||
report.ModelEvents = []ModelEvent{
|
||||
{
|
||||
EventType: "price_cut",
|
||||
ModelName: "glm-5",
|
||||
ProviderName: "Zhipu",
|
||||
OperatorName: "Zhipu",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 -25%",
|
||||
Summary: "价格下降已足以影响中文通用场景默认选型。",
|
||||
PriceChangePct: -25,
|
||||
Priority: 100,
|
||||
EventType: "price_cut",
|
||||
ModelName: "glm-5",
|
||||
ProviderName: "Zhipu",
|
||||
OperatorName: "Zhipu",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 -25%",
|
||||
Summary: "价格下降已足以影响中文通用场景默认选型。",
|
||||
SourceKindLabel: "价格快照",
|
||||
PrimarySource: "pricing_history",
|
||||
UpdatedAt: "2026-05-13 10:00",
|
||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 25%",
|
||||
PriceChangePct: -25,
|
||||
Priority: 100,
|
||||
},
|
||||
{
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
Priority: 90,
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
ProviderName: "DeepSeek",
|
||||
OperatorName: "OpenRouter",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池,值得重新评估低成本编码默认选择。",
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: "OpenRouter / region_pricing",
|
||||
UpdatedAt: "2026-05-13 09:30",
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
Priority: 90,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -404,39 +447,54 @@ func TestBuildHeadlineItemsUsesModelEvents(t *testing.T) {
|
||||
if items[0].Baseline != "较昨日 -25%" {
|
||||
t.Fatalf("expected event baseline to be preserved, got %+v", items[0])
|
||||
}
|
||||
if items[0].SourceKindLabel != "价格快照" || items[0].PrimarySource != "pricing_history" {
|
||||
t.Fatalf("expected event evidence fields to be preserved, got %+v", items[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildHeadlineItemsDeduplicatesSameModel(t *testing.T) {
|
||||
report := sampleReportForV1()
|
||||
report.ModelEvents = []ModelEvent{
|
||||
{
|
||||
EventType: "price_cut",
|
||||
ModelName: "OpenAI: GPT-4o",
|
||||
ProviderName: "OpenAI",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 -20%",
|
||||
Summary: "价格下降影响默认成本。",
|
||||
PriceChangePct: -20,
|
||||
Priority: 95,
|
||||
EventType: "price_cut",
|
||||
ModelName: "OpenAI: GPT-4o",
|
||||
ProviderName: "OpenAI",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 -20%",
|
||||
Summary: "价格下降影响默认成本。",
|
||||
SourceKindLabel: "价格快照",
|
||||
PrimarySource: "pricing_history",
|
||||
UpdatedAt: "2026-05-13 10:00",
|
||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日下降 20%",
|
||||
PriceChangePct: -20,
|
||||
Priority: 95,
|
||||
},
|
||||
{
|
||||
EventType: "price_increase",
|
||||
ModelName: "OpenAI: GPT-4o",
|
||||
ProviderName: "OpenAI",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 +5%",
|
||||
Summary: "同日另有上调记录。",
|
||||
PriceChangePct: 5,
|
||||
Priority: 80,
|
||||
EventType: "price_increase",
|
||||
ModelName: "OpenAI: GPT-4o",
|
||||
ProviderName: "OpenAI",
|
||||
TrustLabel: "官方来源",
|
||||
Baseline: "较昨日 +5%",
|
||||
Summary: "同日另有上调记录。",
|
||||
SourceKindLabel: "价格快照",
|
||||
PrimarySource: "pricing_history",
|
||||
UpdatedAt: "2026-05-13 11:00",
|
||||
EvidenceDetail: "pricing_history 记录到输入价格较昨日上涨 5%",
|
||||
PriceChangePct: 5,
|
||||
Priority: 80,
|
||||
},
|
||||
{
|
||||
EventType: "new_model",
|
||||
ModelName: "Claude Opus 4.7",
|
||||
ProviderName: "Anthropic",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型上线。",
|
||||
Priority: 70,
|
||||
EventType: "new_model",
|
||||
ModelName: "Claude Opus 4.7",
|
||||
ProviderName: "Anthropic",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型上线。",
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: "OpenRouter / region_pricing",
|
||||
UpdatedAt: "2026-05-13 09:00",
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
Priority: 70,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -449,3 +507,30 @@ func TestBuildHeadlineItemsDeduplicatesSameModel(t *testing.T) {
|
||||
t.Fatalf("expected duplicate model event to be removed, got %+v", items)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeadlineItemFromModelEventIncludesEvidenceFields(t *testing.T) {
|
||||
item := headlineItemFromModelEvent(ModelEvent{
|
||||
EventType: "new_model",
|
||||
ModelName: "DeepSeek-V4-Flash",
|
||||
TrustLabel: "聚合来源",
|
||||
Baseline: "首次出现",
|
||||
Summary: "新模型进入情报池。",
|
||||
SourceKindLabel: "模型快照",
|
||||
PrimarySource: "OpenRouter / region_pricing",
|
||||
UpdatedAt: "2026-05-13 09:30",
|
||||
EvidenceDetail: "models.created_at = 今日,且已存在最新价格快照",
|
||||
})
|
||||
|
||||
if item.SourceKindLabel != "模型快照" {
|
||||
t.Fatalf("expected source kind label to be propagated, got %+v", item)
|
||||
}
|
||||
if item.PrimarySource != "OpenRouter / region_pricing" {
|
||||
t.Fatalf("expected primary source to be propagated, got %+v", item)
|
||||
}
|
||||
if item.UpdatedAt != "2026-05-13 09:30" {
|
||||
t.Fatalf("expected updated at to be propagated, got %+v", item)
|
||||
}
|
||||
if item.EvidenceDetail == "" {
|
||||
t.Fatalf("expected evidence detail to be populated, got %+v", item)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user